aboutsummaryrefslogtreecommitdiff
path: root/src/command_buffer/commands/pipeline.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/command_buffer/commands/pipeline.rs')
-rw-r--r--src/command_buffer/commands/pipeline.rs2975
1 files changed, 2975 insertions, 0 deletions
diff --git a/src/command_buffer/commands/pipeline.rs b/src/command_buffer/commands/pipeline.rs
new file mode 100644
index 0000000..27a162a
--- /dev/null
+++ b/src/command_buffer/commands/pipeline.rs
@@ -0,0 +1,2975 @@
+// 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",
+ ),
+ }
+ }
+}