diff options
Diffstat (limited to 'src/render_pass/render_pass.rs')
-rw-r--r-- | src/render_pass/render_pass.rs | 911 |
1 files changed, 911 insertions, 0 deletions
diff --git a/src/render_pass/render_pass.rs b/src/render_pass/render_pass.rs new file mode 100644 index 0000000..629dc8e --- /dev/null +++ b/src/render_pass/render_pass.rs @@ -0,0 +1,911 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use crate::check_errors; +use crate::device::Device; +use crate::device::DeviceOwned; +use crate::format::FormatTy; +use crate::image::ImageLayout; +use crate::image::SampleCount; +use crate::pipeline::shader::ShaderInterface; +use crate::render_pass::AttachmentDesc; +use crate::render_pass::LoadOp; +use crate::render_pass::RenderPassDesc; +use crate::render_pass::SubpassDesc; +use crate::Error; +use crate::OomError; +use crate::VulkanObject; +use smallvec::SmallVec; +use std::error; +use std::fmt; +use std::marker::PhantomData; +use std::mem::MaybeUninit; +use std::ptr; +use std::sync::Arc; +use std::sync::Mutex; + +/// An object representing the discrete steps in which rendering is done. +/// +/// A render pass in Vulkan is made up of three parts: +/// - A list of attachments, which are image views that are inputs, outputs or intermediate stages +/// in the rendering process. +/// - One or more subpasses, which are the steps in which the rendering process, takes place, +/// and the attachments that are used for each step. +/// - Dependencies, which describe how the input and output data of each subpass is to be passed +/// from one subpass to the next. +/// +/// In order to create a render pass, you must create a `RenderPassDesc` object that describes the +/// render pass, then pass it to `RenderPass::new`. +/// +/// ``` +/// use vulkano::render_pass::RenderPass; +/// use vulkano::render_pass::RenderPassDesc; +/// +/// # let device: std::sync::Arc<vulkano::device::Device> = return; +/// let desc = RenderPassDesc::empty(); +/// let render_pass = RenderPass::new(device.clone(), desc).unwrap(); +/// ``` +/// +/// This example creates a render pass with no attachment and one single subpass that doesn't draw +/// on anything. While it's sometimes useful, most of the time it's not what you want. +/// +/// The easiest way to create a "real" render pass is to use the `single_pass_renderpass!` macro. +/// +/// ``` +/// # #[macro_use] extern crate vulkano; +/// # fn main() { +/// # let device: std::sync::Arc<vulkano::device::Device> = return; +/// use vulkano::format::Format; +/// +/// let render_pass = single_pass_renderpass!(device.clone(), +/// attachments: { +/// // `foo` is a custom name we give to the first and only attachment. +/// foo: { +/// load: Clear, +/// store: Store, +/// format: Format::R8G8B8A8Unorm, +/// samples: 1, +/// } +/// }, +/// pass: { +/// color: [foo], // Repeat the attachment name here. +/// depth_stencil: {} +/// } +/// ).unwrap(); +/// # } +/// ``` +/// +/// See the documentation of the macro for more details. TODO: put link here +pub struct RenderPass { + // The internal Vulkan object. + render_pass: ash::vk::RenderPass, + + // Device this render pass was created from. + device: Arc<Device>, + + // Description of the render pass. + desc: RenderPassDesc, + + // Cache of the granularity of the render pass. + granularity: Mutex<Option<[u32; 2]>>, +} + +impl RenderPass { + /// Builds a new render pass. + /// + /// # Panic + /// + /// - Can panic if it detects some violations in the restrictions. Only inexpensive checks are + /// performed. `debug_assert!` is used, so some restrictions are only checked in debug + /// mode. + /// + pub fn new( + device: Arc<Device>, + description: RenderPassDesc, + ) -> Result<RenderPass, RenderPassCreationError> { + let fns = device.fns(); + + // If the first use of an attachment in this render pass is as an input attachment, and + // the attachment is not also used as a color or depth/stencil attachment in the same + // subpass, then loadOp must not be VK_ATTACHMENT_LOAD_OP_CLEAR + debug_assert!(description.attachments().into_iter().enumerate().all( + |(atch_num, attachment)| { + if attachment.load != LoadOp::Clear { + return true; + } + + for p in description.subpasses() { + if p.color_attachments + .iter() + .find(|&&(a, _)| a == atch_num) + .is_some() + { + return true; + } + if let Some((a, _)) = p.depth_stencil { + if a == atch_num { + return true; + } + } + if p.input_attachments + .iter() + .find(|&&(a, _)| a == atch_num) + .is_some() + { + return false; + } + } + + true + } + )); + + let attachments = description + .attachments() + .iter() + .map(|attachment| { + ash::vk::AttachmentDescription { + flags: ash::vk::AttachmentDescriptionFlags::empty(), // FIXME: may alias flag + format: attachment.format.into(), + samples: attachment.samples.into(), + load_op: attachment.load.into(), + store_op: attachment.store.into(), + stencil_load_op: attachment.stencil_load.into(), + stencil_store_op: attachment.stencil_store.into(), + initial_layout: attachment.initial_layout.into(), + final_layout: attachment.final_layout.into(), + } + }) + .collect::<SmallVec<[_; 16]>>(); + + // We need to pass pointers to vkAttachmentReference structs when creating the render pass. + // Therefore we need to allocate them in advance. + // + // This block allocates, for each pass, in order, all color attachment references, then all + // input attachment references, then all resolve attachment references, then the depth + // stencil attachment reference. + let attachment_references = description + .subpasses() + .iter() + .flat_map(|pass| { + // Performing some validation with debug asserts. + debug_assert!( + pass.resolve_attachments.is_empty() + || pass.resolve_attachments.len() == pass.color_attachments.len() + ); + debug_assert!(pass + .resolve_attachments + .iter() + .all(|a| attachments[a.0].samples == ash::vk::SampleCountFlags::TYPE_1)); + debug_assert!( + pass.resolve_attachments.is_empty() + || pass + .color_attachments + .iter() + .all(|a| attachments[a.0].samples.as_raw() > 1) + ); + debug_assert!( + pass.resolve_attachments.is_empty() + || pass + .resolve_attachments + .iter() + .zip(pass.color_attachments.iter()) + .all(|(r, c)| { attachments[r.0].format == attachments[c.0].format }) + ); + debug_assert!(pass + .color_attachments + .iter() + .cloned() + .chain(pass.depth_stencil.clone().into_iter()) + .chain(pass.input_attachments.iter().cloned()) + .chain(pass.resolve_attachments.iter().cloned()) + .all(|(a, _)| { + pass.preserve_attachments + .iter() + .find(|&&b| a == b) + .is_none() + })); + debug_assert!(pass + .color_attachments + .iter() + .cloned() + .chain(pass.depth_stencil.clone().into_iter()) + .all(|(atch, layout)| { + if let Some(r) = pass.input_attachments.iter().find(|r| r.0 == atch) { + r.1 == layout + } else { + true + } + })); + + let resolve = pass.resolve_attachments.iter().map(|&(offset, img_la)| { + debug_assert!(offset < attachments.len()); + ash::vk::AttachmentReference { + attachment: offset as u32, + layout: img_la.into(), + } + }); + + let color = pass.color_attachments.iter().map(|&(offset, img_la)| { + debug_assert!(offset < attachments.len()); + ash::vk::AttachmentReference { + attachment: offset as u32, + layout: img_la.into(), + } + }); + + let input = pass.input_attachments.iter().map(|&(offset, img_la)| { + debug_assert!(offset < attachments.len()); + ash::vk::AttachmentReference { + attachment: offset as u32, + layout: img_la.into(), + } + }); + + let depthstencil = if let Some((offset, img_la)) = pass.depth_stencil { + Some(ash::vk::AttachmentReference { + attachment: offset as u32, + layout: img_la.into(), + }) + } else { + None + } + .into_iter(); + + color.chain(input).chain(resolve).chain(depthstencil) + }) + .collect::<SmallVec<[_; 16]>>(); + + // Same as `attachment_references` but only for the preserve attachments. + // This is separate because attachment references are u32s and not `vkAttachmentReference` + // structs. + let preserve_attachments_references = description + .subpasses() + .iter() + .flat_map(|pass| { + pass.preserve_attachments + .iter() + .map(|&offset| offset as u32) + }) + .collect::<SmallVec<[_; 16]>>(); + + // Now iterating over passes. + let passes = unsafe { + // `ref_index` and `preserve_ref_index` are increased during the loop and point to the + // next element to use in respectively `attachment_references` and + // `preserve_attachments_references`. + let mut ref_index = 0usize; + let mut preserve_ref_index = 0usize; + let mut out: SmallVec<[_; 16]> = SmallVec::new(); + + for pass in description.subpasses() { + if pass.color_attachments.len() as u32 + > device + .physical_device() + .properties() + .max_color_attachments + { + return Err(RenderPassCreationError::ColorAttachmentsLimitExceeded); + } + + let color_attachments = attachment_references.as_ptr().offset(ref_index as isize); + ref_index += pass.color_attachments.len(); + let input_attachments = attachment_references.as_ptr().offset(ref_index as isize); + ref_index += pass.input_attachments.len(); + let resolve_attachments = attachment_references.as_ptr().offset(ref_index as isize); + ref_index += pass.resolve_attachments.len(); + let depth_stencil = if pass.depth_stencil.is_some() { + let a = attachment_references.as_ptr().offset(ref_index as isize); + ref_index += 1; + a + } else { + ptr::null() + }; + + let preserve_attachments = preserve_attachments_references + .as_ptr() + .offset(preserve_ref_index as isize); + preserve_ref_index += pass.preserve_attachments.len(); + + out.push(ash::vk::SubpassDescription { + flags: ash::vk::SubpassDescriptionFlags::empty(), + pipeline_bind_point: ash::vk::PipelineBindPoint::GRAPHICS, + input_attachment_count: pass.input_attachments.len() as u32, + p_input_attachments: if pass.input_attachments.is_empty() { + ptr::null() + } else { + input_attachments + }, + color_attachment_count: pass.color_attachments.len() as u32, + p_color_attachments: if pass.color_attachments.is_empty() { + ptr::null() + } else { + color_attachments + }, + p_resolve_attachments: if pass.resolve_attachments.is_empty() { + ptr::null() + } else { + resolve_attachments + }, + p_depth_stencil_attachment: depth_stencil, + preserve_attachment_count: pass.preserve_attachments.len() as u32, + p_preserve_attachments: if pass.preserve_attachments.is_empty() { + ptr::null() + } else { + preserve_attachments + }, + }); + } + + assert!(!out.is_empty()); + // If these assertions fails, there's a serious bug in the code above ^. + debug_assert!(ref_index == attachment_references.len()); + debug_assert!(preserve_ref_index == preserve_attachments_references.len()); + + out + }; + + let dependencies = description + .dependencies() + .iter() + .map(|dependency| { + debug_assert!( + dependency.source_subpass as u32 == ash::vk::SUBPASS_EXTERNAL + || dependency.source_subpass < passes.len() + ); + debug_assert!( + dependency.destination_subpass as u32 == ash::vk::SUBPASS_EXTERNAL + || dependency.destination_subpass < passes.len() + ); + + ash::vk::SubpassDependency { + src_subpass: dependency.source_subpass as u32, + dst_subpass: dependency.destination_subpass as u32, + src_stage_mask: dependency.source_stages.into(), + dst_stage_mask: dependency.destination_stages.into(), + src_access_mask: dependency.source_access.into(), + dst_access_mask: dependency.destination_access.into(), + dependency_flags: if dependency.by_region { + ash::vk::DependencyFlags::BY_REGION + } else { + ash::vk::DependencyFlags::empty() + }, + } + }) + .collect::<SmallVec<[_; 16]>>(); + + let multiview_create_info = match description.multiview() { + Some(multiview) => { + debug_assert!(device.enabled_features().multiview); + debug_assert!( + device + .physical_device() + .properties() + .max_multiview_view_count + .unwrap_or(0) + >= multiview.used_layer_count() + ); + + // each subpass must have a corresponding view mask + // or there are no view masks at all (which is probably a bug because + // nothing will get drawn) + debug_assert!( + multiview.view_masks.len() == passes.len() || multiview.view_masks.is_empty() + ); + + // either all subpasses must have a non-zero view mask or all must be zero + // (multiview is considered to be disabled when all view masks are zero) + debug_assert!( + multiview.view_masks.iter().all(|&mask| mask != 0) + || multiview.view_masks.iter().all(|&mask| mask == 0) + ); + + // one view offset for each dependency + // or no view offsets at all + debug_assert!( + dependencies.len() == multiview.view_offsets.len() + || multiview.view_offsets.is_empty() + ); + + // VUID-VkRenderPassCreateInfo-pNext-02512 + debug_assert!(dependencies.iter().zip(&multiview.view_offsets).all( + |(dependency, &view_offset)| dependency + .dependency_flags + .contains(ash::vk::DependencyFlags::VIEW_LOCAL) + || view_offset == 0 + )); + + // VUID-VkRenderPassCreateInfo-pNext-02514 + debug_assert!( + multiview.view_masks.iter().any(|&view_mask| view_mask != 0) + || dependencies.iter().all(|dependency| !dependency + .dependency_flags + .contains(ash::vk::DependencyFlags::VIEW_LOCAL)) + ); + + // VUID-VkRenderPassCreateInfo-pNext-02515 + debug_assert!( + multiview.view_masks.iter().any(|&view_mask| view_mask != 0) + || multiview.correlation_masks.is_empty() + ); + + // VUID-VkRenderPassMultiviewCreateInfo-pCorrelationMasks-00841 + // ensure that each view index is contained in at most one correlation mask + // by checking for any overlap in all pairs of correlation masks + debug_assert!(multiview + .correlation_masks + .iter() + .enumerate() + .all(|(i, &mask)| multiview.correlation_masks[i + 1..] + .iter() + .all(|&other_mask| other_mask & mask == 0))); + + ash::vk::RenderPassMultiviewCreateInfo { + subpass_count: passes.len() as u32, + p_view_masks: multiview.view_masks.as_ptr(), + dependency_count: dependencies.len() as u32, + p_view_offsets: multiview.view_offsets.as_ptr(), + correlation_mask_count: multiview.correlation_masks.len() as u32, + p_correlation_masks: multiview.correlation_masks.as_ptr(), + ..Default::default() + } + } + None => ash::vk::RenderPassMultiviewCreateInfo::default(), + }; + + let render_pass = unsafe { + let infos = ash::vk::RenderPassCreateInfo { + p_next: if description.multiview().is_none() { + ptr::null() + } else { + &multiview_create_info as *const _ as _ + }, + flags: ash::vk::RenderPassCreateFlags::empty(), + attachment_count: attachments.len() as u32, + p_attachments: if attachments.is_empty() { + ptr::null() + } else { + attachments.as_ptr() + }, + subpass_count: passes.len() as u32, + p_subpasses: if passes.is_empty() { + ptr::null() + } else { + passes.as_ptr() + }, + dependency_count: dependencies.len() as u32, + p_dependencies: if dependencies.is_empty() { + ptr::null() + } else { + dependencies.as_ptr() + }, + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + check_errors(fns.v1_0.create_render_pass( + device.internal_object(), + &infos, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(RenderPass { + device: device.clone(), + render_pass, + desc: description, + granularity: Mutex::new(None), + }) + } + + /// Builds a render pass with one subpass and no attachment. + /// + /// This method is useful for quick tests. + #[inline] + pub fn empty_single_pass(device: Arc<Device>) -> Result<RenderPass, RenderPassCreationError> { + RenderPass::new(device, RenderPassDesc::empty()) + } + + #[inline] + pub fn inner(&self) -> RenderPassSys { + RenderPassSys(self.render_pass, PhantomData) + } + + /// Returns the granularity of this render pass. + /// + /// If the render area of a render pass in a command buffer is a multiple of this granularity, + /// then the performance will be optimal. Performances are always optimal for render areas + /// that cover the whole framebuffer. + pub fn granularity(&self) -> [u32; 2] { + let mut granularity = self.granularity.lock().unwrap(); + + if let Some(&granularity) = granularity.as_ref() { + return granularity; + } + + unsafe { + let fns = self.device.fns(); + let mut out = MaybeUninit::uninit(); + fns.v1_0.get_render_area_granularity( + self.device.internal_object(), + self.render_pass, + out.as_mut_ptr(), + ); + + let out = out.assume_init(); + debug_assert_ne!(out.width, 0); + debug_assert_ne!(out.height, 0); + let gran = [out.width, out.height]; + *granularity = Some(gran); + gran + } + } + + /// Returns the description of the render pass. + #[inline] + pub fn desc(&self) -> &RenderPassDesc { + &self.desc + } +} + +unsafe impl DeviceOwned for RenderPass { + #[inline] + fn device(&self) -> &Arc<Device> { + &self.device + } +} + +impl fmt::Debug for RenderPass { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fmt.debug_struct("RenderPass") + .field("raw", &self.render_pass) + .field("device", &self.device) + .field("desc", &self.desc) + .finish() + } +} + +impl Drop for RenderPass { + #[inline] + fn drop(&mut self) { + unsafe { + let fns = self.device.fns(); + fns.v1_0.destroy_render_pass( + self.device.internal_object(), + self.render_pass, + ptr::null(), + ); + } + } +} + +/// Opaque object that represents the render pass' internals. +#[derive(Debug, Copy, Clone)] +pub struct RenderPassSys<'a>(ash::vk::RenderPass, PhantomData<&'a ()>); + +unsafe impl<'a> VulkanObject for RenderPassSys<'a> { + type Object = ash::vk::RenderPass; + + #[inline] + fn internal_object(&self) -> ash::vk::RenderPass { + self.0 + } +} + +/// Error that can happen when creating a compute pipeline. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RenderPassCreationError { + /// Not enough memory. + OomError(OomError), + /// The maximum number of color attachments has been exceeded. + ColorAttachmentsLimitExceeded, +} + +impl error::Error for RenderPassCreationError { + #[inline] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + RenderPassCreationError::OomError(ref err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for RenderPassCreationError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + fmt, + "{}", + match *self { + RenderPassCreationError::OomError(_) => "not enough memory available", + RenderPassCreationError::ColorAttachmentsLimitExceeded => { + "the maximum number of color attachments has been exceeded" + } + } + ) + } +} + +impl From<OomError> for RenderPassCreationError { + #[inline] + fn from(err: OomError) -> RenderPassCreationError { + RenderPassCreationError::OomError(err) + } +} + +impl From<Error> for RenderPassCreationError { + #[inline] + fn from(err: Error) -> RenderPassCreationError { + match err { + err @ Error::OutOfHostMemory => RenderPassCreationError::OomError(OomError::from(err)), + err @ Error::OutOfDeviceMemory => { + RenderPassCreationError::OomError(OomError::from(err)) + } + _ => panic!("unexpected error: {:?}", err), + } + } +} + +/// Represents a subpass within a `RenderPass` object. +/// +/// This struct doesn't correspond to anything in Vulkan. It is simply an equivalent to a +/// tuple of a render pass and subpass index. Contrary to a tuple, however, the existence of the +/// subpass is checked when the object is created. When you have a `Subpass` you are guaranteed +/// that the given subpass does exist. +#[derive(Debug, Clone)] +pub struct Subpass { + render_pass: Arc<RenderPass>, + subpass_id: u32, +} + +impl Subpass { + /// Returns a handle that represents a subpass of a render pass. + #[inline] + pub fn from(render_pass: Arc<RenderPass>, id: u32) -> Option<Subpass> { + if (id as usize) < render_pass.desc().subpasses().len() { + Some(Subpass { + render_pass, + subpass_id: id, + }) + } else { + None + } + } + + #[inline] + fn subpass_desc(&self) -> &SubpassDesc { + &self.render_pass.desc().subpasses()[self.subpass_id as usize] + } + + #[inline] + fn attachment_desc(&self, atch_num: usize) -> &AttachmentDesc { + &self.render_pass.desc().attachments()[atch_num] + } + + /// Returns the number of color attachments in this subpass. + #[inline] + pub fn num_color_attachments(&self) -> u32 { + self.subpass_desc().color_attachments.len() as u32 + } + + /// Returns true if the subpass has a depth attachment or a depth-stencil attachment. + #[inline] + pub fn has_depth(&self) -> bool { + let subpass_desc = self.subpass_desc(); + let atch_num = match subpass_desc.depth_stencil { + Some((d, _)) => d, + None => return false, + }; + + match self.attachment_desc(atch_num).format.ty() { + FormatTy::Depth => true, + FormatTy::Stencil => false, + FormatTy::DepthStencil => true, + _ => unreachable!(), + } + } + + /// Returns true if the subpass has a depth attachment or a depth-stencil attachment whose + /// layout is not `DepthStencilReadOnlyOptimal`. + #[inline] + pub fn has_writable_depth(&self) -> bool { + let subpass_desc = self.subpass_desc(); + let atch_num = match subpass_desc.depth_stencil { + Some((d, l)) => { + if l == ImageLayout::DepthStencilReadOnlyOptimal { + return false; + } + d + } + None => return false, + }; + + match self.attachment_desc(atch_num).format.ty() { + FormatTy::Depth => true, + FormatTy::Stencil => false, + FormatTy::DepthStencil => true, + _ => unreachable!(), + } + } + + /// Returns true if the subpass has a stencil attachment or a depth-stencil attachment. + #[inline] + pub fn has_stencil(&self) -> bool { + let subpass_desc = self.subpass_desc(); + let atch_num = match subpass_desc.depth_stencil { + Some((d, _)) => d, + None => return false, + }; + + match self.attachment_desc(atch_num).format.ty() { + FormatTy::Depth => false, + FormatTy::Stencil => true, + FormatTy::DepthStencil => true, + _ => unreachable!(), + } + } + + /// Returns true if the subpass has a stencil attachment or a depth-stencil attachment whose + /// layout is not `DepthStencilReadOnlyOptimal`. + #[inline] + pub fn has_writable_stencil(&self) -> bool { + let subpass_desc = self.subpass_desc(); + + let atch_num = match subpass_desc.depth_stencil { + Some((d, l)) => { + if l == ImageLayout::DepthStencilReadOnlyOptimal { + return false; + } + d + } + None => return false, + }; + + match self.attachment_desc(atch_num).format.ty() { + FormatTy::Depth => false, + FormatTy::Stencil => true, + FormatTy::DepthStencil => true, + _ => unreachable!(), + } + } + + /// Returns true if the subpass has any color or depth/stencil attachment. + #[inline] + pub fn has_color_or_depth_stencil_attachment(&self) -> bool { + if self.num_color_attachments() >= 1 { + return true; + } + + let subpass_desc = self.subpass_desc(); + match subpass_desc.depth_stencil { + Some((d, _)) => true, + None => false, + } + } + + /// Returns the number of samples in the color and/or depth/stencil attachments. Returns `None` + /// if there is no such attachment in this subpass. + #[inline] + pub fn num_samples(&self) -> Option<SampleCount> { + let subpass_desc = self.subpass_desc(); + + // TODO: chain input attachments as well? + subpass_desc + .color_attachments + .iter() + .cloned() + .chain(subpass_desc.depth_stencil.clone().into_iter()) + .filter_map(|a| self.render_pass.desc().attachments().get(a.0)) + .next() + .map(|a| a.samples) + } + + /// Returns the render pass of this subpass. + #[inline] + pub fn render_pass(&self) -> &Arc<RenderPass> { + &self.render_pass + } + + /// Returns the index of this subpass within the renderpass. + #[inline] + pub fn index(&self) -> u32 { + self.subpass_id + } + + /// Returns `true` if this subpass is compatible with the fragment output definition. + // TODO: return proper error + pub fn is_compatible_with(&self, shader_interface: &ShaderInterface) -> bool { + self.render_pass + .desc() + .is_compatible_with_shader(self.subpass_id, shader_interface) + } +} + +impl From<Subpass> for (Arc<RenderPass>, u32) { + #[inline] + fn from(value: Subpass) -> (Arc<RenderPass>, u32) { + (value.render_pass, value.subpass_id) + } +} + +#[cfg(test)] +mod tests { + use crate::format::Format; + use crate::render_pass::RenderPass; + use crate::render_pass::RenderPassCreationError; + + #[test] + fn empty() { + let (device, _) = gfx_dev_and_queue!(); + let _ = RenderPass::empty_single_pass(device).unwrap(); + } + + #[test] + fn too_many_color_atch() { + let (device, _) = gfx_dev_and_queue!(); + + if device + .physical_device() + .properties() + .max_color_attachments + >= 10 + { + return; // test ignored + } + + let rp = single_pass_renderpass! { + device.clone(), + attachments: { + a1: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, + a2: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, + a3: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, + a4: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, + a5: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, + a6: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, + a7: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, + a8: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, + a9: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, + a10: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, } + }, + pass: { + color: [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10], + depth_stencil: {} + } + }; + + match rp { + Err(RenderPassCreationError::ColorAttachmentsLimitExceeded) => (), + _ => panic!(), + } + } + + #[test] + fn non_zero_granularity() { + let (device, _) = gfx_dev_and_queue!(); + + let rp = single_pass_renderpass! { + device.clone(), + attachments: { + a: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, } + }, + pass: { + color: [a], + depth_stencil: {} + } + } + .unwrap(); + + let granularity = rp.granularity(); + assert_ne!(granularity[0], 0); + assert_ne!(granularity[1], 0); + } +} |