aboutsummaryrefslogtreecommitdiff
path: root/src/render_pass/create.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/render_pass/create.rs')
-rw-r--r--src/render_pass/create.rs1979
1 files changed, 1979 insertions, 0 deletions
diff --git a/src/render_pass/create.rs b/src/render_pass/create.rs
new file mode 100644
index 0000000..07c4506
--- /dev/null
+++ b/src/render_pass/create.rs
@@ -0,0 +1,1979 @@
+// Copyright (c) 2022 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 super::{
+ AttachmentDescription, AttachmentReference, LoadOp, RenderPass, RenderPassCreateInfo,
+ SubpassDependency, SubpassDescription,
+};
+use crate::{
+ device::Device,
+ format::FormatFeatures,
+ image::{ImageAspects, ImageLayout, SampleCount},
+ sync::{AccessFlags, DependencyFlags, PipelineStages},
+ OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject,
+};
+use smallvec::SmallVec;
+use std::{
+ error::Error,
+ fmt::{Display, Error as FmtError, Formatter},
+ mem::MaybeUninit,
+ ptr,
+};
+
+impl RenderPass {
+ pub(super) fn validate(
+ device: &Device,
+ create_info: &mut RenderPassCreateInfo,
+ ) -> Result<(), RenderPassCreationError> {
+ let properties = device.physical_device().properties();
+
+ let RenderPassCreateInfo {
+ attachments,
+ subpasses,
+ dependencies,
+ correlated_view_masks,
+ _ne: _,
+ } = create_info;
+
+ /*
+ Attachments
+ */
+
+ let mut attachment_potential_format_features = Vec::with_capacity(attachments.len());
+
+ for (atch_num, attachment) in attachments.iter().enumerate() {
+ let &AttachmentDescription {
+ format,
+ samples,
+ load_op,
+ store_op,
+ stencil_load_op,
+ stencil_store_op,
+ initial_layout,
+ final_layout,
+ _ne: _,
+ } = attachment;
+ let atch_num = atch_num as u32;
+
+ // VUID-VkAttachmentDescription2-finalLayout-03061
+ if matches!(
+ final_layout,
+ ImageLayout::Undefined | ImageLayout::Preinitialized
+ ) {
+ return Err(RenderPassCreationError::AttachmentLayoutInvalid {
+ attachment: atch_num,
+ });
+ }
+
+ let format = format.unwrap();
+ let aspects = format.aspects();
+
+ // VUID-VkAttachmentDescription2-format-parameter
+ format.validate_device(device)?;
+
+ // VUID-VkAttachmentDescription2-samples-parameter
+ samples.validate_device(device)?;
+
+ for load_op in [load_op, stencil_load_op] {
+ // VUID-VkAttachmentDescription2-loadOp-parameter
+ // VUID-VkAttachmentDescription2-stencilLoadOp-parameter
+ load_op.validate_device(device)?;
+ }
+
+ for store_op in [store_op, stencil_store_op] {
+ // VUID-VkAttachmentDescription2-storeOp-parameter
+ // VUID-VkAttachmentDescription2-stencilStoreOp-parameter
+ store_op.validate_device(device)?;
+ }
+
+ for layout in [initial_layout, final_layout] {
+ // VUID-VkAttachmentDescription2-initialLayout-parameter
+ // VUID-VkAttachmentDescription2-finalLayout-parameter
+ layout.validate_device(device)?;
+
+ if aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) {
+ // VUID-VkAttachmentDescription2-format-03281
+ // VUID-VkAttachmentDescription2-format-03283
+ if matches!(layout, ImageLayout::ColorAttachmentOptimal) {
+ return Err(RenderPassCreationError::AttachmentLayoutInvalid {
+ attachment: atch_num,
+ });
+ }
+ } else {
+ // VUID-VkAttachmentDescription2-format-03280
+ // VUID-VkAttachmentDescription2-format-03282
+ // VUID-VkAttachmentDescription2-format-06487
+ // VUID-VkAttachmentDescription2-format-06488
+ if matches!(
+ layout,
+ ImageLayout::DepthStencilAttachmentOptimal
+ | ImageLayout::DepthStencilReadOnlyOptimal
+ | ImageLayout::DepthAttachmentStencilReadOnlyOptimal
+ | ImageLayout::DepthReadOnlyStencilAttachmentOptimal
+ ) {
+ return Err(RenderPassCreationError::AttachmentLayoutInvalid {
+ attachment: atch_num,
+ });
+ }
+ }
+ }
+
+ // Use unchecked, because all validation has been done above.
+ attachment_potential_format_features.push(unsafe {
+ device
+ .physical_device()
+ .format_properties_unchecked(format)
+ .potential_format_features()
+ });
+ }
+
+ /*
+ Subpasses
+ */
+
+ // VUID-VkRenderPassCreateInfo2-subpassCount-arraylength
+ assert!(!subpasses.is_empty());
+
+ let is_multiview = subpasses[0].view_mask != 0;
+
+ // VUID-VkSubpassDescription2-multiview-06558
+ if is_multiview && !device.enabled_features().multiview {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.subpasses[0].view_mask` is not `0`",
+ requires_one_of: RequiresOneOf {
+ features: &["multiview"],
+ ..Default::default()
+ },
+ });
+ }
+
+ let mut attachment_used = vec![false; attachments.len()];
+
+ for (subpass_num, subpass) in subpasses.iter_mut().enumerate() {
+ let &mut SubpassDescription {
+ view_mask,
+ ref mut input_attachments,
+ ref color_attachments,
+ ref resolve_attachments,
+ ref depth_stencil_attachment,
+ ref preserve_attachments,
+ _ne: _,
+ } = subpass;
+ let subpass_num = subpass_num as u32;
+
+ // VUID-VkRenderPassCreateInfo2-viewMask-03058
+ if (view_mask != 0) != is_multiview {
+ return Err(RenderPassCreationError::SubpassMultiviewMismatch {
+ subpass: subpass_num,
+ multiview: subpass.view_mask != 0,
+ first_subpass_multiview: is_multiview,
+ });
+ }
+
+ let view_count = u32::BITS - view_mask.leading_zeros();
+
+ // VUID-VkSubpassDescription2-viewMask-06706
+ if view_count > properties.max_multiview_view_count.unwrap_or(0) {
+ return Err(
+ RenderPassCreationError::SubpassMaxMultiviewViewCountExceeded {
+ subpass: subpass_num,
+ view_count,
+ max: properties.max_multiview_view_count.unwrap_or(0),
+ },
+ );
+ }
+
+ // VUID-VkSubpassDescription2-colorAttachmentCount-03063
+ if color_attachments.len() as u32 > properties.max_color_attachments {
+ return Err(
+ RenderPassCreationError::SubpassMaxColorAttachmentsExceeded {
+ subpass: subpass_num,
+ color_attachment_count: color_attachments.len() as u32,
+ max: properties.max_color_attachments,
+ },
+ );
+ }
+
+ // Track the layout of each attachment used in this subpass
+ let mut layouts = vec![None; attachments.len()];
+
+ // Common checks for all attachment types
+ let mut check_attachment = |atch_ref: &AttachmentReference| {
+ // VUID-VkAttachmentReference2-layout-parameter
+ atch_ref.layout.validate_device(device)?;
+
+ // VUID?
+ atch_ref.aspects.validate_device(device)?;
+
+ // VUID-VkRenderPassCreateInfo2-attachment-03051
+ let atch = attachments.get(atch_ref.attachment as usize).ok_or(
+ RenderPassCreationError::SubpassAttachmentOutOfRange {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ },
+ )?;
+
+ // VUID-VkSubpassDescription2-layout-02528
+ match &mut layouts[atch_ref.attachment as usize] {
+ Some(layout) if *layout == atch_ref.layout => (),
+ Some(_) => {
+ return Err(RenderPassCreationError::SubpassAttachmentLayoutMismatch {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ })
+ }
+ layout @ None => *layout = Some(atch_ref.layout),
+ }
+
+ let first_use =
+ !std::mem::replace(&mut attachment_used[atch_ref.attachment as usize], true);
+
+ if first_use {
+ // VUID-VkRenderPassCreateInfo2-pAttachments-02522
+ if atch.load_op == LoadOp::Clear
+ && matches!(
+ atch_ref.layout,
+ ImageLayout::ShaderReadOnlyOptimal
+ | ImageLayout::DepthStencilReadOnlyOptimal
+ | ImageLayout::DepthReadOnlyStencilAttachmentOptimal
+ )
+ {
+ return Err(RenderPassCreationError::AttachmentFirstUseLoadOpInvalid {
+ attachment: atch_ref.attachment,
+ first_use_subpass: subpass_num,
+ });
+ }
+
+ // VUID-VkRenderPassCreateInfo2-pAttachments-02523
+ if atch.stencil_load_op == LoadOp::Clear
+ && matches!(
+ atch_ref.layout,
+ ImageLayout::ShaderReadOnlyOptimal
+ | ImageLayout::DepthStencilReadOnlyOptimal
+ | ImageLayout::DepthAttachmentStencilReadOnlyOptimal
+ )
+ {
+ return Err(RenderPassCreationError::AttachmentFirstUseLoadOpInvalid {
+ attachment: atch_ref.attachment,
+ first_use_subpass: subpass_num,
+ });
+ }
+ }
+
+ let potential_format_features =
+ &attachment_potential_format_features[atch_ref.attachment as usize];
+
+ Ok((atch, potential_format_features, first_use))
+ };
+
+ /*
+ Check color attachments
+ */
+
+ let mut color_samples = None;
+
+ for atch_ref in color_attachments.iter().flatten() {
+ let (atch, features, _first_use) = check_attachment(atch_ref)?;
+
+ // VUID-VkSubpassDescription2-pColorAttachments-02898
+ if !features.intersects(FormatFeatures::COLOR_ATTACHMENT) {
+ return Err(
+ RenderPassCreationError::SubpassAttachmentFormatUsageNotSupported {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ usage: "color",
+ },
+ );
+ }
+
+ // VUID-VkAttachmentReference2-layout-03077
+ // VUID-VkSubpassDescription2-attachment-06913
+ // VUID-VkSubpassDescription2-attachment-06916
+ if matches!(
+ atch_ref.layout,
+ ImageLayout::Undefined
+ | ImageLayout::Preinitialized
+ | ImageLayout::PresentSrc
+ | ImageLayout::DepthStencilAttachmentOptimal
+ | ImageLayout::ShaderReadOnlyOptimal
+ | ImageLayout::DepthAttachmentStencilReadOnlyOptimal
+ | ImageLayout::DepthReadOnlyStencilAttachmentOptimal
+ ) {
+ return Err(RenderPassCreationError::SubpassAttachmentLayoutInvalid {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ usage: "color",
+ });
+ }
+
+ // Not required by spec, but enforced by Vulkano for sanity.
+ if !atch_ref.aspects.is_empty() {
+ return Err(RenderPassCreationError::SubpassAttachmentAspectsNotEmpty {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ });
+ }
+
+ // VUID-VkSubpassDescription2-pColorAttachments-03069
+ match &mut color_samples {
+ Some(samples) if *samples == atch.samples => (),
+ Some(samples) => {
+ return Err(
+ RenderPassCreationError::SubpassColorDepthStencilAttachmentSamplesMismatch {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ samples: atch.samples,
+ first_samples: *samples,
+ },
+ )
+ }
+ samples @ None => *samples = Some(atch.samples),
+ }
+ }
+
+ /*
+ Check depth/stencil attachment
+ */
+
+ if let Some(atch_ref) = depth_stencil_attachment.as_ref() {
+ let (atch, features, _first_use) = check_attachment(atch_ref)?;
+
+ // VUID-VkSubpassDescription2-pDepthStencilAttachment-02900
+ if !features.intersects(FormatFeatures::DEPTH_STENCIL_ATTACHMENT) {
+ return Err(
+ RenderPassCreationError::SubpassAttachmentFormatUsageNotSupported {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ usage: "depth/stencil",
+ },
+ );
+ }
+
+ // VUID-VkAttachmentReference2-layout-03077
+ // VUID-VkSubpassDescription2-attachment-06915
+ if matches!(
+ atch_ref.layout,
+ ImageLayout::Undefined
+ | ImageLayout::Preinitialized
+ | ImageLayout::PresentSrc
+ | ImageLayout::ColorAttachmentOptimal
+ | ImageLayout::ShaderReadOnlyOptimal
+ ) {
+ return Err(RenderPassCreationError::SubpassAttachmentLayoutInvalid {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ usage: "depth/stencil",
+ });
+ }
+
+ // Not required by spec, but enforced by Vulkano for sanity.
+ if !atch_ref.aspects.is_empty() {
+ return Err(RenderPassCreationError::SubpassAttachmentAspectsNotEmpty {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ });
+ }
+
+ // VUID-VkSubpassDescription2-pDepthStencilAttachment-04440
+ if color_attachments
+ .iter()
+ .flatten()
+ .any(|color_atch_ref| color_atch_ref.attachment == atch_ref.attachment)
+ {
+ return Err(
+ RenderPassCreationError::SubpassAttachmentUsageColorDepthStencil {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ },
+ );
+ }
+
+ // VUID-VkSubpassDescription2-pDepthStencilAttachment-03071
+ if let Some(samples) = color_samples.filter(|samples| *samples != atch.samples) {
+ return Err(
+ RenderPassCreationError::SubpassColorDepthStencilAttachmentSamplesMismatch {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ samples: atch.samples,
+ first_samples: samples,
+ },
+ );
+ }
+ }
+
+ /*
+ Check input attachments
+ This must be placed after color and depth/stencil checks so that `first_use`
+ will be true for VUID-VkSubpassDescription2-loadOp-03064.
+ */
+
+ for atch_ref in input_attachments.iter_mut().flatten() {
+ let (atch, features, first_use) = check_attachment(atch_ref)?;
+
+ // VUID-VkSubpassDescription2-pInputAttachments-02897
+ if !features.intersects(
+ FormatFeatures::COLOR_ATTACHMENT | FormatFeatures::DEPTH_STENCIL_ATTACHMENT,
+ ) {
+ return Err(
+ RenderPassCreationError::SubpassAttachmentFormatUsageNotSupported {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ usage: "input",
+ },
+ );
+ }
+
+ // VUID-VkAttachmentReference2-layout-03077
+ // VUID-VkSubpassDescription2-attachment-06912
+ if matches!(
+ atch_ref.layout,
+ ImageLayout::Undefined
+ | ImageLayout::Preinitialized
+ | ImageLayout::PresentSrc
+ | ImageLayout::ColorAttachmentOptimal
+ | ImageLayout::DepthStencilAttachmentOptimal
+ ) {
+ return Err(RenderPassCreationError::SubpassAttachmentLayoutInvalid {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ usage: "input",
+ });
+ }
+
+ let atch_aspects = atch.format.unwrap().aspects();
+
+ if atch_ref.aspects.is_empty() {
+ // VUID-VkSubpassDescription2-attachment-02800
+ atch_ref.aspects = atch_aspects;
+ } else if atch_ref.aspects != atch_aspects {
+ if !(device.api_version() >= Version::V1_1
+ || device.enabled_extensions().khr_create_renderpass2
+ || device.enabled_extensions().khr_maintenance2)
+ {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.subpasses` has an element, where \
+ `input_attachments` has an element that is `Some(atch_ref)`, \
+ where `atch_ref.aspects` does not match the aspects of the \
+ attachment itself",
+ requires_one_of: RequiresOneOf {
+ api_version: Some(Version::V1_1),
+ device_extensions: &["khr_create_renderpass2", "khr_maintenance2"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkSubpassDescription2-attachment-02801
+ // VUID-VkSubpassDescription2-attachment-04563
+ // VUID-VkRenderPassCreateInfo2-attachment-02525
+ if !atch_aspects.contains(atch_ref.aspects) {
+ return Err(
+ RenderPassCreationError::SubpassInputAttachmentAspectsNotCompatible {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ },
+ );
+ }
+ }
+
+ // VUID-VkSubpassDescription2-loadOp-03064
+ if first_use && atch.load_op == LoadOp::Clear {
+ return Err(RenderPassCreationError::AttachmentFirstUseLoadOpInvalid {
+ attachment: atch_ref.attachment,
+ first_use_subpass: subpass_num,
+ });
+ }
+ }
+
+ /*
+ Check resolve attachments
+ */
+
+ // VUID-VkSubpassDescription2-pResolveAttachments-parameter
+ if !(resolve_attachments.is_empty()
+ || resolve_attachments.len() == color_attachments.len())
+ {
+ return Err(
+ RenderPassCreationError::SubpassResolveAttachmentsColorAttachmentsLenMismatch {
+ subpass: subpass_num,
+ },
+ );
+ }
+
+ for (atch_ref, color_atch_ref) in resolve_attachments
+ .iter()
+ .zip(subpass.color_attachments.iter())
+ .filter_map(|(r, c)| r.as_ref().map(|r| (r, c.as_ref())))
+ {
+ let (atch, features, _first_use) = check_attachment(atch_ref)?;
+
+ // VUID-VkSubpassDescription2-pResolveAttachments-02899
+ if !features.intersects(FormatFeatures::COLOR_ATTACHMENT) {
+ return Err(
+ RenderPassCreationError::SubpassAttachmentFormatUsageNotSupported {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ usage: "resolve",
+ },
+ );
+ }
+
+ // VUID-VkAttachmentReference2-layout-03077
+ // VUID-VkSubpassDescription2-attachment-06914
+ // VUID-VkSubpassDescription2-attachment-06917
+ if matches!(
+ atch_ref.layout,
+ ImageLayout::Undefined
+ | ImageLayout::Preinitialized
+ | ImageLayout::PresentSrc
+ | ImageLayout::DepthStencilAttachmentOptimal
+ | ImageLayout::ShaderReadOnlyOptimal
+ | ImageLayout::DepthAttachmentStencilReadOnlyOptimal
+ | ImageLayout::DepthReadOnlyStencilAttachmentOptimal
+ ) {
+ return Err(RenderPassCreationError::SubpassAttachmentLayoutInvalid {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ usage: "resolve",
+ });
+ }
+
+ // Not required by spec, but enforced by Vulkano for sanity.
+ if !atch_ref.aspects.is_empty() {
+ return Err(RenderPassCreationError::SubpassAttachmentAspectsNotEmpty {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ });
+ }
+
+ // VUID-VkSubpassDescription2-pResolveAttachments-03065
+ let color_atch_ref = color_atch_ref.ok_or({
+ RenderPassCreationError::SubpassResolveAttachmentWithoutColorAttachment {
+ subpass: subpass_num,
+ }
+ })?;
+ let color_atch = &attachments[color_atch_ref.attachment as usize];
+
+ // VUID-VkSubpassDescription2-pResolveAttachments-03067
+ if atch.samples != SampleCount::Sample1 {
+ return Err(
+ RenderPassCreationError::SubpassResolveAttachmentMultisampled {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ },
+ );
+ }
+
+ // VUID-VkSubpassDescription2-pResolveAttachments-03066
+ if color_atch.samples == SampleCount::Sample1 {
+ return Err(
+ RenderPassCreationError::SubpassColorAttachmentWithResolveNotMultisampled {
+ subpass: subpass_num,
+ attachment: atch_ref.attachment,
+ },
+ );
+ }
+
+ // VUID-VkSubpassDescription2-pResolveAttachments-03068
+ if atch.format != color_atch.format {
+ return Err(
+ RenderPassCreationError::SubpassResolveAttachmentFormatMismatch {
+ subpass: subpass_num,
+ resolve_attachment: atch_ref.attachment,
+ color_attachment: color_atch_ref.attachment,
+ },
+ );
+ }
+ }
+
+ /*
+ Check preserve attachments
+ */
+
+ for &atch in preserve_attachments {
+ // VUID-VkRenderPassCreateInfo2-attachment-03051
+ if atch as usize >= attachments.len() {
+ return Err(RenderPassCreationError::SubpassAttachmentOutOfRange {
+ subpass: subpass_num,
+ attachment: atch,
+ });
+ }
+
+ // VUID-VkSubpassDescription2-pPreserveAttachments-03074
+ if layouts[atch as usize].is_some() {
+ return Err(
+ RenderPassCreationError::SubpassPreserveAttachmentUsedElsewhere {
+ subpass: subpass_num,
+ attachment: atch,
+ },
+ );
+ }
+ }
+ }
+
+ /*
+ Dependencies
+ */
+
+ for (dependency_num, dependency) in dependencies.iter().enumerate() {
+ let &SubpassDependency {
+ src_subpass,
+ dst_subpass,
+ src_stages,
+ dst_stages,
+ src_access,
+ dst_access,
+ dependency_flags,
+ view_offset,
+ _ne: _,
+ } = dependency;
+ let dependency_num = dependency_num as u32;
+
+ for (stages, access) in [(src_stages, src_access), (dst_stages, dst_access)] {
+ if !device.enabled_features().synchronization2 {
+ if stages.is_2() {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where \
+ `src_stages` or `dst_stages` contains flags from \
+ `VkPipelineStageFlagBits2`",
+ requires_one_of: RequiresOneOf {
+ features: &["synchronization2"],
+ ..Default::default()
+ },
+ });
+ }
+
+ if access.is_2() {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where \
+ `src_access` or `dst_access` contains flags from \
+ `VkAccessFlagBits2`",
+ requires_one_of: RequiresOneOf {
+ features: &["synchronization2"],
+ ..Default::default()
+ },
+ });
+ }
+ } else if !(device.api_version() >= Version::V1_2
+ || device.enabled_extensions().khr_create_renderpass2)
+ {
+ // If synchronization2 is enabled but we don't have create_renderpass2,
+ // we are unable to use extension structs, so we can't use the
+ // extra flag bits.
+
+ if stages.is_2() {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where \
+ `src_stages` or `dst_stages` contains flags from \
+ `VkPipelineStageFlagBits2`",
+ requires_one_of: RequiresOneOf {
+ api_version: Some(Version::V1_2),
+ device_extensions: &["khr_create_renderpass2"],
+ ..Default::default()
+ },
+ });
+ }
+
+ if access.is_2() {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where \
+ `src_access` or `dst_access` contains flags from \
+ `VkAccessFlagBits2`",
+ requires_one_of: RequiresOneOf {
+ api_version: Some(Version::V1_2),
+ device_extensions: &["khr_create_renderpass2"],
+ ..Default::default()
+ },
+ });
+ }
+ }
+
+ // VUID-VkMemoryBarrier2-srcStageMask-parameter
+ // VUID-VkMemoryBarrier2-dstStageMask-parameter
+ stages.validate_device(device)?;
+
+ // VUID-VkMemoryBarrier2-srcAccessMask-parameter
+ // VUID-VkMemoryBarrier2-dstAccessMask-parameter
+ access.validate_device(device)?;
+
+ // VUID-VkMemoryBarrier2-srcStageMask-03929
+ // VUID-VkMemoryBarrier2-dstStageMask-03929
+ if stages.intersects(PipelineStages::GEOMETRY_SHADER)
+ && !device.enabled_features().geometry_shader
+ {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where `stages` \
+ contains `PipelineStages::GEOMETRY_SHADER`",
+ requires_one_of: RequiresOneOf {
+ features: &["geometry_shader"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkMemoryBarrier2-srcStageMask-03930
+ // VUID-VkMemoryBarrier2-dstStageMask-03930
+ if stages.intersects(
+ PipelineStages::TESSELLATION_CONTROL_SHADER
+ | PipelineStages::TESSELLATION_EVALUATION_SHADER,
+ ) && !device.enabled_features().tessellation_shader
+ {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where `stages` \
+ contains `PipelineStages::TESSELLATION_CONTROL_SHADER` or \
+ `PipelineStages::TESSELLATION_EVALUATION_SHADER`",
+ requires_one_of: RequiresOneOf {
+ features: &["tessellation_shader"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkMemoryBarrier2-srcStageMask-03931
+ // VUID-VkMemoryBarrier2-dstStageMask-03931
+ if stages.intersects(PipelineStages::CONDITIONAL_RENDERING)
+ && !device.enabled_features().conditional_rendering
+ {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where `stages` \
+ contains `PipelineStages::CONDITIONAL_RENDERING`",
+ requires_one_of: RequiresOneOf {
+ features: &["conditional_rendering"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkMemoryBarrier2-srcStageMask-03932
+ // VUID-VkMemoryBarrier2-dstStageMask-03932
+ if stages.intersects(PipelineStages::FRAGMENT_DENSITY_PROCESS)
+ && !device.enabled_features().fragment_density_map
+ {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where `stages` \
+ contains `PipelineStages::FRAGMENT_DENSITY_PROCESS`",
+ requires_one_of: RequiresOneOf {
+ features: &["fragment_density_map"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkMemoryBarrier2-srcStageMask-03933
+ // VUID-VkMemoryBarrier2-dstStageMask-03933
+ if stages.intersects(PipelineStages::TRANSFORM_FEEDBACK)
+ && !device.enabled_features().transform_feedback
+ {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where `stages` \
+ contains `PipelineStages::TRANSFORM_FEEDBACK`",
+ requires_one_of: RequiresOneOf {
+ features: &["transform_feedback"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkMemoryBarrier2-srcStageMask-03934
+ // VUID-VkMemoryBarrier2-dstStageMask-03934
+ if stages.intersects(PipelineStages::MESH_SHADER)
+ && !device.enabled_features().mesh_shader
+ {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where `stages` \
+ contains `PipelineStages::MESH_SHADER`",
+ requires_one_of: RequiresOneOf {
+ features: &["mesh_shader"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkMemoryBarrier2-srcStageMask-03935
+ // VUID-VkMemoryBarrier2-dstStageMask-03935
+ if stages.intersects(PipelineStages::TASK_SHADER)
+ && !device.enabled_features().task_shader
+ {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where `stages` \
+ contains `PipelineStages::TASK_SHADER`",
+ requires_one_of: RequiresOneOf {
+ features: &["task_shader"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkMemoryBarrier2-shadingRateImage-07316
+ // VUID-VkMemoryBarrier2-shadingRateImage-07316
+ if stages.intersects(PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT)
+ && !(device.enabled_features().attachment_fragment_shading_rate
+ || device.enabled_features().shading_rate_image)
+ {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where `stages` \
+ contains `PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT`",
+ requires_one_of: RequiresOneOf {
+ features: &["attachment_fragment_shading_rate", "shading_rate_image"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkMemoryBarrier2-srcStageMask-04957
+ // VUID-VkMemoryBarrier2-dstStageMask-04957
+ if stages.intersects(PipelineStages::SUBPASS_SHADING)
+ && !device.enabled_features().subpass_shading
+ {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where `stages` \
+ contains `PipelineStages::SUBPASS_SHADING`",
+ requires_one_of: RequiresOneOf {
+ features: &["subpass_shading"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkMemoryBarrier2-srcStageMask-04995
+ // VUID-VkMemoryBarrier2-dstStageMask-04995
+ if stages.intersects(PipelineStages::INVOCATION_MASK)
+ && !device.enabled_features().invocation_mask
+ {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where `stages` \
+ contains `PipelineStages::INVOCATION_MASK`",
+ requires_one_of: RequiresOneOf {
+ features: &["invocation_mask"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkSubpassDependency2-srcStageMask-03937
+ // VUID-VkSubpassDependency2-dstStageMask-03937
+ if stages.is_empty() && !device.enabled_features().synchronization2 {
+ return Err(RenderPassCreationError::RequirementNotMet {
+ required_for: "`create_info.dependencies` has an element where `stages` \
+ is empty",
+ requires_one_of: RequiresOneOf {
+ features: &["synchronization2"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkSubpassDependency2-srcAccessMask-03088
+ // VUID-VkSubpassDependency2-dstAccessMask-03089
+ if !AccessFlags::from(stages).contains(access) {
+ return Err(
+ RenderPassCreationError::DependencyAccessNotSupportedByStages {
+ dependency: dependency_num,
+ },
+ );
+ }
+ }
+
+ if dependency_flags.intersects(DependencyFlags::VIEW_LOCAL) {
+ // VUID-VkRenderPassCreateInfo2-viewMask-03059
+ if !is_multiview {
+ return Err(
+ RenderPassCreationError::DependencyViewLocalMultiviewNotEnabled {
+ dependency: dependency_num,
+ },
+ );
+ }
+ } else {
+ // VUID-VkSubpassDependency2-dependencyFlags-03092
+ if view_offset != 0 {
+ return Err(
+ RenderPassCreationError::DependencyViewOffzetNonzeroWithoutViewLocal {
+ dependency: dependency_num,
+ },
+ );
+ }
+ }
+
+ // VUID-VkSubpassDependency2-srcSubpass-03085
+ if src_subpass.is_none() && dst_subpass.is_none() {
+ return Err(RenderPassCreationError::DependencyBothSubpassesExternal {
+ dependency: dependency_num,
+ });
+ }
+
+ for (subpass, stages) in [(src_subpass, src_stages), (dst_subpass, dst_stages)] {
+ if let Some(subpass) = subpass {
+ // VUID-VkRenderPassCreateInfo2-srcSubpass-02526
+ // VUID-VkRenderPassCreateInfo2-dstSubpass-02527
+ if subpass as usize >= subpasses.len() {
+ return Err(RenderPassCreationError::DependencySubpassOutOfRange {
+ dependency: dependency_num,
+ subpass,
+ });
+ }
+
+ let remaining_stages = stages
+ - (PipelineStages::DRAW_INDIRECT
+ | PipelineStages::INDEX_INPUT
+ | PipelineStages::VERTEX_ATTRIBUTE_INPUT
+ | PipelineStages::VERTEX_SHADER
+ | PipelineStages::TESSELLATION_CONTROL_SHADER
+ | PipelineStages::TESSELLATION_EVALUATION_SHADER
+ | PipelineStages::GEOMETRY_SHADER
+ | PipelineStages::TRANSFORM_FEEDBACK
+ | PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT
+ | PipelineStages::EARLY_FRAGMENT_TESTS
+ | PipelineStages::FRAGMENT_SHADER
+ | PipelineStages::LATE_FRAGMENT_TESTS
+ | PipelineStages::COLOR_ATTACHMENT_OUTPUT
+ | PipelineStages::ALL_GRAPHICS);
+
+ // VUID-VkRenderPassCreateInfo2-pDependencies-03054
+ // VUID-VkRenderPassCreateInfo2-pDependencies-03055
+ if !remaining_stages.is_empty() {
+ return Err(RenderPassCreationError::DependencyStageNotSupported {
+ dependency: dependency_num,
+ });
+ }
+ } else {
+ // VUID-VkSubpassDependency2-dependencyFlags-03090
+ // VUID-VkSubpassDependency2-dependencyFlags-03091
+ if dependency_flags.intersects(DependencyFlags::VIEW_LOCAL) {
+ return Err(
+ RenderPassCreationError::DependencyViewLocalExternalDependency {
+ dependency: dependency_num,
+ },
+ );
+ }
+ }
+ }
+
+ if let (Some(src_subpass), Some(dst_subpass)) = (src_subpass, dst_subpass) {
+ // VUID-VkSubpassDependency2-srcSubpass-03084
+ if src_subpass > dst_subpass {
+ return Err(
+ RenderPassCreationError::DependencySourceSubpassAfterDestinationSubpass {
+ dependency: dependency_num,
+ },
+ );
+ }
+
+ if src_subpass == dst_subpass {
+ let framebuffer_stages = PipelineStages::EARLY_FRAGMENT_TESTS
+ | PipelineStages::FRAGMENT_SHADER
+ | PipelineStages::LATE_FRAGMENT_TESTS
+ | PipelineStages::COLOR_ATTACHMENT_OUTPUT;
+
+ // VUID-VkSubpassDependency2-srcSubpass-06810
+ if src_stages.intersects(framebuffer_stages)
+ && !(dst_stages - framebuffer_stages).is_empty()
+ {
+ return Err(
+ RenderPassCreationError::DependencySelfDependencySourceStageAfterDestinationStage {
+ dependency: dependency_num,
+ },
+ );
+ }
+
+ // VUID-VkSubpassDependency2-srcSubpass-02245
+ if src_stages.intersects(framebuffer_stages)
+ && dst_stages.intersects(framebuffer_stages)
+ && !dependency_flags.intersects(DependencyFlags::BY_REGION)
+ {
+ return Err(
+ RenderPassCreationError::DependencySelfDependencyFramebufferStagesWithoutByRegion {
+ dependency: dependency_num,
+ },
+ );
+ }
+
+ if dependency_flags.intersects(DependencyFlags::VIEW_LOCAL) {
+ // VUID-VkSubpassDependency2-viewOffset-02530
+ if view_offset != 0 {
+ return Err(
+ RenderPassCreationError::DependencySelfDependencyViewLocalNonzeroViewOffset {
+ dependency: dependency_num,
+ },
+ );
+ }
+ } else {
+ // VUID-VkRenderPassCreateInfo2-pDependencies-03060
+ if subpasses[src_subpass as usize].view_mask.count_ones() > 1 {
+ return Err(
+ RenderPassCreationError::DependencySelfDependencyViewMaskMultiple {
+ dependency: dependency_num,
+ subpass: src_subpass,
+ },
+ );
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ Correlated view masks
+ */
+
+ // VUID-VkRenderPassCreateInfo2-viewMask-03057
+ if !correlated_view_masks.is_empty() {
+ if !is_multiview {
+ return Err(RenderPassCreationError::CorrelatedViewMasksMultiviewNotEnabled);
+ }
+
+ // VUID-VkRenderPassCreateInfo2-pCorrelatedViewMasks-03056
+ correlated_view_masks.iter().try_fold(0, |total, &mask| {
+ if total & mask != 0 {
+ Err(RenderPassCreationError::CorrelatedViewMasksOverlapping)
+ } else {
+ Ok(total | mask)
+ }
+ })?;
+ }
+
+ Ok(())
+ }
+
+ pub(super) unsafe fn create_v2(
+ device: &Device,
+ create_info: &RenderPassCreateInfo,
+ ) -> Result<ash::vk::RenderPass, RenderPassCreationError> {
+ let RenderPassCreateInfo {
+ attachments,
+ subpasses,
+ dependencies,
+ correlated_view_masks,
+ _ne: _,
+ } = create_info;
+
+ let attachments_vk = attachments
+ .iter()
+ .map(|attachment| ash::vk::AttachmentDescription2 {
+ flags: ash::vk::AttachmentDescriptionFlags::empty(),
+ format: attachment
+ .format
+ .map_or(ash::vk::Format::UNDEFINED, |f| f.into()),
+ samples: attachment.samples.into(),
+ load_op: attachment.load_op.into(),
+ store_op: attachment.store_op.into(),
+ stencil_load_op: attachment.stencil_load_op.into(),
+ stencil_store_op: attachment.stencil_store_op.into(),
+ initial_layout: attachment.initial_layout.into(),
+ final_layout: attachment.final_layout.into(),
+ ..Default::default()
+ })
+ .collect::<SmallVec<[_; 4]>>();
+
+ let attachment_references_vk = subpasses
+ .iter()
+ .flat_map(|subpass| {
+ (subpass.input_attachments.iter())
+ .chain(subpass.color_attachments.iter())
+ .chain(subpass.resolve_attachments.iter())
+ .map(Option::as_ref)
+ .chain(subpass.depth_stencil_attachment.iter().map(Some))
+ .map(|atch_ref| {
+ if let Some(atch_ref) = atch_ref {
+ ash::vk::AttachmentReference2 {
+ attachment: atch_ref.attachment,
+ layout: atch_ref.layout.into(),
+ aspect_mask: atch_ref.aspects.into(),
+ ..Default::default()
+ }
+ } else {
+ ash::vk::AttachmentReference2 {
+ attachment: ash::vk::ATTACHMENT_UNUSED,
+ ..Default::default()
+ }
+ }
+ })
+ })
+ .collect::<SmallVec<[_; 8]>>();
+
+ let subpasses_vk = {
+ // `ref_index` is increased during the loop and points to the next element to use
+ // in `attachment_references_vk`.
+ let mut ref_index = 0usize;
+ let out: SmallVec<[_; 4]> = subpasses
+ .iter()
+ .map(|subpass| {
+ let input_attachments = attachment_references_vk.as_ptr().add(ref_index);
+ ref_index += subpass.input_attachments.len();
+ let color_attachments = attachment_references_vk.as_ptr().add(ref_index);
+ ref_index += subpass.color_attachments.len();
+ let resolve_attachments = attachment_references_vk.as_ptr().add(ref_index);
+ ref_index += subpass.resolve_attachments.len();
+ let depth_stencil = if subpass.depth_stencil_attachment.is_some() {
+ let a = attachment_references_vk.as_ptr().add(ref_index);
+ ref_index += 1;
+ a
+ } else {
+ ptr::null()
+ };
+
+ ash::vk::SubpassDescription2 {
+ flags: ash::vk::SubpassDescriptionFlags::empty(),
+ pipeline_bind_point: ash::vk::PipelineBindPoint::GRAPHICS, // TODO: any need to make this user-specifiable?
+ view_mask: subpass.view_mask,
+ input_attachment_count: subpass.input_attachments.len() as u32,
+ p_input_attachments: if subpass.input_attachments.is_empty() {
+ ptr::null()
+ } else {
+ input_attachments
+ },
+ color_attachment_count: subpass.color_attachments.len() as u32,
+ p_color_attachments: if subpass.color_attachments.is_empty() {
+ ptr::null()
+ } else {
+ color_attachments
+ },
+ p_resolve_attachments: if subpass.resolve_attachments.is_empty() {
+ ptr::null()
+ } else {
+ resolve_attachments
+ },
+ p_depth_stencil_attachment: depth_stencil,
+ preserve_attachment_count: subpass.preserve_attachments.len() as u32,
+ p_preserve_attachments: if subpass.preserve_attachments.is_empty() {
+ ptr::null()
+ } else {
+ subpass.preserve_attachments.as_ptr()
+ },
+ ..Default::default()
+ }
+ })
+ .collect();
+
+ // If this assertion fails, there's a serious bug in the code above ^.
+ debug_assert!(ref_index == attachment_references_vk.len());
+
+ out
+ };
+
+ let memory_barriers_vk: SmallVec<[_; 4]> = if device.enabled_features().synchronization2 {
+ debug_assert!(
+ device.api_version() >= Version::V1_3
+ || device.enabled_extensions().khr_synchronization2
+ );
+ dependencies
+ .iter()
+ .map(|dependency| ash::vk::MemoryBarrier2 {
+ src_stage_mask: dependency.src_stages.into(),
+ src_access_mask: dependency.src_access.into(),
+ dst_stage_mask: dependency.dst_stages.into(),
+ dst_access_mask: dependency.dst_access.into(),
+ ..Default::default()
+ })
+ .collect()
+ } else {
+ SmallVec::new()
+ };
+
+ let dependencies_vk = dependencies
+ .iter()
+ .enumerate()
+ .map(|(index, dependency)| {
+ ash::vk::SubpassDependency2 {
+ p_next: memory_barriers_vk
+ .get(index)
+ .map_or(ptr::null(), |mb| mb as *const _ as *const _),
+ src_subpass: dependency.src_subpass.unwrap_or(ash::vk::SUBPASS_EXTERNAL),
+ dst_subpass: dependency.dst_subpass.unwrap_or(ash::vk::SUBPASS_EXTERNAL),
+ src_stage_mask: dependency.src_stages.into(),
+ dst_stage_mask: dependency.dst_stages.into(),
+ src_access_mask: dependency.src_access.into(),
+ dst_access_mask: dependency.dst_access.into(),
+ dependency_flags: dependency.dependency_flags.into(),
+ // VUID-VkSubpassDependency2-dependencyFlags-03092
+ view_offset: dependency.view_offset,
+ ..Default::default()
+ }
+ })
+ .collect::<SmallVec<[_; 4]>>();
+
+ let create_info = ash::vk::RenderPassCreateInfo2 {
+ flags: ash::vk::RenderPassCreateFlags::empty(),
+ attachment_count: attachments_vk.len() as u32,
+ p_attachments: if attachments_vk.is_empty() {
+ ptr::null()
+ } else {
+ attachments_vk.as_ptr()
+ },
+ subpass_count: subpasses_vk.len() as u32,
+ p_subpasses: if subpasses_vk.is_empty() {
+ ptr::null()
+ } else {
+ subpasses_vk.as_ptr()
+ },
+ dependency_count: dependencies_vk.len() as u32,
+ p_dependencies: if dependencies_vk.is_empty() {
+ ptr::null()
+ } else {
+ dependencies_vk.as_ptr()
+ },
+ correlated_view_mask_count: correlated_view_masks.len() as u32,
+ p_correlated_view_masks: correlated_view_masks.as_ptr(),
+ ..Default::default()
+ };
+
+ Ok({
+ let fns = device.fns();
+ let mut output = MaybeUninit::uninit();
+
+ if device.api_version() >= Version::V1_2 {
+ (fns.v1_2.create_render_pass2)(
+ device.handle(),
+ &create_info,
+ ptr::null(),
+ output.as_mut_ptr(),
+ )
+ } else {
+ (fns.khr_create_renderpass2.create_render_pass2_khr)(
+ device.handle(),
+ &create_info,
+ ptr::null(),
+ output.as_mut_ptr(),
+ )
+ }
+ .result()
+ .map_err(VulkanError::from)?;
+
+ output.assume_init()
+ })
+ }
+
+ pub(super) unsafe fn create_v1(
+ device: &Device,
+ create_info: &RenderPassCreateInfo,
+ ) -> Result<ash::vk::RenderPass, RenderPassCreationError> {
+ let RenderPassCreateInfo {
+ attachments,
+ subpasses,
+ dependencies,
+ correlated_view_masks,
+ _ne: _,
+ } = create_info;
+
+ let attachments_vk = attachments
+ .iter()
+ .map(|attachment| ash::vk::AttachmentDescription {
+ flags: ash::vk::AttachmentDescriptionFlags::empty(),
+ format: attachment
+ .format
+ .map_or(ash::vk::Format::UNDEFINED, |f| f.into()),
+ samples: attachment.samples.into(),
+ load_op: attachment.load_op.into(),
+ store_op: attachment.store_op.into(),
+ stencil_load_op: attachment.stencil_load_op.into(),
+ stencil_store_op: attachment.stencil_store_op.into(),
+ initial_layout: attachment.initial_layout.into(),
+ final_layout: attachment.final_layout.into(),
+ })
+ .collect::<SmallVec<[_; 4]>>();
+
+ let attachment_references_vk = subpasses
+ .iter()
+ .flat_map(|subpass| {
+ (subpass.input_attachments.iter())
+ .chain(subpass.color_attachments.iter())
+ .chain(subpass.resolve_attachments.iter())
+ .map(Option::as_ref)
+ .chain(subpass.depth_stencil_attachment.iter().map(Some))
+ .map(|atch_ref| {
+ if let Some(atch_ref) = atch_ref {
+ ash::vk::AttachmentReference {
+ attachment: atch_ref.attachment,
+ layout: atch_ref.layout.into(),
+ }
+ } else {
+ ash::vk::AttachmentReference {
+ attachment: ash::vk::ATTACHMENT_UNUSED,
+ layout: Default::default(),
+ }
+ }
+ })
+ })
+ .collect::<SmallVec<[_; 8]>>();
+
+ let subpasses_vk = {
+ // `ref_index` is increased during the loop and points to the next element to use
+ // in `attachment_references_vk`.
+ let mut ref_index = 0usize;
+ let out: SmallVec<[_; 4]> = subpasses
+ .iter()
+ .map(|subpass| {
+ let input_attachments = attachment_references_vk.as_ptr().add(ref_index);
+ ref_index += subpass.input_attachments.len();
+ let color_attachments = attachment_references_vk.as_ptr().add(ref_index);
+ ref_index += subpass.color_attachments.len();
+ let resolve_attachments = attachment_references_vk.as_ptr().add(ref_index);
+ ref_index += subpass.resolve_attachments.len();
+ let depth_stencil = if subpass.depth_stencil_attachment.is_some() {
+ let a = attachment_references_vk.as_ptr().add(ref_index);
+ ref_index += 1;
+ a
+ } else {
+ ptr::null()
+ };
+
+ ash::vk::SubpassDescription {
+ flags: ash::vk::SubpassDescriptionFlags::empty(),
+ pipeline_bind_point: ash::vk::PipelineBindPoint::GRAPHICS,
+ input_attachment_count: subpass.input_attachments.len() as u32,
+ p_input_attachments: if subpass.input_attachments.is_empty() {
+ ptr::null()
+ } else {
+ input_attachments
+ },
+ color_attachment_count: subpass.color_attachments.len() as u32,
+ p_color_attachments: if subpass.color_attachments.is_empty() {
+ ptr::null()
+ } else {
+ color_attachments
+ },
+ p_resolve_attachments: if subpass.resolve_attachments.is_empty() {
+ ptr::null()
+ } else {
+ resolve_attachments
+ },
+ p_depth_stencil_attachment: depth_stencil,
+ preserve_attachment_count: subpass.preserve_attachments.len() as u32,
+ p_preserve_attachments: if subpass.preserve_attachments.is_empty() {
+ ptr::null()
+ } else {
+ subpass.preserve_attachments.as_ptr()
+ },
+ }
+ })
+ .collect();
+
+ // If this assertion fails, there's a serious bug in the code above ^.
+ debug_assert!(ref_index == attachment_references_vk.len());
+
+ out
+ };
+
+ let dependencies_vk = dependencies
+ .iter()
+ .map(|dependency| ash::vk::SubpassDependency {
+ src_subpass: dependency.src_subpass.unwrap_or(ash::vk::SUBPASS_EXTERNAL),
+ dst_subpass: dependency.dst_subpass.unwrap_or(ash::vk::SUBPASS_EXTERNAL),
+ src_stage_mask: dependency.src_stages.into(),
+ dst_stage_mask: dependency.dst_stages.into(),
+ src_access_mask: dependency.src_access.into(),
+ dst_access_mask: dependency.dst_access.into(),
+ dependency_flags: dependency.dependency_flags.into(),
+ })
+ .collect::<SmallVec<[_; 4]>>();
+
+ /* Input attachment aspect */
+
+ let input_attachment_aspect_references: SmallVec<[_; 8]> = if device.api_version()
+ >= Version::V1_1
+ || device.enabled_extensions().khr_maintenance2
+ {
+ subpasses
+ .iter()
+ .enumerate()
+ .flat_map(|(subpass_num, subpass)| {
+ subpass.input_attachments.iter().enumerate().flat_map(
+ move |(atch_num, atch_ref)| {
+ atch_ref.as_ref().map(|atch_ref| {
+ ash::vk::InputAttachmentAspectReference {
+ subpass: subpass_num as u32,
+ input_attachment_index: atch_num as u32,
+ aspect_mask: atch_ref.aspects.into(),
+ }
+ })
+ },
+ )
+ })
+ .collect()
+ } else {
+ SmallVec::new()
+ };
+
+ let mut input_attachment_aspect_create_info =
+ if !input_attachment_aspect_references.is_empty() {
+ Some(ash::vk::RenderPassInputAttachmentAspectCreateInfo {
+ aspect_reference_count: input_attachment_aspect_references.len() as u32,
+ p_aspect_references: input_attachment_aspect_references.as_ptr(),
+ ..Default::default()
+ })
+ } else {
+ None
+ };
+
+ /* Multiview */
+
+ let is_multiview = subpasses[0].view_mask != 0;
+
+ let (multiview_view_masks, multiview_view_offsets): (SmallVec<[_; 4]>, SmallVec<[_; 4]>) =
+ if is_multiview {
+ (
+ subpasses.iter().map(|subpass| subpass.view_mask).collect(),
+ dependencies
+ .iter()
+ .map(|dependency| dependency.view_offset)
+ .collect(),
+ )
+ } else {
+ (SmallVec::new(), SmallVec::new())
+ };
+
+ let mut multiview_create_info = if is_multiview {
+ debug_assert!(multiview_view_masks.len() == subpasses.len());
+ debug_assert!(multiview_view_offsets.len() == dependencies.len());
+
+ Some(ash::vk::RenderPassMultiviewCreateInfo {
+ subpass_count: multiview_view_masks.len() as u32,
+ p_view_masks: multiview_view_masks.as_ptr(),
+ dependency_count: multiview_view_offsets.len() as u32,
+ p_view_offsets: multiview_view_offsets.as_ptr(),
+ correlation_mask_count: correlated_view_masks.len() as u32,
+ p_correlation_masks: correlated_view_masks.as_ptr(),
+ ..Default::default()
+ })
+ } else {
+ None
+ };
+
+ /* Create */
+
+ let mut create_info = ash::vk::RenderPassCreateInfo {
+ flags: ash::vk::RenderPassCreateFlags::empty(),
+ attachment_count: attachments_vk.len() as u32,
+ p_attachments: if attachments_vk.is_empty() {
+ ptr::null()
+ } else {
+ attachments_vk.as_ptr()
+ },
+ subpass_count: subpasses_vk.len() as u32,
+ p_subpasses: if subpasses_vk.is_empty() {
+ ptr::null()
+ } else {
+ subpasses_vk.as_ptr()
+ },
+ dependency_count: dependencies_vk.len() as u32,
+ p_dependencies: if dependencies_vk.is_empty() {
+ ptr::null()
+ } else {
+ dependencies_vk.as_ptr()
+ },
+ ..Default::default()
+ };
+
+ if let Some(input_attachment_aspect_create_info) =
+ input_attachment_aspect_create_info.as_mut()
+ {
+ input_attachment_aspect_create_info.p_next = create_info.p_next;
+ create_info.p_next = input_attachment_aspect_create_info as *const _ as *const _;
+ }
+
+ if let Some(multiview_create_info) = multiview_create_info.as_mut() {
+ multiview_create_info.p_next = create_info.p_next;
+ create_info.p_next = multiview_create_info as *const _ as *const _;
+ }
+
+ Ok({
+ let fns = device.fns();
+ let mut output = MaybeUninit::uninit();
+ (fns.v1_0.create_render_pass)(
+ device.handle(),
+ &create_info,
+ ptr::null(),
+ output.as_mut_ptr(),
+ )
+ .result()
+ .map_err(VulkanError::from)?;
+ output.assume_init()
+ })
+ }
+}
+
+/// Error that can happen when creating a `RenderPass`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum RenderPassCreationError {
+ /// Not enough memory.
+ OomError(OomError),
+
+ RequirementNotMet {
+ required_for: &'static str,
+ requires_one_of: RequiresOneOf,
+ },
+
+ /// An attachment is first used in the render pass with a read-only layout or as an input
+ /// attachment, but its `load_op` or `stencil_load_op` is [`LoadOp::Clear`].
+ AttachmentFirstUseLoadOpInvalid {
+ attachment: u32,
+ first_use_subpass: u32,
+ },
+
+ /// An attachment has an `initial_layout` or `final_layout` value that is invalid for the
+ /// provided `format`.
+ AttachmentLayoutInvalid { attachment: u32 },
+
+ /// Correlated view masks were included, but multiview is not enabled on the render pass.
+ CorrelatedViewMasksMultiviewNotEnabled,
+
+ /// The provided correlated view masks contain a bit that is set in more than one element.
+ CorrelatedViewMasksOverlapping,
+
+ /// A subpass dependency specified an access type that was not supported by the given stages.
+ DependencyAccessNotSupportedByStages { dependency: u32 },
+
+ /// A subpass dependency has both `src_subpass` and `dst_subpass` set to `None`.
+ DependencyBothSubpassesExternal { dependency: u32 },
+
+ /// A subpass dependency specifies a subpass self-dependency and includes framebuffer stages in
+ /// both `src_stages` and `dst_stages`, but the `by_region` dependency was not enabled.
+ DependencySelfDependencyFramebufferStagesWithoutByRegion { dependency: u32 },
+
+ /// A subpass dependency specifies a subpass self-dependency and includes
+ /// non-framebuffer stages, but the latest stage in `src_stages` is after the earliest stage
+ /// in `dst_stages`.
+ DependencySelfDependencySourceStageAfterDestinationStage { dependency: u32 },
+
+ /// A subpass dependency specifies a subpass self-dependency and has the `view_local` dependency
+ /// enabled, but the inner offset value was not 0.
+ DependencySelfDependencyViewLocalNonzeroViewOffset { dependency: u32 },
+
+ /// A subpass dependency specifies a subpass self-dependency without the `view_local`
+ /// dependency, but the referenced subpass has more than one bit set in its `view_mask`.
+ DependencySelfDependencyViewMaskMultiple { dependency: u32, subpass: u32 },
+
+ /// A subpass dependency has a `src_subpass` that is later than the `dst_subpass`.
+ DependencySourceSubpassAfterDestinationSubpass { dependency: u32 },
+
+ /// A subpass dependency has a bit set in the `src_stages` or `dst_stages` that is
+ /// not supported for graphics pipelines.
+ DependencyStageNotSupported { dependency: u32 },
+
+ /// A subpass index in a subpass dependency is not less than the number of subpasses in the
+ /// render pass.
+ DependencySubpassOutOfRange { dependency: u32, subpass: u32 },
+
+ /// In a subpass dependency, `dependency_flags` contains [`VIEW_LOCAL`], but `src_subpass` or
+ /// `dst_subpass` were set to `None`.
+ ///
+ /// [`VIEW_LOCAL`]: crate::sync::DependencyFlags::VIEW_LOCAL
+ DependencyViewLocalExternalDependency { dependency: u32 },
+
+ /// In a subpass dependency, `dependency_flags` contains [`VIEW_LOCAL`], but multiview is not
+ /// enabled on the render pass.
+ ///
+ /// [`VIEW_LOCAL`]: crate::sync::DependencyFlags::VIEW_LOCAL
+ DependencyViewLocalMultiviewNotEnabled { dependency: u32 },
+
+ /// In a subpass dependency, `view_offset` is not zero, but `dependency_flags` does not contain
+ /// [`VIEW_LOCAL`].
+ ///
+ /// [`VIEW_LOCAL`]: crate::sync::DependencyFlags::VIEW_LOCAL
+ DependencyViewOffzetNonzeroWithoutViewLocal { dependency: u32 },
+
+ /// A reference to an attachment used other than as an input attachment in a subpass has
+ /// one or more aspects selected.
+ SubpassAttachmentAspectsNotEmpty { subpass: u32, attachment: u32 },
+
+ /// An attachment used as an attachment in a subpass has a layout that is not supported for
+ /// that usage.
+ SubpassAttachmentLayoutInvalid {
+ subpass: u32,
+ attachment: u32,
+ usage: &'static str,
+ },
+
+ /// The layouts of all uses of an attachment in a subpass do not match.
+ SubpassAttachmentLayoutMismatch { subpass: u32, attachment: u32 },
+
+ /// An attachment index in a subpass is not less than the number of attachments in the render
+ /// pass.
+ SubpassAttachmentOutOfRange { subpass: u32, attachment: u32 },
+
+ /// An attachment is used as both a color attachment and a depth/stencil attachment in a
+ /// subpass.
+ SubpassAttachmentUsageColorDepthStencil { subpass: u32, attachment: u32 },
+
+ /// An attachment used as an attachment in a subpass has a format that does not support that
+ /// usage.
+ SubpassAttachmentFormatUsageNotSupported {
+ subpass: u32,
+ attachment: u32,
+ usage: &'static str,
+ },
+
+ /// An attachment used as a color attachment in a subpass with resolve attachments has a
+ /// `samples` value of [`SampleCount::Sample1`].
+ SubpassColorAttachmentWithResolveNotMultisampled { subpass: u32, attachment: u32 },
+
+ /// An attachment used as a color or depth/stencil attachment in a subpass has a `samples` value
+ /// that is different from the first color attachment.
+ SubpassColorDepthStencilAttachmentSamplesMismatch {
+ subpass: u32,
+ attachment: u32,
+ samples: SampleCount,
+ first_samples: SampleCount,
+ },
+
+ /// A reference to an attachment used as an input attachment in a subpass selects aspects that
+ /// are not present in the format of the attachment.
+ SubpassInputAttachmentAspectsNotCompatible { subpass: u32, attachment: u32 },
+
+ /// The `max_color_attachments` limit has been exceeded for a subpass.
+ SubpassMaxColorAttachmentsExceeded {
+ subpass: u32,
+ color_attachment_count: u32,
+ max: u32,
+ },
+
+ /// The `max_multiview_view_count` limit has been exceeded for a subpass.
+ SubpassMaxMultiviewViewCountExceeded {
+ subpass: u32,
+ view_count: u32,
+ max: u32,
+ },
+
+ /// The multiview state (whether `view_mask` is nonzero) of a subpass is different from the
+ /// first subpass.
+ SubpassMultiviewMismatch {
+ subpass: u32,
+ multiview: bool,
+ first_subpass_multiview: bool,
+ },
+
+ /// An attachment marked as a preserve attachment in a subpass is also used as an attachment
+ /// in that subpass.
+ SubpassPreserveAttachmentUsedElsewhere { subpass: u32, attachment: u32 },
+
+ /// The `resolve_attachments` field of a subpass was not empty, but its length did not match
+ /// the length of `color_attachments`.
+ SubpassResolveAttachmentsColorAttachmentsLenMismatch { subpass: u32 },
+
+ /// An attachment used as a resolve attachment in a subpass has a `format` value different from
+ /// the corresponding color attachment.
+ SubpassResolveAttachmentFormatMismatch {
+ subpass: u32,
+ resolve_attachment: u32,
+ color_attachment: u32,
+ },
+
+ /// An attachment used as a resolve attachment in a subpass has a `samples` value other than
+ /// [`SampleCount::Sample1`].
+ SubpassResolveAttachmentMultisampled { subpass: u32, attachment: u32 },
+
+ /// A resolve attachment in a subpass is `Some`, but the corresponding color attachment is
+ /// `None`.
+ SubpassResolveAttachmentWithoutColorAttachment { subpass: u32 },
+}
+
+impl Error for RenderPassCreationError {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match self {
+ RenderPassCreationError::OomError(err) => Some(err),
+ _ => None,
+ }
+ }
+}
+
+impl Display for RenderPassCreationError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
+ match self {
+ Self::OomError(_) => write!(f, "not enough memory available"),
+ Self::RequirementNotMet {
+ required_for,
+ requires_one_of,
+ } => write!(
+ f,
+ "a requirement was not met for: {}; requires one of: {}",
+ required_for, requires_one_of,
+ ),
+ Self::AttachmentFirstUseLoadOpInvalid {
+ attachment,
+ first_use_subpass,
+ } => write!(
+ f,
+ "attachment {} is first used in the render pass in subpass {} with a read-only \
+ layout or as an input attachment, but its `load_op` or `stencil_load_op` is \
+ `LoadOp::Clear`",
+ attachment, first_use_subpass,
+ ),
+ Self::AttachmentLayoutInvalid { attachment } => write!(
+ f,
+ "attachment {} has an `initial_layout` or `final_layout` value that is invalid for \
+ the provided `format`",
+ attachment,
+ ),
+ Self::CorrelatedViewMasksMultiviewNotEnabled => write!(
+ f,
+ "correlated view masks were included, but multiview is not enabled on the render \
+ pass",
+ ),
+ Self::CorrelatedViewMasksOverlapping => write!(
+ f,
+ "the provided correlated view masks contain a bit that is set in more than one \
+ element",
+ ),
+ Self::DependencyAccessNotSupportedByStages { dependency } => write!(
+ f,
+ "subpass dependency {} specified an access type that was not supported by the \
+ given stages",
+ dependency,
+ ),
+ Self::DependencySelfDependencyFramebufferStagesWithoutByRegion { dependency } => {
+ write!(
+ f,
+ "subpass dependency {} specifies a subpass self-dependency and includes \
+ framebuffer stages in both `src_stages` and `dst_stages`, but the \
+ `by_region` dependency was not enabled",
+ dependency,
+ )
+ }
+ Self::DependencySelfDependencySourceStageAfterDestinationStage { dependency } => {
+ write!(
+ f,
+ "subpass dependency {} specifies a subpass self-dependency and includes \
+ non-framebuffer stages, but the latest stage in `src_stages` is after the \
+ earliest stage in `dst_stages`",
+ dependency,
+ )
+ }
+ Self::DependencySelfDependencyViewLocalNonzeroViewOffset { dependency } => write!(
+ f,
+ "subpass dependency {} specifies a subpass self-dependency and has the \
+ `view_local` dependency enabled, but the inner offset value was not 0",
+ dependency,
+ ),
+ Self::DependencySelfDependencyViewMaskMultiple {
+ dependency,
+ subpass,
+ } => write!(
+ f,
+ "subpass dependency {} specifies a subpass self-dependency without the \
+ `view_local` dependency, but the referenced subpass {} has more than one bit set \
+ in its `view_mask`",
+ dependency, subpass,
+ ),
+ Self::DependencySourceSubpassAfterDestinationSubpass { dependency } => write!(
+ f,
+ "subpass dependency {} has a `src_subpass` that is later than the \
+ `dst_subpass`",
+ dependency,
+ ),
+ Self::DependencyStageNotSupported { dependency } => write!(
+ f,
+ "subpass dependency {} has a bit set in the `src_stages` or \
+ `dst_stages` that is not supported for graphics pipelines",
+ dependency,
+ ),
+ Self::DependencyBothSubpassesExternal { dependency } => write!(
+ f,
+ "subpass dependency {} has both `src_subpass` and `dst_subpass` set to \
+ `None`",
+ dependency,
+ ),
+ Self::DependencySubpassOutOfRange {
+ dependency,
+ subpass,
+ } => write!(
+ f,
+ "the subpass index {} in subpass dependency {} is not less than the number of \
+ subpasses in the render pass",
+ subpass, dependency,
+ ),
+ Self::DependencyViewLocalExternalDependency { dependency } => write!(
+ f,
+ "in subpass dependency {}, `dependency_flags` contains `VIEW_LOCAL`, but \
+ `src_subpass` or `dst_subpass` were set to `None`",
+ dependency,
+ ),
+ Self::DependencyViewLocalMultiviewNotEnabled { dependency } => write!(
+ f,
+ "in subpass dependency {}, `dependency_flags` contains `VIEW_LOCAL`, but \
+ multiview is not enabled on the render pass",
+ dependency,
+ ),
+ Self::DependencyViewOffzetNonzeroWithoutViewLocal { dependency } => write!(
+ f,
+ "in subpass dependency {}, `view_offset` is not zero, but `dependency_flags` does \
+ not contain `VIEW_LOCAL`",
+ dependency,
+ ),
+ Self::SubpassAttachmentAspectsNotEmpty {
+ subpass,
+ attachment,
+ } => write!(
+ f,
+ "a reference to attachment {} used other than as an input attachment in subpass {} \
+ has one or more aspects selected",
+ attachment, subpass,
+ ),
+ Self::SubpassAttachmentLayoutMismatch {
+ subpass,
+ attachment,
+ } => write!(
+ f,
+ "the layouts of all uses of attachment {} in subpass {} do not match.",
+ attachment, subpass,
+ ),
+ Self::SubpassAttachmentLayoutInvalid {
+ subpass,
+ attachment,
+ usage,
+ } => write!(
+ f,
+ "attachment {} used as {} attachment in subpass {} has a layout that is not \
+ supported for that usage",
+ attachment, usage, subpass,
+ ),
+ Self::SubpassAttachmentOutOfRange {
+ subpass,
+ attachment,
+ } => write!(
+ f,
+ "the attachment index {} in subpass {} is not less than the number of attachments \
+ in the render pass",
+ attachment, subpass,
+ ),
+ Self::SubpassAttachmentUsageColorDepthStencil {
+ subpass,
+ attachment,
+ } => write!(
+ f,
+ "attachment {} is used as both a color attachment and a depth/stencil attachment \
+ in subpass {}",
+ attachment, subpass,
+ ),
+ Self::SubpassAttachmentFormatUsageNotSupported {
+ subpass,
+ attachment,
+ usage,
+ } => write!(
+ f,
+ "attachment {} used as {} attachment in subpass {} has a format that does not \
+ support that usage",
+ attachment, usage, subpass,
+ ),
+ Self::SubpassColorAttachmentWithResolveNotMultisampled {
+ subpass,
+ attachment,
+ } => write!(
+ f,
+ "attachment {} used as a color attachment in subpass {} with resolve attachments \
+ has a `samples` value of `SampleCount::Sample1`",
+ attachment, subpass,
+ ),
+ Self::SubpassColorDepthStencilAttachmentSamplesMismatch {
+ subpass,
+ attachment,
+ samples,
+ first_samples,
+ } => write!(
+ f,
+ "attachment {} used as a color or depth/stencil attachment in subpass {} has a \
+ `samples` value {:?} that is different from the first color attachment ({:?})",
+ attachment, subpass, samples, first_samples,
+ ),
+ Self::SubpassInputAttachmentAspectsNotCompatible {
+ subpass,
+ attachment,
+ } => write!(
+ f,
+ "a reference to attachment {} used as an input attachment in subpass {} selects \
+ aspects that are not present in the format of the attachment",
+ attachment, subpass,
+ ),
+ Self::SubpassMaxColorAttachmentsExceeded { .. } => {
+ write!(f, "the `max_color_attachments` limit has been exceeded")
+ }
+ Self::SubpassMaxMultiviewViewCountExceeded { .. } => write!(
+ f,
+ "the `max_multiview_view_count` limit has been exceeded for a subpass",
+ ),
+ Self::SubpassMultiviewMismatch {
+ subpass,
+ multiview,
+ first_subpass_multiview,
+ } => write!(
+ f,
+ "the multiview state (whether `view_mask` is nonzero) of subpass {} is {}, which \
+ is different from the first subpass ({})",
+ subpass, multiview, first_subpass_multiview,
+ ),
+ Self::SubpassPreserveAttachmentUsedElsewhere {
+ subpass,
+ attachment,
+ } => write!(
+ f,
+ "attachment {} marked as a preserve attachment in subpass {} is also used as an \
+ attachment in that subpass",
+ attachment, subpass,
+ ),
+ Self::SubpassResolveAttachmentsColorAttachmentsLenMismatch { subpass } => write!(
+ f,
+ "the `resolve_attachments` field of subpass {} was not empty, but its length did \
+ not match the length of `color_attachments`",
+ subpass,
+ ),
+ Self::SubpassResolveAttachmentFormatMismatch {
+ subpass,
+ resolve_attachment,
+ color_attachment,
+ } => write!(
+ f,
+ "attachment {} used as a resolve attachment in subpass {} has a `format` value \
+ different from the corresponding color attachment {}",
+ subpass, resolve_attachment, color_attachment,
+ ),
+ Self::SubpassResolveAttachmentMultisampled {
+ subpass,
+ attachment,
+ } => write!(
+ f,
+ "attachment {} used as a resolve attachment in subpass {} has a `samples` value \
+ other than `SampleCount::Sample1`",
+ attachment, subpass,
+ ),
+ Self::SubpassResolveAttachmentWithoutColorAttachment { subpass } => write!(
+ f,
+ "a resolve attachment in subpass {} is `Some`, but the corresponding color \
+ attachment is `None`",
+ subpass,
+ ),
+ }
+ }
+}
+
+impl From<OomError> for RenderPassCreationError {
+ fn from(err: OomError) -> RenderPassCreationError {
+ RenderPassCreationError::OomError(err)
+ }
+}
+
+impl From<VulkanError> for RenderPassCreationError {
+ fn from(err: VulkanError) -> RenderPassCreationError {
+ match err {
+ err @ VulkanError::OutOfHostMemory => {
+ RenderPassCreationError::OomError(OomError::from(err))
+ }
+ err @ VulkanError::OutOfDeviceMemory => {
+ RenderPassCreationError::OomError(OomError::from(err))
+ }
+ _ => panic!("unexpected error: {:?}", err),
+ }
+ }
+}
+
+impl From<RequirementNotMet> for RenderPassCreationError {
+ fn from(err: RequirementNotMet) -> Self {
+ Self::RequirementNotMet {
+ required_for: err.required_for,
+ requires_one_of: err.requires_one_of,
+ }
+ }
+}