• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2022 The vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9 
10 use super::{CommandBufferBuilder, RenderPassStateType, SetOrPush};
11 use crate::{
12     buffer::{BufferContents, BufferUsage, Subbuffer},
13     command_buffer::{allocator::CommandBufferAllocator, commands::bind_push::BindPushError},
14     descriptor_set::{
15         check_descriptor_write, layout::DescriptorType, DescriptorBindingResources,
16         DescriptorSetResources, DescriptorSetWithOffsets, DescriptorSetsCollection,
17         DescriptorWriteInfo, WriteDescriptorSet,
18     },
19     device::{DeviceOwned, QueueFlags},
20     memory::is_aligned,
21     pipeline::{
22         graphics::{
23             input_assembly::{Index, IndexType},
24             render_pass::PipelineRenderPassType,
25             vertex_input::VertexBuffersCollection,
26         },
27         ComputePipeline, GraphicsPipeline, PipelineBindPoint, PipelineLayout,
28     },
29     DeviceSize, RequiresOneOf, VulkanObject,
30 };
31 use smallvec::SmallVec;
32 use std::{cmp::min, mem::size_of_val, os::raw::c_void, sync::Arc};
33 
34 impl<L, A> CommandBufferBuilder<L, A>
35 where
36     A: CommandBufferAllocator,
37 {
38     /// Binds descriptor sets for future dispatch or draw calls.
39     ///
40     /// # Panics
41     ///
42     /// - Panics if the queue family of the command buffer does not support `pipeline_bind_point`.
43     /// - Panics if the highest descriptor set slot being bound is not less than the number of sets
44     ///   in `pipeline_layout`.
45     /// - Panics if `self` and any element of `descriptor_sets` do not belong to the same device.
bind_descriptor_sets( &mut self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: Arc<PipelineLayout>, first_set: u32, descriptor_sets: impl DescriptorSetsCollection, ) -> &mut Self46     pub fn bind_descriptor_sets(
47         &mut self,
48         pipeline_bind_point: PipelineBindPoint,
49         pipeline_layout: Arc<PipelineLayout>,
50         first_set: u32,
51         descriptor_sets: impl DescriptorSetsCollection,
52     ) -> &mut Self {
53         let descriptor_sets = descriptor_sets.into_vec();
54         self.validate_bind_descriptor_sets(
55             pipeline_bind_point,
56             &pipeline_layout,
57             first_set,
58             &descriptor_sets,
59         )
60         .unwrap();
61 
62         unsafe {
63             self.bind_descriptor_sets_unchecked(
64                 pipeline_bind_point,
65                 pipeline_layout,
66                 first_set,
67                 descriptor_sets,
68             )
69         }
70     }
71 
validate_bind_descriptor_sets( &self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: &PipelineLayout, first_set: u32, descriptor_sets: &[DescriptorSetWithOffsets], ) -> Result<(), BindPushError>72     fn validate_bind_descriptor_sets(
73         &self,
74         pipeline_bind_point: PipelineBindPoint,
75         pipeline_layout: &PipelineLayout,
76         first_set: u32,
77         descriptor_sets: &[DescriptorSetWithOffsets],
78     ) -> Result<(), BindPushError> {
79         // VUID-vkCmdBindDescriptorSets-pipelineBindPoint-parameter
80         pipeline_bind_point.validate_device(self.device())?;
81 
82         let queue_family_properties = self.queue_family_properties();
83 
84         // VUID-vkCmdBindDescriptorSets-commandBuffer-cmdpool
85         // VUID-vkCmdBindDescriptorSets-pipelineBindPoint-00361
86         match pipeline_bind_point {
87             PipelineBindPoint::Compute => {
88                 if !queue_family_properties
89                     .queue_flags
90                     .intersects(QueueFlags::COMPUTE)
91                 {
92                     return Err(BindPushError::NotSupportedByQueueFamily);
93                 }
94             }
95             PipelineBindPoint::Graphics => {
96                 if !queue_family_properties
97                     .queue_flags
98                     .intersects(QueueFlags::GRAPHICS)
99                 {
100                     return Err(BindPushError::NotSupportedByQueueFamily);
101                 }
102             }
103         }
104 
105         // VUID-vkCmdBindDescriptorSets-firstSet-00360
106         if first_set + descriptor_sets.len() as u32 > pipeline_layout.set_layouts().len() as u32 {
107             return Err(BindPushError::DescriptorSetOutOfRange {
108                 set_num: first_set + descriptor_sets.len() as u32,
109                 pipeline_layout_set_count: pipeline_layout.set_layouts().len() as u32,
110             });
111         }
112 
113         let properties = self.device().physical_device().properties();
114         let uniform_alignment = properties.min_uniform_buffer_offset_alignment;
115         let storage_alignment = properties.min_storage_buffer_offset_alignment;
116 
117         for (i, set) in descriptor_sets.iter().enumerate() {
118             let set_num = first_set + i as u32;
119             let (set, dynamic_offsets) = set.as_ref();
120 
121             // VUID-vkCmdBindDescriptorSets-commonparent
122             assert_eq!(self.device(), set.device());
123 
124             let set_layout = set.layout();
125             let pipeline_set_layout = &pipeline_layout.set_layouts()[set_num as usize];
126 
127             // VUID-vkCmdBindDescriptorSets-pDescriptorSets-00358
128             if !pipeline_set_layout.is_compatible_with(set_layout) {
129                 return Err(BindPushError::DescriptorSetNotCompatible { set_num });
130             }
131 
132             let mut dynamic_offsets_remaining = dynamic_offsets;
133             let mut required_dynamic_offset_count = 0;
134 
135             for (&binding_num, binding) in set_layout.bindings() {
136                 let required_alignment = match binding.descriptor_type {
137                     DescriptorType::UniformBufferDynamic => uniform_alignment,
138                     DescriptorType::StorageBufferDynamic => storage_alignment,
139                     _ => continue,
140                 };
141 
142                 let count = if binding.variable_descriptor_count {
143                     set.variable_descriptor_count()
144                 } else {
145                     binding.descriptor_count
146                 } as usize;
147 
148                 required_dynamic_offset_count += count;
149 
150                 if !dynamic_offsets_remaining.is_empty() {
151                     let split_index = min(count, dynamic_offsets_remaining.len());
152                     let dynamic_offsets = &dynamic_offsets_remaining[..split_index];
153                     dynamic_offsets_remaining = &dynamic_offsets_remaining[split_index..];
154 
155                     let elements = match set.resources().binding(binding_num) {
156                         Some(DescriptorBindingResources::Buffer(elements)) => elements.as_slice(),
157                         _ => unreachable!(),
158                     };
159 
160                     for (index, (&offset, element)) in
161                         dynamic_offsets.iter().zip(elements).enumerate()
162                     {
163                         // VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01971
164                         // VUID-vkCmdBindDescriptorSets-pDynamicOffsets-01972
165                         if !is_aligned(offset as DeviceSize, required_alignment) {
166                             return Err(BindPushError::DynamicOffsetNotAligned {
167                                 set_num,
168                                 binding_num,
169                                 index: index as u32,
170                                 offset,
171                                 required_alignment,
172                             });
173                         }
174 
175                         if let Some((buffer, range)) = element {
176                             // VUID-vkCmdBindDescriptorSets-pDescriptorSets-01979
177                             if offset as DeviceSize + range.end > buffer.size() {
178                                 return Err(BindPushError::DynamicOffsetOutOfBufferBounds {
179                                     set_num,
180                                     binding_num,
181                                     index: index as u32,
182                                     offset,
183                                     range_end: range.end,
184                                     buffer_size: buffer.size(),
185                                 });
186                             }
187                         }
188                     }
189                 }
190             }
191 
192             // VUID-vkCmdBindDescriptorSets-dynamicOffsetCount-00359
193             if dynamic_offsets.len() != required_dynamic_offset_count {
194                 return Err(BindPushError::DynamicOffsetCountMismatch {
195                     set_num,
196                     provided_count: dynamic_offsets.len(),
197                     required_count: required_dynamic_offset_count,
198                 });
199             }
200         }
201 
202         Ok(())
203     }
204 
205     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
bind_descriptor_sets_unchecked( &mut self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: Arc<PipelineLayout>, first_set: u32, descriptor_sets: impl DescriptorSetsCollection, ) -> &mut Self206     pub unsafe fn bind_descriptor_sets_unchecked(
207         &mut self,
208         pipeline_bind_point: PipelineBindPoint,
209         pipeline_layout: Arc<PipelineLayout>,
210         first_set: u32,
211         descriptor_sets: impl DescriptorSetsCollection,
212     ) -> &mut Self {
213         let descriptor_sets_with_offsets = descriptor_sets.into_vec();
214 
215         if descriptor_sets_with_offsets.is_empty() {
216             return self;
217         }
218 
219         let descriptor_sets: SmallVec<[_; 12]> = descriptor_sets_with_offsets
220             .iter()
221             .map(|x| x.as_ref().0.inner().handle())
222             .collect();
223         let dynamic_offsets: SmallVec<[_; 32]> = descriptor_sets_with_offsets
224             .iter()
225             .flat_map(|x| x.as_ref().1.iter().copied())
226             .collect();
227 
228         let fns = self.device().fns();
229         (fns.v1_0.cmd_bind_descriptor_sets)(
230             self.handle(),
231             pipeline_bind_point.into(),
232             pipeline_layout.handle(),
233             first_set,
234             descriptor_sets.len() as u32,
235             descriptor_sets.as_ptr(),
236             dynamic_offsets.len() as u32,
237             dynamic_offsets.as_ptr(),
238         );
239 
240         let state = self.builder_state.invalidate_descriptor_sets(
241             pipeline_bind_point,
242             pipeline_layout.clone(),
243             first_set,
244             descriptor_sets_with_offsets.len() as u32,
245         );
246 
247         self.resources
248             .reserve(descriptor_sets_with_offsets.len() + 1);
249 
250         for (set_num, descriptor_set_with_offsets) in
251             descriptor_sets_with_offsets.into_iter().enumerate()
252         {
253             let descriptor_set = descriptor_set_with_offsets.as_ref().0.clone();
254             state.descriptor_sets.insert(
255                 first_set + set_num as u32,
256                 SetOrPush::Set(descriptor_set_with_offsets),
257             );
258             self.resources.push(Box::new(descriptor_set));
259         }
260 
261         self.resources.push(Box::new(pipeline_layout));
262 
263         self.next_command_index += 1;
264         self
265     }
266 
267     /// Binds an index buffer for future indexed draw calls.
268     ///
269     /// # Panics
270     ///
271     /// - Panics if the queue family of the command buffer does not support graphics operations.
272     /// - Panics if `self` and `index_buffer` do not belong to the same device.
273     /// - Panics if `index_buffer` does not have the [`BufferUsage::INDEX_BUFFER`] usage enabled.
274     /// - If the index buffer contains `u8` indices, panics if the [`index_type_uint8`] feature is
275     ///   not enabled on the device.
276     ///
277     /// [`BufferUsage::INDEX_BUFFER`]: crate::buffer::BufferUsage::INDEX_BUFFER
278     /// [`index_type_uint8`]: crate::device::Features::index_type_uint8
bind_index_buffer<I: Index>(&mut self, index_buffer: Subbuffer<[I]>) -> &mut Self279     pub fn bind_index_buffer<I: Index>(&mut self, index_buffer: Subbuffer<[I]>) -> &mut Self {
280         self.validate_bind_index_buffer(index_buffer.as_bytes(), I::ty())
281             .unwrap();
282 
283         unsafe { self.bind_index_buffer_unchecked(index_buffer.into_bytes(), I::ty()) }
284     }
285 
validate_bind_index_buffer( &self, index_buffer: &Subbuffer<[u8]>, index_type: IndexType, ) -> Result<(), BindPushError>286     fn validate_bind_index_buffer(
287         &self,
288         index_buffer: &Subbuffer<[u8]>,
289         index_type: IndexType,
290     ) -> Result<(), BindPushError> {
291         let queue_family_properties = self.queue_family_properties();
292 
293         // VUID-vkCmdBindIndexBuffer-commandBuffer-cmdpool
294         if !queue_family_properties
295             .queue_flags
296             .intersects(QueueFlags::GRAPHICS)
297         {
298             return Err(BindPushError::NotSupportedByQueueFamily);
299         }
300 
301         // VUID-vkCmdBindIndexBuffer-commonparent
302         assert_eq!(self.device(), index_buffer.device());
303 
304         // VUID-vkCmdBindIndexBuffer-buffer-00433
305         if !index_buffer
306             .buffer()
307             .usage()
308             .intersects(BufferUsage::INDEX_BUFFER)
309         {
310             return Err(BindPushError::IndexBufferMissingUsage);
311         }
312 
313         // VUID-vkCmdBindIndexBuffer-indexType-02765
314         if index_type == IndexType::U8 && !self.device().enabled_features().index_type_uint8 {
315             return Err(BindPushError::RequirementNotMet {
316                 required_for: "`index_type` is `IndexType::U8`",
317                 requires_one_of: RequiresOneOf {
318                     features: &["index_type_uint8"],
319                     ..Default::default()
320                 },
321             });
322         }
323 
324         // TODO:
325         // VUID-vkCmdBindIndexBuffer-offset-00432
326 
327         Ok(())
328     }
329 
330     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
bind_index_buffer_unchecked( &mut self, buffer: Subbuffer<[u8]>, index_type: IndexType, ) -> &mut Self331     pub unsafe fn bind_index_buffer_unchecked(
332         &mut self,
333         buffer: Subbuffer<[u8]>,
334         index_type: IndexType,
335     ) -> &mut Self {
336         let fns = self.device().fns();
337         (fns.v1_0.cmd_bind_index_buffer)(
338             self.handle(),
339             buffer.buffer().handle(),
340             buffer.offset(),
341             index_type.into(),
342         );
343 
344         self.builder_state.index_buffer = Some((buffer.clone(), index_type));
345         self.resources.push(Box::new(buffer));
346 
347         self.next_command_index += 1;
348         self
349     }
350 
351     /// Binds a compute pipeline for future dispatch calls.
352     ///
353     /// # Panics
354     ///
355     /// - Panics if the queue family of the command buffer does not support compute operations.
356     /// - Panics if `self` and `pipeline` do not belong to the same device.
bind_pipeline_compute(&mut self, pipeline: Arc<ComputePipeline>) -> &mut Self357     pub fn bind_pipeline_compute(&mut self, pipeline: Arc<ComputePipeline>) -> &mut Self {
358         self.validate_bind_pipeline_compute(&pipeline).unwrap();
359 
360         unsafe { self.bind_pipeline_compute_unchecked(pipeline) }
361     }
362 
validate_bind_pipeline_compute( &self, pipeline: &ComputePipeline, ) -> Result<(), BindPushError>363     fn validate_bind_pipeline_compute(
364         &self,
365         pipeline: &ComputePipeline,
366     ) -> Result<(), BindPushError> {
367         let queue_family_properties = self.queue_family_properties();
368 
369         // VUID-vkCmdBindPipeline-pipelineBindPoint-00777
370         if !queue_family_properties
371             .queue_flags
372             .intersects(QueueFlags::COMPUTE)
373         {
374             return Err(BindPushError::NotSupportedByQueueFamily);
375         }
376 
377         // VUID-vkCmdBindPipeline-commonparent
378         assert_eq!(self.device(), pipeline.device());
379 
380         Ok(())
381     }
382 
383     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
bind_pipeline_compute_unchecked( &mut self, pipeline: Arc<ComputePipeline>, ) -> &mut Self384     pub unsafe fn bind_pipeline_compute_unchecked(
385         &mut self,
386         pipeline: Arc<ComputePipeline>,
387     ) -> &mut Self {
388         let fns = self.device().fns();
389         (fns.v1_0.cmd_bind_pipeline)(
390             self.handle(),
391             ash::vk::PipelineBindPoint::COMPUTE,
392             pipeline.handle(),
393         );
394 
395         self.builder_state.pipeline_compute = Some(pipeline.clone());
396         self.resources.push(Box::new(pipeline));
397 
398         self.next_command_index += 1;
399         self
400     }
401 
402     /// Binds a graphics pipeline for future draw calls.
403     ///
404     /// # Panics
405     ///
406     /// - Panics if the queue family of the command buffer does not support graphics operations.
407     /// - Panics if `self` and `pipeline` do not belong to the same device.
bind_pipeline_graphics(&mut self, pipeline: Arc<GraphicsPipeline>) -> &mut Self408     pub fn bind_pipeline_graphics(&mut self, pipeline: Arc<GraphicsPipeline>) -> &mut Self {
409         self.validate_bind_pipeline_graphics(&pipeline).unwrap();
410 
411         unsafe { self.bind_pipeline_graphics_unchecked(pipeline) }
412     }
413 
validate_bind_pipeline_graphics( &self, pipeline: &GraphicsPipeline, ) -> Result<(), BindPushError>414     fn validate_bind_pipeline_graphics(
415         &self,
416         pipeline: &GraphicsPipeline,
417     ) -> Result<(), BindPushError> {
418         let queue_family_properties = self.queue_family_properties();
419 
420         // VUID-vkCmdBindPipeline-pipelineBindPoint-00778
421         if !queue_family_properties
422             .queue_flags
423             .intersects(QueueFlags::GRAPHICS)
424         {
425             return Err(BindPushError::NotSupportedByQueueFamily);
426         }
427 
428         // VUID-vkCmdBindPipeline-commonparent
429         assert_eq!(self.device(), pipeline.device());
430 
431         if let Some(last_pipeline) =
432             self.builder_state
433                 .render_pass
434                 .as_ref()
435                 .and_then(|render_pass_state| match &render_pass_state.render_pass {
436                     RenderPassStateType::BeginRendering(state) if state.pipeline_used => {
437                         self.builder_state.pipeline_graphics.as_ref()
438                     }
439                     _ => None,
440                 })
441         {
442             if let (
443                 PipelineRenderPassType::BeginRendering(pipeline_rendering_info),
444                 PipelineRenderPassType::BeginRendering(last_pipeline_rendering_info),
445             ) = (pipeline.render_pass(), last_pipeline.render_pass())
446             {
447                 // VUID-vkCmdBindPipeline-pipeline-06195
448                 // VUID-vkCmdBindPipeline-pipeline-06196
449                 if pipeline_rendering_info.color_attachment_formats
450                     != last_pipeline_rendering_info.color_attachment_formats
451                 {
452                     return Err(BindPushError::PreviousPipelineColorAttachmentFormatMismatch);
453                 }
454 
455                 // VUID-vkCmdBindPipeline-pipeline-06197
456                 if pipeline_rendering_info.depth_attachment_format
457                     != last_pipeline_rendering_info.depth_attachment_format
458                 {
459                     return Err(BindPushError::PreviousPipelineDepthAttachmentFormatMismatch);
460                 }
461 
462                 // VUID-vkCmdBindPipeline-pipeline-06194
463                 if pipeline_rendering_info.stencil_attachment_format
464                     != last_pipeline_rendering_info.stencil_attachment_format
465                 {
466                     return Err(BindPushError::PreviousPipelineStencilAttachmentFormatMismatch);
467                 }
468             }
469         }
470 
471         // VUID-vkCmdBindPipeline-pipeline-00781
472         // TODO:
473 
474         Ok(())
475     }
476 
477     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
bind_pipeline_graphics_unchecked( &mut self, pipeline: Arc<GraphicsPipeline>, ) -> &mut Self478     pub unsafe fn bind_pipeline_graphics_unchecked(
479         &mut self,
480         pipeline: Arc<GraphicsPipeline>,
481     ) -> &mut Self {
482         let fns = self.device().fns();
483         (fns.v1_0.cmd_bind_pipeline)(
484             self.handle(),
485             ash::vk::PipelineBindPoint::GRAPHICS,
486             pipeline.handle(),
487         );
488 
489         // Reset any states that are fixed in the new pipeline. The pipeline bind command will
490         // overwrite these states.
491         self.builder_state.reset_dynamic_states(
492             pipeline
493                 .dynamic_states()
494                 .filter(|(_, d)| !d) // not dynamic
495                 .map(|(s, _)| s),
496         );
497         self.builder_state.pipeline_graphics = Some(pipeline.clone());
498         self.resources.push(Box::new(pipeline));
499 
500         self.next_command_index += 1;
501         self
502     }
503 
504     /// Binds vertex buffers for future draw calls.
505     ///
506     /// # Panics
507     ///
508     /// - Panics if the queue family of the command buffer does not support graphics operations.
509     /// - Panics if the highest vertex buffer binding being bound is greater than the
510     ///   [`max_vertex_input_bindings`] device property.
511     /// - Panics if `self` and any element of `vertex_buffers` do not belong to the same device.
512     /// - Panics if any element of `vertex_buffers` does not have the
513     ///   [`BufferUsage::VERTEX_BUFFER`] usage enabled.
514     ///
515     /// [`max_vertex_input_bindings`]: crate::device::Properties::max_vertex_input_bindings
516     /// [`BufferUsage::VERTEX_BUFFER`]: crate::buffer::BufferUsage::VERTEX_BUFFER
bind_vertex_buffers( &mut self, first_binding: u32, vertex_buffers: impl VertexBuffersCollection, ) -> &mut Self517     pub fn bind_vertex_buffers(
518         &mut self,
519         first_binding: u32,
520         vertex_buffers: impl VertexBuffersCollection,
521     ) -> &mut Self {
522         let vertex_buffers = vertex_buffers.into_vec();
523         self.validate_bind_vertex_buffers(first_binding, &vertex_buffers)
524             .unwrap();
525 
526         unsafe { self.bind_vertex_buffers_unchecked(first_binding, vertex_buffers) }
527     }
528 
validate_bind_vertex_buffers( &self, first_binding: u32, vertex_buffers: &[Subbuffer<[u8]>], ) -> Result<(), BindPushError>529     fn validate_bind_vertex_buffers(
530         &self,
531         first_binding: u32,
532         vertex_buffers: &[Subbuffer<[u8]>],
533     ) -> Result<(), BindPushError> {
534         let queue_family_properties = self.queue_family_properties();
535 
536         // VUID-vkCmdBindVertexBuffers-commandBuffer-cmdpool
537         if !queue_family_properties
538             .queue_flags
539             .intersects(QueueFlags::GRAPHICS)
540         {
541             return Err(BindPushError::NotSupportedByQueueFamily);
542         }
543 
544         // VUID-vkCmdBindVertexBuffers-firstBinding-00624
545         // VUID-vkCmdBindVertexBuffers-firstBinding-00625
546         if first_binding + vertex_buffers.len() as u32
547             > self
548                 .device()
549                 .physical_device()
550                 .properties()
551                 .max_vertex_input_bindings
552         {
553             return Err(BindPushError::MaxVertexInputBindingsExceeded {
554                 _binding_count: first_binding + vertex_buffers.len() as u32,
555                 _max: self
556                     .device()
557                     .physical_device()
558                     .properties()
559                     .max_vertex_input_bindings,
560             });
561         }
562 
563         for buffer in vertex_buffers {
564             // VUID-vkCmdBindVertexBuffers-commonparent
565             assert_eq!(self.device(), buffer.device());
566 
567             // VUID-vkCmdBindVertexBuffers-pBuffers-00627
568             if !buffer
569                 .buffer()
570                 .usage()
571                 .intersects(BufferUsage::VERTEX_BUFFER)
572             {
573                 return Err(BindPushError::VertexBufferMissingUsage);
574             }
575         }
576 
577         Ok(())
578     }
579 
580     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
bind_vertex_buffers_unchecked( &mut self, first_binding: u32, buffers: impl VertexBuffersCollection, ) -> &mut Self581     pub unsafe fn bind_vertex_buffers_unchecked(
582         &mut self,
583         first_binding: u32,
584         buffers: impl VertexBuffersCollection,
585     ) -> &mut Self {
586         let buffers = buffers.into_vec();
587 
588         if buffers.is_empty() {
589             return self;
590         }
591 
592         let (buffers_vk, offsets_vk): (SmallVec<[_; 4]>, SmallVec<[_; 4]>) = buffers
593             .iter()
594             .map(|buffer| (buffer.buffer().handle(), buffer.offset()))
595             .unzip();
596 
597         let fns = self.device().fns();
598         (fns.v1_0.cmd_bind_vertex_buffers)(
599             self.handle(),
600             first_binding,
601             buffers_vk.len() as u32,
602             buffers_vk.as_ptr(),
603             offsets_vk.as_ptr(),
604         );
605 
606         self.resources.reserve(buffers.len());
607 
608         for (i, buffer) in buffers.into_iter().enumerate() {
609             self.builder_state
610                 .vertex_buffers
611                 .insert(first_binding + i as u32, buffer.clone());
612             self.resources.push(Box::new(buffer));
613         }
614 
615         self.next_command_index += 1;
616         self
617     }
618 
619     /// Sets push constants for future dispatch or draw calls.
620     ///
621     /// # Panics
622     ///
623     /// - Panics if `offset` is not a multiple of 4.
624     /// - Panics if the size of `push_constants` is not a multiple of 4.
625     /// - Panics if any of the bytes in `push_constants` do not fall within any of the pipeline
626     ///   layout's push constant ranges.
push_constants( &mut self, pipeline_layout: Arc<PipelineLayout>, offset: u32, push_constants: &(impl BufferContents + ?Sized), ) -> &mut Self627     pub fn push_constants(
628         &mut self,
629         pipeline_layout: Arc<PipelineLayout>,
630         offset: u32,
631         push_constants: &(impl BufferContents + ?Sized),
632     ) -> &mut Self {
633         self.validate_push_constants(&pipeline_layout, offset, size_of_val(push_constants) as u32)
634             .unwrap();
635 
636         unsafe { self.push_constants_unchecked(pipeline_layout, offset, push_constants) }
637     }
638 
validate_push_constants( &self, pipeline_layout: &PipelineLayout, offset: u32, data_size: u32, ) -> Result<(), BindPushError>639     fn validate_push_constants(
640         &self,
641         pipeline_layout: &PipelineLayout,
642         offset: u32,
643         data_size: u32,
644     ) -> Result<(), BindPushError> {
645         if offset % 4 != 0 {
646             return Err(BindPushError::PushConstantsOffsetNotAligned);
647         }
648 
649         if data_size % 4 != 0 {
650             return Err(BindPushError::PushConstantsSizeNotAligned);
651         }
652 
653         let mut current_offset = offset;
654         let mut remaining_size = data_size;
655 
656         for range in pipeline_layout
657             .push_constant_ranges_disjoint()
658             .iter()
659             .skip_while(|range| range.offset + range.size <= offset)
660         {
661             // there is a gap between ranges, but the passed push_constants contains
662             // some bytes in this gap, exit the loop and report error
663             if range.offset > current_offset {
664                 break;
665             }
666 
667             // push the minimum of the whole remaining data, and the part until the end of this range
668             let push_size = remaining_size.min(range.offset + range.size - current_offset);
669             current_offset += push_size;
670             remaining_size -= push_size;
671 
672             if remaining_size == 0 {
673                 break;
674             }
675         }
676 
677         if remaining_size != 0 {
678             return Err(BindPushError::PushConstantsDataOutOfRange {
679                 offset: current_offset,
680             });
681         }
682 
683         Ok(())
684     }
685 
686     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
push_constants_unchecked( &mut self, pipeline_layout: Arc<PipelineLayout>, offset: u32, push_constants: &(impl BufferContents + ?Sized), ) -> &mut Self687     pub unsafe fn push_constants_unchecked(
688         &mut self,
689         pipeline_layout: Arc<PipelineLayout>,
690         offset: u32,
691         push_constants: &(impl BufferContents + ?Sized),
692     ) -> &mut Self {
693         let mut current_offset = offset;
694         let mut remaining_size = size_of_val(push_constants) as u32;
695 
696         let fns = self.device().fns();
697 
698         for range in pipeline_layout
699             .push_constant_ranges_disjoint()
700             .iter()
701             .skip_while(|range| range.offset + range.size <= offset)
702         {
703             // there is a gap between ranges, but the passed push_constants contains
704             // some bytes in this gap, exit the loop and report error
705             if range.offset > current_offset {
706                 break;
707             }
708 
709             // push the minimum of the whole remaining data, and the part until the end of this range
710             let push_size = min(remaining_size, range.offset + range.size - current_offset);
711             let data_offset = (current_offset - offset) as usize;
712 
713             (fns.v1_0.cmd_push_constants)(
714                 self.handle(),
715                 pipeline_layout.handle(),
716                 range.stages.into(),
717                 current_offset,
718                 push_size,
719                 (push_constants as *const _ as *const c_void).add(data_offset),
720             );
721 
722             current_offset += push_size;
723             remaining_size -= push_size;
724 
725             if remaining_size == 0 {
726                 break;
727             }
728         }
729 
730         debug_assert!(remaining_size == 0);
731 
732         // TODO: Push constant invalidations.
733         // The Vulkan spec currently is unclear about this, so Vulkano currently just marks
734         // push constants as set, and never unsets them. See:
735         // https://github.com/KhronosGroup/Vulkan-Docs/issues/1485
736         // https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/2711
737         self.builder_state
738             .push_constants
739             .insert(offset..offset + size_of_val(push_constants) as u32);
740         self.builder_state.push_constants_pipeline_layout = Some(pipeline_layout.clone());
741         self.resources.push(Box::new(pipeline_layout));
742 
743         self.next_command_index += 1;
744         self
745     }
746 
747     /// Pushes descriptor data directly into the command buffer for future dispatch or draw calls.
748     ///
749     /// # Panics
750     ///
751     /// - Panics if the queue family of the command buffer does not support `pipeline_bind_point`.
752     /// - Panics if the [`khr_push_descriptor`] extension is not enabled on the device.
753     /// - Panics if `set_num` is not less than the number of sets in `pipeline_layout`.
754     /// - Panics if an element of `descriptor_writes` is not compatible with `pipeline_layout`.
755     ///
756     /// [`khr_push_descriptor`]: crate::device::DeviceExtensions::khr_push_descriptor
push_descriptor_set( &mut self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: Arc<PipelineLayout>, set_num: u32, descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>, ) -> &mut Self757     pub fn push_descriptor_set(
758         &mut self,
759         pipeline_bind_point: PipelineBindPoint,
760         pipeline_layout: Arc<PipelineLayout>,
761         set_num: u32,
762         descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>,
763     ) -> &mut Self {
764         let descriptor_writes: SmallVec<[_; 8]> = descriptor_writes.into_iter().collect();
765         self.validate_push_descriptor_set(
766             pipeline_bind_point,
767             &pipeline_layout,
768             set_num,
769             &descriptor_writes,
770         )
771         .unwrap();
772 
773         unsafe {
774             self.push_descriptor_set_unchecked(
775                 pipeline_bind_point,
776                 pipeline_layout,
777                 set_num,
778                 descriptor_writes,
779             )
780         }
781     }
782 
validate_push_descriptor_set( &self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: &PipelineLayout, set_num: u32, descriptor_writes: &[WriteDescriptorSet], ) -> Result<(), BindPushError>783     fn validate_push_descriptor_set(
784         &self,
785         pipeline_bind_point: PipelineBindPoint,
786         pipeline_layout: &PipelineLayout,
787         set_num: u32,
788         descriptor_writes: &[WriteDescriptorSet],
789     ) -> Result<(), BindPushError> {
790         if !self.device().enabled_extensions().khr_push_descriptor {
791             return Err(BindPushError::RequirementNotMet {
792                 required_for: "`CommandBufferBuilder::push_descriptor_set`",
793                 requires_one_of: RequiresOneOf {
794                     device_extensions: &["khr_push_descriptor"],
795                     ..Default::default()
796                 },
797             });
798         }
799 
800         // VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-parameter
801         pipeline_bind_point.validate_device(self.device())?;
802 
803         let queue_family_properties = self.queue_family_properties();
804 
805         // VUID-vkCmdPushDescriptorSetKHR-commandBuffer-cmdpool
806         // VUID-vkCmdPushDescriptorSetKHR-pipelineBindPoint-00363
807         match pipeline_bind_point {
808             PipelineBindPoint::Compute => {
809                 if !queue_family_properties
810                     .queue_flags
811                     .intersects(QueueFlags::COMPUTE)
812                 {
813                     return Err(BindPushError::NotSupportedByQueueFamily);
814                 }
815             }
816             PipelineBindPoint::Graphics => {
817                 if !queue_family_properties
818                     .queue_flags
819                     .intersects(QueueFlags::GRAPHICS)
820                 {
821                     return Err(BindPushError::NotSupportedByQueueFamily);
822                 }
823             }
824         }
825 
826         // VUID-vkCmdPushDescriptorSetKHR-commonparent
827         assert_eq!(self.device(), pipeline_layout.device());
828 
829         // VUID-vkCmdPushDescriptorSetKHR-set-00364
830         if set_num as usize > pipeline_layout.set_layouts().len() {
831             return Err(BindPushError::DescriptorSetOutOfRange {
832                 set_num,
833                 pipeline_layout_set_count: pipeline_layout.set_layouts().len() as u32,
834             });
835         }
836 
837         let descriptor_set_layout = &pipeline_layout.set_layouts()[set_num as usize];
838 
839         // VUID-vkCmdPushDescriptorSetKHR-set-00365
840         if !descriptor_set_layout.push_descriptor() {
841             return Err(BindPushError::DescriptorSetNotPush { set_num });
842         }
843 
844         for write in descriptor_writes {
845             check_descriptor_write(write, descriptor_set_layout, 0)?;
846         }
847 
848         Ok(())
849     }
850 
851     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
push_descriptor_set_unchecked( &mut self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: Arc<PipelineLayout>, set_num: u32, descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>, ) -> &mut Self852     pub unsafe fn push_descriptor_set_unchecked(
853         &mut self,
854         pipeline_bind_point: PipelineBindPoint,
855         pipeline_layout: Arc<PipelineLayout>,
856         set_num: u32,
857         descriptor_writes: impl IntoIterator<Item = WriteDescriptorSet>,
858     ) -> &mut Self {
859         let descriptor_writes: SmallVec<[WriteDescriptorSet; 8]> =
860             descriptor_writes.into_iter().collect();
861 
862         debug_assert!(self.device().enabled_extensions().khr_push_descriptor);
863 
864         let (infos, mut writes): (SmallVec<[_; 8]>, SmallVec<[_; 8]>) = descriptor_writes
865             .iter()
866             .map(|write| {
867                 let binding =
868                     &pipeline_layout.set_layouts()[set_num as usize].bindings()[&write.binding()];
869 
870                 (
871                     write.to_vulkan_info(binding.descriptor_type),
872                     write.to_vulkan(ash::vk::DescriptorSet::null(), binding.descriptor_type),
873                 )
874             })
875             .unzip();
876 
877         if writes.is_empty() {
878             return self;
879         }
880 
881         // Set the info pointers separately.
882         for (info, write) in infos.iter().zip(writes.iter_mut()) {
883             match info {
884                 DescriptorWriteInfo::Image(info) => {
885                     write.descriptor_count = info.len() as u32;
886                     write.p_image_info = info.as_ptr();
887                 }
888                 DescriptorWriteInfo::Buffer(info) => {
889                     write.descriptor_count = info.len() as u32;
890                     write.p_buffer_info = info.as_ptr();
891                 }
892                 DescriptorWriteInfo::BufferView(info) => {
893                     write.descriptor_count = info.len() as u32;
894                     write.p_texel_buffer_view = info.as_ptr();
895                 }
896             }
897 
898             debug_assert!(write.descriptor_count != 0);
899         }
900 
901         let fns = self.device().fns();
902         (fns.khr_push_descriptor.cmd_push_descriptor_set_khr)(
903             self.handle(),
904             pipeline_bind_point.into(),
905             pipeline_layout.handle(),
906             set_num,
907             writes.len() as u32,
908             writes.as_ptr(),
909         );
910 
911         let state = self.builder_state.invalidate_descriptor_sets(
912             pipeline_bind_point,
913             pipeline_layout.clone(),
914             set_num,
915             1,
916         );
917         let descriptor_set_layout = state.pipeline_layout.set_layouts()[set_num as usize].as_ref();
918         debug_assert!(descriptor_set_layout.push_descriptor());
919 
920         let set_resources = match state.descriptor_sets.entry(set_num).or_insert_with(|| {
921             SetOrPush::Push(DescriptorSetResources::new(descriptor_set_layout, 0))
922         }) {
923             SetOrPush::Push(set_resources) => set_resources,
924             _ => unreachable!(),
925         };
926 
927         for write in &descriptor_writes {
928             set_resources.update(write);
929         }
930 
931         self.resources.push(Box::new(pipeline_layout));
932 
933         self.next_command_index += 1;
934         self
935     }
936 }
937