Skip to main content

vulkano/command_buffer/commands/
bind_push.rs

1use crate::{
2    buffer::{BufferContents, BufferUsage, IndexBuffer, Subbuffer},
3    command_buffer::{auto::SetOrPush, sys::RecordingCommandBuffer, AutoCommandBufferBuilder},
4    descriptor_set::{
5        layout::{DescriptorBindingFlags, DescriptorSetLayoutCreateFlags, DescriptorType},
6        sys::RawDescriptorSet,
7        DescriptorBindingResources, DescriptorBufferInfo, DescriptorSetResources,
8        DescriptorSetWithOffsets, DescriptorSetsCollection, WriteDescriptorSet,
9    },
10    device::{DeviceOwned, QueueFlags},
11    memory::is_aligned,
12    pipeline::{
13        graphics::vertex_input::VertexBuffersCollection, ray_tracing::RayTracingPipeline,
14        ComputePipeline, GraphicsPipeline, PipelineBindPoint, PipelineLayout,
15    },
16    DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, Version, VulkanObject,
17};
18use smallvec::SmallVec;
19use std::{cmp::min, ffi::c_void, mem::size_of, ptr, sync::Arc};
20
21/// # Commands to bind or push state for pipeline execution commands.
22///
23/// These commands require a queue with a pipeline type that uses the given state.
24impl<L> AutoCommandBufferBuilder<L> {
25    /// Binds descriptor sets for future dispatch or draw calls.
26    pub fn bind_descriptor_sets(
27        &mut self,
28        pipeline_bind_point: PipelineBindPoint,
29        pipeline_layout: Arc<PipelineLayout>,
30        first_set: u32,
31        descriptor_sets: impl DescriptorSetsCollection,
32    ) -> Result<&mut Self, Box<ValidationError>> {
33        let descriptor_sets = descriptor_sets.into_vec();
34        self.validate_bind_descriptor_sets(
35            pipeline_bind_point,
36            &pipeline_layout,
37            first_set,
38            &descriptor_sets,
39        )?;
40
41        Ok(unsafe {
42            self.bind_descriptor_sets_unchecked(
43                pipeline_bind_point,
44                pipeline_layout,
45                first_set,
46                descriptor_sets,
47            )
48        })
49    }
50
51    // TODO: The validation here is somewhat duplicated because of how different the parameters are
52    // here compared to the raw command buffer.
53    fn validate_bind_descriptor_sets(
54        &self,
55        pipeline_bind_point: PipelineBindPoint,
56        pipeline_layout: &PipelineLayout,
57        first_set: u32,
58        descriptor_sets: &[DescriptorSetWithOffsets],
59    ) -> Result<(), Box<ValidationError>> {
60        self.inner.validate_bind_descriptor_sets_inner(
61            pipeline_bind_point,
62            pipeline_layout,
63            first_set,
64            descriptor_sets.len(),
65        )?;
66
67        let properties = self.device().physical_device().properties();
68
69        for (descriptor_sets_index, set) in descriptor_sets.iter().enumerate() {
70            let set_num = first_set + descriptor_sets_index as u32;
71            let (set, dynamic_offsets) = set.as_ref();
72
73            // VUID-vkCmdBindDescriptorSets-commonparent
74            assert_eq!(self.device(), set.device());
75
76            let set_layout = set.layout();
77            let pipeline_set_layout = &pipeline_layout.set_layouts()[set_num as usize];
78
79            if !pipeline_set_layout.is_compatible_with(set_layout) {
80                return Err(Box::new(ValidationError {
81                    problem: format!(
82                        "`descriptor_sets[{0}]` (for set number {1}) is not compatible with \
83                        `pipeline_layout.set_layouts()[{1}]`",
84                        descriptor_sets_index, set_num
85                    )
86                    .into(),
87                    vuids: &["VUID-vkCmdBindDescriptorSets-pDescriptorSets-00358"],
88                    ..Default::default()
89                }));
90            }
91
92            let mut dynamic_offsets_remaining = dynamic_offsets;
93            let mut required_dynamic_offset_count = 0;
94
95            for (&binding_num, binding) in set_layout.bindings() {
96                let required_alignment = match binding.descriptor_type {
97                    DescriptorType::UniformBufferDynamic => {
98                        properties.min_uniform_buffer_offset_alignment
99                    }
100                    DescriptorType::StorageBufferDynamic => {
101                        properties.min_storage_buffer_offset_alignment
102                    }
103                    _ => continue,
104                };
105
106                let count = if binding
107                    .binding_flags
108                    .intersects(DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT)
109                {
110                    set.variable_descriptor_count()
111                } else {
112                    binding.descriptor_count
113                } as usize;
114
115                required_dynamic_offset_count += count;
116
117                if !dynamic_offsets_remaining.is_empty() {
118                    let split_index = min(count, dynamic_offsets_remaining.len());
119                    let dynamic_offsets = &dynamic_offsets_remaining[..split_index];
120                    dynamic_offsets_remaining = &dynamic_offsets_remaining[split_index..];
121
122                    let resources = set.resources();
123                    let elements = match resources.binding(binding_num) {
124                        Some(DescriptorBindingResources::Buffer(elements)) => elements.as_slice(),
125                        _ => unreachable!(),
126                    };
127
128                    for (index, (&offset, element)) in
129                        dynamic_offsets.iter().zip(elements).enumerate()
130                    {
131                        if !is_aligned(offset as DeviceSize, required_alignment) {
132                            match binding.descriptor_type {
133                                DescriptorType::UniformBufferDynamic => {
134                                    return Err(Box::new(ValidationError {
135                                        problem: format!(
136                                            "the descriptor type of `descriptor_sets[{}]` \
137                                            (for set number {}) is \
138                                            `DescriptorType::UniformBufferDynamic`, but the \
139                                            dynamic offset provided for binding {} index {} is \
140                                            not aligned to the \
141                                            `min_uniform_buffer_offset_alignment` device property",
142                                            descriptor_sets_index, set_num, binding_num, index,
143                                        )
144                                        .into(),
145                                        vuids: &[
146                                            "VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01971",
147                                        ],
148                                        ..Default::default()
149                                    }));
150                                }
151                                DescriptorType::StorageBufferDynamic => {
152                                    return Err(Box::new(ValidationError {
153                                        problem: format!(
154                                            "the descriptor type of `descriptor_sets[{}]` \
155                                            (for set number {}) is \
156                                            `DescriptorType::StorageBufferDynamic`, but the \
157                                            dynamic offset provided for binding {} index {} is \
158                                            not aligned to the \
159                                            `min_storage_buffer_offset_alignment` device property",
160                                            descriptor_sets_index, set_num, binding_num, index,
161                                        )
162                                        .into(),
163                                        vuids: &[
164                                            "VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01972",
165                                        ],
166                                        ..Default::default()
167                                    }));
168                                }
169                                _ => unreachable!(),
170                            }
171                        }
172
173                        if let Some(buffer_info) = element {
174                            let DescriptorBufferInfo { buffer, range } = buffer_info;
175
176                            if offset as DeviceSize + range.end > buffer.size() {
177                                return Err(Box::new(ValidationError {
178                                    problem: format!(
179                                        "the dynamic offset of `descriptor_sets[{}]` \
180                                        (for set number {}) for binding {} index {}, when \
181                                        added to `range.end` of the descriptor write, is \
182                                        greater than the size of the bound buffer",
183                                        descriptor_sets_index, set_num, binding_num, index,
184                                    )
185                                    .into(),
186                                    vuids: &["VUID-vkCmdBindDescriptorSets-pDescriptorSets-01979"],
187                                    ..Default::default()
188                                }));
189                            }
190                        }
191                    }
192                }
193            }
194
195            if dynamic_offsets.len() != required_dynamic_offset_count {
196                return Err(Box::new(ValidationError {
197                    problem: format!(
198                        "the number of dynamic offsets provided for `descriptor_sets[{}]` \
199                        (for set number {}) does not equal the number required ({})",
200                        descriptor_sets_index, set_num, required_dynamic_offset_count,
201                    )
202                    .into(),
203                    vuids: &["VUID-vkCmdBindDescriptorSets-dynamicOffsetCount-00359"],
204                    ..Default::default()
205                }));
206            }
207        }
208
209        Ok(())
210    }
211
212    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
213    pub unsafe fn bind_descriptor_sets_unchecked(
214        &mut self,
215        pipeline_bind_point: PipelineBindPoint,
216        pipeline_layout: Arc<PipelineLayout>,
217        first_set: u32,
218        descriptor_sets: impl DescriptorSetsCollection,
219    ) -> &mut Self {
220        let descriptor_sets = descriptor_sets.into_vec();
221        if descriptor_sets.is_empty() {
222            return self;
223        }
224
225        let state = self.builder_state.invalidate_descriptor_sets(
226            pipeline_bind_point,
227            pipeline_layout.clone(),
228            first_set,
229            descriptor_sets.len() as u32,
230        );
231
232        for (set_num, set) in descriptor_sets.iter().enumerate() {
233            state
234                .descriptor_sets
235                .insert(first_set + set_num as u32, SetOrPush::Set(set.clone()));
236        }
237
238        self.add_command(
239            "bind_descriptor_sets",
240            Default::default(),
241            move |out: &mut RecordingCommandBuffer| {
242                let dynamic_offsets: SmallVec<[_; 32]> = descriptor_sets
243                    .iter()
244                    .flat_map(|x| x.as_ref().1.iter().copied())
245                    .collect();
246                let descriptor_sets: SmallVec<[_; 12]> = descriptor_sets
247                    .iter()
248                    .map(|x| x.as_ref().0.as_raw())
249                    .collect();
250
251                unsafe {
252                    out.bind_descriptor_sets_unchecked(
253                        pipeline_bind_point,
254                        &pipeline_layout,
255                        first_set,
256                        &descriptor_sets,
257                        &dynamic_offsets,
258                    )
259                };
260            },
261        );
262
263        self
264    }
265
266    /// Binds an index buffer for future indexed draw calls.
267    pub fn bind_index_buffer(
268        &mut self,
269        index_buffer: impl Into<IndexBuffer>,
270    ) -> Result<&mut Self, Box<ValidationError>> {
271        let index_buffer = index_buffer.into();
272        self.validate_bind_index_buffer(&index_buffer)?;
273
274        Ok(unsafe { self.bind_index_buffer_unchecked(index_buffer) })
275    }
276
277    fn validate_bind_index_buffer(
278        &self,
279        index_buffer: &IndexBuffer,
280    ) -> Result<(), Box<ValidationError>> {
281        self.inner.validate_bind_index_buffer(index_buffer)?;
282
283        Ok(())
284    }
285
286    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
287    pub unsafe fn bind_index_buffer_unchecked(
288        &mut self,
289        index_buffer: impl Into<IndexBuffer>,
290    ) -> &mut Self {
291        let index_buffer = index_buffer.into();
292        self.builder_state.index_buffer = Some(index_buffer.clone());
293        self.add_command(
294            "bind_index_buffer",
295            Default::default(),
296            move |out: &mut RecordingCommandBuffer| {
297                unsafe { out.bind_index_buffer_unchecked(&index_buffer) };
298            },
299        );
300
301        self
302    }
303
304    /// Binds a compute pipeline for future dispatch calls.
305    pub fn bind_pipeline_compute(
306        &mut self,
307        pipeline: Arc<ComputePipeline>,
308    ) -> Result<&mut Self, Box<ValidationError>> {
309        self.validate_bind_pipeline_compute(&pipeline)?;
310
311        Ok(unsafe { self.bind_pipeline_compute_unchecked(pipeline) })
312    }
313
314    fn validate_bind_pipeline_compute(
315        &self,
316        pipeline: &ComputePipeline,
317    ) -> Result<(), Box<ValidationError>> {
318        self.inner.validate_bind_pipeline_compute(pipeline)?;
319
320        Ok(())
321    }
322
323    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
324    pub unsafe fn bind_pipeline_compute_unchecked(
325        &mut self,
326        pipeline: Arc<ComputePipeline>,
327    ) -> &mut Self {
328        self.builder_state.pipeline_compute = Some(pipeline.clone());
329        self.add_command(
330            "bind_pipeline_compute",
331            Default::default(),
332            move |out: &mut RecordingCommandBuffer| {
333                unsafe { out.bind_pipeline_compute_unchecked(&pipeline) };
334            },
335        );
336
337        self
338    }
339
340    /// Binds a graphics pipeline for future draw calls.
341    pub fn bind_pipeline_graphics(
342        &mut self,
343        pipeline: Arc<GraphicsPipeline>,
344    ) -> Result<&mut Self, Box<ValidationError>> {
345        self.validate_bind_pipeline_graphics(&pipeline)?;
346
347        Ok(unsafe { self.bind_pipeline_graphics_unchecked(pipeline) })
348    }
349
350    fn validate_bind_pipeline_graphics(
351        &self,
352        pipeline: &GraphicsPipeline,
353    ) -> Result<(), Box<ValidationError>> {
354        self.inner.validate_bind_pipeline_graphics(pipeline)?;
355
356        // VUID-vkCmdBindPipeline-pipeline-00781
357        // TODO:
358
359        Ok(())
360    }
361
362    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
363    pub unsafe fn bind_pipeline_graphics_unchecked(
364        &mut self,
365        pipeline: Arc<GraphicsPipeline>,
366    ) -> &mut Self {
367        // Reset any states that are fixed in the new pipeline. The pipeline bind command will
368        // overwrite these states.
369        self.builder_state
370            .reset_dynamic_states(pipeline.fixed_state().iter().copied());
371        self.builder_state.pipeline_graphics = Some(pipeline.clone());
372        self.add_command(
373            "bind_pipeline_graphics",
374            Default::default(),
375            move |out: &mut RecordingCommandBuffer| {
376                unsafe { out.bind_pipeline_graphics_unchecked(&pipeline) };
377            },
378        );
379
380        self
381    }
382
383    /// Binds a ray tracing pipeline for future ray tracing calls.
384    pub fn bind_pipeline_ray_tracing(
385        &mut self,
386        pipeline: Arc<RayTracingPipeline>,
387    ) -> Result<&mut Self, Box<ValidationError>> {
388        self.inner.validate_bind_pipeline_ray_tracing(&pipeline)?;
389        Ok(unsafe { self.bind_pipeline_ray_tracing_unchecked(pipeline) })
390    }
391
392    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
393    pub unsafe fn bind_pipeline_ray_tracing_unchecked(
394        &mut self,
395        pipeline: Arc<RayTracingPipeline>,
396    ) -> &mut Self {
397        self.builder_state.pipeline_ray_tracing = Some(pipeline.clone());
398        self.add_command(
399            "bind_pipeline_ray_tracing",
400            Default::default(),
401            move |out: &mut RecordingCommandBuffer| {
402                unsafe { out.bind_pipeline_ray_tracing_unchecked(&pipeline) };
403            },
404        );
405
406        self
407    }
408
409    /// Binds vertex buffers for future draw calls.
410    pub fn bind_vertex_buffers(
411        &mut self,
412        first_binding: u32,
413        vertex_buffers: impl VertexBuffersCollection,
414    ) -> Result<&mut Self, Box<ValidationError>> {
415        let vertex_buffers = vertex_buffers.into_vec();
416        self.validate_bind_vertex_buffers(first_binding, &vertex_buffers)?;
417
418        Ok(unsafe { self.bind_vertex_buffers_unchecked(first_binding, vertex_buffers) })
419    }
420
421    fn validate_bind_vertex_buffers(
422        &self,
423        first_binding: u32,
424        vertex_buffers: &[Subbuffer<[u8]>],
425    ) -> Result<(), Box<ValidationError>> {
426        self.inner
427            .validate_bind_vertex_buffers(first_binding, vertex_buffers)?;
428
429        Ok(())
430    }
431
432    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
433    pub unsafe fn bind_vertex_buffers_unchecked(
434        &mut self,
435        first_binding: u32,
436        vertex_buffers: impl VertexBuffersCollection,
437    ) -> &mut Self {
438        let vertex_buffers = vertex_buffers.into_vec();
439
440        for (i, buffer) in vertex_buffers.iter().enumerate() {
441            self.builder_state
442                .vertex_buffers
443                .insert(first_binding + i as u32, buffer.clone());
444        }
445
446        self.add_command(
447            "bind_vertex_buffers",
448            Default::default(),
449            move |out: &mut RecordingCommandBuffer| {
450                unsafe { out.bind_vertex_buffers_unchecked(first_binding, &vertex_buffers) };
451            },
452        );
453
454        self
455    }
456
457    /// Sets push constants for future dispatch or draw calls.
458    pub fn push_constants<Pc>(
459        &mut self,
460        pipeline_layout: Arc<PipelineLayout>,
461        offset: u32,
462        push_constants: Pc,
463    ) -> Result<&mut Self, Box<ValidationError>>
464    where
465        Pc: BufferContents,
466    {
467        let size = size_of::<Pc>() as u32;
468
469        if size == 0 {
470            return Ok(self);
471        }
472
473        self.validate_push_constants(&pipeline_layout, offset, &push_constants)?;
474
475        Ok(unsafe { self.push_constants_unchecked(pipeline_layout, offset, push_constants) })
476    }
477
478    fn validate_push_constants<Pc: BufferContents>(
479        &self,
480        pipeline_layout: &PipelineLayout,
481        offset: u32,
482        push_constants: &Pc,
483    ) -> Result<(), Box<ValidationError>> {
484        self.inner
485            .validate_push_constants(pipeline_layout, offset, push_constants)?;
486
487        Ok(())
488    }
489
490    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
491    pub unsafe fn push_constants_unchecked<Pc>(
492        &mut self,
493        pipeline_layout: Arc<PipelineLayout>,
494        offset: u32,
495        push_constants: Pc,
496    ) -> &mut Self
497    where
498        Pc: BufferContents,
499    {
500        // TODO: Push constant invalidations.
501        // The Vulkan spec currently is unclear about this, so Vulkano currently just marks
502        // push constants as set, and never unsets them. See:
503        // https://github.com/KhronosGroup/Vulkan-Docs/issues/1485
504        // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/2711
505        self.builder_state
506            .push_constants
507            .insert(offset..offset + size_of::<Pc>() as u32);
508        self.builder_state.push_constants_pipeline_layout = Some(pipeline_layout.clone());
509
510        self.add_command(
511            "push_constants",
512            Default::default(),
513            move |out: &mut RecordingCommandBuffer| {
514                unsafe { out.push_constants_unchecked(&pipeline_layout, offset, &push_constants) };
515            },
516        );
517
518        self
519    }
520
521    /// Pushes descriptor data directly into the command buffer for future dispatch or draw calls.
522    pub fn push_descriptor_set(
523        &mut self,
524        pipeline_bind_point: PipelineBindPoint,
525        pipeline_layout: Arc<PipelineLayout>,
526        set_num: u32,
527        descriptor_writes: SmallVec<[WriteDescriptorSet; 8]>,
528    ) -> Result<&mut Self, Box<ValidationError>> {
529        self.validate_push_descriptor_set(
530            pipeline_bind_point,
531            &pipeline_layout,
532            set_num,
533            &descriptor_writes,
534        )?;
535
536        Ok(unsafe {
537            self.push_descriptor_set_unchecked(
538                pipeline_bind_point,
539                pipeline_layout,
540                set_num,
541                descriptor_writes,
542            )
543        })
544    }
545
546    fn validate_push_descriptor_set(
547        &self,
548        pipeline_bind_point: PipelineBindPoint,
549        pipeline_layout: &PipelineLayout,
550        set_num: u32,
551        descriptor_writes: &[WriteDescriptorSet],
552    ) -> Result<(), Box<ValidationError>> {
553        self.inner.validate_push_descriptor_set(
554            pipeline_bind_point,
555            pipeline_layout,
556            set_num,
557            descriptor_writes,
558        )?;
559
560        Ok(())
561    }
562
563    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
564    pub unsafe fn push_descriptor_set_unchecked(
565        &mut self,
566        pipeline_bind_point: PipelineBindPoint,
567        pipeline_layout: Arc<PipelineLayout>,
568        set_num: u32,
569        descriptor_writes: SmallVec<[WriteDescriptorSet; 8]>,
570    ) -> &mut Self {
571        let state = self.builder_state.invalidate_descriptor_sets(
572            pipeline_bind_point,
573            pipeline_layout.clone(),
574            set_num,
575            1,
576        );
577        let layout = state.pipeline_layout.set_layouts()[set_num as usize].as_ref();
578        debug_assert!(layout
579            .flags()
580            .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR));
581
582        let set_resources = match state
583            .descriptor_sets
584            .entry(set_num)
585            .or_insert_with(|| SetOrPush::Push(DescriptorSetResources::new(layout, 0)))
586        {
587            SetOrPush::Push(set_resources) => set_resources,
588            _ => unreachable!(),
589        };
590
591        for write in &descriptor_writes {
592            set_resources.write(write, layout);
593        }
594
595        self.add_command(
596            "push_descriptor_set",
597            Default::default(),
598            move |out: &mut RecordingCommandBuffer| {
599                unsafe {
600                    out.push_descriptor_set_unchecked(
601                        pipeline_bind_point,
602                        &pipeline_layout,
603                        set_num,
604                        &descriptor_writes,
605                    )
606                };
607            },
608        );
609
610        self
611    }
612}
613
614impl RecordingCommandBuffer {
615    #[inline]
616    pub unsafe fn bind_descriptor_sets(
617        &mut self,
618        pipeline_bind_point: PipelineBindPoint,
619        pipeline_layout: &PipelineLayout,
620        first_set: u32,
621        descriptor_sets: &[&RawDescriptorSet],
622        dynamic_offsets: &[u32],
623    ) -> Result<&mut Self, Box<ValidationError>> {
624        self.validate_bind_descriptor_sets(
625            pipeline_bind_point,
626            pipeline_layout,
627            first_set,
628            descriptor_sets,
629            dynamic_offsets,
630        )?;
631
632        Ok(unsafe {
633            self.bind_descriptor_sets_unchecked(
634                pipeline_bind_point,
635                pipeline_layout,
636                first_set,
637                descriptor_sets,
638                dynamic_offsets,
639            )
640        })
641    }
642
643    fn validate_bind_descriptor_sets(
644        &self,
645        pipeline_bind_point: PipelineBindPoint,
646        pipeline_layout: &PipelineLayout,
647        first_set: u32,
648        descriptor_sets: &[&RawDescriptorSet],
649        dynamic_offsets: &[u32],
650    ) -> Result<(), Box<ValidationError>> {
651        self.validate_bind_descriptor_sets_inner(
652            pipeline_bind_point,
653            pipeline_layout,
654            first_set,
655            descriptor_sets.len(),
656        )?;
657
658        let properties = self.device().physical_device().properties();
659        let mut dynamic_offsets_remaining = dynamic_offsets;
660        let mut required_dynamic_offset_count = 0;
661
662        for (descriptor_sets_index, set) in descriptor_sets.iter().enumerate() {
663            let set_num = first_set + descriptor_sets_index as u32;
664
665            // VUID-vkCmdBindDescriptorSets-commonparent
666            assert_eq!(self.device(), set.device());
667
668            let set_layout = set.layout();
669            let pipeline_set_layout = &pipeline_layout.set_layouts()[set_num as usize];
670
671            if !pipeline_set_layout.is_compatible_with(set_layout) {
672                return Err(Box::new(ValidationError {
673                    problem: format!(
674                        "`descriptor_sets[{0}]` (for set number {1}) is not compatible with \
675                        `pipeline_layout.set_layouts()[{1}]`",
676                        descriptor_sets_index, set_num
677                    )
678                    .into(),
679                    vuids: &["VUID-vkCmdBindDescriptorSets-pDescriptorSets-00358"],
680                    ..Default::default()
681                }));
682            }
683
684            for (&binding_num, binding) in set_layout.bindings() {
685                let required_alignment = match binding.descriptor_type {
686                    DescriptorType::UniformBufferDynamic => {
687                        properties.min_uniform_buffer_offset_alignment
688                    }
689                    DescriptorType::StorageBufferDynamic => {
690                        properties.min_storage_buffer_offset_alignment
691                    }
692                    _ => continue,
693                };
694
695                let count = if binding
696                    .binding_flags
697                    .intersects(DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT)
698                {
699                    set.variable_descriptor_count()
700                } else {
701                    binding.descriptor_count
702                } as usize;
703
704                required_dynamic_offset_count += count;
705
706                if !dynamic_offsets_remaining.is_empty() {
707                    let split_index = min(count, dynamic_offsets_remaining.len());
708                    let dynamic_offsets = &dynamic_offsets_remaining[..split_index];
709                    dynamic_offsets_remaining = &dynamic_offsets_remaining[split_index..];
710
711                    for (index, &offset) in dynamic_offsets.iter().enumerate() {
712                        if !is_aligned(offset as DeviceSize, required_alignment) {
713                            match binding.descriptor_type {
714                                DescriptorType::UniformBufferDynamic => {
715                                    return Err(Box::new(ValidationError {
716                                        problem: format!(
717                                            "the descriptor type of `descriptor_sets[{}]` \
718                                            (for set number {}) is \
719                                            `DescriptorType::UniformBufferDynamic`, but the \
720                                            dynamic offset provided for binding {} index {} is \
721                                            not aligned to the \
722                                            `min_uniform_buffer_offset_alignment` device property",
723                                            descriptor_sets_index, set_num, binding_num, index,
724                                        )
725                                        .into(),
726                                        vuids: &[
727                                            "VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01971",
728                                        ],
729                                        ..Default::default()
730                                    }));
731                                }
732                                DescriptorType::StorageBufferDynamic => {
733                                    return Err(Box::new(ValidationError {
734                                        problem: format!(
735                                            "the descriptor type of `descriptor_sets[{}]` \
736                                            (for set number {}) is \
737                                            `DescriptorType::StorageBufferDynamic`, but the \
738                                            dynamic offset provided for binding {} index {} is \
739                                            not aligned to the \
740                                            `min_storage_buffer_offset_alignment` device property",
741                                            descriptor_sets_index, set_num, binding_num, index,
742                                        )
743                                        .into(),
744                                        vuids: &[
745                                            "VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01972",
746                                        ],
747                                        ..Default::default()
748                                    }));
749                                }
750                                _ => unreachable!(),
751                            }
752                        }
753                    }
754                }
755            }
756        }
757
758        if dynamic_offsets.len() != required_dynamic_offset_count {
759            return Err(Box::new(ValidationError {
760                problem: format!(
761                    "the number of dynamic offsets provided does not equal the number required \
762                    ({})",
763                    required_dynamic_offset_count,
764                )
765                .into(),
766                vuids: &["VUID-vkCmdBindDescriptorSets-dynamicOffsetCount-00359"],
767                ..Default::default()
768            }));
769        }
770
771        Ok(())
772    }
773
774    fn validate_bind_descriptor_sets_inner(
775        &self,
776        pipeline_bind_point: PipelineBindPoint,
777        pipeline_layout: &PipelineLayout,
778        first_set: u32,
779        descriptor_sets: usize,
780    ) -> Result<(), Box<ValidationError>> {
781        pipeline_bind_point
782            .validate_device(self.device())
783            .map_err(|err| {
784                err.add_context("pipeline_bind_point")
785                    .set_vuids(&["VUID-vkCmdBindDescriptorSets-pipelineBindPoint-parameter"])
786            })?;
787
788        let queue_family_properties = self.queue_family_properties();
789
790        match pipeline_bind_point {
791            PipelineBindPoint::Compute => {
792                if !queue_family_properties
793                    .queue_flags
794                    .intersects(QueueFlags::COMPUTE)
795                {
796                    return Err(Box::new(ValidationError {
797                        context: "pipeline_bind_point".into(),
798                        problem: "is `PipelineBindPoint::Compute`, but \
799                            the queue family of the command buffer does not support \
800                            compute operations"
801                            .into(),
802                        vuids: &[
803                            "VUID-vkCmdBindDescriptorSets-pipelineBindPoint-00361",
804                            "VUID-vkCmdBindDescriptorSets-commandBuffer-cmdpool",
805                        ],
806                        ..Default::default()
807                    }));
808                }
809            }
810            PipelineBindPoint::Graphics => {
811                if !queue_family_properties
812                    .queue_flags
813                    .intersects(QueueFlags::GRAPHICS)
814                {
815                    return Err(Box::new(ValidationError {
816                        context: "pipeline_bind_point".into(),
817                        problem: "is `PipelineBindPoint::Graphics`, but \
818                            the queue family of the command buffer does not support \
819                            graphics operations"
820                            .into(),
821                        vuids: &[
822                            "VUID-vkCmdBindDescriptorSets-pipelineBindPoint-00361",
823                            "VUID-vkCmdBindDescriptorSets-commandBuffer-cmdpool",
824                        ],
825                        ..Default::default()
826                    }));
827                }
828            }
829            PipelineBindPoint::RayTracing => {
830                if !queue_family_properties
831                    .queue_flags
832                    .intersects(QueueFlags::COMPUTE)
833                {
834                    return Err(Box::new(ValidationError {
835                        context: "pipeline_bind_point".into(),
836                        problem: "is `PipelineBindPoint::RayTracing`, but \
837                            the queue family of the command buffer does not support \
838                            compute operations"
839                            .into(),
840                        vuids: &[
841                            "VUID-vkCmdBindDescriptorSets-pipelineBindPoint-02391",
842                            "VUID-vkCmdBindDescriptorSets-commandBuffer-cmdpool",
843                        ],
844                        ..Default::default()
845                    }));
846                }
847            }
848        }
849
850        if first_set + descriptor_sets as u32 > pipeline_layout.set_layouts().len() as u32 {
851            return Err(Box::new(ValidationError {
852                problem: "`first_set + descriptor_sets.len()` is greater than \
853                    `pipeline_layout.set_layouts().len()`"
854                    .into(),
855                vuids: &["VUID-vkCmdBindDescriptorSets-firstSet-00360"],
856                ..Default::default()
857            }));
858        }
859
860        Ok(())
861    }
862
863    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
864    pub unsafe fn bind_descriptor_sets_unchecked(
865        &mut self,
866        pipeline_bind_point: PipelineBindPoint,
867        pipeline_layout: &PipelineLayout,
868        first_set: u32,
869        descriptor_sets: &[&RawDescriptorSet],
870        dynamic_offsets: &[u32],
871    ) -> &mut Self {
872        if descriptor_sets.is_empty() {
873            return self;
874        }
875
876        let descriptor_sets_vk: SmallVec<[_; 12]> =
877            descriptor_sets.iter().map(|x| x.handle()).collect();
878
879        let fns = self.device().fns();
880        unsafe {
881            (fns.v1_0.cmd_bind_descriptor_sets)(
882                self.handle(),
883                pipeline_bind_point.into(),
884                pipeline_layout.handle(),
885                first_set,
886                descriptor_sets_vk.len() as u32,
887                descriptor_sets_vk.as_ptr(),
888                dynamic_offsets.len() as u32,
889                dynamic_offsets.as_ptr(),
890            )
891        };
892
893        self
894    }
895
896    #[inline]
897    pub unsafe fn bind_index_buffer(
898        &mut self,
899        index_buffer: &IndexBuffer,
900    ) -> Result<&mut Self, Box<ValidationError>> {
901        self.validate_bind_index_buffer(index_buffer)?;
902
903        Ok(unsafe { self.bind_index_buffer_unchecked(index_buffer) })
904    }
905
906    fn validate_bind_index_buffer(
907        &self,
908        index_buffer: &IndexBuffer,
909    ) -> Result<(), Box<ValidationError>> {
910        if !self
911            .queue_family_properties()
912            .queue_flags
913            .intersects(QueueFlags::GRAPHICS)
914        {
915            return Err(Box::new(ValidationError {
916                problem: "the queue family of the command buffer does not support \
917                    graphics operations"
918                    .into(),
919                vuids: &["VUID-vkCmdBindIndexBuffer-commandBuffer-cmdpool"],
920                ..Default::default()
921            }));
922        }
923
924        let index_buffer_bytes = index_buffer.as_bytes();
925
926        // VUID-vkCmdBindIndexBuffer-commonparent
927        assert_eq!(self.device(), index_buffer_bytes.device());
928
929        if !index_buffer_bytes
930            .buffer()
931            .usage()
932            .intersects(BufferUsage::INDEX_BUFFER)
933        {
934            return Err(Box::new(ValidationError {
935                context: "index_buffer.usage()".into(),
936                problem: "does not contain `BufferUsage::INDEX_BUFFER`".into(),
937                vuids: &["VUID-vkCmdBindIndexBuffer-buffer-00433"],
938                ..Default::default()
939            }));
940        }
941
942        if matches!(index_buffer, IndexBuffer::U8(_))
943            && !self.device().enabled_features().index_type_uint8
944        {
945            return Err(Box::new(ValidationError {
946                context: "index_buffer".into(),
947                problem: "is `IndexBuffer::U8`".into(),
948                requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceFeature(
949                    "index_type_uint8",
950                )])]),
951                vuids: &["VUID-vkCmdBindIndexBuffer-indexType-02765"],
952            }));
953        }
954
955        // TODO:
956        // VUID-vkCmdBindIndexBuffer-offset-00432
957
958        Ok(())
959    }
960
961    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
962    pub unsafe fn bind_index_buffer_unchecked(&mut self, index_buffer: &IndexBuffer) -> &mut Self {
963        let index_buffer_bytes = index_buffer.as_bytes();
964
965        let fns = self.device().fns();
966        unsafe {
967            (fns.v1_0.cmd_bind_index_buffer)(
968                self.handle(),
969                index_buffer_bytes.buffer().handle(),
970                index_buffer_bytes.offset(),
971                index_buffer.index_type().into(),
972            )
973        };
974
975        self
976    }
977
978    #[inline]
979    pub unsafe fn bind_pipeline_compute(
980        &mut self,
981        pipeline: &ComputePipeline,
982    ) -> Result<&mut Self, Box<ValidationError>> {
983        self.validate_bind_pipeline_compute(pipeline)?;
984
985        Ok(unsafe { self.bind_pipeline_compute_unchecked(pipeline) })
986    }
987
988    fn validate_bind_pipeline_compute(
989        &self,
990        pipeline: &ComputePipeline,
991    ) -> Result<(), Box<ValidationError>> {
992        if !self
993            .queue_family_properties()
994            .queue_flags
995            .intersects(QueueFlags::COMPUTE)
996        {
997            return Err(Box::new(ValidationError {
998                problem: "the queue family of the command buffer does not support \
999                    compute operations"
1000                    .into(),
1001                vuids: &["VUID-vkCmdBindPipeline-pipelineBindPoint-00777"],
1002                ..Default::default()
1003            }));
1004        }
1005
1006        // VUID-vkCmdBindPipeline-commonparent
1007        assert_eq!(self.device(), pipeline.device());
1008
1009        Ok(())
1010    }
1011
1012    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1013    pub unsafe fn bind_pipeline_compute_unchecked(
1014        &mut self,
1015        pipeline: &ComputePipeline,
1016    ) -> &mut Self {
1017        let fns = self.device().fns();
1018        unsafe {
1019            (fns.v1_0.cmd_bind_pipeline)(
1020                self.handle(),
1021                ash::vk::PipelineBindPoint::COMPUTE,
1022                pipeline.handle(),
1023            )
1024        };
1025
1026        self
1027    }
1028
1029    #[inline]
1030    pub unsafe fn bind_pipeline_graphics(
1031        &mut self,
1032        pipeline: &GraphicsPipeline,
1033    ) -> Result<&mut Self, Box<ValidationError>> {
1034        self.validate_bind_pipeline_graphics(pipeline)?;
1035
1036        Ok(unsafe { self.bind_pipeline_graphics_unchecked(pipeline) })
1037    }
1038
1039    fn validate_bind_pipeline_graphics(
1040        &self,
1041        pipeline: &GraphicsPipeline,
1042    ) -> Result<(), Box<ValidationError>> {
1043        if !self
1044            .queue_family_properties()
1045            .queue_flags
1046            .intersects(QueueFlags::GRAPHICS)
1047        {
1048            return Err(Box::new(ValidationError {
1049                problem: "the queue family of the command buffer does not support \
1050                    graphics operations"
1051                    .into(),
1052                vuids: &["VUID-vkCmdBindPipeline-pipelineBindPoint-00778"],
1053                ..Default::default()
1054            }));
1055        }
1056
1057        // VUID-vkCmdBindPipeline-commonparent
1058        assert_eq!(self.device(), pipeline.device());
1059
1060        Ok(())
1061    }
1062
1063    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1064    pub unsafe fn bind_pipeline_graphics_unchecked(
1065        &mut self,
1066        pipeline: &GraphicsPipeline,
1067    ) -> &mut Self {
1068        let fns = self.device().fns();
1069        unsafe {
1070            (fns.v1_0.cmd_bind_pipeline)(
1071                self.handle(),
1072                ash::vk::PipelineBindPoint::GRAPHICS,
1073                pipeline.handle(),
1074            )
1075        };
1076
1077        self
1078    }
1079
1080    pub unsafe fn bind_pipeline_ray_tracing(
1081        &mut self,
1082        pipeline: &RayTracingPipeline,
1083    ) -> Result<&mut Self, Box<ValidationError>> {
1084        self.validate_bind_pipeline_ray_tracing(pipeline)?;
1085        Ok(unsafe { self.bind_pipeline_ray_tracing_unchecked(pipeline) })
1086    }
1087
1088    fn validate_bind_pipeline_ray_tracing(
1089        &self,
1090        pipeline: &RayTracingPipeline,
1091    ) -> Result<(), Box<ValidationError>> {
1092        if !self
1093            .queue_family_properties()
1094            .queue_flags
1095            .intersects(QueueFlags::COMPUTE)
1096        {
1097            return Err(Box::new(ValidationError {
1098                problem: "the queue family of the command buffer does not support \
1099                    compute operations"
1100                    .into(),
1101                vuids: &["VUID-vkCmdBindPipeline-pipelineBindPoint-02391"],
1102                ..Default::default()
1103            }));
1104        }
1105
1106        // VUID-vkCmdBindPipeline-commonparent
1107        assert_eq!(self.device(), pipeline.device());
1108
1109        // TODO: VUID-vkCmdBindPipeline-pipelineBindPoint-06721
1110
1111        Ok(())
1112    }
1113
1114    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1115    pub unsafe fn bind_pipeline_ray_tracing_unchecked(
1116        &mut self,
1117        pipeline: &RayTracingPipeline,
1118    ) -> &mut Self {
1119        let fns = self.device().fns();
1120        unsafe {
1121            (fns.v1_0.cmd_bind_pipeline)(
1122                self.handle(),
1123                ash::vk::PipelineBindPoint::RAY_TRACING_KHR,
1124                pipeline.handle(),
1125            )
1126        };
1127
1128        self
1129    }
1130
1131    #[inline]
1132    pub unsafe fn bind_vertex_buffers(
1133        &mut self,
1134        first_binding: u32,
1135        vertex_buffers: &[Subbuffer<[u8]>],
1136    ) -> Result<&mut Self, Box<ValidationError>> {
1137        self.validate_bind_vertex_buffers(first_binding, vertex_buffers)?;
1138
1139        Ok(unsafe { self.bind_vertex_buffers_unchecked(first_binding, vertex_buffers) })
1140    }
1141
1142    fn validate_bind_vertex_buffers(
1143        &self,
1144        first_binding: u32,
1145        vertex_buffers: &[Subbuffer<[u8]>],
1146    ) -> Result<(), Box<ValidationError>> {
1147        if !self
1148            .queue_family_properties()
1149            .queue_flags
1150            .intersects(QueueFlags::GRAPHICS)
1151        {
1152            return Err(Box::new(ValidationError {
1153                problem: "the queue family of the command buffer does not support \
1154                    graphics operations"
1155                    .into(),
1156                vuids: &["VUID-vkCmdBindVertexBuffers-commandBuffer-cmdpool"],
1157                ..Default::default()
1158            }));
1159        }
1160
1161        let properties = self.device().physical_device().properties();
1162
1163        if first_binding + vertex_buffers.len() as u32 > properties.max_vertex_input_bindings {
1164            return Err(Box::new(ValidationError {
1165                problem: "`first_binding + vertex_buffers.len()` is greater than the \
1166                    `max_vertex_input_bindings` limit"
1167                    .into(),
1168                vuids: &[
1169                    "VUID-vkCmdBindVertexBuffers-firstBinding-00624",
1170                    "VUID-vkCmdBindVertexBuffers-firstBinding-00625",
1171                ],
1172                ..Default::default()
1173            }));
1174        }
1175
1176        for (vertex_buffers_index, buffer) in vertex_buffers.iter().enumerate() {
1177            // VUID-vkCmdBindVertexBuffers-commonparent
1178            assert_eq!(self.device(), buffer.device());
1179
1180            if !buffer
1181                .buffer()
1182                .usage()
1183                .intersects(BufferUsage::VERTEX_BUFFER)
1184            {
1185                return Err(Box::new(ValidationError {
1186                    context: format!("vertex_buffers[{}].usage()", vertex_buffers_index).into(),
1187                    problem: "does not contain `BufferUsage::VERTEX_BUFFER`".into(),
1188                    vuids: &["VUID-vkCmdBindVertexBuffers-pBuffers-00627"],
1189                    ..Default::default()
1190                }));
1191            }
1192        }
1193
1194        Ok(())
1195    }
1196
1197    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1198    pub unsafe fn bind_vertex_buffers_unchecked(
1199        &mut self,
1200        first_binding: u32,
1201        vertex_buffers: &[Subbuffer<[u8]>],
1202    ) -> &mut Self {
1203        if vertex_buffers.is_empty() {
1204            return self;
1205        }
1206
1207        let device = self.device();
1208
1209        if device.api_version() >= Version::V1_3
1210            || device.enabled_extensions().ext_extended_dynamic_state
1211            || device.enabled_extensions().ext_shader_object
1212        {
1213            let mut buffers_vk: SmallVec<[_; 2]> = SmallVec::with_capacity(vertex_buffers.len());
1214            let mut offsets_vk: SmallVec<[_; 2]> = SmallVec::with_capacity(vertex_buffers.len());
1215            let mut sizes_vk: SmallVec<[_; 2]> = SmallVec::with_capacity(vertex_buffers.len());
1216
1217            for buffer in vertex_buffers {
1218                buffers_vk.push(buffer.buffer().handle());
1219                offsets_vk.push(buffer.offset());
1220                sizes_vk.push(buffer.size());
1221            }
1222
1223            let fns = self.device().fns();
1224            let cmd_bind_vertex_buffers2 = if device.api_version() >= Version::V1_3 {
1225                fns.v1_3.cmd_bind_vertex_buffers2
1226            } else if device.enabled_extensions().ext_extended_dynamic_state {
1227                fns.ext_extended_dynamic_state.cmd_bind_vertex_buffers2_ext
1228            } else {
1229                fns.ext_shader_object.cmd_bind_vertex_buffers2_ext
1230            };
1231
1232            unsafe {
1233                cmd_bind_vertex_buffers2(
1234                    self.handle(),
1235                    first_binding,
1236                    buffers_vk.len() as u32,
1237                    buffers_vk.as_ptr(),
1238                    offsets_vk.as_ptr(),
1239                    sizes_vk.as_ptr(),
1240                    ptr::null(),
1241                )
1242            }
1243        } else {
1244            let mut buffers_vk: SmallVec<[_; 2]> = SmallVec::with_capacity(vertex_buffers.len());
1245            let mut offsets_vk: SmallVec<[_; 2]> = SmallVec::with_capacity(vertex_buffers.len());
1246
1247            for buffer in vertex_buffers {
1248                buffers_vk.push(buffer.buffer().handle());
1249                offsets_vk.push(buffer.offset());
1250            }
1251
1252            let fns = self.device().fns();
1253            unsafe {
1254                (fns.v1_0.cmd_bind_vertex_buffers)(
1255                    self.handle(),
1256                    first_binding,
1257                    buffers_vk.len() as u32,
1258                    buffers_vk.as_ptr(),
1259                    offsets_vk.as_ptr(),
1260                )
1261            };
1262        }
1263
1264        self
1265    }
1266
1267    #[inline]
1268    pub unsafe fn push_constants<Pc>(
1269        &mut self,
1270        pipeline_layout: &PipelineLayout,
1271        offset: u32,
1272        push_constants: &Pc,
1273    ) -> Result<&mut Self, Box<ValidationError>>
1274    where
1275        Pc: BufferContents,
1276    {
1277        self.validate_push_constants(pipeline_layout, offset, push_constants)?;
1278
1279        Ok(unsafe { self.push_constants_unchecked(pipeline_layout, offset, push_constants) })
1280    }
1281
1282    fn validate_push_constants<Pc: BufferContents>(
1283        &self,
1284        pipeline_layout: &PipelineLayout,
1285        offset: u32,
1286        _push_constants: &Pc,
1287    ) -> Result<(), Box<ValidationError>> {
1288        let mut remaining_size = size_of::<Pc>();
1289
1290        if offset % 4 != 0 {
1291            return Err(Box::new(ValidationError {
1292                context: "offset".into(),
1293                problem: "is not a multiple of 4".into(),
1294                vuids: &["VUID-vkCmdPushConstants-offset-00368"],
1295                ..Default::default()
1296            }));
1297        }
1298
1299        if remaining_size % 4 != 0 {
1300            return Err(Box::new(ValidationError {
1301                context: "push_constants".into(),
1302                problem: "the size is not a multiple of 4".into(),
1303                vuids: &["VUID-vkCmdPushConstants-size-00369"],
1304                ..Default::default()
1305            }));
1306        }
1307
1308        let properties = self.device().physical_device().properties();
1309
1310        if offset >= properties.max_push_constants_size {
1311            return Err(Box::new(ValidationError {
1312                context: "offset".into(),
1313                problem: "is not less than the `max_push_constants_size` limit".into(),
1314                vuids: &["VUID-vkCmdPushConstants-offset-00370"],
1315                ..Default::default()
1316            }));
1317        }
1318
1319        if offset as usize + remaining_size > properties.max_push_constants_size as usize {
1320            return Err(Box::new(ValidationError {
1321                problem: "`offset` + the size of `push_constants` is not less than or \
1322                    equal to the `max_push_constants_size` limit"
1323                    .into(),
1324                vuids: &["VUID-vkCmdPushConstants-size-00371"],
1325                ..Default::default()
1326            }));
1327        }
1328
1329        let mut current_offset = offset as usize;
1330
1331        for range in pipeline_layout
1332            .push_constant_ranges_disjoint()
1333            .iter()
1334            .skip_while(|range| range.offset + range.size <= offset)
1335        {
1336            // there is a gap between ranges, but the passed push_constants contains
1337            // some bytes in this gap, exit the loop and report error
1338            if range.offset as usize > current_offset {
1339                break;
1340            }
1341
1342            // push the minimum of the whole remaining data, and the part until the end of this
1343            // range
1344            let push_size =
1345                remaining_size.min(range.offset as usize + range.size as usize - current_offset);
1346            current_offset += push_size;
1347            remaining_size -= push_size;
1348
1349            if remaining_size == 0 {
1350                break;
1351            }
1352        }
1353
1354        if remaining_size != 0 {
1355            return Err(Box::new(ValidationError {
1356                problem: "one or more bytes of `push_constants` are not within any push constant \
1357                    range of `pipeline_layout`"
1358                    .into(),
1359                vuids: &["VUID-vkCmdPushConstants-offset-01795"],
1360                ..Default::default()
1361            }));
1362        }
1363
1364        Ok(())
1365    }
1366
1367    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1368    pub unsafe fn push_constants_unchecked<Pc>(
1369        &mut self,
1370        pipeline_layout: &PipelineLayout,
1371        offset: u32,
1372        push_constants: &Pc,
1373    ) -> &mut Self
1374    where
1375        Pc: BufferContents,
1376    {
1377        let size = u32::try_from(size_of::<Pc>()).unwrap();
1378
1379        if size == 0 {
1380            return self;
1381        }
1382
1383        let fns = self.device().fns();
1384        let mut current_offset = offset;
1385        let mut remaining_size = size;
1386
1387        for range in pipeline_layout
1388            .push_constant_ranges_disjoint()
1389            .iter()
1390            .skip_while(|range| range.offset + range.size <= offset)
1391        {
1392            // there is a gap between ranges, but the passed push_constants contains
1393            // some bytes in this gap, exit the loop and report error
1394            if range.offset > current_offset {
1395                break;
1396            }
1397
1398            // push the minimum of the whole remaining data, and the part until the end of this
1399            // range
1400            let push_size = remaining_size.min(range.offset + range.size - current_offset);
1401            let data_offset = (current_offset - offset) as usize;
1402            debug_assert!(data_offset < size as usize);
1403            let data = unsafe { <*const _>::cast::<c_void>(push_constants).add(data_offset) };
1404
1405            unsafe {
1406                (fns.v1_0.cmd_push_constants)(
1407                    self.handle(),
1408                    pipeline_layout.handle(),
1409                    range.stages.into(),
1410                    current_offset,
1411                    push_size,
1412                    data,
1413                )
1414            };
1415
1416            current_offset += push_size;
1417            remaining_size -= push_size;
1418
1419            if remaining_size == 0 {
1420                break;
1421            }
1422        }
1423
1424        debug_assert!(remaining_size == 0);
1425
1426        self
1427    }
1428
1429    #[inline]
1430    pub unsafe fn push_descriptor_set(
1431        &mut self,
1432        pipeline_bind_point: PipelineBindPoint,
1433        pipeline_layout: &PipelineLayout,
1434        set_num: u32,
1435        descriptor_writes: &[WriteDescriptorSet],
1436    ) -> Result<&mut Self, Box<ValidationError>> {
1437        self.validate_push_descriptor_set(
1438            pipeline_bind_point,
1439            pipeline_layout,
1440            set_num,
1441            descriptor_writes,
1442        )?;
1443
1444        Ok(unsafe {
1445            self.push_descriptor_set_unchecked(
1446                pipeline_bind_point,
1447                pipeline_layout,
1448                set_num,
1449                descriptor_writes,
1450            )
1451        })
1452    }
1453
1454    fn validate_push_descriptor_set(
1455        &self,
1456        pipeline_bind_point: PipelineBindPoint,
1457        pipeline_layout: &PipelineLayout,
1458        set_num: u32,
1459        descriptor_writes: &[WriteDescriptorSet],
1460    ) -> Result<(), Box<ValidationError>> {
1461        if !self.device().enabled_extensions().khr_push_descriptor {
1462            return Err(Box::new(ValidationError {
1463                requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
1464                    "khr_push_descriptor",
1465                )])]),
1466                ..Default::default()
1467            }));
1468        }
1469
1470        pipeline_bind_point
1471            .validate_device(self.device())
1472            .map_err(|err| {
1473                err.add_context("pipeline_bind_point")
1474                    .set_vuids(&["VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-parameter"])
1475            })?;
1476
1477        let queue_family_properties = self.queue_family_properties();
1478
1479        match pipeline_bind_point {
1480            PipelineBindPoint::Compute => {
1481                if !queue_family_properties
1482                    .queue_flags
1483                    .intersects(QueueFlags::COMPUTE)
1484                {
1485                    return Err(Box::new(ValidationError {
1486                        context: "self".into(),
1487                        problem: "`pipeline_bind_point` is `PipelineBindPoint::Compute`, and the \
1488                            queue family does not support compute operations"
1489                            .into(),
1490                        vuids: &[
1491                            "VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-00363",
1492                            "VUID-vkCmdPushDescriptorSetKHR-commandBuffer-cmdpool",
1493                        ],
1494                        ..Default::default()
1495                    }));
1496                }
1497            }
1498            PipelineBindPoint::Graphics => {
1499                if !queue_family_properties
1500                    .queue_flags
1501                    .intersects(QueueFlags::GRAPHICS)
1502                {
1503                    return Err(Box::new(ValidationError {
1504                        context: "self".into(),
1505                        problem: "`pipeline_bind_point` is `PipelineBindPoint::Graphics`, and the \
1506                            queue family does not support graphics operations"
1507                            .into(),
1508                        vuids: &[
1509                            "VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-00363",
1510                            "VUID-vkCmdPushDescriptorSetKHR-commandBuffer-cmdpool",
1511                        ],
1512                        ..Default::default()
1513                    }));
1514                }
1515            }
1516            PipelineBindPoint::RayTracing => {
1517                if !queue_family_properties
1518                    .queue_flags
1519                    .intersects(QueueFlags::COMPUTE)
1520                {
1521                    return Err(Box::new(ValidationError {
1522                        context: "self".into(),
1523                        problem:
1524                            "`pipeline_bind_point` is `PipelineBindPoint::RayTracing`, and the \
1525                            queue family does not support compute operations"
1526                                .into(),
1527                        vuids: &[
1528                            "VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-02391",
1529                            "VUID-vkCmdPushDescriptorSetKHR-commandBuffer-cmdpool",
1530                        ],
1531                        ..Default::default()
1532                    }));
1533                }
1534            }
1535        }
1536
1537        // VUID-vkCmdPushDescriptorSetKHR-commonparent
1538        assert_eq!(self.device(), pipeline_layout.device());
1539
1540        if set_num as usize > pipeline_layout.set_layouts().len() {
1541            return Err(Box::new(ValidationError {
1542                problem: "`set_num` is greater than the number of descriptor set layouts in \
1543                    `pipeline_layout`"
1544                    .into(),
1545                vuids: &["VUID-vkCmdPushDescriptorSetKHR-set-00364"],
1546                ..Default::default()
1547            }));
1548        }
1549
1550        let descriptor_set_layout = &pipeline_layout.set_layouts()[set_num as usize];
1551
1552        if !descriptor_set_layout
1553            .flags()
1554            .intersects(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR)
1555        {
1556            return Err(Box::new(ValidationError {
1557                problem: "the descriptor set layout with the number `set_num` in \
1558                    `pipeline_layout` was not created with the \
1559                    `DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR` flag"
1560                    .into(),
1561                vuids: &["VUID-vkCmdPushDescriptorSetKHR-set-00365"],
1562                ..Default::default()
1563            }));
1564        }
1565
1566        for (index, write) in descriptor_writes.iter().enumerate() {
1567            write
1568                .validate(descriptor_set_layout, 0)
1569                .map_err(|err| err.add_context(format!("descriptor_writes[{}]", index)))?;
1570        }
1571
1572        Ok(())
1573    }
1574
1575    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1576    pub unsafe fn push_descriptor_set_unchecked(
1577        &mut self,
1578        pipeline_bind_point: PipelineBindPoint,
1579        pipeline_layout: &PipelineLayout,
1580        set_num: u32,
1581        descriptor_writes: &[WriteDescriptorSet],
1582    ) -> &mut Self {
1583        if descriptor_writes.is_empty() {
1584            return self;
1585        }
1586
1587        let set_layout_bindings = &pipeline_layout.set_layouts()[set_num as usize].bindings();
1588        let writes_fields1_vk: SmallVec<[_; 8]> = descriptor_writes
1589            .iter()
1590            .map(|write| {
1591                let default_image_layout = set_layout_bindings[&write.binding()]
1592                    .descriptor_type
1593                    .default_image_layout();
1594                write.to_vk_fields1(default_image_layout)
1595            })
1596            .collect();
1597        let mut writes_extensions_vk: SmallVec<[_; 8]> = descriptor_writes
1598            .iter()
1599            .zip(&writes_fields1_vk)
1600            .map(|(write, fields1_vk)| write.to_vk_extensions(fields1_vk))
1601            .collect();
1602        let writes_vk: SmallVec<[_; 8]> = descriptor_writes
1603            .iter()
1604            .zip(&writes_fields1_vk)
1605            .zip(&mut writes_extensions_vk)
1606            .map(|((write, write_info_vk), write_extension_vk)| {
1607                write.to_vk(
1608                    ash::vk::DescriptorSet::null(),
1609                    set_layout_bindings[&write.binding()].descriptor_type,
1610                    write_info_vk,
1611                    write_extension_vk,
1612                )
1613            })
1614            .collect();
1615
1616        let fns = self.device().fns();
1617        unsafe {
1618            (fns.khr_push_descriptor.cmd_push_descriptor_set_khr)(
1619                self.handle(),
1620                pipeline_bind_point.into(),
1621                pipeline_layout.handle(),
1622                set_num,
1623                writes_vk.len() as u32,
1624                writes_vk.as_ptr(),
1625            )
1626        };
1627
1628        self
1629    }
1630}