blob: 27a162aedd81c42f9f5ff6348b39e00006e4612d [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 crate::{
buffer::{view::BufferView, BufferUsage, Subbuffer},
command_buffer::{
allocator::CommandBufferAllocator,
auto::{RenderPassState, RenderPassStateType},
synced::{Command, Resource, SyncCommandBufferBuilder, SyncCommandBufferBuilderError},
sys::UnsafeCommandBufferBuilder,
AutoCommandBufferBuilder, DispatchIndirectCommand, DrawIndexedIndirectCommand,
DrawIndirectCommand, ResourceInCommand, ResourceUseRef, SubpassContents,
},
descriptor_set::{layout::DescriptorType, DescriptorBindingResources},
device::{DeviceOwned, QueueFlags},
format::{Format, FormatFeatures},
image::{
view::ImageViewType, ImageAccess, ImageAspects, ImageSubresourceRange, ImageViewAbstract,
SampleCount,
},
pipeline::{
graphics::{
input_assembly::{PrimitiveTopology, PrimitiveTopologyClass},
render_pass::PipelineRenderPassType,
vertex_input::VertexInputRate,
},
DynamicState, GraphicsPipeline, PartialStateMode, Pipeline, PipelineLayout,
},
sampler::{Sampler, SamplerImageViewIncompatibleError},
shader::{DescriptorBindingRequirements, ShaderScalarType, ShaderStage},
sync::{AccessFlags, PipelineMemoryAccess, PipelineStages},
DeviceSize, RequiresOneOf, VulkanObject,
};
use std::{
cmp::min,
error::Error,
fmt::{Display, Error as FmtError, Formatter},
mem::size_of,
ops::Range,
sync::Arc,
};
/// # Commands to execute a bound pipeline.
///
/// Dispatch commands require a compute queue, draw commands require a graphics queue.
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
/// Perform a single compute operation using a compute pipeline.
///
/// A compute pipeline must have been bound using
/// [`bind_pipeline_compute`](Self::bind_pipeline_compute). Any resources used by the compute
/// pipeline, such as descriptor sets, must have been set beforehand.
pub fn dispatch(
&mut self,
group_counts: [u32; 3],
) -> Result<&mut Self, PipelineExecutionError> {
self.validate_dispatch(group_counts)?;
unsafe {
self.inner.dispatch(group_counts)?;
}
Ok(self)
}
fn validate_dispatch(&self, group_counts: [u32; 3]) -> Result<(), PipelineExecutionError> {
let queue_family_properties = self.queue_family_properties();
// VUID-vkCmdDispatch-commandBuffer-cmdpool
if !queue_family_properties
.queue_flags
.intersects(QueueFlags::COMPUTE)
{
return Err(PipelineExecutionError::NotSupportedByQueueFamily);
}
// VUID-vkCmdDispatch-renderpass
if self.render_pass_state.is_some() {
return Err(PipelineExecutionError::ForbiddenInsideRenderPass);
}
// VUID-vkCmdDispatch-None-02700
let pipeline = match self.state().pipeline_compute() {
Some(x) => x.as_ref(),
None => return Err(PipelineExecutionError::PipelineNotBound),
};
self.validate_pipeline_descriptor_sets(pipeline)?;
self.validate_pipeline_push_constants(pipeline.layout())?;
let max = self
.device()
.physical_device()
.properties()
.max_compute_work_group_count;
// VUID-vkCmdDispatch-groupCountX-00386
// VUID-vkCmdDispatch-groupCountY-00387
// VUID-vkCmdDispatch-groupCountZ-00388
if group_counts[0] > max[0] || group_counts[1] > max[1] || group_counts[2] > max[2] {
return Err(PipelineExecutionError::MaxComputeWorkGroupCountExceeded {
requested: group_counts,
max,
});
}
Ok(())
}
/// Perform multiple compute operations using a compute pipeline. One dispatch is performed for
/// each [`DispatchIndirectCommand`] struct in `indirect_buffer`.
///
/// A compute pipeline must have been bound using
/// [`bind_pipeline_compute`](Self::bind_pipeline_compute). Any resources used by the compute
/// pipeline, such as descriptor sets, must have been set beforehand.
pub fn dispatch_indirect(
&mut self,
indirect_buffer: Subbuffer<[DispatchIndirectCommand]>,
) -> Result<&mut Self, PipelineExecutionError> {
self.validate_dispatch_indirect(indirect_buffer.as_bytes())?;
unsafe {
self.inner.dispatch_indirect(indirect_buffer)?;
}
Ok(self)
}
fn validate_dispatch_indirect(
&self,
indirect_buffer: &Subbuffer<[u8]>,
) -> Result<(), PipelineExecutionError> {
let queue_family_properties = self.queue_family_properties();
// VUID-vkCmdDispatchIndirect-commandBuffer-cmdpool
if !queue_family_properties
.queue_flags
.intersects(QueueFlags::COMPUTE)
{
return Err(PipelineExecutionError::NotSupportedByQueueFamily);
}
// VUID-vkCmdDispatchIndirect-renderpass
if self.render_pass_state.is_some() {
return Err(PipelineExecutionError::ForbiddenInsideRenderPass);
}
// VUID-vkCmdDispatchIndirect-None-02700
let pipeline = match self.state().pipeline_compute() {
Some(x) => x.as_ref(),
None => return Err(PipelineExecutionError::PipelineNotBound),
};
self.validate_pipeline_descriptor_sets(pipeline)?;
self.validate_pipeline_push_constants(pipeline.layout())?;
self.validate_indirect_buffer(indirect_buffer)?;
Ok(())
}
/// Perform a single draw operation using a graphics pipeline.
///
/// The parameters specify the first vertex and the number of vertices to draw, and the first
/// instance and number of instances. For non-instanced drawing, specify `instance_count` as 1
/// and `first_instance` as 0.
///
/// A graphics pipeline must have been bound using
/// [`bind_pipeline_graphics`](Self::bind_pipeline_graphics). Any resources used by the graphics
/// pipeline, such as descriptor sets, vertex buffers and dynamic state, must have been set
/// beforehand. If the bound graphics pipeline uses vertex buffers, then the provided vertex and
/// instance ranges must be in range of the bound vertex buffers.
pub fn draw(
&mut self,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) -> Result<&mut Self, PipelineExecutionError> {
self.validate_draw(vertex_count, instance_count, first_vertex, first_instance)?;
unsafe {
self.inner
.draw(vertex_count, instance_count, first_vertex, first_instance)?;
}
if let RenderPassStateType::BeginRendering(state) =
&mut self.render_pass_state.as_mut().unwrap().render_pass
{
state.pipeline_used = true;
}
Ok(self)
}
fn validate_draw(
&self,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) -> Result<(), PipelineExecutionError> {
// VUID-vkCmdDraw-renderpass
let render_pass_state = self
.render_pass_state
.as_ref()
.ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?;
// VUID-vkCmdDraw-None-02700
let pipeline = match self.state().pipeline_graphics() {
Some(x) => x.as_ref(),
None => return Err(PipelineExecutionError::PipelineNotBound),
};
self.validate_pipeline_descriptor_sets(pipeline)?;
self.validate_pipeline_push_constants(pipeline.layout())?;
self.validate_pipeline_graphics_dynamic_state(pipeline)?;
self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?;
self.validate_pipeline_graphics_vertex_buffers(
pipeline,
Some((first_vertex, vertex_count)),
Some((first_instance, instance_count)),
)?;
Ok(())
}
/// Perform multiple draw operations using a graphics pipeline.
///
/// One draw is performed for each [`DrawIndirectCommand`] struct in `indirect_buffer`.
/// The maximum number of draw commands in the buffer is limited by the
/// [`max_draw_indirect_count`](crate::device::Properties::max_draw_indirect_count) limit.
/// This limit is 1 unless the
/// [`multi_draw_indirect`](crate::device::Features::multi_draw_indirect) feature has been
/// enabled.
///
/// A graphics pipeline must have been bound using
/// [`bind_pipeline_graphics`](Self::bind_pipeline_graphics). Any resources used by the graphics
/// pipeline, such as descriptor sets, vertex buffers and dynamic state, must have been set
/// beforehand. If the bound graphics pipeline uses vertex buffers, then the vertex and instance
/// ranges of each `DrawIndirectCommand` in the indirect buffer must be in range of the bound
/// vertex buffers.
pub fn draw_indirect(
&mut self,
indirect_buffer: Subbuffer<[DrawIndirectCommand]>,
) -> Result<&mut Self, PipelineExecutionError> {
let draw_count = indirect_buffer.len() as u32;
let stride = size_of::<DrawIndirectCommand>() as u32;
self.validate_draw_indirect(indirect_buffer.as_bytes(), draw_count, stride)?;
unsafe {
self.inner
.draw_indirect(indirect_buffer, draw_count, stride)?;
}
if let RenderPassStateType::BeginRendering(state) =
&mut self.render_pass_state.as_mut().unwrap().render_pass
{
state.pipeline_used = true;
}
Ok(self)
}
fn validate_draw_indirect(
&self,
indirect_buffer: &Subbuffer<[u8]>,
draw_count: u32,
_stride: u32,
) -> Result<(), PipelineExecutionError> {
// VUID-vkCmdDrawIndirect-renderpass
let render_pass_state = self
.render_pass_state
.as_ref()
.ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?;
// VUID-vkCmdDrawIndirect-None-02700
let pipeline = match self.state().pipeline_graphics() {
Some(x) => x.as_ref(),
None => return Err(PipelineExecutionError::PipelineNotBound),
};
self.validate_pipeline_descriptor_sets(pipeline)?;
self.validate_pipeline_push_constants(pipeline.layout())?;
self.validate_pipeline_graphics_dynamic_state(pipeline)?;
self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?;
self.validate_pipeline_graphics_vertex_buffers(pipeline, None, None)?;
self.validate_indirect_buffer(indirect_buffer)?;
// VUID-vkCmdDrawIndirect-drawCount-02718
if draw_count > 1 && !self.device().enabled_features().multi_draw_indirect {
return Err(PipelineExecutionError::RequirementNotMet {
required_for: "`draw_count` is greater than `1`",
requires_one_of: RequiresOneOf {
features: &["multi_draw_indirect"],
..Default::default()
},
});
}
let max = self
.device()
.physical_device()
.properties()
.max_draw_indirect_count;
// VUID-vkCmdDrawIndirect-drawCount-02719
if draw_count > max {
return Err(PipelineExecutionError::MaxDrawIndirectCountExceeded {
provided: draw_count,
max,
});
}
Ok(())
}
/// Perform a single draw operation using a graphics pipeline, using an index buffer.
///
/// The parameters specify the first index and the number of indices in the index buffer that
/// should be used, and the first instance and number of instances. For non-instanced drawing,
/// specify `instance_count` as 1 and `first_instance` as 0. The `vertex_offset` is a constant
/// value that should be added to each index in the index buffer to produce the final vertex
/// number to be used.
///
/// An index buffer must have been bound using
/// [`bind_index_buffer`](Self::bind_index_buffer), and the provided index range must be in
/// range of the bound index buffer.
///
/// A graphics pipeline must have been bound using
/// [`bind_pipeline_graphics`](Self::bind_pipeline_graphics). Any resources used by the graphics
/// pipeline, such as descriptor sets, vertex buffers and dynamic state, must have been set
/// beforehand. If the bound graphics pipeline uses vertex buffers, then the provided instance
/// range must be in range of the bound vertex buffers. The vertex indices in the index buffer
/// must be in range of the bound vertex buffers.
pub fn draw_indexed(
&mut self,
index_count: u32,
instance_count: u32,
first_index: u32,
vertex_offset: i32,
first_instance: u32,
) -> Result<&mut Self, PipelineExecutionError> {
self.validate_draw_indexed(
index_count,
instance_count,
first_index,
vertex_offset,
first_instance,
)?;
unsafe {
self.inner.draw_indexed(
index_count,
instance_count,
first_index,
vertex_offset,
first_instance,
)?;
}
if let RenderPassStateType::BeginRendering(state) =
&mut self.render_pass_state.as_mut().unwrap().render_pass
{
state.pipeline_used = true;
}
Ok(self)
}
fn validate_draw_indexed(
&self,
index_count: u32,
instance_count: u32,
first_index: u32,
_vertex_offset: i32,
first_instance: u32,
) -> Result<(), PipelineExecutionError> {
// TODO: how to handle an index out of range of the vertex buffers?
// VUID-vkCmdDrawIndexed-renderpass
let render_pass_state = self
.render_pass_state
.as_ref()
.ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?;
// VUID-vkCmdDrawIndexed-None-02700
let pipeline = match self.state().pipeline_graphics() {
Some(x) => x.as_ref(),
None => return Err(PipelineExecutionError::PipelineNotBound),
};
self.validate_pipeline_descriptor_sets(pipeline)?;
self.validate_pipeline_push_constants(pipeline.layout())?;
self.validate_pipeline_graphics_dynamic_state(pipeline)?;
self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?;
self.validate_pipeline_graphics_vertex_buffers(
pipeline,
None,
Some((first_instance, instance_count)),
)?;
self.validate_index_buffer(Some((first_index, index_count)))?;
Ok(())
}
/// Perform multiple draw operations using a graphics pipeline, using an index buffer.
///
/// One draw is performed for each [`DrawIndexedIndirectCommand`] struct in `indirect_buffer`.
/// The maximum number of draw commands in the buffer is limited by the
/// [`max_draw_indirect_count`](crate::device::Properties::max_draw_indirect_count) limit.
/// This limit is 1 unless the
/// [`multi_draw_indirect`](crate::device::Features::multi_draw_indirect) feature has been
/// enabled.
///
/// An index buffer must have been bound using
/// [`bind_index_buffer`](Self::bind_index_buffer), and the index ranges of each
/// `DrawIndexedIndirectCommand` in the indirect buffer must be in range of the bound index
/// buffer.
///
/// A graphics pipeline must have been bound using
/// [`bind_pipeline_graphics`](Self::bind_pipeline_graphics). Any resources used by the graphics
/// pipeline, such as descriptor sets, vertex buffers and dynamic state, must have been set
/// beforehand. If the bound graphics pipeline uses vertex buffers, then the instance ranges of
/// each `DrawIndexedIndirectCommand` in the indirect buffer must be in range of the bound
/// vertex buffers.
pub fn draw_indexed_indirect(
&mut self,
indirect_buffer: Subbuffer<[DrawIndexedIndirectCommand]>,
) -> Result<&mut Self, PipelineExecutionError> {
let draw_count = indirect_buffer.len() as u32;
let stride = size_of::<DrawIndexedIndirectCommand>() as u32;
self.validate_draw_indexed_indirect(indirect_buffer.as_bytes(), draw_count, stride)?;
unsafe {
self.inner
.draw_indexed_indirect(indirect_buffer, draw_count, stride)?;
}
if let RenderPassStateType::BeginRendering(state) =
&mut self.render_pass_state.as_mut().unwrap().render_pass
{
state.pipeline_used = true;
}
Ok(self)
}
fn validate_draw_indexed_indirect(
&self,
indirect_buffer: &Subbuffer<[u8]>,
draw_count: u32,
_stride: u32,
) -> Result<(), PipelineExecutionError> {
// VUID-vkCmdDrawIndexedIndirect-renderpass
let render_pass_state = self
.render_pass_state
.as_ref()
.ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?;
// VUID-vkCmdDrawIndexedIndirect-None-02700
let pipeline = match self.state().pipeline_graphics() {
Some(x) => x.as_ref(),
None => return Err(PipelineExecutionError::PipelineNotBound),
};
self.validate_pipeline_descriptor_sets(pipeline)?;
self.validate_pipeline_push_constants(pipeline.layout())?;
self.validate_pipeline_graphics_dynamic_state(pipeline)?;
self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?;
self.validate_pipeline_graphics_vertex_buffers(pipeline, None, None)?;
self.validate_index_buffer(None)?;
self.validate_indirect_buffer(indirect_buffer)?;
// VUID-vkCmdDrawIndexedIndirect-drawCount-02718
if draw_count > 1 && !self.device().enabled_features().multi_draw_indirect {
return Err(PipelineExecutionError::RequirementNotMet {
required_for: "`draw_count` is greater than `1`",
requires_one_of: RequiresOneOf {
features: &["multi_draw_indirect"],
..Default::default()
},
});
}
let max = self
.device()
.physical_device()
.properties()
.max_draw_indirect_count;
// VUID-vkCmdDrawIndexedIndirect-drawCount-02719
if draw_count > max {
return Err(PipelineExecutionError::MaxDrawIndirectCountExceeded {
provided: draw_count,
max,
});
}
Ok(())
}
fn validate_index_buffer(
&self,
indices: Option<(u32, u32)>,
) -> Result<(), PipelineExecutionError> {
let current_state = self.state();
// VUID?
let (index_buffer, index_type) = match current_state.index_buffer() {
Some(x) => x,
None => return Err(PipelineExecutionError::IndexBufferNotBound),
};
if let Some((first_index, index_count)) = indices {
let max_index_count = (index_buffer.size() / index_type.size()) as u32;
// // VUID-vkCmdDrawIndexed-firstIndex-04932
if first_index + index_count > max_index_count {
return Err(PipelineExecutionError::IndexBufferRangeOutOfBounds {
highest_index: first_index + index_count,
max_index_count,
});
}
}
Ok(())
}
fn validate_indirect_buffer(
&self,
buffer: &Subbuffer<[u8]>,
) -> Result<(), PipelineExecutionError> {
// VUID-vkCmdDispatchIndirect-commonparent
assert_eq!(self.device(), buffer.device());
// VUID-vkCmdDispatchIndirect-buffer-02709
if !buffer
.buffer()
.usage()
.intersects(BufferUsage::INDIRECT_BUFFER)
{
return Err(PipelineExecutionError::IndirectBufferMissingUsage);
}
// VUID-vkCmdDispatchIndirect-offset-02710
// TODO:
Ok(())
}
fn validate_pipeline_descriptor_sets<Pl: Pipeline>(
&self,
pipeline: &Pl,
) -> Result<(), PipelineExecutionError> {
fn validate_resources<T>(
set_num: u32,
binding_num: u32,
binding_reqs: &DescriptorBindingRequirements,
elements: &[Option<T>],
mut extra_check: impl FnMut(u32, &T) -> Result<(), DescriptorResourceInvalidError>,
) -> Result<(), PipelineExecutionError> {
let elements_to_check = if let Some(descriptor_count) = binding_reqs.descriptor_count {
// The shader has a fixed-sized array, so it will never access more than
// the first `descriptor_count` elements.
elements.get(..descriptor_count as usize).ok_or({
// There are less than `descriptor_count` elements in `elements`
PipelineExecutionError::DescriptorResourceInvalid {
set_num,
binding_num,
index: elements.len() as u32,
error: DescriptorResourceInvalidError::Missing,
}
})?
} else {
// The shader has a runtime-sized array, so any element could potentially
// be accessed. We must check them all.
elements
};
for (index, element) in elements_to_check.iter().enumerate() {
let index = index as u32;
// VUID-vkCmdDispatch-None-02699
let element = match element {
Some(x) => x,
None => {
return Err(PipelineExecutionError::DescriptorResourceInvalid {
set_num,
binding_num,
index,
error: DescriptorResourceInvalidError::Missing,
})
}
};
if let Err(error) = extra_check(index, element) {
return Err(PipelineExecutionError::DescriptorResourceInvalid {
set_num,
binding_num,
index,
error,
});
}
}
Ok(())
}
if pipeline.num_used_descriptor_sets() == 0 {
return Ok(());
}
let current_state = self.state();
// VUID-vkCmdDispatch-None-02697
let bindings_pipeline_layout =
match current_state.descriptor_sets_pipeline_layout(pipeline.bind_point()) {
Some(x) => x,
None => return Err(PipelineExecutionError::PipelineLayoutNotCompatible),
};
// VUID-vkCmdDispatch-None-02697
if !pipeline.layout().is_compatible_with(
bindings_pipeline_layout,
pipeline.num_used_descriptor_sets(),
) {
return Err(PipelineExecutionError::PipelineLayoutNotCompatible);
}
for (&(set_num, binding_num), binding_reqs) in pipeline.descriptor_binding_requirements() {
let layout_binding =
&pipeline.layout().set_layouts()[set_num as usize].bindings()[&binding_num];
let check_buffer =
|_index: u32, (_buffer, _range): &(Subbuffer<[u8]>, Range<DeviceSize>)| Ok(());
let check_buffer_view = |index: u32, buffer_view: &Arc<BufferView>| {
for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
.chain(binding_reqs.descriptors.get(&None))
{
if layout_binding.descriptor_type == DescriptorType::StorageTexelBuffer {
// VUID-vkCmdDispatch-OpTypeImage-06423
if binding_reqs.image_format.is_none()
&& !desc_reqs.memory_write.is_empty()
&& !buffer_view
.format_features()
.intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT)
{
return Err(DescriptorResourceInvalidError::StorageWriteWithoutFormatNotSupported);
}
// VUID-vkCmdDispatch-OpTypeImage-06424
if binding_reqs.image_format.is_none()
&& !desc_reqs.memory_read.is_empty()
&& !buffer_view
.format_features()
.intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT)
{
return Err(DescriptorResourceInvalidError::StorageReadWithoutFormatNotSupported);
}
}
}
Ok(())
};
let check_image_view_common = |index: u32, image_view: &Arc<dyn ImageViewAbstract>| {
for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
.chain(binding_reqs.descriptors.get(&None))
{
// VUID-vkCmdDispatch-None-02691
if desc_reqs.storage_image_atomic
&& !image_view
.format_features()
.intersects(FormatFeatures::STORAGE_IMAGE_ATOMIC)
{
return Err(DescriptorResourceInvalidError::StorageImageAtomicNotSupported);
}
if layout_binding.descriptor_type == DescriptorType::StorageImage {
// VUID-vkCmdDispatch-OpTypeImage-06423
if binding_reqs.image_format.is_none()
&& !desc_reqs.memory_write.is_empty()
&& !image_view
.format_features()
.intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT)
{
return Err(
DescriptorResourceInvalidError::StorageWriteWithoutFormatNotSupported,
);
}
// VUID-vkCmdDispatch-OpTypeImage-06424
if binding_reqs.image_format.is_none()
&& !desc_reqs.memory_read.is_empty()
&& !image_view
.format_features()
.intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT)
{
return Err(
DescriptorResourceInvalidError::StorageReadWithoutFormatNotSupported,
);
}
}
}
/*
Instruction/Sampler/Image View Validation
https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap16.html#textures-input-validation
*/
// The SPIR-V Image Format is not compatible with the image view’s format.
if let Some(format) = binding_reqs.image_format {
if image_view.format() != Some(format) {
return Err(DescriptorResourceInvalidError::ImageViewFormatMismatch {
required: format,
provided: image_view.format(),
});
}
}
// Rules for viewType
if let Some(image_view_type) = binding_reqs.image_view_type {
if image_view.view_type() != image_view_type {
return Err(DescriptorResourceInvalidError::ImageViewTypeMismatch {
required: image_view_type,
provided: image_view.view_type(),
});
}
}
// - If the image was created with VkImageCreateInfo::samples equal to
// VK_SAMPLE_COUNT_1_BIT, the instruction must have MS = 0.
// - If the image was created with VkImageCreateInfo::samples not equal to
// VK_SAMPLE_COUNT_1_BIT, the instruction must have MS = 1.
if binding_reqs.image_multisampled
!= (image_view.image().samples() != SampleCount::Sample1)
{
return Err(
DescriptorResourceInvalidError::ImageViewMultisampledMismatch {
required: binding_reqs.image_multisampled,
provided: image_view.image().samples() != SampleCount::Sample1,
},
);
}
// - If the Sampled Type of the OpTypeImage does not match the numeric format of the
// image, as shown in the SPIR-V Sampled Type column of the
// Interpretation of Numeric Format table.
// - If the signedness of any read or sample operation does not match the signedness of
// the image’s format.
if let Some(scalar_type) = binding_reqs.image_scalar_type {
let aspects = image_view.subresource_range().aspects;
let view_scalar_type = ShaderScalarType::from(
if aspects.intersects(
ImageAspects::COLOR
| ImageAspects::PLANE_0
| ImageAspects::PLANE_1
| ImageAspects::PLANE_2,
) {
image_view.format().unwrap().type_color().unwrap()
} else if aspects.intersects(ImageAspects::DEPTH) {
image_view.format().unwrap().type_depth().unwrap()
} else if aspects.intersects(ImageAspects::STENCIL) {
image_view.format().unwrap().type_stencil().unwrap()
} else {
// Per `ImageViewBuilder::aspects` and
// VUID-VkDescriptorImageInfo-imageView-01976
unreachable!()
},
);
if scalar_type != view_scalar_type {
return Err(
DescriptorResourceInvalidError::ImageViewScalarTypeMismatch {
required: scalar_type,
provided: view_scalar_type,
},
);
}
}
Ok(())
};
let check_sampler_common = |index: u32, sampler: &Arc<Sampler>| {
for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
.chain(binding_reqs.descriptors.get(&None))
{
// VUID-vkCmdDispatch-None-02703
// VUID-vkCmdDispatch-None-02704
if desc_reqs.sampler_no_unnormalized_coordinates
&& sampler.unnormalized_coordinates()
{
return Err(
DescriptorResourceInvalidError::SamplerUnnormalizedCoordinatesNotAllowed,
);
}
// - OpImageFetch, OpImageSparseFetch, OpImage*Gather, and OpImageSparse*Gather must not
// be used with a sampler that enables sampler Y′CBCR conversion.
// - The ConstOffset and Offset operands must not be used with a sampler that enables
// sampler Y′CBCR conversion.
if desc_reqs.sampler_no_ycbcr_conversion
&& sampler.sampler_ycbcr_conversion().is_some()
{
return Err(
DescriptorResourceInvalidError::SamplerYcbcrConversionNotAllowed,
);
}
/*
Instruction/Sampler/Image View Validation
https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap16.html#textures-input-validation
*/
// - The SPIR-V instruction is one of the OpImage*Dref* instructions and the sampler
// compareEnable is VK_FALSE
// - The SPIR-V instruction is not one of the OpImage*Dref* instructions and the sampler
// compareEnable is VK_TRUE
if desc_reqs.sampler_compare != sampler.compare().is_some() {
return Err(DescriptorResourceInvalidError::SamplerCompareMismatch {
required: desc_reqs.sampler_compare,
provided: sampler.compare().is_some(),
});
}
}
Ok(())
};
let check_image_view = |index: u32, image_view: &Arc<dyn ImageViewAbstract>| {
check_image_view_common(index, image_view)?;
if let Some(sampler) = layout_binding.immutable_samplers.get(index as usize) {
check_sampler_common(index, sampler)?;
}
Ok(())
};
let check_image_view_sampler =
|index: u32, (image_view, sampler): &(Arc<dyn ImageViewAbstract>, Arc<Sampler>)| {
check_image_view_common(index, image_view)?;
check_sampler_common(index, sampler)?;
Ok(())
};
let check_sampler = |index: u32, sampler: &Arc<Sampler>| {
check_sampler_common(index, sampler)?;
for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
.chain(binding_reqs.descriptors.get(&None))
{
// Check sampler-image compatibility. Only done for separate samplers;
// combined image samplers are checked when updating the descriptor set.
// If the image view isn't actually present in the resources, then just skip it.
// It will be caught later by check_resources.
let iter = desc_reqs.sampler_with_images.iter().filter_map(|id| {
current_state
.descriptor_set(pipeline.bind_point(), id.set)
.and_then(|set| set.resources().binding(id.binding))
.and_then(|res| match res {
DescriptorBindingResources::ImageView(elements) => elements
.get(id.index as usize)
.and_then(|opt| opt.as_ref().map(|opt| (id, opt))),
_ => None,
})
});
for (id, image_view) in iter {
if let Err(error) = sampler.check_can_sample(image_view.as_ref()) {
return Err(
DescriptorResourceInvalidError::SamplerImageViewIncompatible {
image_view_set_num: id.set,
image_view_binding_num: id.binding,
image_view_index: id.index,
error,
},
);
}
}
}
Ok(())
};
let check_none = |index: u32, _: &()| {
if let Some(sampler) = layout_binding.immutable_samplers.get(index as usize) {
check_sampler(index, sampler)?;
}
Ok(())
};
let set_resources = match current_state.descriptor_set(pipeline.bind_point(), set_num) {
Some(x) => x.resources(),
None => return Err(PipelineExecutionError::DescriptorSetNotBound { set_num }),
};
let binding_resources = set_resources.binding(binding_num).unwrap();
match binding_resources {
DescriptorBindingResources::None(elements) => {
validate_resources(set_num, binding_num, binding_reqs, elements, check_none)?;
}
DescriptorBindingResources::Buffer(elements) => {
validate_resources(set_num, binding_num, binding_reqs, elements, check_buffer)?;
}
DescriptorBindingResources::BufferView(elements) => {
validate_resources(
set_num,
binding_num,
binding_reqs,
elements,
check_buffer_view,
)?;
}
DescriptorBindingResources::ImageView(elements) => {
validate_resources(
set_num,
binding_num,
binding_reqs,
elements,
check_image_view,
)?;
}
DescriptorBindingResources::ImageViewSampler(elements) => {
validate_resources(
set_num,
binding_num,
binding_reqs,
elements,
check_image_view_sampler,
)?;
}
DescriptorBindingResources::Sampler(elements) => {
validate_resources(
set_num,
binding_num,
binding_reqs,
elements,
check_sampler,
)?;
}
}
}
Ok(())
}
fn validate_pipeline_push_constants(
&self,
pipeline_layout: &PipelineLayout,
) -> Result<(), PipelineExecutionError> {
if pipeline_layout.push_constant_ranges().is_empty()
|| self.device().enabled_features().maintenance4
{
return Ok(());
}
let current_state = self.state();
// VUID-vkCmdDispatch-maintenance4-06425
let constants_pipeline_layout = match current_state.push_constants_pipeline_layout() {
Some(x) => x,
None => return Err(PipelineExecutionError::PushConstantsMissing),
};
// VUID-vkCmdDispatch-maintenance4-06425
if pipeline_layout.handle() != constants_pipeline_layout.handle()
&& pipeline_layout.push_constant_ranges()
!= constants_pipeline_layout.push_constant_ranges()
{
return Err(PipelineExecutionError::PushConstantsNotCompatible);
}
let set_bytes = current_state.push_constants();
// VUID-vkCmdDispatch-maintenance4-06425
if !pipeline_layout
.push_constant_ranges()
.iter()
.all(|pc_range| set_bytes.contains(pc_range.offset..pc_range.offset + pc_range.size))
{
return Err(PipelineExecutionError::PushConstantsMissing);
}
Ok(())
}
fn validate_pipeline_graphics_dynamic_state(
&self,
pipeline: &GraphicsPipeline,
) -> Result<(), PipelineExecutionError> {
let device = pipeline.device();
let current_state = self.state();
// VUID-vkCmdDraw-commandBuffer-02701
for dynamic_state in pipeline
.dynamic_states()
.filter(|(_, d)| *d)
.map(|(s, _)| s)
{
match dynamic_state {
DynamicState::BlendConstants => {
// VUID?
if current_state.blend_constants().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::ColorWriteEnable => {
// VUID-vkCmdDraw-attachmentCount-06667
let enables = if let Some(enables) = current_state.color_write_enable() {
enables
} else {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
};
// VUID-vkCmdDraw-attachmentCount-06667
if enables.len() < pipeline.color_blend_state().unwrap().attachments.len() {
return Err(
PipelineExecutionError::DynamicColorWriteEnableNotEnoughValues {
color_write_enable_count: enables.len() as u32,
attachment_count: pipeline
.color_blend_state()
.unwrap()
.attachments
.len() as u32,
},
);
}
}
DynamicState::CullMode => {
// VUID?
if current_state.cull_mode().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::DepthBias => {
// VUID?
if current_state.depth_bias().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::DepthBiasEnable => {
// VUID-vkCmdDraw-None-04877
if current_state.depth_bias_enable().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::DepthBounds => {
// VUID?
if current_state.depth_bounds().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::DepthBoundsTestEnable => {
// VUID?
if current_state.depth_bounds_test_enable().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::DepthCompareOp => {
// VUID?
if current_state.depth_compare_op().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::DepthTestEnable => {
// VUID?
if current_state.depth_test_enable().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::DepthWriteEnable => {
// VUID?
if current_state.depth_write_enable().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
// TODO: Check if the depth buffer is writable
}
DynamicState::DiscardRectangle => {
let discard_rectangle_count =
match pipeline.discard_rectangle_state().unwrap().rectangles {
PartialStateMode::Dynamic(count) => count,
_ => unreachable!(),
};
for num in 0..discard_rectangle_count {
// VUID?
if current_state.discard_rectangle(num).is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
}
DynamicState::ExclusiveScissor => todo!(),
DynamicState::FragmentShadingRate => todo!(),
DynamicState::FrontFace => {
// VUID?
if current_state.front_face().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::LineStipple => {
// VUID?
if current_state.line_stipple().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::LineWidth => {
// VUID?
if current_state.line_width().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::LogicOp => {
// VUID-vkCmdDraw-logicOp-04878
if current_state.logic_op().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::PatchControlPoints => {
// VUID-vkCmdDraw-None-04875
if current_state.patch_control_points().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::PrimitiveRestartEnable => {
// VUID-vkCmdDraw-None-04879
let primitive_restart_enable =
if let Some(enable) = current_state.primitive_restart_enable() {
enable
} else {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
};
if primitive_restart_enable {
let topology = match pipeline.input_assembly_state().topology {
PartialStateMode::Fixed(topology) => topology,
PartialStateMode::Dynamic(_) => {
if let Some(topology) = current_state.primitive_topology() {
topology
} else {
return Err(PipelineExecutionError::DynamicStateNotSet {
dynamic_state: DynamicState::PrimitiveTopology,
});
}
}
};
match topology {
PrimitiveTopology::PointList
| PrimitiveTopology::LineList
| PrimitiveTopology::TriangleList
| PrimitiveTopology::LineListWithAdjacency
| PrimitiveTopology::TriangleListWithAdjacency => {
// VUID?
if !device.enabled_features().primitive_topology_list_restart {
return Err(PipelineExecutionError::RequirementNotMet {
required_for: "The bound pipeline sets \
`DynamicState::PrimitiveRestartEnable` and the \
current primitive topology is \
`PrimitiveTopology::*List`",
requires_one_of: RequiresOneOf {
features: &["primitive_topology_list_restart"],
..Default::default()
},
});
}
}
PrimitiveTopology::PatchList => {
// VUID?
if !device
.enabled_features()
.primitive_topology_patch_list_restart
{
return Err(PipelineExecutionError::RequirementNotMet {
required_for: "The bound pipeline sets \
`DynamicState::PrimitiveRestartEnable` and the \
current primitive topology is \
`PrimitiveTopology::PatchList`",
requires_one_of: RequiresOneOf {
features: &["primitive_topology_patch_list_restart"],
..Default::default()
},
});
}
}
_ => (),
}
}
}
DynamicState::PrimitiveTopology => {
// VUID-vkCmdDraw-primitiveTopology-03420
let topology = if let Some(topology) = current_state.primitive_topology() {
topology
} else {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
};
if pipeline.shader(ShaderStage::TessellationControl).is_some() {
// VUID?
if !matches!(topology, PrimitiveTopology::PatchList) {
return Err(PipelineExecutionError::DynamicPrimitiveTopologyInvalid {
topology,
});
}
} else {
// VUID?
if matches!(topology, PrimitiveTopology::PatchList) {
return Err(PipelineExecutionError::DynamicPrimitiveTopologyInvalid {
topology,
});
}
}
let required_topology_class = match pipeline.input_assembly_state().topology {
PartialStateMode::Dynamic(topology_class) => topology_class,
_ => unreachable!(),
};
// VUID-vkCmdDraw-primitiveTopology-03420
if topology.class() != required_topology_class {
return Err(
PipelineExecutionError::DynamicPrimitiveTopologyClassMismatch {
provided_class: topology.class(),
required_class: required_topology_class,
},
);
}
// TODO: check that the topology matches the geometry shader
}
DynamicState::RasterizerDiscardEnable => {
// VUID-vkCmdDraw-None-04876
if current_state.rasterizer_discard_enable().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::RayTracingPipelineStackSize => unreachable!(
"RayTracingPipelineStackSize dynamic state should not occur on a graphics pipeline"
),
DynamicState::SampleLocations => todo!(),
DynamicState::Scissor => {
for num in 0..pipeline.viewport_state().unwrap().count().unwrap() {
// VUID?
if current_state.scissor(num).is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
}
DynamicState::ScissorWithCount => {
// VUID-vkCmdDraw-scissorCount-03418
// VUID-vkCmdDraw-viewportCount-03419
let scissor_count = if let Some(scissors) = current_state.scissor_with_count() {
scissors.len() as u32
} else {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
};
// Check if the counts match, but only if the viewport count is fixed.
// If the viewport count is also dynamic, then the
// DynamicState::ViewportWithCount match arm will handle it.
if let Some(viewport_count) = pipeline.viewport_state().unwrap().count() {
// VUID-vkCmdDraw-scissorCount-03418
if viewport_count != scissor_count {
return Err(
PipelineExecutionError::DynamicViewportScissorCountMismatch {
viewport_count,
scissor_count,
},
);
}
}
}
DynamicState::StencilCompareMask => {
let state = current_state.stencil_compare_mask();
// VUID?
if state.front.is_none() || state.back.is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::StencilOp => {
let state = current_state.stencil_op();
// VUID?
if state.front.is_none() || state.back.is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::StencilReference => {
let state = current_state.stencil_reference();
// VUID?
if state.front.is_none() || state.back.is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::StencilTestEnable => {
// VUID?
if current_state.stencil_test_enable().is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
// TODO: Check if the stencil buffer is writable
}
DynamicState::StencilWriteMask => {
let state = current_state.stencil_write_mask();
// VUID?
if state.front.is_none() || state.back.is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
DynamicState::VertexInput => todo!(),
DynamicState::VertexInputBindingStride => todo!(),
DynamicState::Viewport => {
for num in 0..pipeline.viewport_state().unwrap().count().unwrap() {
// VUID?
if current_state.viewport(num).is_none() {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
}
}
DynamicState::ViewportCoarseSampleOrder => todo!(),
DynamicState::ViewportShadingRatePalette => todo!(),
DynamicState::ViewportWithCount => {
// VUID-vkCmdDraw-viewportCount-03417
let viewport_count = if let Some(viewports) = current_state.viewport_with_count() {
viewports.len() as u32
} else {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
};
let scissor_count = if let Some(scissor_count) =
pipeline.viewport_state().unwrap().count()
{
// The scissor count is fixed.
scissor_count
} else {
// VUID-vkCmdDraw-viewportCount-03419
// The scissor count is also dynamic.
if let Some(scissors) = current_state.scissor_with_count() {
scissors.len() as u32
} else {
return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
}
};
// VUID-vkCmdDraw-viewportCount-03417
// VUID-vkCmdDraw-viewportCount-03419
if viewport_count != scissor_count {
return Err(
PipelineExecutionError::DynamicViewportScissorCountMismatch {
viewport_count,
scissor_count,
},
);
}
// TODO: VUID-vkCmdDrawIndexed-primitiveFragmentShadingRateWithMultipleViewports-04552
// If the primitiveFragmentShadingRateWithMultipleViewports limit is not supported,
// the bound graphics pipeline was created with the
// VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT dynamic state enabled, and any of the
// shader stages of the bound graphics pipeline write to the PrimitiveShadingRateKHR
// built-in, then vkCmdSetViewportWithCountEXT must have been called in the current
// command buffer prior to this drawing command, and the viewportCount parameter of
// vkCmdSetViewportWithCountEXT must be 1
}
DynamicState::ViewportWScaling => todo!(),
DynamicState::TessellationDomainOrigin => todo!(),
DynamicState::DepthClampEnable => todo!(),
DynamicState::PolygonMode => todo!(),
DynamicState::RasterizationSamples => todo!(),
DynamicState::SampleMask => todo!(),
DynamicState::AlphaToCoverageEnable => todo!(),
DynamicState::AlphaToOneEnable => todo!(),
DynamicState::LogicOpEnable => todo!(),
DynamicState::ColorBlendEnable => todo!(),
DynamicState::ColorBlendEquation => todo!(),
DynamicState::ColorWriteMask => todo!(),
DynamicState::RasterizationStream => todo!(),
DynamicState::ConservativeRasterizationMode => todo!(),
DynamicState::ExtraPrimitiveOverestimationSize => todo!(),
DynamicState::DepthClipEnable => todo!(),
DynamicState::SampleLocationsEnable => todo!(),
DynamicState::ColorBlendAdvanced => todo!(),
DynamicState::ProvokingVertexMode => todo!(),
DynamicState::LineRasterizationMode => todo!(),
DynamicState::LineStippleEnable => todo!(),
DynamicState::DepthClipNegativeOneToOne => todo!(),
DynamicState::ViewportWScalingEnable => todo!(),
DynamicState::ViewportSwizzle => todo!(),
DynamicState::CoverageToColorEnable => todo!(),
DynamicState::CoverageToColorLocation => todo!(),
DynamicState::CoverageModulationMode => todo!(),
DynamicState::CoverageModulationTableEnable => todo!(),
DynamicState::CoverageModulationTable => todo!(),
DynamicState::ShadingRateImageEnable => todo!(),
DynamicState::RepresentativeFragmentTestEnable => todo!(),
DynamicState::CoverageReductionMode => todo!(),
}
}
Ok(())
}
fn validate_pipeline_graphics_render_pass(
&self,
pipeline: &GraphicsPipeline,
render_pass_state: &RenderPassState,
) -> Result<(), PipelineExecutionError> {
// VUID?
if render_pass_state.contents != SubpassContents::Inline {
return Err(PipelineExecutionError::ForbiddenWithSubpassContents {
subpass_contents: render_pass_state.contents,
});
}
match (&render_pass_state.render_pass, pipeline.render_pass()) {
(
RenderPassStateType::BeginRenderPass(state),
PipelineRenderPassType::BeginRenderPass(pipeline_subpass),
) => {
// VUID-vkCmdDraw-renderPass-02684
if !pipeline_subpass
.render_pass()
.is_compatible_with(state.subpass.render_pass())
{
return Err(PipelineExecutionError::PipelineRenderPassNotCompatible);
}
// VUID-vkCmdDraw-subpass-02685
if pipeline_subpass.index() != state.subpass.index() {
return Err(PipelineExecutionError::PipelineSubpassMismatch {
pipeline: pipeline_subpass.index(),
current: state.subpass.index(),
});
}
}
(
RenderPassStateType::BeginRendering(current_rendering_info),
PipelineRenderPassType::BeginRendering(pipeline_rendering_info),
) => {
// VUID-vkCmdDraw-viewMask-06178
if pipeline_rendering_info.view_mask != render_pass_state.view_mask {
return Err(PipelineExecutionError::PipelineViewMaskMismatch {
pipeline_view_mask: pipeline_rendering_info.view_mask,
required_view_mask: render_pass_state.view_mask,
});
}
// VUID-vkCmdDraw-colorAttachmentCount-06179
if pipeline_rendering_info.color_attachment_formats.len()
!= current_rendering_info.color_attachment_formats.len()
{
return Err(
PipelineExecutionError::PipelineColorAttachmentCountMismatch {
pipeline_count: pipeline_rendering_info.color_attachment_formats.len()
as u32,
required_count: current_rendering_info.color_attachment_formats.len()
as u32,
},
);
}
for (color_attachment_index, required_format, pipeline_format) in
current_rendering_info
.color_attachment_formats
.iter()
.zip(
pipeline_rendering_info
.color_attachment_formats
.iter()
.copied(),
)
.enumerate()
.filter_map(|(i, (r, p))| r.map(|r| (i as u32, r, p)))
{
// VUID-vkCmdDraw-colorAttachmentCount-06180
if Some(required_format) != pipeline_format {
return Err(
PipelineExecutionError::PipelineColorAttachmentFormatMismatch {
color_attachment_index,
pipeline_format,
required_format,
},
);
}
}
if let Some((required_format, pipeline_format)) = current_rendering_info
.depth_attachment_format
.map(|r| (r, pipeline_rendering_info.depth_attachment_format))
{
// VUID-vkCmdDraw-pDepthAttachment-06181
if Some(required_format) != pipeline_format {
return Err(
PipelineExecutionError::PipelineDepthAttachmentFormatMismatch {
pipeline_format,
required_format,
},
);
}
}
if let Some((required_format, pipeline_format)) = current_rendering_info
.stencil_attachment_format
.map(|r| (r, pipeline_rendering_info.stencil_attachment_format))
{
// VUID-vkCmdDraw-pStencilAttachment-06182
if Some(required_format) != pipeline_format {
return Err(
PipelineExecutionError::PipelineStencilAttachmentFormatMismatch {
pipeline_format,
required_format,
},
);
}
}
// VUID-vkCmdDraw-imageView-06172
// VUID-vkCmdDraw-imageView-06173
// VUID-vkCmdDraw-imageView-06174
// VUID-vkCmdDraw-imageView-06175
// VUID-vkCmdDraw-imageView-06176
// VUID-vkCmdDraw-imageView-06177
// TODO:
}
_ => return Err(PipelineExecutionError::PipelineRenderPassTypeMismatch),
}
// VUID-vkCmdDraw-None-02686
// TODO:
Ok(())
}
fn validate_pipeline_graphics_vertex_buffers(
&self,
pipeline: &GraphicsPipeline,
vertices: Option<(u32, u32)>,
instances: Option<(u32, u32)>,
) -> Result<(), PipelineExecutionError> {
let vertex_input = pipeline.vertex_input_state();
let mut vertices_in_buffers: Option<u64> = None;
let mut instances_in_buffers: Option<u64> = None;
let current_state = self.state();
for (&binding_num, binding_desc) in &vertex_input.bindings {
// VUID-vkCmdDraw-None-04007
let vertex_buffer = match current_state.vertex_buffer(binding_num) {
Some(x) => x,
None => return Err(PipelineExecutionError::VertexBufferNotBound { binding_num }),
};
let mut num_elements = vertex_buffer.size() / binding_desc.stride as u64;
match binding_desc.input_rate {
VertexInputRate::Vertex => {
vertices_in_buffers = Some(if let Some(x) = vertices_in_buffers {
min(x, num_elements)
} else {
num_elements
});
}
VertexInputRate::Instance { divisor } => {
if divisor == 0 {
// A divisor of 0 means the same instance data is used for all instances,
// so we can draw any number of instances from a single element.
// The buffer must contain at least one element though.
if num_elements != 0 {
num_elements = u64::MAX;
}
} else {
// If divisor is e.g. 2, we use only half the amount of data from the source
// buffer, so the number of instances that can be drawn is twice as large.
num_elements = num_elements.saturating_mul(divisor as u64);
}
instances_in_buffers = Some(if let Some(x) = instances_in_buffers {
min(x, num_elements)
} else {
num_elements
});
}
};
}
if let Some((first_vertex, vertex_count)) = vertices {
let vertices_needed = first_vertex as u64 + vertex_count as u64;
if let Some(vertices_in_buffers) = vertices_in_buffers {
// VUID-vkCmdDraw-None-02721
if vertices_needed > vertices_in_buffers {
return Err(PipelineExecutionError::VertexBufferVertexRangeOutOfBounds {
vertices_needed,
vertices_in_buffers,
});
}
}
}
if let Some((first_instance, instance_count)) = instances {
let instances_needed = first_instance as u64 + instance_count as u64;
if let Some(instances_in_buffers) = instances_in_buffers {
// VUID-vkCmdDraw-None-02721
if instances_needed > instances_in_buffers {
return Err(
PipelineExecutionError::VertexBufferInstanceRangeOutOfBounds {
instances_needed,
instances_in_buffers,
},
);
}
}
let view_mask = match pipeline.render_pass() {
PipelineRenderPassType::BeginRenderPass(subpass) => {
subpass.render_pass().views_used()
}
PipelineRenderPassType::BeginRendering(rendering_info) => rendering_info.view_mask,
};
if view_mask != 0 {
let max = pipeline
.device()
.physical_device()
.properties()
.max_multiview_instance_index
.unwrap_or(0);
let highest_instance = instances_needed.saturating_sub(1);
// VUID-vkCmdDraw-maxMultiviewInstanceIndex-02688
if highest_instance > max as u64 {
return Err(PipelineExecutionError::MaxMultiviewInstanceIndexExceeded {
highest_instance,
max,
});
}
}
}
Ok(())
}
}
impl SyncCommandBufferBuilder {
/// Calls `vkCmdDispatch` on the builder.
#[inline]
pub unsafe fn dispatch(
&mut self,
group_counts: [u32; 3],
) -> Result<(), SyncCommandBufferBuilderError> {
struct Cmd {
group_counts: [u32; 3],
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"dispatch"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.dispatch(self.group_counts);
}
}
let command_index = self.commands.len();
let command_name = "dispatch";
let pipeline = self
.current_state
.pipeline_compute
.as_ref()
.unwrap()
.as_ref();
let mut resources = Vec::new();
self.add_descriptor_sets(&mut resources, command_index, command_name, pipeline);
for resource in &resources {
self.check_resource_conflicts(resource)?;
}
self.commands.push(Box::new(Cmd { group_counts }));
for resource in resources {
self.add_resource(resource);
}
Ok(())
}
/// Calls `vkCmdDispatchIndirect` on the builder.
#[inline]
pub unsafe fn dispatch_indirect(
&mut self,
indirect_buffer: Subbuffer<[DispatchIndirectCommand]>,
) -> Result<(), SyncCommandBufferBuilderError> {
struct Cmd {
indirect_buffer: Subbuffer<[DispatchIndirectCommand]>,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"dispatch_indirect"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.dispatch_indirect(&self.indirect_buffer);
}
}
let command_index = self.commands.len();
let command_name = "dispatch_indirect";
let pipeline = self
.current_state
.pipeline_compute
.as_ref()
.unwrap()
.as_ref();
let mut resources = Vec::new();
self.add_descriptor_sets(&mut resources, command_index, command_name, pipeline);
self.add_indirect_buffer(
&mut resources,
command_index,
command_name,
indirect_buffer.as_bytes(),
);
for resource in &resources {
self.check_resource_conflicts(resource)?;
}
self.commands.push(Box::new(Cmd { indirect_buffer }));
for resource in resources {
self.add_resource(resource);
}
Ok(())
}
/// Calls `vkCmdDraw` on the builder.
#[inline]
pub unsafe fn draw(
&mut self,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) -> Result<(), SyncCommandBufferBuilderError> {
struct Cmd {
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"draw"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.draw(
self.vertex_count,
self.instance_count,
self.first_vertex,
self.first_instance,
);
}
}
let command_index = self.commands.len();
let command_name = "draw";
let pipeline = self
.current_state
.pipeline_graphics
.as_ref()
.unwrap()
.as_ref();
let mut resources = Vec::new();
self.add_descriptor_sets(&mut resources, command_index, command_name, pipeline);
self.add_vertex_buffers(&mut resources, command_index, command_name, pipeline);
for resource in &resources {
self.check_resource_conflicts(resource)?;
}
self.commands.push(Box::new(Cmd {
vertex_count,
instance_count,
first_vertex,
first_instance,
}));
for resource in resources {
self.add_resource(resource);
}
Ok(())
}
/// Calls `vkCmdDrawIndexed` on the builder.
#[inline]
pub unsafe fn draw_indexed(
&mut self,
index_count: u32,
instance_count: u32,
first_index: u32,
vertex_offset: i32,
first_instance: u32,
) -> Result<(), SyncCommandBufferBuilderError> {
struct Cmd {
index_count: u32,
instance_count: u32,
first_index: u32,
vertex_offset: i32,
first_instance: u32,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"draw_indexed"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.draw_indexed(
self.index_count,
self.instance_count,
self.first_index,
self.vertex_offset,
self.first_instance,
);
}
}
let command_index = self.commands.len();
let command_name = "draw_indexed";
let pipeline = self
.current_state
.pipeline_graphics
.as_ref()
.unwrap()
.as_ref();
let mut resources = Vec::new();
self.add_descriptor_sets(&mut resources, command_index, command_name, pipeline);
self.add_vertex_buffers(&mut resources, command_index, command_name, pipeline);
self.add_index_buffer(&mut resources, command_index, command_name);
for resource in &resources {
self.check_resource_conflicts(resource)?;
}
self.commands.push(Box::new(Cmd {
index_count,
instance_count,
first_index,
vertex_offset,
first_instance,
}));
for resource in resources {
self.add_resource(resource);
}
Ok(())
}
/// Calls `vkCmdDrawIndirect` on the builder.
#[inline]
pub unsafe fn draw_indirect(
&mut self,
indirect_buffer: Subbuffer<[DrawIndirectCommand]>,
draw_count: u32,
stride: u32,
) -> Result<(), SyncCommandBufferBuilderError> {
struct Cmd {
indirect_buffer: Subbuffer<[DrawIndirectCommand]>,
draw_count: u32,
stride: u32,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"draw_indirect"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.draw_indirect(&self.indirect_buffer, self.draw_count, self.stride);
}
}
let command_index = self.commands.len();
let command_name = "draw_indirect";
let pipeline = self
.current_state
.pipeline_graphics
.as_ref()
.unwrap()
.as_ref();
let mut resources = Vec::new();
self.add_descriptor_sets(&mut resources, command_index, command_name, pipeline);
self.add_vertex_buffers(&mut resources, command_index, command_name, pipeline);
self.add_indirect_buffer(
&mut resources,
command_index,
command_name,
indirect_buffer.as_bytes(),
);
for resource in &resources {
self.check_resource_conflicts(resource)?;
}
self.commands.push(Box::new(Cmd {
indirect_buffer,
draw_count,
stride,
}));
for resource in resources {
self.add_resource(resource);
}
Ok(())
}
/// Calls `vkCmdDrawIndexedIndirect` on the builder.
#[inline]
pub unsafe fn draw_indexed_indirect(
&mut self,
indirect_buffer: Subbuffer<[DrawIndexedIndirectCommand]>,
draw_count: u32,
stride: u32,
) -> Result<(), SyncCommandBufferBuilderError> {
struct Cmd {
indirect_buffer: Subbuffer<[DrawIndexedIndirectCommand]>,
draw_count: u32,
stride: u32,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"draw_indexed_indirect"
}
unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) {
out.draw_indexed_indirect(&self.indirect_buffer, self.draw_count, self.stride);
}
}
let command_index = self.commands.len();
let command_name = "draw_indexed_indirect";
let pipeline = self
.current_state
.pipeline_graphics
.as_ref()
.unwrap()
.as_ref();
let mut resources = Vec::new();
self.add_descriptor_sets(&mut resources, command_index, command_name, pipeline);
self.add_vertex_buffers(&mut resources, command_index, command_name, pipeline);
self.add_index_buffer(&mut resources, command_index, command_name);
self.add_indirect_buffer(
&mut resources,
command_index,
command_name,
indirect_buffer.as_bytes(),
);
for resource in &resources {
self.check_resource_conflicts(resource)?;
}
self.commands.push(Box::new(Cmd {
indirect_buffer,
draw_count,
stride,
}));
for resource in resources {
self.add_resource(resource);
}
Ok(())
}
fn add_descriptor_sets<Pl: Pipeline>(
&self,
resources: &mut Vec<(ResourceUseRef, Resource)>,
command_index: usize,
command_name: &'static str,
pipeline: &Pl,
) {
let descriptor_sets_state = match self
.current_state
.descriptor_sets
.get(&pipeline.bind_point())
{
Some(x) => x,
None => return,
};
for (&(set, binding), binding_reqs) in pipeline.descriptor_binding_requirements() {
// TODO: Can things be refactored so that the pipeline layout isn't needed at all?
let descriptor_type = descriptor_sets_state.pipeline_layout.set_layouts()[set as usize]
.bindings()[&binding]
.descriptor_type;
let (access_read, access_write) = match descriptor_type {
DescriptorType::Sampler => continue,
DescriptorType::InputAttachment => {
// FIXME: This is tricky. Since we read from the input attachment
// and this input attachment is being written in an earlier pass,
// vulkano will think that it needs to put a pipeline barrier and will
// return a `Conflict` error. For now as a work-around we simply ignore
// input attachments.
continue;
}
DescriptorType::CombinedImageSampler
| DescriptorType::SampledImage
| DescriptorType::UniformTexelBuffer => (Some(AccessFlags::SHADER_READ), None),
DescriptorType::StorageImage
| DescriptorType::StorageTexelBuffer
| DescriptorType::StorageBuffer
| DescriptorType::StorageBufferDynamic => (
Some(AccessFlags::SHADER_READ),
Some(AccessFlags::SHADER_WRITE),
),
DescriptorType::UniformBuffer | DescriptorType::UniformBufferDynamic => {
(Some(AccessFlags::UNIFORM_READ), None)
}
};
let memory_iter = move |index: u32| {
let mut stages_read = PipelineStages::empty();
let mut stages_write = PipelineStages::empty();
for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
.chain(binding_reqs.descriptors.get(&None))
{
stages_read |= desc_reqs.memory_read.into();
stages_write |= desc_reqs.memory_write.into();
}
let memory_read = (!stages_read.is_empty()).then(|| PipelineMemoryAccess {
stages: stages_read,
access: access_read.unwrap(),
exclusive: false,
});
let memory_write = (!stages_write.is_empty()).then(|| PipelineMemoryAccess {
stages: stages_write,
access: access_write.unwrap(),
exclusive: true,
});
[memory_read, memory_write].into_iter().flatten()
};
let buffer_resource =
|(index, buffer, range): (u32, Subbuffer<[u8]>, Range<DeviceSize>)| {
memory_iter(index).map(move |memory| {
(
ResourceUseRef {
command_index,
command_name,
resource_in_command: ResourceInCommand::DescriptorSet {
set,
binding,
index,
},
secondary_use_ref: None,
},
Resource::Buffer {
buffer: buffer.clone(),
range: range.clone(),
memory,
},
)
})
};
let image_resource = |(index, image, subresource_range): (
u32,
Arc<dyn ImageAccess>,
ImageSubresourceRange,
)| {
let layout = image
.descriptor_layouts()
.expect("descriptor_layouts must return Some when used in an image view")
.layout_for(descriptor_type);
memory_iter(index).map(move |memory| {
(
ResourceUseRef {
command_index,
command_name,
resource_in_command: ResourceInCommand::DescriptorSet {
set,
binding,
index,
},
secondary_use_ref: None,
},
Resource::Image {
image: image.clone(),
subresource_range: subresource_range.clone(),
memory,
start_layout: layout,
end_layout: layout,
},
)
})
};
let descriptor_set_state = &descriptor_sets_state.descriptor_sets[&set];
match descriptor_set_state.resources().binding(binding).unwrap() {
DescriptorBindingResources::None(_) => continue,
DescriptorBindingResources::Buffer(elements) => {
if matches!(
descriptor_type,
DescriptorType::UniformBufferDynamic | DescriptorType::StorageBufferDynamic
) {
let dynamic_offsets = descriptor_set_state.dynamic_offsets();
resources.extend(
(elements.iter().enumerate())
.filter_map(|(index, element)| {
element.as_ref().map(|(buffer, range)| {
let dynamic_offset = dynamic_offsets[index] as DeviceSize;
(
index as u32,
buffer.clone(),
dynamic_offset + range.start
..dynamic_offset + range.end,
)
})
})
.flat_map(buffer_resource),
);
} else {
resources.extend(
(elements.iter().enumerate())
.filter_map(|(index, element)| {
element.as_ref().map(|(buffer, range)| {
(index as u32, buffer.clone(), range.clone())
})
})
.flat_map(buffer_resource),
);
}
}
DescriptorBindingResources::BufferView(elements) => {
resources.extend(
(elements.iter().enumerate())
.filter_map(|(index, element)| {
element.as_ref().map(|buffer_view| {
(
index as u32,
buffer_view.buffer().clone(),
buffer_view.range(),
)
})
})
.flat_map(buffer_resource),
);
}
DescriptorBindingResources::ImageView(elements) => {
resources.extend(
(elements.iter().enumerate())
.filter_map(|(index, element)| {
element.as_ref().map(|image_view| {
(
index as u32,
image_view.image(),
image_view.subresource_range().clone(),
)
})
})
.flat_map(image_resource),
);
}
DescriptorBindingResources::ImageViewSampler(elements) => {
resources.extend(
(elements.iter().enumerate())
.filter_map(|(index, element)| {
element.as_ref().map(|(image_view, _)| {
(
index as u32,
image_view.image(),
image_view.subresource_range().clone(),
)
})
})
.flat_map(image_resource),
);
}
DescriptorBindingResources::Sampler(_) => (),
}
}
}
fn add_vertex_buffers(
&self,
resources: &mut Vec<(ResourceUseRef, Resource)>,
command_index: usize,
command_name: &'static str,
pipeline: &GraphicsPipeline,
) {
resources.extend(
pipeline
.vertex_input_state()
.bindings
.iter()
.map(|(&binding, _)| {
let vertex_buffer = &self.current_state.vertex_buffers[&binding];
(
ResourceUseRef {
command_index,
command_name,
resource_in_command: ResourceInCommand::VertexBuffer { binding },
secondary_use_ref: None,
},
Resource::Buffer {
buffer: vertex_buffer.clone(),
range: 0..vertex_buffer.size(), // TODO:
memory: PipelineMemoryAccess {
stages: PipelineStages::VERTEX_INPUT,
access: AccessFlags::VERTEX_ATTRIBUTE_READ,
exclusive: false,
},
},
)
}),
);
}
fn add_index_buffer(
&self,
resources: &mut Vec<(ResourceUseRef, Resource)>,
command_index: usize,
command_name: &'static str,
) {
let index_buffer = &self.current_state.index_buffer.as_ref().unwrap().0;
resources.push((
ResourceUseRef {
command_index,
command_name,
resource_in_command: ResourceInCommand::IndexBuffer,
secondary_use_ref: None,
},
Resource::Buffer {
buffer: index_buffer.clone(),
range: 0..index_buffer.size(), // TODO:
memory: PipelineMemoryAccess {
stages: PipelineStages::VERTEX_INPUT,
access: AccessFlags::INDEX_READ,
exclusive: false,
},
},
));
}
fn add_indirect_buffer(
&self,
resources: &mut Vec<(ResourceUseRef, Resource)>,
command_index: usize,
command_name: &'static str,
indirect_buffer: &Subbuffer<[u8]>,
) {
resources.push((
ResourceUseRef {
command_index,
command_name,
resource_in_command: ResourceInCommand::IndirectBuffer,
secondary_use_ref: None,
},
Resource::Buffer {
buffer: indirect_buffer.clone(),
range: 0..indirect_buffer.size(), // TODO:
memory: PipelineMemoryAccess {
stages: PipelineStages::DRAW_INDIRECT,
access: AccessFlags::INDIRECT_COMMAND_READ,
exclusive: false,
},
},
));
}
}
impl UnsafeCommandBufferBuilder {
/// Calls `vkCmdDispatch` on the builder.
#[inline]
pub unsafe fn dispatch(&mut self, group_counts: [u32; 3]) {
debug_assert!({
let max_group_counts = self
.device
.physical_device()
.properties()
.max_compute_work_group_count;
group_counts[0] <= max_group_counts[0]
&& group_counts[1] <= max_group_counts[1]
&& group_counts[2] <= max_group_counts[2]
});
let fns = self.device.fns();
(fns.v1_0.cmd_dispatch)(
self.handle,
group_counts[0],
group_counts[1],
group_counts[2],
);
}
/// Calls `vkCmdDispatchIndirect` on the builder.
#[inline]
pub unsafe fn dispatch_indirect(&mut self, buffer: &Subbuffer<[DispatchIndirectCommand]>) {
let fns = self.device.fns();
debug_assert!(buffer.offset() < buffer.buffer().size());
debug_assert!(buffer
.buffer()
.usage()
.intersects(BufferUsage::INDIRECT_BUFFER));
debug_assert_eq!(buffer.offset() % 4, 0);
(fns.v1_0.cmd_dispatch_indirect)(self.handle, buffer.buffer().handle(), buffer.offset());
}
/// Calls `vkCmdDraw` on the builder.
#[inline]
pub unsafe fn draw(
&mut self,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) {
let fns = self.device.fns();
(fns.v1_0.cmd_draw)(
self.handle,
vertex_count,
instance_count,
first_vertex,
first_instance,
);
}
/// Calls `vkCmdDrawIndexed` on the builder.
#[inline]
pub unsafe fn draw_indexed(
&mut self,
index_count: u32,
instance_count: u32,
first_index: u32,
vertex_offset: i32,
first_instance: u32,
) {
let fns = self.device.fns();
(fns.v1_0.cmd_draw_indexed)(
self.handle,
index_count,
instance_count,
first_index,
vertex_offset,
first_instance,
);
}
/// Calls `vkCmdDrawIndirect` on the builder.
#[inline]
pub unsafe fn draw_indirect(
&mut self,
buffer: &Subbuffer<[DrawIndirectCommand]>,
draw_count: u32,
stride: u32,
) {
let fns = self.device.fns();
debug_assert!(
draw_count == 0
|| ((stride % 4) == 0)
&& stride as usize >= size_of::<ash::vk::DrawIndirectCommand>()
);
debug_assert!(buffer.offset() < buffer.buffer().size());
debug_assert!(buffer
.buffer()
.usage()
.intersects(BufferUsage::INDIRECT_BUFFER));
(fns.v1_0.cmd_draw_indirect)(
self.handle,
buffer.buffer().handle(),
buffer.offset(),
draw_count,
stride,
);
}
/// Calls `vkCmdDrawIndexedIndirect` on the builder.
#[inline]
pub unsafe fn draw_indexed_indirect(
&mut self,
buffer: &Subbuffer<[DrawIndexedIndirectCommand]>,
draw_count: u32,
stride: u32,
) {
let fns = self.device.fns();
debug_assert!(buffer.offset() < buffer.buffer().size());
debug_assert!(buffer
.buffer()
.usage()
.intersects(BufferUsage::INDIRECT_BUFFER));
(fns.v1_0.cmd_draw_indexed_indirect)(
self.handle,
buffer.buffer().handle(),
buffer.offset(),
draw_count,
stride,
);
}
}
/// Error that can happen when recording a bound pipeline execution command.
#[derive(Debug, Clone)]
pub enum PipelineExecutionError {
SyncCommandBufferBuilderError(SyncCommandBufferBuilderError),
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
/// The resource bound to a descriptor set binding at a particular index is not compatible
/// with the requirements of the pipeline and shaders.
DescriptorResourceInvalid {
set_num: u32,
binding_num: u32,
index: u32,
error: DescriptorResourceInvalidError,
},
/// The pipeline layout requires a descriptor set bound to a set number, but none was bound.
DescriptorSetNotBound {
set_num: u32,
},
/// The bound pipeline uses a dynamic color write enable setting, but the number of provided
/// enable values is less than the number of attachments in the current render subpass.
DynamicColorWriteEnableNotEnoughValues {
color_write_enable_count: u32,
attachment_count: u32,
},
/// The bound pipeline uses a dynamic primitive topology, but the provided topology is of a
/// different topology class than what the pipeline requires.
DynamicPrimitiveTopologyClassMismatch {
provided_class: PrimitiveTopologyClass,
required_class: PrimitiveTopologyClass,
},
/// The bound pipeline uses a dynamic primitive topology, but the provided topology is not
/// compatible with the shader stages in the pipeline.
DynamicPrimitiveTopologyInvalid {
topology: PrimitiveTopology,
},
/// The pipeline requires a particular dynamic state, but this state was not set.
DynamicStateNotSet {
dynamic_state: DynamicState,
},
/// The bound pipeline uses a dynamic scissor and/or viewport count, but the scissor count
/// does not match the viewport count.
DynamicViewportScissorCountMismatch {
viewport_count: u32,
scissor_count: u32,
},
/// Operation forbidden inside a render pass.
ForbiddenInsideRenderPass,
/// Operation forbidden outside a render pass.
ForbiddenOutsideRenderPass,
/// Operation forbidden inside a render subpass with the specified contents.
ForbiddenWithSubpassContents {
subpass_contents: SubpassContents,
},
/// An indexed draw command was recorded, but no index buffer was bound.
IndexBufferNotBound,
/// The highest index to be drawn exceeds the available number of indices in the bound index
/// buffer.
IndexBufferRangeOutOfBounds {
highest_index: u32,
max_index_count: u32,
},
/// The `indirect_buffer` usage was not enabled on the indirect buffer.
IndirectBufferMissingUsage,
/// The `max_compute_work_group_count` limit has been exceeded.
MaxComputeWorkGroupCountExceeded {
requested: [u32; 3],
max: [u32; 3],
},
/// The `max_draw_indirect_count` limit has been exceeded.
MaxDrawIndirectCountExceeded {
provided: u32,
max: u32,
},
/// The `max_multiview_instance_index` limit has been exceeded.
MaxMultiviewInstanceIndexExceeded {
highest_instance: u64,
max: u32,
},
/// The queue family doesn't allow this operation.
NotSupportedByQueueFamily,
/// The color attachment count in the bound pipeline does not match the count of the current
/// render pass.
PipelineColorAttachmentCountMismatch {
pipeline_count: u32,
required_count: u32,
},
/// The format of a color attachment in the bound pipeline does not match the format of the
/// corresponding color attachment in the current render pass.
PipelineColorAttachmentFormatMismatch {
color_attachment_index: u32,
pipeline_format: Option<Format>,
required_format: Format,
},
/// The format of the depth attachment in the bound pipeline does not match the format of the
/// depth attachment in the current render pass.
PipelineDepthAttachmentFormatMismatch {
pipeline_format: Option<Format>,
required_format: Format,
},
/// The bound pipeline is not compatible with the layout used to bind the descriptor sets.
PipelineLayoutNotCompatible,
/// No pipeline was bound to the bind point used by the operation.
PipelineNotBound,
/// The bound graphics pipeline uses a render pass that is not compatible with the currently
/// active render pass.
PipelineRenderPassNotCompatible,
/// The bound graphics pipeline uses a render pass of a different type than the currently
/// active render pass.
PipelineRenderPassTypeMismatch,
/// The bound graphics pipeline uses a render subpass index that doesn't match the currently
/// active subpass index.
PipelineSubpassMismatch {
pipeline: u32,
current: u32,
},
/// The format of the stencil attachment in the bound pipeline does not match the format of the
/// stencil attachment in the current render pass.
PipelineStencilAttachmentFormatMismatch {
pipeline_format: Option<Format>,
required_format: Format,
},
/// The view mask of the bound pipeline does not match the view mask of the current render pass.
PipelineViewMaskMismatch {
pipeline_view_mask: u32,
required_view_mask: u32,
},
/// The push constants are not compatible with the pipeline layout.
PushConstantsNotCompatible,
/// Not all push constants used by the pipeline have been set.
PushConstantsMissing,
/// The bound graphics pipeline requires a vertex buffer bound to a binding number, but none
/// was bound.
VertexBufferNotBound {
binding_num: u32,
},
/// The number of instances to be drawn exceeds the available number of indices in the
/// bound vertex buffers used by the pipeline.
VertexBufferInstanceRangeOutOfBounds {
instances_needed: u64,
instances_in_buffers: u64,
},
/// The number of vertices to be drawn exceeds the lowest available number of vertices in the
/// bound vertex buffers used by the pipeline.
VertexBufferVertexRangeOutOfBounds {
vertices_needed: u64,
vertices_in_buffers: u64,
},
}
impl Error for PipelineExecutionError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::SyncCommandBufferBuilderError(err) => Some(err),
Self::DescriptorResourceInvalid { error, .. } => Some(error),
_ => None,
}
}
}
impl Display for PipelineExecutionError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::SyncCommandBufferBuilderError(_) => write!(f, "a SyncCommandBufferBuilderError"),
Self::RequirementNotMet {
required_for,
requires_one_of,
} => write!(
f,
"a requirement was not met for: {}; requires one of: {}",
required_for, requires_one_of,
),
Self::DescriptorResourceInvalid {
set_num,
binding_num,
index,
..
} => write!(
f,
"the resource bound to descriptor set {} binding {} at index {} is not compatible \
with the requirements of the pipeline and shaders",
set_num, binding_num, index,
),
Self::DescriptorSetNotBound { set_num } => write!(
f,
"the pipeline layout requires a descriptor set bound to set number {}, but none \
was bound",
set_num,
),
Self::DynamicColorWriteEnableNotEnoughValues {
color_write_enable_count,
attachment_count,
} => write!(
f,
"the bound pipeline uses a dynamic color write enable setting, but the number of \
provided enable values ({}) is less than the number of attachments in the current \
render subpass ({})",
color_write_enable_count, attachment_count,
),
Self::DynamicPrimitiveTopologyClassMismatch {
provided_class,
required_class,
} => write!(
f,
"The bound pipeline uses a dynamic primitive topology, but the provided topology \
is of a different topology class ({:?}) than what the pipeline requires ({:?})",
provided_class, required_class,
),
Self::DynamicPrimitiveTopologyInvalid { topology } => write!(
f,
"the bound pipeline uses a dynamic primitive topology, but the provided topology \
({:?}) is not compatible with the shader stages in the pipeline",
topology,
),
Self::DynamicStateNotSet { dynamic_state } => write!(
f,
"the pipeline requires the dynamic state {:?}, but this state was not set",
dynamic_state,
),
Self::DynamicViewportScissorCountMismatch {
viewport_count,
scissor_count,
} => write!(
f,
"the bound pipeline uses a dynamic scissor and/or viewport count, but the scissor \
count ({}) does not match the viewport count ({})",
scissor_count, viewport_count,
),
Self::ForbiddenInsideRenderPass => {
write!(f, "operation forbidden inside a render pass")
}
Self::ForbiddenOutsideRenderPass => {
write!(f, "operation forbidden outside a render pass")
}
Self::ForbiddenWithSubpassContents { subpass_contents } => write!(
f,
"operation forbidden inside a render subpass with contents {:?}",
subpass_contents,
),
Self::IndexBufferNotBound => write!(
f,
"an indexed draw command was recorded, but no index buffer was bound",
),
Self::IndexBufferRangeOutOfBounds {
highest_index,
max_index_count,
} => write!(
f,
"the highest index to be drawn ({}) exceeds the available number of indices in the \
bound index buffer ({})",
highest_index, max_index_count,
),
Self::IndirectBufferMissingUsage => write!(
f,
"the `indirect_buffer` usage was not enabled on the indirect buffer",
),
Self::MaxComputeWorkGroupCountExceeded { .. } => write!(
f,
"the `max_compute_work_group_count` limit has been exceeded",
),
Self::MaxDrawIndirectCountExceeded { .. } => {
write!(f, "the `max_draw_indirect_count` limit has been exceeded")
}
Self::MaxMultiviewInstanceIndexExceeded { .. } => write!(
f,
"the `max_multiview_instance_index` limit has been exceeded",
),
Self::NotSupportedByQueueFamily => {
write!(f, "the queue family doesn't allow this operation")
}
Self::PipelineColorAttachmentCountMismatch {
pipeline_count,
required_count,
} => write!(
f,
"the color attachment count in the bound pipeline ({}) does not match the count of \
the current render pass ({})",
pipeline_count, required_count,
),
Self::PipelineColorAttachmentFormatMismatch {
color_attachment_index,
pipeline_format,
required_format,
} => write!(
f,
"the format of color attachment {} in the bound pipeline ({:?}) does not match the \
format of the corresponding color attachment in the current render pass ({:?})",
color_attachment_index, pipeline_format, required_format,
),
Self::PipelineDepthAttachmentFormatMismatch {
pipeline_format,
required_format,
} => write!(
f,
"the format of the depth attachment in the bound pipeline ({:?}) does not match \
the format of the depth attachment in the current render pass ({:?})",
pipeline_format, required_format,
),
Self::PipelineLayoutNotCompatible => write!(
f,
"the bound pipeline is not compatible with the layout used to bind the descriptor \
sets",
),
Self::PipelineNotBound => write!(
f,
"no pipeline was bound to the bind point used by the operation",
),
Self::PipelineRenderPassNotCompatible => write!(
f,
"the bound graphics pipeline uses a render pass that is not compatible with the \
currently active render pass",
),
Self::PipelineRenderPassTypeMismatch => write!(
f,
"the bound graphics pipeline uses a render pass of a different type than the \
currently active render pass",
),
Self::PipelineSubpassMismatch { pipeline, current } => write!(
f,
"the bound graphics pipeline uses a render subpass index ({}) that doesn't match \
the currently active subpass index ({})",
pipeline, current,
),
Self::PipelineStencilAttachmentFormatMismatch {
pipeline_format,
required_format,
} => write!(
f,
"the format of the stencil attachment in the bound pipeline ({:?}) does not match \
the format of the stencil attachment in the current render pass ({:?})",
pipeline_format, required_format,
),
Self::PipelineViewMaskMismatch {
pipeline_view_mask,
required_view_mask,
} => write!(
f,
"the view mask of the bound pipeline ({}) does not match the view mask of the \
current render pass ({})",
pipeline_view_mask, required_view_mask,
),
Self::PushConstantsNotCompatible => write!(
f,
"the push constants are not compatible with the pipeline layout",
),
Self::PushConstantsMissing => write!(
f,
"not all push constants used by the pipeline have been set",
),
Self::VertexBufferNotBound { binding_num } => write!(
f,
"the bound graphics pipeline requires a vertex buffer bound to binding number {}, \
but none was bound",
binding_num,
),
Self::VertexBufferInstanceRangeOutOfBounds {
instances_needed,
instances_in_buffers,
} => write!(
f,
"the number of instances to be drawn ({}) exceeds the available number of \
instances in the bound vertex buffers ({}) used by the pipeline",
instances_needed, instances_in_buffers,
),
Self::VertexBufferVertexRangeOutOfBounds {
vertices_needed,
vertices_in_buffers,
} => write!(
f,
"the number of vertices to be drawn ({}) exceeds the available number of vertices \
in the bound vertex buffers ({}) used by the pipeline",
vertices_needed, vertices_in_buffers,
),
}
}
}
impl From<SyncCommandBufferBuilderError> for PipelineExecutionError {
fn from(err: SyncCommandBufferBuilderError) -> Self {
Self::SyncCommandBufferBuilderError(err)
}
}
#[derive(Clone, Copy, Debug)]
pub enum DescriptorResourceInvalidError {
ImageViewFormatMismatch {
required: Format,
provided: Option<Format>,
},
ImageViewMultisampledMismatch {
required: bool,
provided: bool,
},
ImageViewScalarTypeMismatch {
required: ShaderScalarType,
provided: ShaderScalarType,
},
ImageViewTypeMismatch {
required: ImageViewType,
provided: ImageViewType,
},
Missing,
SamplerCompareMismatch {
required: bool,
provided: bool,
},
SamplerImageViewIncompatible {
image_view_set_num: u32,
image_view_binding_num: u32,
image_view_index: u32,
error: SamplerImageViewIncompatibleError,
},
SamplerUnnormalizedCoordinatesNotAllowed,
SamplerYcbcrConversionNotAllowed,
StorageImageAtomicNotSupported,
StorageReadWithoutFormatNotSupported,
StorageWriteWithoutFormatNotSupported,
}
impl Error for DescriptorResourceInvalidError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::SamplerImageViewIncompatible { error, .. } => Some(error),
_ => None,
}
}
}
impl Display for DescriptorResourceInvalidError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::ImageViewFormatMismatch { provided, required } => write!(
f,
"the format of the bound image view ({:?}) does not match what the pipeline \
requires ({:?})",
provided, required,
),
Self::ImageViewMultisampledMismatch { provided, required } => write!(
f,
"the multisampling of the bound image ({}) does not match what the pipeline \
requires ({})",
provided, required,
),
Self::ImageViewScalarTypeMismatch { provided, required } => write!(
f,
"the scalar type of the format and aspect of the bound image view ({:?}) does not \
match what the pipeline requires ({:?})",
provided, required,
),
Self::ImageViewTypeMismatch { provided, required } => write!(
f,
"the image view type of the bound image view ({:?}) does not match what the \
pipeline requires ({:?})",
provided, required,
),
Self::Missing => write!(f, "no resource was bound"),
Self::SamplerImageViewIncompatible { .. } => write!(
f,
"the bound sampler samples an image view that is not compatible with that sampler",
),
Self::SamplerCompareMismatch { provided, required } => write!(
f,
"the depth comparison state of the bound sampler ({}) does not match what the \
pipeline requires ({})",
provided, required,
),
Self::SamplerUnnormalizedCoordinatesNotAllowed => write!(
f,
"the bound sampler is required to have unnormalized coordinates disabled",
),
Self::SamplerYcbcrConversionNotAllowed => write!(
f,
"the bound sampler is required to have no attached sampler YCbCr conversion",
),
Self::StorageImageAtomicNotSupported => write!(
f,
"the bound image view does not support the `storage_image_atomic` format feature",
),
Self::StorageReadWithoutFormatNotSupported => write!(
f,
"the bound image view or buffer view does not support the \
`storage_read_without_format` format feature",
),
Self::StorageWriteWithoutFormatNotSupported => write!(
f,
"the bound image view or buffer view does not support the \
`storage_write_without_format` format feature",
),
}
}
}