// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. //! Description of the steps of the rendering process, and the images used as input or output. //! //! # Render passes and framebuffers //! //! There are two concepts in Vulkan: //! //! - A *render pass* describes the overall process of drawing a frame. It is subdivided into one //! or more subpasses. //! - A *framebuffer* contains the list of image views that are attached during the drawing of //! each subpass. //! //! Render passes are typically created at initialization only (for example during a loading //! screen) because they can be costly, while framebuffers can be created and destroyed either at //! initialization or during the frame. //! //! Consequently you can create graphics pipelines from a render pass object alone. //! A `Framebuffer` object is only needed when you actually add draw commands to a command buffer. pub use self::{ create::RenderPassCreationError, framebuffer::{Framebuffer, FramebufferCreateInfo, FramebufferCreationError}, }; use crate::{ device::{Device, DeviceOwned}, format::Format, image::{ImageAspects, ImageLayout, SampleCount}, macros::{impl_id_counter, vulkan_bitflags_enum, vulkan_enum}, shader::ShaderInterface, sync::{AccessFlags, DependencyFlags, PipelineStages}, Version, VulkanObject, }; use std::{cmp::max, mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc}; #[macro_use] mod macros; mod create; mod framebuffer; /// 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. /// /// ``` /// use vulkano::render_pass::{RenderPass, RenderPassCreateInfo, SubpassDescription}; /// /// # let device: std::sync::Arc = return; /// let render_pass = RenderPass::new( /// device.clone(), /// RenderPassCreateInfo { /// subpasses: vec![SubpassDescription::default()], /// ..Default::default() /// }, /// ).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 = 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::R8G8B8A8_UNORM, /// 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 #[derive(Debug)] pub struct RenderPass { handle: ash::vk::RenderPass, device: Arc, id: NonZeroU64, attachments: Vec, subpasses: Vec, dependencies: Vec, correlated_view_masks: Vec, attachment_uses: Vec>, granularity: [u32; 2], views_used: u32, } impl RenderPass { /// Creates a new `RenderPass`. /// /// # Panics /// /// - Panics if `create_info.subpasses` is empty. /// - Panics if any element of `create_info.attachments` has a `format` of `None`. pub fn new( device: Arc, mut create_info: RenderPassCreateInfo, ) -> Result, RenderPassCreationError> { Self::validate(&device, &mut create_info)?; let handle = unsafe { if device.api_version() >= Version::V1_2 || device.enabled_extensions().khr_create_renderpass2 { Self::create_v2(&device, &create_info)? } else { Self::create_v1(&device, &create_info)? } }; unsafe { Ok(Self::from_handle(device, handle, create_info)) } } /// 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, ) -> Result, RenderPassCreationError> { RenderPass::new( device, RenderPassCreateInfo { subpasses: vec![SubpassDescription::default()], ..Default::default() }, ) } /// Creates a new `RenderPass` from a raw object handle. /// /// # Safety /// /// - `handle` must be a valid Vulkan object handle created from `device`. /// - `create_info` must match the info used to create the object. #[inline] pub unsafe fn from_handle( device: Arc, handle: ash::vk::RenderPass, create_info: RenderPassCreateInfo, ) -> Arc { let RenderPassCreateInfo { attachments, subpasses, dependencies, correlated_view_masks, _ne: _, } = create_info; let granularity = Self::get_granularity(&device, handle); let mut attachment_uses: Vec> = vec![None; attachments.len()]; let mut views_used = 0; for (index, subpass_desc) in subpasses.iter().enumerate() { let index = index as u32; let &SubpassDescription { view_mask, ref input_attachments, ref color_attachments, ref resolve_attachments, ref depth_stencil_attachment, .. } = subpass_desc; for atch_ref in (input_attachments.iter().flatten()) .chain(color_attachments.iter().flatten()) .chain(resolve_attachments.iter().flatten()) .chain(depth_stencil_attachment.iter()) { match &mut attachment_uses[atch_ref.attachment as usize] { Some(attachment_use) => attachment_use.last_use_subpass = index, attachment_use @ None => { *attachment_use = Some(AttachmentUse { first_use_subpass: index, last_use_subpass: index, }) } } } views_used = max(views_used, u32::BITS - view_mask.leading_zeros()); } Arc::new(RenderPass { handle, device, id: Self::next_id(), attachments, subpasses, dependencies, correlated_view_masks, attachment_uses, granularity, views_used, }) } unsafe fn get_granularity(device: &Arc, handle: ash::vk::RenderPass) -> [u32; 2] { let fns = device.fns(); let mut out = MaybeUninit::uninit(); (fns.v1_0.get_render_area_granularity)(device.handle(), handle, out.as_mut_ptr()); let out = out.assume_init(); debug_assert_ne!(out.width, 0); debug_assert_ne!(out.height, 0); [out.width, out.height] } /// Returns the attachments of the render pass. #[inline] pub fn attachments(&self) -> &[AttachmentDescription] { &self.attachments } /// Returns the subpasses of the render pass. #[inline] pub fn subpasses(&self) -> &[SubpassDescription] { &self.subpasses } /// Returns the dependencies of the render pass. #[inline] pub fn dependencies(&self) -> &[SubpassDependency] { &self.dependencies } /// Returns the correlated view masks of the render pass. #[inline] pub fn correlated_view_masks(&self) -> &[u32] { &self.correlated_view_masks } /// If the render pass has multiview enabled, returns the number of views used by the render /// pass. Returns 0 if multiview is not enabled. #[inline] pub fn views_used(&self) -> u32 { self.views_used } /// 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. #[inline] pub fn granularity(&self) -> [u32; 2] { self.granularity } /// Returns the first subpass of the render pass. #[inline] pub fn first_subpass(self: Arc) -> Subpass { Subpass { render_pass: self, subpass_id: 0, // Guaranteed to exist } } /// Returns `true` if this render pass is compatible with the other render pass, /// as defined in the [`Render Pass Compatibility` section of the Vulkan specs](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap8.html#renderpass-compatibility). pub fn is_compatible_with(&self, other: &RenderPass) -> bool { if self == other { return true; } let Self { handle: _, device: _, id: _, attachments: attachments1, subpasses: subpasses1, dependencies: dependencies1, correlated_view_masks: correlated_view_masks1, attachment_uses: _, granularity: _, views_used: _, } = self; let Self { handle: _, device: _, id: _, attachments: attachments2, subpasses: subpasses2, dependencies: dependencies2, attachment_uses: _, correlated_view_masks: correlated_view_masks2, granularity: _, views_used: _, } = other; if attachments1.len() != attachments2.len() { return false; } if !attachments1 .iter() .zip(attachments2) .all(|(attachment_desc1, attachment_desc2)| { let AttachmentDescription { format: format1, samples: samples1, load_op: _, store_op: _, stencil_load_op: _, stencil_store_op: _, initial_layout: _, final_layout: _, _ne: _, } = attachment_desc1; let AttachmentDescription { format: format2, samples: samples2, load_op: _, store_op: _, stencil_load_op: _, stencil_store_op: _, initial_layout: _, final_layout: _, _ne: _, } = attachment_desc2; format1 == format2 && samples1 == samples2 }) { return false; } let are_atch_refs_compatible = |atch_ref1, atch_ref2| match (atch_ref1, atch_ref2) { (None, None) => true, (Some(atch_ref1), Some(atch_ref2)) => { let &AttachmentReference { attachment: attachment1, layout: _, aspects: aspects1, _ne: _, } = atch_ref1; let AttachmentDescription { format: format1, samples: samples1, load_op: _, store_op: _, stencil_load_op: _, stencil_store_op: _, initial_layout: _, final_layout: _, _ne: _, } = &attachments1[attachment1 as usize]; let &AttachmentReference { attachment: attachment2, layout: _, aspects: aspects2, _ne: _, } = atch_ref2; let AttachmentDescription { format: format2, samples: samples2, load_op: _, store_op: _, stencil_load_op: _, stencil_store_op: _, initial_layout: _, final_layout: _, _ne: _, } = &attachments2[attachment2 as usize]; format1 == format2 && samples1 == samples2 && aspects1 == aspects2 } _ => false, }; if subpasses1.len() != subpasses2.len() { return false; } if !(subpasses1.iter()) .zip(subpasses2.iter()) .all(|(subpass1, subpass2)| { let SubpassDescription { view_mask: view_mask1, input_attachments: input_attachments1, color_attachments: color_attachments1, resolve_attachments: resolve_attachments1, depth_stencil_attachment: depth_stencil_attachment1, preserve_attachments: _, _ne: _, } = subpass1; let SubpassDescription { view_mask: view_mask2, input_attachments: input_attachments2, color_attachments: color_attachments2, resolve_attachments: resolve_attachments2, depth_stencil_attachment: depth_stencil_attachment2, preserve_attachments: _, _ne: _, } = subpass2; if !(0..max(input_attachments1.len(), input_attachments2.len())).all(|i| { are_atch_refs_compatible( input_attachments1.get(i).and_then(|x| x.as_ref()), input_attachments2.get(i).and_then(|x| x.as_ref()), ) }) { return false; } if !(0..max(color_attachments1.len(), color_attachments2.len())).all(|i| { are_atch_refs_compatible( color_attachments1.get(i).and_then(|x| x.as_ref()), color_attachments2.get(i).and_then(|x| x.as_ref()), ) }) { return false; } if subpasses1.len() > 1 && !(0..max(resolve_attachments1.len(), resolve_attachments2.len())).all(|i| { are_atch_refs_compatible( resolve_attachments1.get(i).and_then(|x| x.as_ref()), resolve_attachments2.get(i).and_then(|x| x.as_ref()), ) }) { return false; } if !are_atch_refs_compatible( depth_stencil_attachment1.as_ref(), depth_stencil_attachment2.as_ref(), ) { return false; } if view_mask1 != view_mask2 { return false; } true }) { return false; } if dependencies1 != dependencies2 { return false; } if correlated_view_masks1 != correlated_view_masks2 { return false; } true } /// Returns `true` if the subpass of this description is compatible with the shader's fragment /// output definition. pub fn is_compatible_with_shader( &self, subpass: u32, shader_interface: &ShaderInterface, ) -> bool { let subpass_descr = match self.subpasses.get(subpass as usize) { Some(s) => s, None => return false, }; for element in shader_interface.elements() { assert!(!element.ty.is_64bit); // TODO: implement let location_range = element.location..element.location + element.ty.num_locations(); for location in location_range { let attachment_id = match subpass_descr.color_attachments.get(location as usize) { Some(Some(atch_ref)) => atch_ref.attachment, _ => return false, }; let _attachment_desc = &self.attachments[attachment_id as usize]; // FIXME: compare formats depending on the number of components and data type /*if attachment_desc.format != element.format { return false; }*/ } } true } } impl Drop for RenderPass { #[inline] fn drop(&mut self) { unsafe { let fns = self.device.fns(); (fns.v1_0.destroy_render_pass)(self.device.handle(), self.handle, ptr::null()); } } } unsafe impl VulkanObject for RenderPass { type Handle = ash::vk::RenderPass; #[inline] fn handle(&self) -> Self::Handle { self.handle } } unsafe impl DeviceOwned for RenderPass { #[inline] fn device(&self) -> &Arc { &self.device } } impl_id_counter!(RenderPass); /// 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, subpass_id: u32, } impl Subpass { /// Returns a handle that represents a subpass of a render pass. #[inline] pub fn from(render_pass: Arc, id: u32) -> Option { if (id as usize) < render_pass.subpasses().len() { Some(Subpass { render_pass, subpass_id: id, }) } else { None } } /// Returns the render pass of this subpass. #[inline] pub fn render_pass(&self) -> &Arc { &self.render_pass } /// Returns the index of this subpass within the renderpass. #[inline] pub fn index(&self) -> u32 { self.subpass_id } /// Returns the subpass description for this subpass. #[inline] pub fn subpass_desc(&self) -> &SubpassDescription { &self.render_pass.subpasses()[self.subpass_id as usize] } /// Returns whether this subpass is the last one in the render pass. If `true` is returned, /// calling `next_subpass` will panic. #[inline] pub fn is_last_subpass(&self) -> bool { self.subpass_id as usize == self.render_pass.subpasses().len() - 1 } /// Advances to the next subpass after this one. /// /// # Panics /// /// - Panics if there are no more render passes. #[inline] pub fn next_subpass(&mut self) { let next_id = self.subpass_id + 1; assert!((next_id as usize) < self.render_pass.subpasses().len()); self.subpass_id = next_id; } #[inline] fn attachment_desc(&self, atch_num: u32) -> &AttachmentDescription { &self.render_pass.attachments()[atch_num as usize] } /// 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_attachment { Some(atch_ref) => atch_ref.attachment, None => return false, }; self.attachment_desc(atch_num) .format .map_or(false, |f| f.aspects().intersects(ImageAspects::DEPTH)) } /// Returns true if the subpass has a depth attachment or a depth-stencil attachment whose /// layout does not have a read-only depth layout. #[inline] pub fn has_writable_depth(&self) -> bool { let subpass_desc = self.subpass_desc(); let atch_num = match &subpass_desc.depth_stencil_attachment { Some(atch_ref) => { if matches!( atch_ref.layout, ImageLayout::DepthStencilReadOnlyOptimal | ImageLayout::DepthReadOnlyStencilAttachmentOptimal ) { return false; } atch_ref.attachment } None => return false, }; self.attachment_desc(atch_num) .format .map_or(false, |f| f.aspects().intersects(ImageAspects::DEPTH)) } /// 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_attachment { Some(atch_ref) => atch_ref.attachment, None => return false, }; self.attachment_desc(atch_num) .format .map_or(false, |f| f.aspects().intersects(ImageAspects::STENCIL)) } /// Returns true if the subpass has a stencil attachment or a depth-stencil attachment whose /// layout does not have a read-only stencil layout. #[inline] pub fn has_writable_stencil(&self) -> bool { let subpass_desc = self.subpass_desc(); let atch_num = match &subpass_desc.depth_stencil_attachment { Some(atch_ref) => { if matches!( atch_ref.layout, ImageLayout::DepthStencilReadOnlyOptimal | ImageLayout::DepthAttachmentStencilReadOnlyOptimal ) { return false; } atch_ref.attachment } None => return false, }; self.attachment_desc(atch_num) .format .map_or(false, |f| f.aspects().intersects(ImageAspects::STENCIL)) } /// 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 { let subpass_desc = self.subpass_desc(); // TODO: chain input attachments as well? subpass_desc .color_attachments .iter() .flatten() .chain(subpass_desc.depth_stencil_attachment.iter()) .filter_map(|atch_ref| { self.render_pass .attachments() .get(atch_ref.attachment as usize) }) .next() .map(|atch_desc| atch_desc.samples) } /// Returns `true` if this subpass is compatible with the fragment output definition. // TODO: return proper error #[inline] pub fn is_compatible_with(&self, shader_interface: &ShaderInterface) -> bool { self.render_pass .is_compatible_with_shader(self.subpass_id, shader_interface) } pub(crate) fn load_op(&self, attachment_index: u32) -> Option { self.render_pass.attachment_uses[attachment_index as usize] .as_ref() .and_then(|attachment_use| { (attachment_use.first_use_subpass == self.subpass_id) .then(|| self.render_pass.attachments[attachment_index as usize].load_op) }) } pub(crate) fn store_op(&self, attachment_index: u32) -> Option { self.render_pass.attachment_uses[attachment_index as usize] .as_ref() .and_then(|attachment_use| { (attachment_use.last_use_subpass == self.subpass_id) .then(|| self.render_pass.attachments[attachment_index as usize].store_op) }) } pub(crate) fn stencil_load_op(&self, attachment_index: u32) -> Option { self.render_pass.attachment_uses[attachment_index as usize] .as_ref() .and_then(|attachment_use| { (attachment_use.first_use_subpass == self.subpass_id).then(|| { self.render_pass.attachments[attachment_index as usize].stencil_load_op }) }) } pub(crate) fn stencil_store_op(&self, attachment_index: u32) -> Option { self.render_pass.attachment_uses[attachment_index as usize] .as_ref() .and_then(|attachment_use| { (attachment_use.last_use_subpass == self.subpass_id).then(|| { self.render_pass.attachments[attachment_index as usize].stencil_store_op }) }) } } impl From for (Arc, u32) { #[inline] fn from(value: Subpass) -> (Arc, u32) { (value.render_pass, value.subpass_id) } } /// Parameters to create a new `RenderPass`. #[derive(Clone, Debug)] pub struct RenderPassCreateInfo { /// The attachments available for the render pass. /// /// The default value is empty. pub attachments: Vec, /// The subpasses that make up this render pass. /// /// A render pass must contain at least one subpass. /// /// The default value is empty, which must be overridden. pub subpasses: Vec, /// The dependencies between subpasses. /// /// The default value is empty. pub dependencies: Vec, /// If multiview rendering is being used (the subpasses have a nonzero `view_mask`), /// this specifies sets of views that may be more efficient to render concurrently, for example /// because they show the same geometry from almost the same perspective. This is an /// optimization hint to the implementation, and does not affect the final result. /// /// The value is a bitmask, so that that for example `0b11` means that the first two views are /// highly correlated, and `0b101` means the first and third view are highly correlated. Each /// view bit must appear in at most one element of the list. /// /// If multiview rendering is not being used, the value must be empty. /// /// The default value is empty. pub correlated_view_masks: Vec, pub _ne: crate::NonExhaustive, } impl Default for RenderPassCreateInfo { #[inline] fn default() -> Self { Self { attachments: Vec::new(), subpasses: Vec::new(), dependencies: Vec::new(), correlated_view_masks: Vec::new(), _ne: crate::NonExhaustive(()), } } } /// Describes an attachment that will be used in a render pass. #[derive(Clone, Copy, Debug)] pub struct AttachmentDescription { /// The format of the image that is going to be bound. /// /// The default value is `None`, which must be overridden. pub format: Option, /// The number of samples of the image that is going to be bound. /// /// The default value is [`SampleCount::Sample1`]. pub samples: SampleCount, /// What the implementation should do with the attachment at the start of the subpass that first /// uses it. /// /// The default value is [`LoadOp::DontCare`]. pub load_op: LoadOp, /// What the implementation should do with the attachment at the end of the subpass that last /// uses it. /// /// The default value is [`StoreOp::DontCare`]. pub store_op: StoreOp, /// The equivalent of `load_op` for the stencil component of the attachment, if any. Irrelevant /// if there is no stencil component. /// /// The default value is [`LoadOp::DontCare`]. pub stencil_load_op: LoadOp, /// The equivalent of `store_op` for the stencil component of the attachment, if any. Irrelevant /// if there is no stencil component. /// /// The default value is [`StoreOp::DontCare`]. pub stencil_store_op: StoreOp, /// The layout that the image must in at the start of the render pass. /// /// The vulkano library will automatically switch to the correct layout if necessary, but it /// is more efficient to set this to the correct value. /// /// The default value is [`ImageLayout::Undefined`], which must be overridden. pub initial_layout: ImageLayout, /// The layout that the image will be transitioned to at the end of the render pass. /// /// The default value is [`ImageLayout::Undefined`], which must be overridden. pub final_layout: ImageLayout, pub _ne: crate::NonExhaustive, } impl Default for AttachmentDescription { #[inline] fn default() -> Self { Self { format: None, samples: SampleCount::Sample1, load_op: LoadOp::DontCare, store_op: StoreOp::DontCare, stencil_load_op: LoadOp::DontCare, stencil_store_op: StoreOp::DontCare, initial_layout: ImageLayout::Undefined, final_layout: ImageLayout::Undefined, _ne: crate::NonExhaustive(()), } } } /// Describes one of the subpasses of a render pass. /// /// A subpass can use zero or more attachments of various types. Attachment types of which there can /// be multiple are listed in a `Vec` in this structure. The index in these `Vec`s corresponds to /// the index used for that attachment type in the shader. /// /// If a particular index is not used in the shader, it can be set to `None` in this structure. /// This is useful if an unused index needs to be skipped but a higher index needs to be specified. /// /// If an attachment is used more than once, i.e. a given `AttachmentReference::attachment` occurs /// more than once in the `SubpassDescription`, then their `AttachmentReference::layout` must be /// the same as well. #[derive(Debug, Clone)] pub struct SubpassDescription { /// If not `0`, enables multiview rendering, and specifies the view indices that are rendered to /// in this subpass. The value is a bitmask, so that that for example `0b11` will draw to the /// first two views and `0b101` will draw to the first and third view. /// /// If set to a nonzero value, it must be nonzero for all subpasses in the render pass, and the /// [`multiview`](crate::device::Features::multiview) feature must be enabled on the device. /// /// The default value is `0`. pub view_mask: u32, /// The attachments of the render pass that are to be used as input attachments in this subpass. /// /// If an attachment is used here for the first time in this render pass, and it's is not also /// used as a color or depth/stencil attachment in this subpass, then the attachment's `load_op` /// must not be [`LoadOp::Clear`]. /// /// The default value is empty. pub input_attachments: Vec>, /// The attachments of the render pass that are to be used as color attachments in this subpass. /// /// The number of color attachments must be less than the /// [`max_color_attachments`](crate::device::Properties::max_color_attachments) limit of the /// physical device. All color attachments must have the same `samples` value. /// /// The default value is empty. pub color_attachments: Vec>, /// The attachments of the render pass that are to be used as resolve attachments in this /// subpass. /// /// This list must either be empty or have the same length as `color_attachments`. If it's not /// empty, then each resolve attachment is paired with the color attachment of the same index. /// The resolve attachments must all have a `samples` value of [`SampleCount::Sample1`], while /// the color attachments must have a `samples` value other than [`SampleCount::Sample1`]. /// Each resolve attachment must have the same `format` as the corresponding color attachment. /// /// The default value is empty. pub resolve_attachments: Vec>, /// The single attachment of the render pass that is to be used as depth-stencil attachment in /// this subpass. /// /// If set to `Some`, the referenced attachment must have the same `samples` value as those in /// `color_attachments`. /// /// The default value is `None`. pub depth_stencil_attachment: Option, /// The indices of attachments of the render pass that will be preserved during this subpass. /// /// The referenced attachments must not be used as any other attachment type in the subpass. /// /// The default value is empty. pub preserve_attachments: Vec, pub _ne: crate::NonExhaustive, } impl Default for SubpassDescription { #[inline] fn default() -> Self { Self { view_mask: 0, color_attachments: Vec::new(), depth_stencil_attachment: None, input_attachments: Vec::new(), resolve_attachments: Vec::new(), preserve_attachments: Vec::new(), _ne: crate::NonExhaustive(()), } } } /// A reference in a subpass description to a particular attachment of the render pass. #[derive(Clone, Debug)] pub struct AttachmentReference { /// The number of the attachment being referred to. /// /// The default value is `0`. pub attachment: u32, /// The image layout that the attachment should be transitioned to at the start of the subpass. /// /// The layout is restricted by the type of attachment that an attachment is being used as. A /// full listing of allowed layouts per type can be found in /// [the Vulkan specification](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap8.html#attachment-type-imagelayout). /// /// The default value is [`ImageLayout::Undefined`], which must be overridden. pub layout: ImageLayout, /// For references to input attachments, the aspects of the image that should be selected. /// For attachment types other than input attachments, the value must be empty. /// /// If empty, all aspects available in the input attachment's `format` will be selected. /// If any fields are set, they must be aspects that are available in the `format` of the /// attachment. /// /// If the value is neither empty nor identical to the aspects of the `format`, the device API /// version must be at least 1.1, or either the /// [`khr_create_renderpass2`](crate::device::DeviceExtensions::khr_create_renderpass2) or the /// [`khr_maintenance2`](crate::device::DeviceExtensions::khr_maintenance2) extensions must be /// enabled on the device. /// /// The default value is [`ImageAspects::empty()`]. pub aspects: ImageAspects, pub _ne: crate::NonExhaustive, } impl Default for AttachmentReference { #[inline] fn default() -> Self { Self { attachment: 0, layout: ImageLayout::Undefined, aspects: ImageAspects::empty(), _ne: crate::NonExhaustive(()), } } } /// A dependency between two subpasses of a render pass. /// /// The implementation is allowed to change the order of the subpasses within a render pass, unless /// you specify that there exists a dependency between two subpasses (ie. the result of one will be /// used as the input of another one). Subpass dependencies work similar to pipeline barriers, /// except that they operate on whole subpasses instead of individual images. /// /// If `src_subpass` and `dst_subpass` are equal, then this specifies a /// [subpass self-dependency](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-pipeline-barriers-subpass-self-dependencies). /// The `src_stages` must all be /// [logically earlier in the pipeline](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-pipeline-stages-order) /// than the `dst_stages`, and if they both contain a /// [framebuffer-space stage](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-framebuffer-regions), /// then `by_region` must be activated. /// /// If `src_subpass` or `dst_subpass` are set to `None`, this specifies an external /// dependency. An external dependency specifies a dependency on commands that were submitted before /// the render pass instance began (for `src_subpass`), or on commands that will be submitted /// after the render pass instance ends (for `dst_subpass`). The values must not both be /// `None`. #[derive(Clone, Debug, PartialEq, Eq)] pub struct SubpassDependency { /// The index of the subpass that writes the data that `dst_subpass` is going to use. /// /// `None` specifies an external dependency. /// /// The default value is `None`. pub src_subpass: Option, /// The index of the subpass that reads the data that `src_subpass` wrote. /// /// `None` specifies an external dependency. /// /// The default value is `None`. pub dst_subpass: Option, /// The pipeline stages that must be finished on `src_subpass` before the /// `dst_stages` of `dst_subpass` can start. /// /// The default value is [`PipelineStages::empty()`]. pub src_stages: PipelineStages, /// The pipeline stages of `dst_subpass` that must wait for the `src_stages` of /// `src_subpass` to be finished. Stages that are earlier than the stages specified here can /// start before the `src_stages` are finished. /// /// The default value is [`PipelineStages::empty()`]. pub dst_stages: PipelineStages, /// The way `src_subpass` accesses the attachments on which we depend. /// /// The default value is [`AccessFlags::empty()`]. pub src_access: AccessFlags, /// The way `dst_subpass` accesses the attachments on which we depend. /// /// The default value is [`AccessFlags::empty()`]. pub dst_access: AccessFlags, /// Dependency flags that modify behavior of the subpass dependency. /// /// If a `src_subpass` equals `dst_subpass`, then: /// - If `src_stages` and `dst_stages` both contain framebuffer-space stages, /// this must include [`BY_REGION`]. /// - If the subpass's `view_mask` has more than one view, /// this must include [`VIEW_LOCAL`]. /// /// The default value is [`DependencyFlags::empty()`]. /// /// [`BY_REGION`]: crate::sync::DependencyFlags::BY_REGION /// [`VIEW_LOCAL`]: crate::sync::DependencyFlags::VIEW_LOCAL pub dependency_flags: DependencyFlags, /// If multiview rendering is being used (the subpasses have a nonzero `view_mask`), and /// `dependency_flags` includes [`VIEW_LOCAL`], specifies an offset relative to the view index /// of `dst_subpass`: each view `d` in `dst_subpass` depends on view `d + view_offset` in /// `src_subpass`. If the source view index does not exist, the dependency is ignored for /// that view. /// /// If `dependency_flags` does not include [`VIEW_LOCAL`], or if `src_subpass` and /// `dst_subpass` are the same, the value must be `0`. /// /// The default value is `0`. /// /// [`VIEW_LOCAL`]: crate::sync::DependencyFlags::VIEW_LOCAL pub view_offset: i32, pub _ne: crate::NonExhaustive, } impl Default for SubpassDependency { #[inline] fn default() -> Self { Self { src_subpass: None, dst_subpass: None, src_stages: PipelineStages::empty(), dst_stages: PipelineStages::empty(), src_access: AccessFlags::empty(), dst_access: AccessFlags::empty(), dependency_flags: DependencyFlags::empty(), view_offset: 0, _ne: crate::NonExhaustive(()), } } } vulkan_enum! { #[non_exhaustive] /// Describes what the implementation should do with an attachment at the start of the subpass. LoadOp = AttachmentLoadOp(i32); /// The content of the attachment will be loaded from memory. This is what you want if you want /// to draw over something existing. /// /// While this is the most intuitive option, it is also the slowest because it uses a lot of /// memory bandwidth. Load = LOAD, /// The content of the attachment will be filled by the implementation with a uniform value /// that you must provide when you start drawing. /// /// This is what you usually use at the start of a frame, in order to reset the content of /// the color, depth and/or stencil buffers. Clear = CLEAR, /// The attachment will have undefined content. /// /// This is what you should use for attachments that you intend to entirely cover with draw /// commands. /// If you are going to fill the attachment with a uniform value, it is better to use `Clear` /// instead. DontCare = DONT_CARE, /* TODO: enable // TODO: document None = NONE_EXT { device_extensions: [ext_load_store_op_none], },*/ } vulkan_enum! { #[non_exhaustive] /// Describes what the implementation should do with an attachment after all the subpasses have /// completed. StoreOp = AttachmentStoreOp(i32); /// The attachment will be stored. This is what you usually want. /// /// While this is the most intuitive option, it is also slower than `DontCare` because it can /// take time to write the data back to memory. Store = STORE, /// What happens is implementation-specific. /// /// This is purely an optimization compared to `Store`. The implementation doesn't need to copy /// from the internal cache to the memory, which saves memory bandwidth. /// /// This doesn't mean that the data won't be copied, as an implementation is also free to not /// use a cache and write the output directly in memory. In other words, the content of the /// image will be undefined. DontCare = DONT_CARE, /* TODO: enable // TODO: document None = NONE { api_version: V1_3, device_extensions: [khr_dynamic_rendering, ext_load_store_op_none, qcom_render_pass_store_ops], },*/ } vulkan_bitflags_enum! { #[non_exhaustive] /// A set of [`ResolveMode`] values. ResolveModes, /// Possible resolve modes for attachments. ResolveMode, = ResolveModeFlags(u32); /// The resolved sample is taken from sample number zero, the other samples are ignored. /// /// This mode is supported for depth and stencil formats, and for color images with an integer /// format. SAMPLE_ZERO, SampleZero = SAMPLE_ZERO, /// The resolved sample is calculated from the average of the samples. /// /// This mode is supported for depth formats, and for color images with a non-integer format. AVERAGE, Average = AVERAGE, /// The resolved sample is calculated from the minimum of the samples. /// /// This mode is supported for depth and stencil formats only. MIN, Min = MIN, /// The resolved sample is calculated from the maximum of the samples. /// /// This mode is supported for depth and stencil formats only. MAX, Max = MAX, } #[derive(Clone, Copy, Debug)] pub(crate) struct AttachmentUse { first_use_subpass: u32, last_use_subpass: u32, } #[cfg(test)] mod tests { use crate::{ format::Format, render_pass::{RenderPass, 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, attachments: { a1: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, a2: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, a3: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, a4: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, a5: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, a6: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, a7: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, a8: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, a9: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, a10: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, }, pass: { color: [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10], depth_stencil: {}, }, ); match rp { Err(RenderPassCreationError::SubpassMaxColorAttachmentsExceeded { .. }) => (), _ => panic!(), } } #[test] fn non_zero_granularity() { let (device, _) = gfx_dev_and_queue!(); let rp = single_pass_renderpass!( device, attachments: { a: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, }, pass: { color: [a], depth_stencil: {}, }, ) .unwrap(); let granularity = rp.granularity(); assert_ne!(granularity[0], 0); assert_ne!(granularity[1], 0); } }