blob: 07c45067b0d5d6c532bffdf725bdede1d604dd24 [file] [log] [blame]
// 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,
}
}
}