// Copyright (c) 2022 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. use 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 { 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::>(); 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::>(); 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::>(); 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 { 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::>(); 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::>(); 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::>(); /* 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 for RenderPassCreationError { fn from(err: OomError) -> RenderPassCreationError { RenderPassCreationError::OomError(err) } } impl From 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 for RenderPassCreationError { fn from(err: RequirementNotMet) -> Self { Self::RequirementNotMet { required_for: err.required_for, requires_one_of: err.requires_one_of, } } }