diff options
Diffstat (limited to 'src/render_pass/create.rs')
-rw-r--r-- | src/render_pass/create.rs | 1979 |
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, + } + } +} |