// 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. use crate::format::ClearValue; use crate::format::Format; use crate::image::ImageLayout; use crate::image::SampleCount; use crate::pipeline::shader::ShaderInterface; use crate::sync::AccessFlags; use crate::sync::PipelineStages; /// The description of a render pass. #[derive(Clone, Debug)] pub struct RenderPassDesc { attachments: Vec, subpasses: Vec, dependencies: Vec, multiview: Option, } impl RenderPassDesc { /// Creates a description of a render pass. pub fn new( attachments: Vec, subpasses: Vec, dependencies: Vec, ) -> RenderPassDesc { RenderPassDesc { attachments, subpasses, dependencies, multiview: None, } } /// Creates a description of a render pass that uses the multiview feature. /// See [`MultiviewDesc`] for an explanation of possible configuration options. pub fn with_multiview( attachments: Vec, subpasses: Vec, dependencies: Vec, multiview: MultiviewDesc, ) -> RenderPassDesc { RenderPassDesc { attachments, subpasses, dependencies, multiview: Some(multiview), } } /// Creates a description of an empty render pass, with one subpass and no attachments. pub fn empty() -> RenderPassDesc { RenderPassDesc { attachments: vec![], subpasses: vec![SubpassDesc { color_attachments: vec![], depth_stencil: None, input_attachments: vec![], resolve_attachments: vec![], preserve_attachments: vec![], }], dependencies: vec![], multiview: None, } } // Returns the attachments of the description. #[inline] pub fn attachments(&self) -> &[AttachmentDesc] { &self.attachments } // Returns the subpasses of the description. #[inline] pub fn subpasses(&self) -> &[SubpassDesc] { &self.subpasses } // Returns the dependencies of the description. #[inline] pub fn dependencies(&self) -> &[SubpassDependencyDesc] { &self.dependencies } // Returns the multiview configuration of the description. #[inline] pub fn multiview(&self) -> &Option { &self.multiview } /// Decodes `I` into a list of clear values where each element corresponds /// to an attachment. The size of the returned iterator must be the same as the number of /// attachments. /// /// When the user enters a render pass, they need to pass a list of clear values to apply to /// the attachments of the framebuffer. This method is then responsible for checking the /// correctness of these values and turning them into a list that can be processed by vulkano. /// /// The format of the clear value **must** match the format of the attachment. Attachments /// that are not loaded with `LoadOp::Clear` must have an entry equal to `ClearValue::None`. pub fn convert_clear_values(&self, values: I) -> impl Iterator where I: IntoIterator, { // FIXME: safety checks values.into_iter() } /// 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 pass_descr = match self.subpasses.get(subpass as usize) { Some(s) => s, None => return false, }; for element in shader_interface.elements() { for location in element.location.clone() { let attachment_id = match pass_descr.color_attachments.get(location as usize) { Some(a) => a.0, None => return false, }; let attachment_desc = &self.attachments[attachment_id]; // FIXME: compare formats depending on the number of components and data type /*if attachment_desc.format != element.format { return false; }*/ } } true } /// Returns `true` if this description is compatible with the other description, /// as defined in the `Render Pass Compatibility` section of the Vulkan specs. // TODO: return proper error pub fn is_compatible_with_desc(&self, other: &RenderPassDesc) -> bool { if self.attachments().len() != other.attachments().len() { return false; } for (my_atch, other_atch) in self.attachments.iter().zip(other.attachments.iter()) { if !my_atch.is_compatible_with(&other_atch) { return false; } } return true; // FIXME: finish } } impl Default for RenderPassDesc { fn default() -> Self { Self::empty() } } /// Describes an attachment that will be used in a render pass. #[derive(Debug, Clone, Copy)] pub struct AttachmentDesc { /// Format of the image that is going to be bound. pub format: Format, /// Number of samples of the image that is going to be bound. pub samples: SampleCount, /// What the implementation should do with that attachment at the start of the render pass. pub load: LoadOp, /// What the implementation should do with that attachment at the end of the render pass. pub store: StoreOp, /// Equivalent of `load` for the stencil component of the attachment, if any. Irrelevant if /// there is no stencil component. pub stencil_load: LoadOp, /// Equivalent of `store` for the stencil component of the attachment, if any. Irrelevant if /// there is no stencil component. pub stencil_store: StoreOp, /// Layout that the image is going to be in at the start of the renderpass. /// /// The vulkano library will automatically switch to the correct layout if necessary, but it /// is more efficient to set this to the correct value. pub initial_layout: ImageLayout, /// Layout that the image will be transitioned to at the end of the renderpass. pub final_layout: ImageLayout, } impl AttachmentDesc { /// Returns true if this attachment is compatible with another attachment, as defined in the /// `Render Pass Compatibility` section of the Vulkan specs. #[inline] pub fn is_compatible_with(&self, other: &AttachmentDesc) -> bool { self.format == other.format && self.samples == other.samples } } /// Describes one of the subpasses of a render pass. /// /// # Restrictions /// /// All these restrictions are checked when the `RenderPass` object is created. /// TODO: that's not the case ^ /// /// - The number of color attachments must be less than the limit of the physical device. /// - All the attachments in `color_attachments` and `depth_stencil` must have the same /// samples count. /// - If any attachment is used as both an input attachment and a color or /// depth/stencil attachment, then each use must use the same layout. /// - Elements of `preserve_attachments` must not be used in any of the other members. /// - If `resolve_attachments` is not empty, then all the resolve attachments must be attachments /// with 1 sample and all the color attachments must have more than 1 sample. /// - If `resolve_attachments` is not empty, all the resolve attachments must have the same format /// as the color attachments. /// - If the first use of an attachment in this renderpass is as an input attachment and the /// attachment is not also used as a color or depth/stencil attachment in the same subpass, /// then the loading operation must not be `Clear`. /// // TODO: add tests for all these restrictions // TODO: allow unused attachments (for example attachment 0 and 2 are used, 1 is unused) #[derive(Debug, Clone)] pub struct SubpassDesc { /// Indices and layouts of attachments to use as color attachments. pub color_attachments: Vec<(usize, ImageLayout)>, // TODO: Vec is slow /// Index and layout of the attachment to use as depth-stencil attachment. pub depth_stencil: Option<(usize, ImageLayout)>, /// Indices and layouts of attachments to use as input attachments. pub input_attachments: Vec<(usize, ImageLayout)>, // TODO: Vec is slow /// If not empty, each color attachment will be resolved into each corresponding entry of /// this list. /// /// If this value is not empty, it **must** be the same length as `color_attachments`. pub resolve_attachments: Vec<(usize, ImageLayout)>, // TODO: Vec is slow /// Indices of attachments that will be preserved during this pass. pub preserve_attachments: Vec, // TODO: Vec is slow } /// Describes 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). #[derive(Debug, Clone, Copy)] pub struct SubpassDependencyDesc { /// Index of the subpass that writes the data that `destination_subpass` is going to use. pub source_subpass: usize, /// Index of the subpass that reads the data that `source_subpass` wrote. pub destination_subpass: usize, /// The pipeline stages that must be finished on the previous subpass before the destination /// subpass can start. pub source_stages: PipelineStages, /// The pipeline stages of the destination subpass that must wait for the source to be finished. /// Stages that are earlier of the stages specified here can start before the source is /// finished. pub destination_stages: PipelineStages, /// The way the source subpass accesses the attachments on which we depend. pub source_access: AccessFlags, /// The way the destination subpass accesses the attachments on which we depend. pub destination_access: AccessFlags, /// If false, then the whole subpass must be finished for the next one to start. If true, then /// the implementation can start the new subpass for some given pixels as long as the previous /// subpass is finished for these given pixels. /// /// In other words, if the previous subpass has some side effects on other parts of an /// attachment, then you should set it to false. /// /// Passing `false` is always safer than passing `true`, but in practice you rarely need to /// pass `false`. pub by_region: bool, } /// Describes what the implementation should do with an attachment after all the subpasses have /// completed. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[repr(i32)] pub enum StoreOp { /// 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 = ash::vk::AttachmentStoreOp::STORE.as_raw(), /// 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 = ash::vk::AttachmentStoreOp::DONT_CARE.as_raw(), } impl From for ash::vk::AttachmentStoreOp { #[inline] fn from(val: StoreOp) -> Self { Self::from_raw(val as i32) } } /// Describes what the implementation should do with an attachment at the start of the subpass. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[repr(i32)] pub enum LoadOp { /// 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 = ash::vk::AttachmentLoadOp::LOAD.as_raw(), /// 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. /// /// See the `draw_inline` and `draw_secondary` methods of `PrimaryComputeBufferBuilder`. Clear = ash::vk::AttachmentLoadOp::CLEAR.as_raw(), /// 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 = ash::vk::AttachmentLoadOp::DONT_CARE.as_raw(), } impl From for ash::vk::AttachmentLoadOp { #[inline] fn from(val: LoadOp) -> Self { Self::from_raw(val as i32) } } /// Describes the `multiview` configuration for the render pass which is used to draw /// to multiple layers of a framebuffer inside of a single render pass. #[derive(Debug, Clone)] pub struct MultiviewDesc { /// The view masks indicate which layers of the framebuffer should be rendered for each subpass. /// Values are bit masks which means that for example `0b11` will draw to the first two layers /// and `0b101` will draw to the first and third layer. pub view_masks: Vec, /// The correlation masks indicate sets of views that may be more efficient to render /// concurrently (usually because they show the same geometry from almost the same perspective). /// Values are bit masks which means that for example `0b11` means the first two layers are /// highly correlated and `0b101` means the first and third layer are highly correlated. pub correlation_masks: Vec, /// The view offsets contain additional information for each subpass dependency that indicate /// which views in the source subpass the views of the destination subpass depend on. pub view_offsets: Vec, } impl MultiviewDesc { /// Returns the index of the layer with the biggest index that is /// referred to by a mask in the multiview description. pub fn highest_used_layer(&self) -> u32 { self.view_masks .iter() .chain(self.correlation_masks.iter()) .map(|&mask| 32 - mask.leading_zeros()) // the highest set bit corresponds to the highest used layer .max() .unwrap_or(0) } /// Returns the amount of layers that are used in the multiview description. pub fn used_layer_count(&self) -> u32 { self.view_masks .iter() .chain(self.correlation_masks.iter()) .fold(0, |acc, &mask| acc | mask) .count_ones() } } /// Possible resolve modes for depth and stencil attachments. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u32)] pub enum ResolveMode { None = ash::vk::ResolveModeFlags::NONE.as_raw(), SampleZero = ash::vk::ResolveModeFlags::SAMPLE_ZERO.as_raw(), Average = ash::vk::ResolveModeFlags::AVERAGE.as_raw(), Min = ash::vk::ResolveModeFlags::MIN.as_raw(), Max = ash::vk::ResolveModeFlags::MAX.as_raw(), } #[derive(Clone, Copy, Debug)] pub struct ResolveModes { pub none: bool, pub sample_zero: bool, pub average: bool, pub min: bool, pub max: bool, } impl From for ResolveModes { #[inline] fn from(val: ash::vk::ResolveModeFlags) -> Self { Self { none: val.intersects(ash::vk::ResolveModeFlags::NONE), sample_zero: val.intersects(ash::vk::ResolveModeFlags::SAMPLE_ZERO), average: val.intersects(ash::vk::ResolveModeFlags::AVERAGE), min: val.intersects(ash::vk::ResolveModeFlags::MIN), max: val.intersects(ash::vk::ResolveModeFlags::MAX), } } }