aboutsummaryrefslogtreecommitdiff
path: root/src/command_buffer/standard/builder/bind_push.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/command_buffer/standard/builder/bind_push.rs')
-rw-r--r--src/command_buffer/standard/builder/bind_push.rs936
1 files changed, 936 insertions, 0 deletions
diff --git a/src/command_buffer/standard/builder/bind_push.rs b/src/command_buffer/standard/builder/bind_push.rs
new file mode 100644
index 0000000..7fe5d7b
--- /dev/null
+++ b/src/command_buffer/standard/builder/bind_push.rs
@@ -0,0 +1,936 @@
+// Copyright (c) 2022 The vulkano developers
+// Licensed under the Apache License, Version 2.0
+// <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
+// at your option. All files in the project carrying such
+// notice may not be copied, modified, or distributed except
+// according to those terms.
+
+use super::{CommandBufferBuilder, RenderPassStateType, SetOrPush};
+use crate::{
+ buffer::{BufferContents, BufferUsage, Subbuffer},
+ command_buffer::{allocator::CommandBufferAllocator, commands::bind_push::BindPushError},
+ descriptor_set::{
+ check_descriptor_write, layout::DescriptorType, DescriptorBindingResources,
+ DescriptorSetResources, DescriptorSetWithOffsets, DescriptorSetsCollection,
+ DescriptorWriteInfo, WriteDescriptorSet,
+ },
+ device::{DeviceOwned, QueueFlags},
+ memory::is_aligned,
+ pipeline::{
+ graphics::{
+ input_assembly::{Index, IndexType},
+ render_pass::PipelineRenderPassType,
+ vertex_input::VertexBuffersCollection,
+ },
+ ComputePipeline, GraphicsPipeline, PipelineBindPoint, PipelineLayout,
+ },
+ DeviceSize, RequiresOneOf, VulkanObject,
+};
+use smallvec::SmallVec;
+use std::{cmp::min, mem::size_of_val, os::raw::c_void, sync::Arc};
+
+impl<L, A> CommandBufferBuilder<L, A>
+where
+ A: CommandBufferAllocator,
+{
+ /// Binds descriptor sets for future dispatch or draw calls.
+ ///
+ /// # Panics
+ ///
+ /// - Panics if the queue family of the command buffer does not support `pipeline_bind_point`.
+ /// - Panics if the highest descriptor set slot being bound is not less than the number of sets
+ /// in `pipeline_layout`.
+ /// - Panics if `self` and any element of `descriptor_sets` do not belong to the same device.
+ pub fn bind_descriptor_sets(
+ &mut self,
+ pipeline_bind_point: PipelineBindPoint,
+ pipeline_layout: Arc<PipelineLayout>,
+ first_set: u32,
+ descriptor_sets: impl DescriptorSetsCollection,
+ ) -> &mut Self {
+ let descriptor_sets = descriptor_sets.into_vec();
+ self.validate_bind_descriptor_sets(
+ pipeline_bind_point,
+ &pipeline_layout,
+ first_set,
+ &descriptor_sets,
+ )
+ .unwrap();
+
+ unsafe {
+ self.bind_descriptor_sets_unchecked(
+ pipeline_bind_point,
+ pipeline_layout,
+ first_set,
+ descriptor_sets,
+ )
+ }
+ }
+
+ fn validate_bind_descriptor_sets(
+ &self,
+ pipeline_bind_point: PipelineBindPoint,
+ pipeline_layout: &PipelineLayout,
+ first_set: u32,
+ descriptor_sets: &[DescriptorSetWithOffsets],
+ ) -> Result<(), BindPushError> {
+ // VUID-vkCmdBindDescriptorSets-pipelineBindPoint-parameter
+ pipeline_bind_point.validate_device(self.device())?;
+
+ let queue_family_properties = self.queue_family_properties();
+
+ // VUID-vkCmdBindDescriptorSets-commandBuffer-cmdpool
+ // VUID-vkCmdBindDescriptorSets-pipelineBindPoint-00361
+ match pipeline_bind_point {
+ PipelineBindPoint::Compute => {
+ if !queue_family_properties
+ .queue_flags
+ .intersects(QueueFlags::COMPUTE)
+ {
+ return Err(BindPushError::NotSupportedByQueueFamily);
+ }
+ }
+ PipelineBindPoint::Graphics => {
+ if !queue_family_properties
+ .queue_flags
+ .intersects(QueueFlags::GRAPHICS)
+ {
+ return Err(BindPushError::NotSupportedByQueueFamily);
+ }
+ }
+ }
+
+ // VUID-vkCmdBindDescriptorSets-firstSet-00360
+ if first_set + descriptor_sets.len() as u32 > pipeline_layout.set_layouts().len() as u32 {
+ return Err(BindPushError::DescriptorSetOutOfRange {
+ set_num: first_set + descriptor_sets.len() as u32,
+ pipeline_layout_set_count: pipeline_layout.set_layouts().len() as u32,
+ });
+ }
+
+ let properties = self.device().physical_device().properties();
+ let uniform_alignment = properties.min_uniform_buffer_offset_alignment;
+ let storage_alignment = properties.min_storage_buffer_offset_alignment;
+
+ for (i, set) in descriptor_sets.iter().enumerate() {
+ let set_num = first_set + i as u32;
+ let (set, dynamic_offsets) = set.as_ref();
+
+ // VUID-vkCmdBindDescriptorSets-commonparent
+ assert_eq!(self.device(), set.device());
+
+ let set_layout = set.layout();
+ let pipeline_set_layout = &pipeline_layout.set_layouts()[set_num as usize];
+
+ // VUID-vkCmdBindDescriptorSets-pDescriptorSets-00358
+ if !pipeline_set_layout.is_compatible_with(set_layout) {
+ return Err(BindPushError::DescriptorSetNotCompatible { set_num });
+ }
+
+ let mut dynamic_offsets_remaining = dynamic_offsets;
+ let mut required_dynamic_offset_count = 0;
+
+ for (&binding_num, binding) in set_layout.bindings() {
+ let required_alignment = match binding.descriptor_type {
+ DescriptorType::UniformBufferDynamic => uniform_alignment,
+ DescriptorType::StorageBufferDynamic => storage_alignment,
+ _ => continue,
+ };
+
+ let count = if binding.variable_descriptor_count {
+ set.variable_descriptor_count()
+ } else {
+ binding.descriptor_count
+ } as usize;
+
+ required_dynamic_offset_count += count;
+
+ if !dynamic_offsets_remaining.is_empty() {
+ let split_index = min(count, dynamic_offsets_remaining.len());
+ let dynamic_offsets = &dynamic_offsets_remaining[..split_index];
+ dynamic_offsets_remaining = &dynamic_offsets_remaining[split_index..];
+
+ let elements = match set.resources().binding(binding_num) {
+ Some(DescriptorBindingResources::Buffer(elements)) => elements.as_slice(),
+ _ => unreachable!(),
+ };
+
+ for (index, (&offset, element)) in
+ dynamic_offsets.iter().zip(elements).enumerate()
+ {
+ // VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01971
+ // VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01972
+ if !is_aligned(offset as DeviceSize, required_alignment) {
+ return Err(BindPushError::DynamicOffsetNotAligned {
+ set_num,
+ binding_num,
+ index: index as u32,
+ offset,
+ required_alignment,
+ });
+ }
+
+ if let Some((buffer, range)) = element {
+ // VUID-vkCmdBindDescriptorSets-pDescriptorSets-01979
+ if offset as DeviceSize + range.end > buffer.size() {
+ return Err(BindPushError::DynamicOffsetOutOfBufferBounds {
+ set_num,
+ binding_num,
+ index: index as u32,
+ offset,
+ range_end: range.end,
+ buffer_size: buffer.size(),
+ });
+ }
+ }
+ }
+ }
+ }
+
+ // VUID-vkCmdBindDescriptorSets-dynamicOffsetCount-00359
+ if dynamic_offsets.len() != required_dynamic_offset_count {
+ return Err(BindPushError::DynamicOffsetCountMismatch {
+ set_num,
+ provided_count: dynamic_offsets.len(),
+ required_count: required_dynamic_offset_count,
+ });
+ }
+ }
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn bind_descriptor_sets_unchecked(
+ &mut self,
+ pipeline_bind_point: PipelineBindPoint,
+ pipeline_layout: Arc<PipelineLayout>,
+ first_set: u32,
+ descriptor_sets: impl DescriptorSetsCollection,
+ ) -> &mut Self {
+ let descriptor_sets_with_offsets = descriptor_sets.into_vec();
+
+ if descriptor_sets_with_offsets.is_empty() {
+ return self;
+ }
+
+ let descriptor_sets: SmallVec<[_; 12]> = descriptor_sets_with_offsets
+ .iter()
+ .map(|x| x.as_ref().0.inner().handle())
+ .collect();
+ let dynamic_offsets: SmallVec<[_; 32]> = descriptor_sets_with_offsets
+ .iter()
+ .flat_map(|x| x.as_ref().1.iter().copied())
+ .collect();
+
+ let fns = self.device().fns();
+ (fns.v1_0.cmd_bind_descriptor_sets)(
+ self.handle(),
+ pipeline_bind_point.into(),
+ pipeline_layout.handle(),
+ first_set,
+ descriptor_sets.len() as u32,
+ descriptor_sets.as_ptr(),
+ dynamic_offsets.len() as u32,
+ dynamic_offsets.as_ptr(),
+ );
+
+ let state = self.builder_state.invalidate_descriptor_sets(
+ pipeline_bind_point,
+ pipeline_layout.clone(),
+ first_set,
+ descriptor_sets_with_offsets.len() as u32,
+ );
+
+ self.resources
+ .reserve(descriptor_sets_with_offsets.len() + 1);
+
+ for (set_num, descriptor_set_with_offsets) in
+ descriptor_sets_with_offsets.into_iter().enumerate()
+ {
+ let descriptor_set = descriptor_set_with_offsets.as_ref().0.clone();
+ state.descriptor_sets.insert(
+ first_set + set_num as u32,
+ SetOrPush::Set(descriptor_set_with_offsets),
+ );
+ self.resources.push(Box::new(descriptor_set));
+ }
+
+ self.resources.push(Box::new(pipeline_layout));
+
+ self.next_command_index += 1;
+ self
+ }
+
+ /// Binds an index buffer for future indexed draw calls.
+ ///
+ /// # Panics
+ ///
+ /// - Panics if the queue family of the command buffer does not support graphics operations.
+ /// - Panics if `self` and `index_buffer` do not belong to the same device.
+ /// - Panics if `index_buffer` does not have the [`BufferUsage::INDEX_BUFFER`] usage enabled.
+ /// - If the index buffer contains `u8` indices, panics if the [`index_type_uint8`] feature is
+ /// not enabled on the device.
+ ///
+ /// [`BufferUsage::INDEX_BUFFER`]: crate::buffer::BufferUsage::INDEX_BUFFER
+ /// [`index_type_uint8`]: crate::device::Features::index_type_uint8
+ pub fn bind_index_buffer<I: Index>(&mut self, index_buffer: Subbuffer<[I]>) -> &mut Self {
+ self.validate_bind_index_buffer(index_buffer.as_bytes(), I::ty())
+ .unwrap();
+
+ unsafe { self.bind_index_buffer_unchecked(index_buffer.into_bytes(), I::ty()) }
+ }
+
+ fn validate_bind_index_buffer(
+ &self,
+ index_buffer: &Subbuffer<[u8]>,
+ index_type: IndexType,
+ ) -> Result<(), BindPushError> {
+ let queue_family_properties = self.queue_family_properties();
+
+ // VUID-vkCmdBindIndexBuffer-commandBuffer-cmdpool
+ if !queue_family_properties
+ .queue_flags
+ .intersects(QueueFlags::GRAPHICS)
+ {
+ return Err(BindPushError::NotSupportedByQueueFamily);
+ }
+
+ // VUID-vkCmdBindIndexBuffer-commonparent
+ assert_eq!(self.device(), index_buffer.device());
+
+ // VUID-vkCmdBindIndexBuffer-buffer-00433
+ if !index_buffer
+ .buffer()
+ .usage()
+ .intersects(BufferUsage::INDEX_BUFFER)
+ {
+ return Err(BindPushError::IndexBufferMissingUsage);
+ }
+
+ // VUID-vkCmdBindIndexBuffer-indexType-02765
+ if index_type == IndexType::U8 && !self.device().enabled_features().index_type_uint8 {
+ return Err(BindPushError::RequirementNotMet {
+ required_for: "`index_type` is `IndexType::U8`",
+ requires_one_of: RequiresOneOf {
+ features: &["index_type_uint8"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // TODO:
+ // VUID-vkCmdBindIndexBuffer-offset-00432
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn bind_index_buffer_unchecked(
+ &mut self,
+ buffer: Subbuffer<[u8]>,
+ index_type: IndexType,
+ ) -> &mut Self {
+ let fns = self.device().fns();
+ (fns.v1_0.cmd_bind_index_buffer)(
+ self.handle(),
+ buffer.buffer().handle(),
+ buffer.offset(),
+ index_type.into(),
+ );
+
+ self.builder_state.index_buffer = Some((buffer.clone(), index_type));
+ self.resources.push(Box::new(buffer));
+
+ self.next_command_index += 1;
+ self
+ }
+
+ /// Binds a compute pipeline for future dispatch calls.
+ ///
+ /// # Panics
+ ///
+ /// - Panics if the queue family of the command buffer does not support compute operations.
+ /// - Panics if `self` and `pipeline` do not belong to the same device.
+ pub fn bind_pipeline_compute(&mut self, pipeline: Arc<ComputePipeline>) -> &mut Self {
+ self.validate_bind_pipeline_compute(&pipeline).unwrap();
+
+ unsafe { self.bind_pipeline_compute_unchecked(pipeline) }
+ }
+
+ fn validate_bind_pipeline_compute(
+ &self,
+ pipeline: &ComputePipeline,
+ ) -> Result<(), BindPushError> {
+ let queue_family_properties = self.queue_family_properties();
+
+ // VUID-vkCmdBindPipeline-pipelineBindPoint-00777
+ if !queue_family_properties
+ .queue_flags
+ .intersects(QueueFlags::COMPUTE)
+ {
+ return Err(BindPushError::NotSupportedByQueueFamily);
+ }
+
+ // VUID-vkCmdBindPipeline-commonparent
+ assert_eq!(self.device(), pipeline.device());
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn bind_pipeline_compute_unchecked(
+ &mut self,
+ pipeline: Arc<ComputePipeline>,
+ ) -> &mut Self {
+ let fns = self.device().fns();
+ (fns.v1_0.cmd_bind_pipeline)(
+ self.handle(),
+ ash::vk::PipelineBindPoint::COMPUTE,
+ pipeline.handle(),
+ );
+
+ self.builder_state.pipeline_compute = Some(pipeline.clone());
+ self.resources.push(Box::new(pipeline));
+
+ self.next_command_index += 1;
+ self
+ }
+
+ /// Binds a graphics pipeline for future draw calls.
+ ///
+ /// # Panics
+ ///
+ /// - Panics if the queue family of the command buffer does not support graphics operations.
+ /// - Panics if `self` and `pipeline` do not belong to the same device.
+ pub fn bind_pipeline_graphics(&mut self, pipeline: Arc<GraphicsPipeline>) -> &mut Self {
+ self.validate_bind_pipeline_graphics(&pipeline).unwrap();
+
+ unsafe { self.bind_pipeline_graphics_unchecked(pipeline) }
+ }
+
+ fn validate_bind_pipeline_graphics(
+ &self,
+ pipeline: &GraphicsPipeline,
+ ) -> Result<(), BindPushError> {
+ let queue_family_properties = self.queue_family_properties();
+
+ // VUID-vkCmdBindPipeline-pipelineBindPoint-00778
+ if !queue_family_properties
+ .queue_flags
+ .intersects(QueueFlags::GRAPHICS)
+ {
+ return Err(BindPushError::NotSupportedByQueueFamily);
+ }
+
+ // VUID-vkCmdBindPipeline-commonparent
+ assert_eq!(self.device(), pipeline.device());
+
+ if let Some(last_pipeline) =
+ self.builder_state
+ .render_pass
+ .as_ref()
+ .and_then(|render_pass_state| match &render_pass_state.render_pass {
+ RenderPassStateType::BeginRendering(state) if state.pipeline_used => {
+ self.builder_state.pipeline_graphics.as_ref()
+ }
+ _ => None,
+ })
+ {
+ if let (
+ PipelineRenderPassType::BeginRendering(pipeline_rendering_info),
+ PipelineRenderPassType::BeginRendering(last_pipeline_rendering_info),
+ ) = (pipeline.render_pass(), last_pipeline.render_pass())
+ {
+ // VUID-vkCmdBindPipeline-pipeline-06195
+ // VUID-vkCmdBindPipeline-pipeline-06196
+ if pipeline_rendering_info.color_attachment_formats
+ != last_pipeline_rendering_info.color_attachment_formats
+ {
+ return Err(BindPushError::PreviousPipelineColorAttachmentFormatMismatch);
+ }
+
+ // VUID-vkCmdBindPipeline-pipeline-06197
+ if pipeline_rendering_info.depth_attachment_format
+ != last_pipeline_rendering_info.depth_attachment_format
+ {
+ return Err(BindPushError::PreviousPipelineDepthAttachmentFormatMismatch);
+ }
+
+ // VUID-vkCmdBindPipeline-pipeline-06194
+ if pipeline_rendering_info.stencil_attachment_format
+ != last_pipeline_rendering_info.stencil_attachment_format
+ {
+ return Err(BindPushError::PreviousPipelineStencilAttachmentFormatMismatch);
+ }
+ }
+ }
+
+ // VUID-vkCmdBindPipeline-pipeline-00781
+ // TODO:
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn bind_pipeline_graphics_unchecked(
+ &mut self,
+ pipeline: Arc<GraphicsPipeline>,
+ ) -> &mut Self {
+ let fns = self.device().fns();
+ (fns.v1_0.cmd_bind_pipeline)(
+ self.handle(),
+ ash::vk::PipelineBindPoint::GRAPHICS,
+ pipeline.handle(),
+ );
+
+ // Reset any states that are fixed in the new pipeline. The pipeline bind command will
+ // overwrite these states.
+ self.builder_state.reset_dynamic_states(
+ pipeline
+ .dynamic_states()
+ .filter(|(_, d)| !d) // not dynamic
+ .map(|(s, _)| s),
+ );
+ self.builder_state.pipeline_graphics = Some(pipeline.clone());
+ self.resources.push(Box::new(pipeline));
+
+ self.next_command_index += 1;
+ self
+ }
+
+ /// Binds vertex buffers for future draw calls.
+ ///
+ /// # Panics
+ ///
+ /// - Panics if the queue family of the command buffer does not support graphics operations.
+ /// - Panics if the highest vertex buffer binding being bound is greater than the
+ /// [`max_vertex_input_bindings`] device property.
+ /// - Panics if `self` and any element of `vertex_buffers` do not belong to the same device.
+ /// - Panics if any element of `vertex_buffers` does not have the
+ /// [`BufferUsage::VERTEX_BUFFER`] usage enabled.
+ ///
+ /// [`max_vertex_input_bindings`]: crate::device::Properties::max_vertex_input_bindings
+ /// [`BufferUsage::VERTEX_BUFFER`]: crate::buffer::BufferUsage::VERTEX_BUFFER
+ pub fn bind_vertex_buffers(
+ &mut self,
+ first_binding: u32,
+ vertex_buffers: impl VertexBuffersCollection,
+ ) -> &mut Self {
+ let vertex_buffers = vertex_buffers.into_vec();
+ self.validate_bind_vertex_buffers(first_binding, &vertex_buffers)
+ .unwrap();
+
+ unsafe { self.bind_vertex_buffers_unchecked(first_binding, vertex_buffers) }
+ }
+
+ fn validate_bind_vertex_buffers(
+ &self,
+ first_binding: u32,
+ vertex_buffers: &[Subbuffer<[u8]>],
+ ) -> Result<(), BindPushError> {
+ let queue_family_properties = self.queue_family_properties();
+
+ // VUID-vkCmdBindVertexBuffers-commandBuffer-cmdpool
+ if !queue_family_properties
+ .queue_flags
+ .intersects(QueueFlags::GRAPHICS)
+ {
+ return Err(BindPushError::NotSupportedByQueueFamily);
+ }
+
+ // VUID-vkCmdBindVertexBuffers-firstBinding-00624
+ // VUID-vkCmdBindVertexBuffers-firstBinding-00625
+ if first_binding + vertex_buffers.len() as u32
+ > self
+ .device()
+ .physical_device()
+ .properties()
+ .max_vertex_input_bindings
+ {
+ return Err(BindPushError::MaxVertexInputBindingsExceeded {
+ _binding_count: first_binding + vertex_buffers.len() as u32,
+ _max: self
+ .device()
+ .physical_device()
+ .properties()
+ .max_vertex_input_bindings,
+ });
+ }
+
+ for buffer in vertex_buffers {
+ // VUID-vkCmdBindVertexBuffers-commonparent
+ assert_eq!(self.device(), buffer.device());
+
+ // VUID-vkCmdBindVertexBuffers-pBuffers-00627
+ if !buffer
+ .buffer()
+ .usage()
+ .intersects(BufferUsage::VERTEX_BUFFER)
+ {
+ return Err(BindPushError::VertexBufferMissingUsage);
+ }
+ }
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn bind_vertex_buffers_unchecked(
+ &mut self,
+ first_binding: u32,
+ buffers: impl VertexBuffersCollection,
+ ) -> &mut Self {
+ let buffers = buffers.into_vec();
+
+ if buffers.is_empty() {
+ return self;
+ }
+
+ let (buffers_vk, offsets_vk): (SmallVec<[_; 4]>, SmallVec<[_; 4]>) = buffers
+ .iter()
+ .map(|buffer| (buffer.buffer().handle(), buffer.offset()))
+ .unzip();
+
+ let fns = self.device().fns();
+ (fns.v1_0.cmd_bind_vertex_buffers)(
+ self.handle(),
+ first_binding,
+ buffers_vk.len() as u32,
+ buffers_vk.as_ptr(),
+ offsets_vk.as_ptr(),
+ );
+
+ self.resources.reserve(buffers.len());
+
+ for (i, buffer) in buffers.into_iter().enumerate() {
+ self.builder_state
+ .vertex_buffers
+ .insert(first_binding + i as u32, buffer.clone());
+ self.resources.push(Box::new(buffer));
+ }
+
+ self.next_command_index += 1;
+ self
+ }
+
+ /// Sets push constants for future dispatch or draw calls.
+ ///
+ /// # Panics
+ ///
+ /// - Panics if `offset` is not a multiple of 4.
+ /// - Panics if the size of `push_constants` is not a multiple of 4.
+ /// - Panics if any of the bytes in `push_constants` do not fall within any of the pipeline
+ /// layout's push constant ranges.
+ pub fn push_constants(
+ &mut self,
+ pipeline_layout: Arc<PipelineLayout>,
+ offset: u32,
+ push_constants: &(impl BufferContents + ?Sized),
+ ) -> &mut Self {
+ self.validate_push_constants(&pipeline_layout, offset, size_of_val(push_constants) as u32)
+ .unwrap();
+
+ unsafe { self.push_constants_unchecked(pipeline_layout, offset, push_constants) }
+ }
+
+ fn validate_push_constants(
+ &self,
+ pipeline_layout: &PipelineLayout,
+ offset: u32,
+ data_size: u32,
+ ) -> Result<(), BindPushError> {
+ if offset % 4 != 0 {
+ return Err(BindPushError::PushConstantsOffsetNotAligned);
+ }
+
+ if data_size % 4 != 0 {
+ return Err(BindPushError::PushConstantsSizeNotAligned);
+ }
+
+ let mut current_offset = offset;
+ let mut remaining_size = data_size;
+
+ for range in pipeline_layout
+ .push_constant_ranges_disjoint()
+ .iter()
+ .skip_while(|range| range.offset + range.size <= offset)
+ {
+ // there is a gap between ranges, but the passed push_constants contains
+ // some bytes in this gap, exit the loop and report error
+ if range.offset > current_offset {
+ break;
+ }
+
+ // push the minimum of the whole remaining data, and the part until the end of this range
+ let push_size = remaining_size.min(range.offset + range.size - current_offset);
+ current_offset += push_size;
+ remaining_size -= push_size;
+
+ if remaining_size == 0 {
+ break;
+ }
+ }
+
+ if remaining_size != 0 {
+ return Err(BindPushError::PushConstantsDataOutOfRange {
+ offset: current_offset,
+ });
+ }
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn push_constants_unchecked(
+ &mut self,
+ pipeline_layout: Arc<PipelineLayout>,
+ offset: u32,
+ push_constants: &(impl BufferContents + ?Sized),
+ ) -> &mut Self {
+ let mut current_offset = offset;
+ let mut remaining_size = size_of_val(push_constants) as u32;
+
+ let fns = self.device().fns();
+
+ for range in pipeline_layout
+ .push_constant_ranges_disjoint()
+ .iter()
+ .skip_while(|range| range.offset + range.size <= offset)
+ {
+ // there is a gap between ranges, but the passed push_constants contains
+ // some bytes in this gap, exit the loop and report error
+ if range.offset > current_offset {
+ break;
+ }
+
+ // push the minimum of the whole remaining data, and the part until the end of this range
+ let push_size = min(remaining_size, range.offset + range.size - current_offset);
+ let data_offset = (current_offset - offset) as usize;
+
+ (fns.v1_0.cmd_push_constants)(
+ self.handle(),
+ pipeline_layout.handle(),
+ range.stages.into(),
+ current_offset,
+ push_size,
+ (push_constants as *const _ as *const c_void).add(data_offset),
+ );
+
+ current_offset += push_size;
+ remaining_size -= push_size;
+
+ if remaining_size == 0 {
+ break;
+ }
+ }
+
+ debug_assert!(remaining_size == 0);
+
+ // TODO: Push constant invalidations.
+ // The Vulkan spec currently is unclear about this, so Vulkano currently just marks
+ // push constants as set, and never unsets them. See:
+ // https://github.com/KhronosGroup/Vulkan-Docs/issues/1485
+ // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/2711
+ self.builder_state
+ .push_constants
+ .insert(offset..offset + size_of_val(push_constants) as u32);
+ self.builder_state.push_constants_pipeline_layout = Some(pipeline_layout.clone());
+ self.resources.push(Box::new(pipeline_layout));
+
+ self.next_command_index += 1;
+ self
+ }
+
+ /// Pushes descriptor data directly into the command buffer for future dispatch or draw calls.
+ ///
+ /// # Panics
+ ///
+ /// - Panics if the queue family of the command buffer does not support `pipeline_bind_point`.
+ /// - Panics if the [`khr_push_descriptor`] extension is not enabled on the device.
+ /// - Panics if `set_num` is not less than the number of sets in `pipeline_layout`.
+ /// - Panics if an element of `descriptor_writes` is not compatible with `pipeline_layout`.
+ ///
+ /// [`khr_push_descriptor`]: crate::device::DeviceExtensions::khr_push_descriptor
+ pub fn push_descriptor_set(
+ &mut self,
+ pipeline_bind_point: PipelineBindPoint,
+ pipeline_layout: Arc<PipelineLayout>,
+ set_num: u32,
+ descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>,
+ ) -> &mut Self {
+ let descriptor_writes: SmallVec<[_; 8]> = descriptor_writes.into_iter().collect();
+ self.validate_push_descriptor_set(
+ pipeline_bind_point,
+ &pipeline_layout,
+ set_num,
+ &descriptor_writes,
+ )
+ .unwrap();
+
+ unsafe {
+ self.push_descriptor_set_unchecked(
+ pipeline_bind_point,
+ pipeline_layout,
+ set_num,
+ descriptor_writes,
+ )
+ }
+ }
+
+ fn validate_push_descriptor_set(
+ &self,
+ pipeline_bind_point: PipelineBindPoint,
+ pipeline_layout: &PipelineLayout,
+ set_num: u32,
+ descriptor_writes: &[WriteDescriptorSet],
+ ) -> Result<(), BindPushError> {
+ if !self.device().enabled_extensions().khr_push_descriptor {
+ return Err(BindPushError::RequirementNotMet {
+ required_for: "`CommandBufferBuilder::push_descriptor_set`",
+ requires_one_of: RequiresOneOf {
+ device_extensions: &["khr_push_descriptor"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-parameter
+ pipeline_bind_point.validate_device(self.device())?;
+
+ let queue_family_properties = self.queue_family_properties();
+
+ // VUID-vkCmdPushDescriptorSetKHR-commandBuffer-cmdpool
+ // VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-00363
+ match pipeline_bind_point {
+ PipelineBindPoint::Compute => {
+ if !queue_family_properties
+ .queue_flags
+ .intersects(QueueFlags::COMPUTE)
+ {
+ return Err(BindPushError::NotSupportedByQueueFamily);
+ }
+ }
+ PipelineBindPoint::Graphics => {
+ if !queue_family_properties
+ .queue_flags
+ .intersects(QueueFlags::GRAPHICS)
+ {
+ return Err(BindPushError::NotSupportedByQueueFamily);
+ }
+ }
+ }
+
+ // VUID-vkCmdPushDescriptorSetKHR-commonparent
+ assert_eq!(self.device(), pipeline_layout.device());
+
+ // VUID-vkCmdPushDescriptorSetKHR-set-00364
+ if set_num as usize > pipeline_layout.set_layouts().len() {
+ return Err(BindPushError::DescriptorSetOutOfRange {
+ set_num,
+ pipeline_layout_set_count: pipeline_layout.set_layouts().len() as u32,
+ });
+ }
+
+ let descriptor_set_layout = &pipeline_layout.set_layouts()[set_num as usize];
+
+ // VUID-vkCmdPushDescriptorSetKHR-set-00365
+ if !descriptor_set_layout.push_descriptor() {
+ return Err(BindPushError::DescriptorSetNotPush { set_num });
+ }
+
+ for write in descriptor_writes {
+ check_descriptor_write(write, descriptor_set_layout, 0)?;
+ }
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn push_descriptor_set_unchecked(
+ &mut self,
+ pipeline_bind_point: PipelineBindPoint,
+ pipeline_layout: Arc<PipelineLayout>,
+ set_num: u32,
+ descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>,
+ ) -> &mut Self {
+ let descriptor_writes: SmallVec<[WriteDescriptorSet; 8]> =
+ descriptor_writes.into_iter().collect();
+
+ debug_assert!(self.device().enabled_extensions().khr_push_descriptor);
+
+ let (infos, mut writes): (SmallVec<[_; 8]>, SmallVec<[_; 8]>) = descriptor_writes
+ .iter()
+ .map(|write| {
+ let binding =
+ &pipeline_layout.set_layouts()[set_num as usize].bindings()[&write.binding()];
+
+ (
+ write.to_vulkan_info(binding.descriptor_type),
+ write.to_vulkan(ash::vk::DescriptorSet::null(), binding.descriptor_type),
+ )
+ })
+ .unzip();
+
+ if writes.is_empty() {
+ return self;
+ }
+
+ // Set the info pointers separately.
+ for (info, write) in infos.iter().zip(writes.iter_mut()) {
+ match info {
+ DescriptorWriteInfo::Image(info) => {
+ write.descriptor_count = info.len() as u32;
+ write.p_image_info = info.as_ptr();
+ }
+ DescriptorWriteInfo::Buffer(info) => {
+ write.descriptor_count = info.len() as u32;
+ write.p_buffer_info = info.as_ptr();
+ }
+ DescriptorWriteInfo::BufferView(info) => {
+ write.descriptor_count = info.len() as u32;
+ write.p_texel_buffer_view = info.as_ptr();
+ }
+ }
+
+ debug_assert!(write.descriptor_count != 0);
+ }
+
+ let fns = self.device().fns();
+ (fns.khr_push_descriptor.cmd_push_descriptor_set_khr)(
+ self.handle(),
+ pipeline_bind_point.into(),
+ pipeline_layout.handle(),
+ set_num,
+ writes.len() as u32,
+ writes.as_ptr(),
+ );
+
+ let state = self.builder_state.invalidate_descriptor_sets(
+ pipeline_bind_point,
+ pipeline_layout.clone(),
+ set_num,
+ 1,
+ );
+ let descriptor_set_layout = state.pipeline_layout.set_layouts()[set_num as usize].as_ref();
+ debug_assert!(descriptor_set_layout.push_descriptor());
+
+ let set_resources = match state.descriptor_sets.entry(set_num).or_insert_with(|| {
+ SetOrPush::Push(DescriptorSetResources::new(descriptor_set_layout, 0))
+ }) {
+ SetOrPush::Push(set_resources) => set_resources,
+ _ => unreachable!(),
+ };
+
+ for write in &descriptor_writes {
+ set_resources.update(write);
+ }
+
+ self.resources.push(Box::new(pipeline_layout));
+
+ self.next_command_index += 1;
+ self
+ }
+}