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::{
11     CommandBufferBuilder, CommandBufferBuilderState, DescriptorSetState, PipelineExecutionError,
12     RenderPassState, RenderPassStateAttachmentInfo, RenderPassStateType, ResourcesState,
13 };
14 use crate::{
15     buffer::{view::BufferView, BufferUsage, Subbuffer},
16     command_buffer::{
17         allocator::CommandBufferAllocator, commands::pipeline::DescriptorResourceInvalidError,
18         DispatchIndirectCommand, DrawIndexedIndirectCommand, DrawIndirectCommand,
19         ResourceInCommand, ResourceUseRef, SubpassContents,
20     },
21     descriptor_set::{layout::DescriptorType, DescriptorBindingResources},
22     device::{DeviceOwned, QueueFlags},
23     format::FormatFeatures,
24     image::{ImageAccess, ImageAspects, ImageSubresourceRange, ImageViewAbstract, SampleCount},
25     pipeline::{
26         graphics::{
27             input_assembly::{IndexType, PrimitiveTopology},
28             render_pass::PipelineRenderPassType,
29             vertex_input::VertexInputRate,
30         },
31         DynamicState, GraphicsPipeline, PartialStateMode, Pipeline, PipelineBindPoint,
32         PipelineLayout,
33     },
34     sampler::Sampler,
35     shader::{
36         DescriptorBindingRequirements, FragmentTestsStages, ShaderScalarType, ShaderStage,
37         ShaderStages,
38     },
39     sync::PipelineStageAccess,
40     DeviceSize, RequiresOneOf, VulkanObject,
41 };
42 use ahash::HashMap;
43 use std::{cmp::min, mem::size_of, ops::Range, sync::Arc};
44 
45 impl<L, A> CommandBufferBuilder<L, A>
46 where
47     A: CommandBufferAllocator,
48 {
49     /// Perform a single compute operation using a compute pipeline.
50     ///
51     /// A compute pipeline must have been bound using [`bind_pipeline_compute`]. Any resources used
52     /// by the compute pipeline, such as descriptor sets, must have been set beforehand.
53     ///
54     /// # Safety
55     ///
56     /// - Appropriate synchronization must be provided for all buffers and images
57     ///   that are accessed by the command.
58     /// - All images that are accessed by the command must be in the expected image layout.
59     ///
60     /// [`bind_pipeline_compute`]: Self::bind_pipeline_compute
61     #[inline]
dispatch( &mut self, group_counts: [u32; 3], ) -> Result<&mut Self, PipelineExecutionError>62     pub unsafe fn dispatch(
63         &mut self,
64         group_counts: [u32; 3],
65     ) -> Result<&mut Self, PipelineExecutionError> {
66         self.validate_dispatch(group_counts)?;
67 
68         unsafe { Ok(self.dispatch_unchecked(group_counts)) }
69     }
70 
validate_dispatch(&self, group_counts: [u32; 3]) -> Result<(), PipelineExecutionError>71     fn validate_dispatch(&self, group_counts: [u32; 3]) -> Result<(), PipelineExecutionError> {
72         let queue_family_properties = self.queue_family_properties();
73 
74         // VUID-vkCmdDispatch-commandBuffer-cmdpool
75         if !queue_family_properties
76             .queue_flags
77             .intersects(QueueFlags::COMPUTE)
78         {
79             return Err(PipelineExecutionError::NotSupportedByQueueFamily);
80         }
81 
82         // VUID-vkCmdDispatch-renderpass
83         if self.builder_state.render_pass.is_some() {
84             return Err(PipelineExecutionError::ForbiddenInsideRenderPass);
85         }
86 
87         // VUID-vkCmdDispatch-None-02700
88         let pipeline = self
89             .builder_state
90             .pipeline_compute
91             .as_ref()
92             .ok_or(PipelineExecutionError::PipelineNotBound)?
93             .as_ref();
94 
95         self.validate_pipeline_descriptor_sets(pipeline)?;
96         self.validate_pipeline_push_constants(pipeline.layout())?;
97 
98         let max = self
99             .device()
100             .physical_device()
101             .properties()
102             .max_compute_work_group_count;
103 
104         // VUID-vkCmdDispatch-groupCountX-00386
105         // VUID-vkCmdDispatch-groupCountY-00387
106         // VUID-vkCmdDispatch-groupCountZ-00388
107         if group_counts[0] > max[0] || group_counts[1] > max[1] || group_counts[2] > max[2] {
108             return Err(PipelineExecutionError::MaxComputeWorkGroupCountExceeded {
109                 requested: group_counts,
110                 max,
111             });
112         }
113 
114         // TODO: sync check
115 
116         Ok(())
117     }
118 
119     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
dispatch_unchecked(&mut self, group_counts: [u32; 3]) -> &mut Self120     pub unsafe fn dispatch_unchecked(&mut self, group_counts: [u32; 3]) -> &mut Self {
121         let fns = self.device().fns();
122         (fns.v1_0.cmd_dispatch)(
123             self.handle(),
124             group_counts[0],
125             group_counts[1],
126             group_counts[2],
127         );
128 
129         let command_index = self.next_command_index;
130         let command_name = "dispatch";
131         let pipeline = self
132             .builder_state
133             .pipeline_compute
134             .as_ref()
135             .unwrap()
136             .as_ref();
137         record_descriptor_sets_access(
138             &mut self.resources_usage_state,
139             command_index,
140             command_name,
141             &self.builder_state.descriptor_sets,
142             pipeline,
143         );
144 
145         self.next_command_index += 1;
146         self
147     }
148 
149     /// Perform multiple compute operations using a compute pipeline. One dispatch is performed for
150     /// each [`DispatchIndirectCommand`] struct in `indirect_buffer`.
151     ///
152     /// A compute pipeline must have been bound using [`bind_pipeline_compute`]. Any resources used
153     /// by the compute pipeline, such as descriptor sets, must have been set beforehand.
154     ///
155     /// # Safety
156     ///
157     /// - Appropriate synchronization must be provided for all buffers and images
158     ///   that are accessed by the command.
159     /// - All images that are accessed by the command must be in the expected image layout.
160     ///
161     /// [`bind_pipeline_compute`]: Self::bind_pipeline_compute
162     #[inline]
dispatch_indirect( &mut self, indirect_buffer: Subbuffer<[DispatchIndirectCommand]>, ) -> Result<&mut Self, PipelineExecutionError>163     pub unsafe fn dispatch_indirect(
164         &mut self,
165         indirect_buffer: Subbuffer<[DispatchIndirectCommand]>,
166     ) -> Result<&mut Self, PipelineExecutionError> {
167         self.validate_dispatch_indirect(indirect_buffer.as_bytes())?;
168 
169         unsafe { Ok(self.dispatch_indirect_unchecked(indirect_buffer)) }
170     }
171 
validate_dispatch_indirect( &self, indirect_buffer: &Subbuffer<[u8]>, ) -> Result<(), PipelineExecutionError>172     fn validate_dispatch_indirect(
173         &self,
174         indirect_buffer: &Subbuffer<[u8]>,
175     ) -> Result<(), PipelineExecutionError> {
176         let queue_family_properties = self.queue_family_properties();
177 
178         // VUID-vkCmdDispatchIndirect-commandBuffer-cmdpool
179         if !queue_family_properties
180             .queue_flags
181             .intersects(QueueFlags::COMPUTE)
182         {
183             return Err(PipelineExecutionError::NotSupportedByQueueFamily);
184         }
185 
186         // VUID-vkCmdDispatchIndirect-renderpass
187         if self.builder_state.render_pass.is_some() {
188             return Err(PipelineExecutionError::ForbiddenInsideRenderPass);
189         }
190 
191         // VUID-vkCmdDispatchIndirect-None-02700
192         let pipeline = self
193             .builder_state
194             .pipeline_compute
195             .as_ref()
196             .ok_or(PipelineExecutionError::PipelineNotBound)?
197             .as_ref();
198 
199         self.validate_pipeline_descriptor_sets(pipeline)?;
200         self.validate_pipeline_push_constants(pipeline.layout())?;
201         self.validate_indirect_buffer(indirect_buffer)?;
202 
203         // TODO: sync check
204 
205         Ok(())
206     }
207 
208     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
dispatch_indirect_unchecked( &mut self, indirect_buffer: Subbuffer<[DispatchIndirectCommand]>, ) -> &mut Self209     pub unsafe fn dispatch_indirect_unchecked(
210         &mut self,
211         indirect_buffer: Subbuffer<[DispatchIndirectCommand]>,
212     ) -> &mut Self {
213         let fns = self.device().fns();
214         (fns.v1_0.cmd_dispatch_indirect)(
215             self.handle(),
216             indirect_buffer.buffer().handle(),
217             indirect_buffer.offset(),
218         );
219 
220         let command_index = self.next_command_index;
221         let command_name = "dispatch_indirect";
222         let pipeline = self
223             .builder_state
224             .pipeline_compute
225             .as_ref()
226             .unwrap()
227             .as_ref();
228         record_descriptor_sets_access(
229             &mut self.resources_usage_state,
230             command_index,
231             command_name,
232             &self.builder_state.descriptor_sets,
233             pipeline,
234         );
235         record_indirect_buffer_access(
236             &mut self.resources_usage_state,
237             command_index,
238             command_name,
239             indirect_buffer.as_bytes(),
240         );
241 
242         self.resources.push(Box::new(indirect_buffer));
243 
244         self.next_command_index += 1;
245         self
246     }
247 
248     /// Perform a single draw operation using a graphics pipeline.
249     ///
250     /// The parameters specify the first vertex and the number of vertices to draw, and the first
251     /// instance and number of instances. For non-instanced drawing, specify `instance_count` as 1
252     /// and `first_instance` as 0.
253     ///
254     /// A graphics pipeline must have been bound using [`bind_pipeline_graphics`]. Any resources
255     /// used by the graphics pipeline, such as descriptor sets, vertex buffers and dynamic state,
256     /// must have been set beforehand. If the bound graphics pipeline uses vertex buffers, then the
257     /// provided vertex and instance ranges must be in range of the bound vertex buffers.
258     ///
259     /// # Safety
260     ///
261     /// - Appropriate synchronization must be provided for all buffers and images
262     ///   that are accessed by the command.
263     /// - All images that are accessed by the command must be in the expected image layout.
264     ///
265     /// [`bind_pipeline_graphics`]: Self::bind_pipeline_graphics
266     #[inline]
draw( &mut self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32, ) -> Result<&mut Self, PipelineExecutionError>267     pub unsafe fn draw(
268         &mut self,
269         vertex_count: u32,
270         instance_count: u32,
271         first_vertex: u32,
272         first_instance: u32,
273     ) -> Result<&mut Self, PipelineExecutionError> {
274         self.validate_draw(vertex_count, instance_count, first_vertex, first_instance)?;
275 
276         unsafe {
277             Ok(self.draw_unchecked(vertex_count, instance_count, first_vertex, first_instance))
278         }
279     }
280 
validate_draw( &self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32, ) -> Result<(), PipelineExecutionError>281     fn validate_draw(
282         &self,
283         vertex_count: u32,
284         instance_count: u32,
285         first_vertex: u32,
286         first_instance: u32,
287     ) -> Result<(), PipelineExecutionError> {
288         // VUID-vkCmdDraw-renderpass
289         let render_pass_state = self
290             .builder_state
291             .render_pass
292             .as_ref()
293             .ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?;
294 
295         // VUID-vkCmdDraw-None-02700
296         let pipeline = self
297             .builder_state
298             .pipeline_graphics
299             .as_ref()
300             .ok_or(PipelineExecutionError::PipelineNotBound)?
301             .as_ref();
302 
303         self.validate_pipeline_descriptor_sets(pipeline)?;
304         self.validate_pipeline_push_constants(pipeline.layout())?;
305         self.validate_pipeline_graphics_dynamic_state(pipeline)?;
306         self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?;
307         self.validate_pipeline_graphics_vertex_buffers(
308             pipeline,
309             Some((first_vertex, vertex_count)),
310             Some((first_instance, instance_count)),
311         )?;
312 
313         // TODO: sync check
314 
315         Ok(())
316     }
317 
318     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
draw_unchecked( &mut self, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32, ) -> &mut Self319     pub unsafe fn draw_unchecked(
320         &mut self,
321         vertex_count: u32,
322         instance_count: u32,
323         first_vertex: u32,
324         first_instance: u32,
325     ) -> &mut Self {
326         let fns = self.device().fns();
327         (fns.v1_0.cmd_draw)(
328             self.handle(),
329             vertex_count,
330             instance_count,
331             first_vertex,
332             first_instance,
333         );
334 
335         let command_index = self.next_command_index;
336         let command_name = "draw";
337         let pipeline = self
338             .builder_state
339             .pipeline_graphics
340             .as_ref()
341             .unwrap()
342             .as_ref();
343         record_descriptor_sets_access(
344             &mut self.resources_usage_state,
345             command_index,
346             command_name,
347             &self.builder_state.descriptor_sets,
348             pipeline,
349         );
350         record_vertex_buffers_access(
351             &mut self.resources_usage_state,
352             command_index,
353             command_name,
354             &self.builder_state.vertex_buffers,
355             pipeline,
356         );
357         record_subpass_attachments_access(
358             &mut self.resources_usage_state,
359             command_index,
360             command_name,
361             self.builder_state.render_pass.as_ref().unwrap(),
362             &self.builder_state,
363             pipeline,
364         );
365 
366         if let RenderPassStateType::BeginRendering(state) =
367             &mut self.builder_state.render_pass.as_mut().unwrap().render_pass
368         {
369             state.pipeline_used = true;
370         }
371 
372         self.next_command_index += 1;
373         self
374     }
375 
376     /// Perform multiple draw operations using a graphics pipeline.
377     ///
378     /// One draw is performed for each [`DrawIndirectCommand`] struct in `indirect_buffer`.
379     /// The maximum number of draw commands in the buffer is limited by the
380     /// [`max_draw_indirect_count`] limit.
381     /// This limit is 1 unless the [`multi_draw_indirect`] feature has been enabled on the device.
382     ///
383     /// A graphics pipeline must have been bound using [`bind_pipeline_graphics`]. Any resources
384     /// used by the graphics pipeline, such as descriptor sets, vertex buffers and dynamic state,
385     /// must have been set beforehand. If the bound graphics pipeline uses vertex buffers, then the
386     /// vertex and instance ranges of each `DrawIndirectCommand` in the indirect buffer must be in
387     /// range of the bound vertex buffers.
388     ///
389     /// # Safety
390     ///
391     /// - Appropriate synchronization must be provided for all buffers and images
392     ///   that are accessed by the command.
393     /// - All images that are accessed by the command must be in the expected image layout.
394     ///
395     /// [`max_draw_indirect_count`]: crate::device::Properties::max_draw_indirect_count
396     /// [`multi_draw_indirect`]: crate::device::Features::multi_draw_indirect
397     /// [`bind_pipeline_graphics`]: Self::bind_pipeline_graphics
398     #[inline]
draw_indirect( &mut self, indirect_buffer: Subbuffer<[DrawIndirectCommand]>, ) -> Result<&mut Self, PipelineExecutionError>399     pub unsafe fn draw_indirect(
400         &mut self,
401         indirect_buffer: Subbuffer<[DrawIndirectCommand]>,
402     ) -> Result<&mut Self, PipelineExecutionError> {
403         let draw_count = indirect_buffer.len() as u32;
404         let stride = size_of::<DrawIndirectCommand>() as u32;
405         self.validate_draw_indirect(indirect_buffer.as_bytes(), draw_count, stride)?;
406 
407         unsafe { Ok(self.draw_indirect_unchecked(indirect_buffer, draw_count, stride)) }
408     }
409 
validate_draw_indirect( &self, indirect_buffer: &Subbuffer<[u8]>, draw_count: u32, _stride: u32, ) -> Result<(), PipelineExecutionError>410     fn validate_draw_indirect(
411         &self,
412         indirect_buffer: &Subbuffer<[u8]>,
413         draw_count: u32,
414         _stride: u32,
415     ) -> Result<(), PipelineExecutionError> {
416         // VUID-vkCmdDrawIndirect-renderpass
417         let render_pass_state = self
418             .builder_state
419             .render_pass
420             .as_ref()
421             .ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?;
422 
423         // VUID-vkCmdDrawIndirect-None-02700
424         let pipeline = self
425             .builder_state
426             .pipeline_graphics
427             .as_ref()
428             .ok_or(PipelineExecutionError::PipelineNotBound)?
429             .as_ref();
430 
431         self.validate_pipeline_descriptor_sets(pipeline)?;
432         self.validate_pipeline_push_constants(pipeline.layout())?;
433         self.validate_pipeline_graphics_dynamic_state(pipeline)?;
434         self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?;
435         self.validate_pipeline_graphics_vertex_buffers(pipeline, None, None)?;
436 
437         self.validate_indirect_buffer(indirect_buffer)?;
438 
439         // VUID-vkCmdDrawIndirect-drawCount-02718
440         if draw_count > 1 && !self.device().enabled_features().multi_draw_indirect {
441             return Err(PipelineExecutionError::RequirementNotMet {
442                 required_for: "`draw_count` is greater than `1`",
443                 requires_one_of: RequiresOneOf {
444                     features: &["multi_draw_indirect"],
445                     ..Default::default()
446                 },
447             });
448         }
449 
450         let max = self
451             .device()
452             .physical_device()
453             .properties()
454             .max_draw_indirect_count;
455 
456         // VUID-vkCmdDrawIndirect-drawCount-02719
457         if draw_count > max {
458             return Err(PipelineExecutionError::MaxDrawIndirectCountExceeded {
459                 provided: draw_count,
460                 max,
461             });
462         }
463 
464         // TODO: sync check
465 
466         Ok(())
467     }
468 
469     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
draw_indirect_unchecked( &mut self, indirect_buffer: Subbuffer<[DrawIndirectCommand]>, draw_count: u32, stride: u32, ) -> &mut Self470     pub unsafe fn draw_indirect_unchecked(
471         &mut self,
472         indirect_buffer: Subbuffer<[DrawIndirectCommand]>,
473         draw_count: u32,
474         stride: u32,
475     ) -> &mut Self {
476         let fns = self.device().fns();
477         (fns.v1_0.cmd_draw_indirect)(
478             self.handle(),
479             indirect_buffer.buffer().handle(),
480             indirect_buffer.offset(),
481             draw_count,
482             stride,
483         );
484 
485         let command_index = self.next_command_index;
486         let command_name = "draw_indirect";
487         let pipeline = self
488             .builder_state
489             .pipeline_graphics
490             .as_ref()
491             .unwrap()
492             .as_ref();
493         record_descriptor_sets_access(
494             &mut self.resources_usage_state,
495             command_index,
496             command_name,
497             &self.builder_state.descriptor_sets,
498             pipeline,
499         );
500         record_vertex_buffers_access(
501             &mut self.resources_usage_state,
502             command_index,
503             command_name,
504             &self.builder_state.vertex_buffers,
505             pipeline,
506         );
507         record_indirect_buffer_access(
508             &mut self.resources_usage_state,
509             command_index,
510             command_name,
511             indirect_buffer.as_bytes(),
512         );
513         record_subpass_attachments_access(
514             &mut self.resources_usage_state,
515             command_index,
516             command_name,
517             self.builder_state.render_pass.as_ref().unwrap(),
518             &self.builder_state,
519             pipeline,
520         );
521 
522         if let RenderPassStateType::BeginRendering(state) =
523             &mut self.builder_state.render_pass.as_mut().unwrap().render_pass
524         {
525             state.pipeline_used = true;
526         }
527 
528         self.resources.push(Box::new(indirect_buffer));
529 
530         self.next_command_index += 1;
531         self
532     }
533 
534     /// Perform a single draw operation using a graphics pipeline, using an index buffer.
535     ///
536     /// The parameters specify the first index and the number of indices in the index buffer that
537     /// should be used, and the first instance and number of instances. For non-instanced drawing,
538     /// specify `instance_count` as 1 and `first_instance` as 0. The `vertex_offset` is a constant
539     /// value that should be added to each index in the index buffer to produce the final vertex
540     /// number to be used.
541     ///
542     /// An index buffer must have been bound using [`bind_index_buffer`], and the provided index
543     /// range must be in range of the bound index buffer.
544     ///
545     /// A graphics pipeline must have been bound using [`bind_pipeline_graphics`]. Any resources
546     /// used by the graphics pipeline, such as descriptor sets, vertex buffers and dynamic state,
547     /// must have been set beforehand. If the bound graphics pipeline uses vertex buffers, then the
548     /// provided instance range must be in range of the bound vertex buffers. The vertex indices in
549     /// the index buffer must be in range of the bound vertex buffers.
550     ///
551     /// # Safety
552     ///
553     /// - Appropriate synchronization must be provided for all buffers and images
554     ///   that are accessed by the command.
555     /// - All images that are accessed by the command must be in the expected image layout.
556     ///
557     /// [`bind_index_buffer`]: Self::bind_index_buffer
558     /// [`bind_pipeline_graphics`]: Self::bind_pipeline_graphics
559     #[inline]
draw_indexed( &mut self, index_count: u32, instance_count: u32, first_index: u32, vertex_offset: i32, first_instance: u32, ) -> Result<&mut Self, PipelineExecutionError>560     pub unsafe fn draw_indexed(
561         &mut self,
562         index_count: u32,
563         instance_count: u32,
564         first_index: u32,
565         vertex_offset: i32,
566         first_instance: u32,
567     ) -> Result<&mut Self, PipelineExecutionError> {
568         self.validate_draw_indexed(
569             index_count,
570             instance_count,
571             first_index,
572             vertex_offset,
573             first_instance,
574         )?;
575 
576         unsafe {
577             Ok(self.draw_indexed_unchecked(
578                 index_count,
579                 instance_count,
580                 first_index,
581                 vertex_offset,
582                 first_instance,
583             ))
584         }
585     }
586 
validate_draw_indexed( &self, index_count: u32, instance_count: u32, first_index: u32, _vertex_offset: i32, first_instance: u32, ) -> Result<(), PipelineExecutionError>587     fn validate_draw_indexed(
588         &self,
589         index_count: u32,
590         instance_count: u32,
591         first_index: u32,
592         _vertex_offset: i32,
593         first_instance: u32,
594     ) -> Result<(), PipelineExecutionError> {
595         // TODO: how to handle an index out of range of the vertex buffers?
596 
597         // VUID-vkCmdDrawIndexed-renderpass
598         let render_pass_state = self
599             .builder_state
600             .render_pass
601             .as_ref()
602             .ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?;
603 
604         // VUID-vkCmdDrawIndexed-None-02700
605         let pipeline = self
606             .builder_state
607             .pipeline_graphics
608             .as_ref()
609             .ok_or(PipelineExecutionError::PipelineNotBound)?
610             .as_ref();
611 
612         self.validate_pipeline_descriptor_sets(pipeline)?;
613         self.validate_pipeline_push_constants(pipeline.layout())?;
614         self.validate_pipeline_graphics_dynamic_state(pipeline)?;
615         self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?;
616         self.validate_pipeline_graphics_vertex_buffers(
617             pipeline,
618             None,
619             Some((first_instance, instance_count)),
620         )?;
621 
622         self.validate_index_buffer(Some((first_index, index_count)))?;
623 
624         // TODO: sync check
625 
626         Ok(())
627     }
628 
629     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
draw_indexed_unchecked( &mut self, index_count: u32, instance_count: u32, first_index: u32, vertex_offset: i32, first_instance: u32, ) -> &mut Self630     pub unsafe fn draw_indexed_unchecked(
631         &mut self,
632         index_count: u32,
633         instance_count: u32,
634         first_index: u32,
635         vertex_offset: i32,
636         first_instance: u32,
637     ) -> &mut Self {
638         let fns = self.device().fns();
639         (fns.v1_0.cmd_draw_indexed)(
640             self.handle(),
641             index_count,
642             instance_count,
643             first_index,
644             vertex_offset,
645             first_instance,
646         );
647 
648         let command_index = self.next_command_index;
649         let command_name = "draw_indexed";
650         let pipeline = self
651             .builder_state
652             .pipeline_graphics
653             .as_ref()
654             .unwrap()
655             .as_ref();
656         record_descriptor_sets_access(
657             &mut self.resources_usage_state,
658             command_index,
659             command_name,
660             &self.builder_state.descriptor_sets,
661             pipeline,
662         );
663         record_vertex_buffers_access(
664             &mut self.resources_usage_state,
665             command_index,
666             command_name,
667             &self.builder_state.vertex_buffers,
668             pipeline,
669         );
670         record_index_buffer_access(
671             &mut self.resources_usage_state,
672             command_index,
673             command_name,
674             &self.builder_state.index_buffer,
675         );
676         record_subpass_attachments_access(
677             &mut self.resources_usage_state,
678             command_index,
679             command_name,
680             self.builder_state.render_pass.as_ref().unwrap(),
681             &self.builder_state,
682             pipeline,
683         );
684 
685         if let RenderPassStateType::BeginRendering(state) =
686             &mut self.builder_state.render_pass.as_mut().unwrap().render_pass
687         {
688             state.pipeline_used = true;
689         }
690 
691         self.next_command_index += 1;
692         self
693     }
694 
695     /// Perform multiple draw operations using a graphics pipeline, using an index buffer.
696     ///
697     /// One draw is performed for each [`DrawIndexedIndirectCommand`] struct in `indirect_buffer`.
698     /// The maximum number of draw commands in the buffer is limited by the
699     /// [`max_draw_indirect_count`] limit.
700     /// This limit is 1 unless the [`multi_draw_indirect`] feature has been enabled on the device.
701     ///
702     /// An index buffer must have been bound using [`bind_index_buffer`], and the index ranges of
703     /// each `DrawIndexedIndirectCommand` in the indirect buffer must be in range of the bound
704     /// index buffer.
705     ///
706     /// A graphics pipeline must have been bound using [`bind_pipeline_graphics`]. Any resources
707     /// used by the graphics pipeline, such as descriptor sets, vertex buffers and dynamic state,
708     /// must have been set beforehand. If the bound graphics pipeline uses vertex buffers, then the
709     /// instance ranges of each `DrawIndexedIndirectCommand` in the indirect buffer must be in
710     /// range of the bound vertex buffers.
711     ///
712     /// # Safety
713     ///
714     /// - Appropriate synchronization must be provided for all buffers and images
715     ///   that are accessed by the command.
716     /// - All images that are accessed by the command must be in the expected image layout.
717     ///
718     /// [`max_draw_indirect_count`]: crate::device::Properties::max_draw_indirect_count
719     /// [`multi_draw_indirect`]: crate::device::Features::multi_draw_indirect
720     /// [`bind_index_buffer`]: Self::bind_index_buffer
721     /// [`bind_pipeline_graphics`]: Self::bind_pipeline_graphics
722     #[inline]
draw_indexed_indirect( &mut self, indirect_buffer: Subbuffer<[DrawIndexedIndirectCommand]>, ) -> Result<&mut Self, PipelineExecutionError>723     pub unsafe fn draw_indexed_indirect(
724         &mut self,
725         indirect_buffer: Subbuffer<[DrawIndexedIndirectCommand]>,
726     ) -> Result<&mut Self, PipelineExecutionError> {
727         let draw_count = indirect_buffer.len() as u32;
728         let stride = size_of::<DrawIndexedIndirectCommand>() as u32;
729         self.validate_draw_indexed_indirect(indirect_buffer.as_bytes(), draw_count, stride)?;
730 
731         unsafe { Ok(self.draw_indexed_indirect_unchecked(indirect_buffer, draw_count, stride)) }
732     }
733 
validate_draw_indexed_indirect( &self, indirect_buffer: &Subbuffer<[u8]>, draw_count: u32, _stride: u32, ) -> Result<(), PipelineExecutionError>734     fn validate_draw_indexed_indirect(
735         &self,
736         indirect_buffer: &Subbuffer<[u8]>,
737         draw_count: u32,
738         _stride: u32,
739     ) -> Result<(), PipelineExecutionError> {
740         // VUID-vkCmdDrawIndexedIndirect-renderpass
741         let render_pass_state = self
742             .builder_state
743             .render_pass
744             .as_ref()
745             .ok_or(PipelineExecutionError::ForbiddenOutsideRenderPass)?;
746 
747         // VUID-vkCmdDrawIndexedIndirect-None-02700
748         let pipeline = self
749             .builder_state
750             .pipeline_graphics
751             .as_ref()
752             .ok_or(PipelineExecutionError::PipelineNotBound)?
753             .as_ref();
754 
755         self.validate_pipeline_descriptor_sets(pipeline)?;
756         self.validate_pipeline_push_constants(pipeline.layout())?;
757         self.validate_pipeline_graphics_dynamic_state(pipeline)?;
758         self.validate_pipeline_graphics_render_pass(pipeline, render_pass_state)?;
759         self.validate_pipeline_graphics_vertex_buffers(pipeline, None, None)?;
760 
761         self.validate_index_buffer(None)?;
762         self.validate_indirect_buffer(indirect_buffer)?;
763 
764         // VUID-vkCmdDrawIndexedIndirect-drawCount-02718
765         if draw_count > 1 && !self.device().enabled_features().multi_draw_indirect {
766             return Err(PipelineExecutionError::RequirementNotMet {
767                 required_for: "`draw_count` is greater than `1`",
768                 requires_one_of: RequiresOneOf {
769                     features: &["multi_draw_indirect"],
770                     ..Default::default()
771                 },
772             });
773         }
774 
775         let max = self
776             .device()
777             .physical_device()
778             .properties()
779             .max_draw_indirect_count;
780 
781         // VUID-vkCmdDrawIndexedIndirect-drawCount-02719
782         if draw_count > max {
783             return Err(PipelineExecutionError::MaxDrawIndirectCountExceeded {
784                 provided: draw_count,
785                 max,
786             });
787         }
788 
789         // TODO: sync check
790 
791         Ok(())
792     }
793 
794     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
draw_indexed_indirect_unchecked( &mut self, indirect_buffer: Subbuffer<[DrawIndexedIndirectCommand]>, draw_count: u32, stride: u32, ) -> &mut Self795     pub unsafe fn draw_indexed_indirect_unchecked(
796         &mut self,
797         indirect_buffer: Subbuffer<[DrawIndexedIndirectCommand]>,
798         draw_count: u32,
799         stride: u32,
800     ) -> &mut Self {
801         let fns = self.device().fns();
802         (fns.v1_0.cmd_draw_indexed_indirect)(
803             self.handle(),
804             indirect_buffer.buffer().handle(),
805             indirect_buffer.offset(),
806             draw_count,
807             stride,
808         );
809 
810         let command_index = self.next_command_index;
811         let command_name = "draw_indexed_indirect";
812         let pipeline = self
813             .builder_state
814             .pipeline_graphics
815             .as_ref()
816             .unwrap()
817             .as_ref();
818         record_descriptor_sets_access(
819             &mut self.resources_usage_state,
820             command_index,
821             command_name,
822             &self.builder_state.descriptor_sets,
823             pipeline,
824         );
825         record_vertex_buffers_access(
826             &mut self.resources_usage_state,
827             command_index,
828             command_name,
829             &self.builder_state.vertex_buffers,
830             pipeline,
831         );
832         record_index_buffer_access(
833             &mut self.resources_usage_state,
834             command_index,
835             command_name,
836             &self.builder_state.index_buffer,
837         );
838         record_indirect_buffer_access(
839             &mut self.resources_usage_state,
840             command_index,
841             command_name,
842             indirect_buffer.as_bytes(),
843         );
844         record_subpass_attachments_access(
845             &mut self.resources_usage_state,
846             command_index,
847             command_name,
848             self.builder_state.render_pass.as_ref().unwrap(),
849             &self.builder_state,
850             pipeline,
851         );
852 
853         if let RenderPassStateType::BeginRendering(state) =
854             &mut self.builder_state.render_pass.as_mut().unwrap().render_pass
855         {
856             state.pipeline_used = true;
857         }
858 
859         self.resources.push(Box::new(indirect_buffer));
860 
861         self.next_command_index += 1;
862         self
863     }
864 
validate_index_buffer( &self, indices: Option<(u32, u32)>, ) -> Result<(), PipelineExecutionError>865     fn validate_index_buffer(
866         &self,
867         indices: Option<(u32, u32)>,
868     ) -> Result<(), PipelineExecutionError> {
869         // VUID?
870         let (index_buffer, index_type) = self
871             .builder_state
872             .index_buffer
873             .as_ref()
874             .ok_or(PipelineExecutionError::IndexBufferNotBound)?;
875 
876         if let Some((first_index, index_count)) = indices {
877             let max_index_count = (index_buffer.size() / index_type.size()) as u32;
878 
879             // // VUID-vkCmdDrawIndexed-firstIndex-04932
880             if first_index + index_count > max_index_count {
881                 return Err(PipelineExecutionError::IndexBufferRangeOutOfBounds {
882                     highest_index: first_index + index_count,
883                     max_index_count,
884                 });
885             }
886         }
887 
888         Ok(())
889     }
890 
validate_indirect_buffer( &self, buffer: &Subbuffer<[u8]>, ) -> Result<(), PipelineExecutionError>891     fn validate_indirect_buffer(
892         &self,
893         buffer: &Subbuffer<[u8]>,
894     ) -> Result<(), PipelineExecutionError> {
895         // VUID-vkCmdDispatchIndirect-commonparent
896         assert_eq!(self.device(), buffer.device());
897 
898         // VUID-vkCmdDispatchIndirect-buffer-02709
899         if !buffer
900             .buffer()
901             .usage()
902             .intersects(BufferUsage::INDIRECT_BUFFER)
903         {
904             return Err(PipelineExecutionError::IndirectBufferMissingUsage);
905         }
906 
907         // VUID-vkCmdDispatchIndirect-offset-02710
908         // TODO:
909 
910         Ok(())
911     }
912 
validate_pipeline_descriptor_sets( &self, pipeline: &impl Pipeline, ) -> Result<(), PipelineExecutionError>913     fn validate_pipeline_descriptor_sets(
914         &self,
915         pipeline: &impl Pipeline,
916     ) -> Result<(), PipelineExecutionError> {
917         fn validate_resources<T>(
918             set_num: u32,
919             binding_num: u32,
920             binding_reqs: &DescriptorBindingRequirements,
921             elements: &[Option<T>],
922             mut extra_check: impl FnMut(u32, &T) -> Result<(), DescriptorResourceInvalidError>,
923         ) -> Result<(), PipelineExecutionError> {
924             let elements_to_check = if let Some(descriptor_count) = binding_reqs.descriptor_count {
925                 // The shader has a fixed-sized array, so it will never access more than
926                 // the first `descriptor_count` elements.
927                 elements.get(..descriptor_count as usize).ok_or({
928                     // There are less than `descriptor_count` elements in `elements`
929                     PipelineExecutionError::DescriptorResourceInvalid {
930                         set_num,
931                         binding_num,
932                         index: elements.len() as u32,
933                         error: DescriptorResourceInvalidError::Missing,
934                     }
935                 })?
936             } else {
937                 // The shader has a runtime-sized array, so any element could potentially
938                 // be accessed. We must check them all.
939                 elements
940             };
941 
942             for (index, element) in elements_to_check.iter().enumerate() {
943                 let index = index as u32;
944 
945                 // VUID-vkCmdDispatch-None-02699
946                 let element = match element {
947                     Some(x) => x,
948                     None => {
949                         return Err(PipelineExecutionError::DescriptorResourceInvalid {
950                             set_num,
951                             binding_num,
952                             index,
953                             error: DescriptorResourceInvalidError::Missing,
954                         })
955                     }
956                 };
957 
958                 if let Err(error) = extra_check(index, element) {
959                     return Err(PipelineExecutionError::DescriptorResourceInvalid {
960                         set_num,
961                         binding_num,
962                         index,
963                         error,
964                     });
965                 }
966             }
967 
968             Ok(())
969         }
970 
971         if pipeline.num_used_descriptor_sets() == 0 {
972             return Ok(());
973         }
974 
975         // VUID-vkCmdDispatch-None-02697
976         let descriptor_set_state = self
977             .builder_state
978             .descriptor_sets
979             .get(&pipeline.bind_point())
980             .ok_or(PipelineExecutionError::PipelineLayoutNotCompatible)?;
981 
982         // VUID-vkCmdDispatch-None-02697
983         if !pipeline.layout().is_compatible_with(
984             &descriptor_set_state.pipeline_layout,
985             pipeline.num_used_descriptor_sets(),
986         ) {
987             return Err(PipelineExecutionError::PipelineLayoutNotCompatible);
988         }
989 
990         for (&(set_num, binding_num), binding_reqs) in pipeline.descriptor_binding_requirements() {
991             let layout_binding =
992                 &pipeline.layout().set_layouts()[set_num as usize].bindings()[&binding_num];
993 
994             let check_buffer =
995                 |_index: u32, (_buffer, _range): &(Subbuffer<[u8]>, Range<DeviceSize>)| Ok(());
996 
997             let check_buffer_view = |index: u32, buffer_view: &Arc<BufferView>| {
998                 for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
999                     .chain(binding_reqs.descriptors.get(&None))
1000                 {
1001                     if layout_binding.descriptor_type == DescriptorType::StorageTexelBuffer {
1002                         // VUID-vkCmdDispatch-OpTypeImage-06423
1003                         if binding_reqs.image_format.is_none()
1004                             && !desc_reqs.memory_write.is_empty()
1005                             && !buffer_view
1006                                 .format_features()
1007                                 .intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT)
1008                         {
1009                             return Err(DescriptorResourceInvalidError::StorageWriteWithoutFormatNotSupported);
1010                         }
1011 
1012                         // VUID-vkCmdDispatch-OpTypeImage-06424
1013                         if binding_reqs.image_format.is_none()
1014                             && !desc_reqs.memory_read.is_empty()
1015                             && !buffer_view
1016                                 .format_features()
1017                                 .intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT)
1018                         {
1019                             return Err(DescriptorResourceInvalidError::StorageReadWithoutFormatNotSupported);
1020                         }
1021                     }
1022                 }
1023 
1024                 Ok(())
1025             };
1026 
1027             let check_image_view_common = |index: u32, image_view: &Arc<dyn ImageViewAbstract>| {
1028                 for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
1029                     .chain(binding_reqs.descriptors.get(&None))
1030                 {
1031                     // VUID-vkCmdDispatch-None-02691
1032                     if desc_reqs.storage_image_atomic
1033                         && !image_view
1034                             .format_features()
1035                             .intersects(FormatFeatures::STORAGE_IMAGE_ATOMIC)
1036                     {
1037                         return Err(DescriptorResourceInvalidError::StorageImageAtomicNotSupported);
1038                     }
1039 
1040                     if layout_binding.descriptor_type == DescriptorType::StorageImage {
1041                         // VUID-vkCmdDispatch-OpTypeImage-06423
1042                         if binding_reqs.image_format.is_none()
1043                             && !desc_reqs.memory_write.is_empty()
1044                             && !image_view
1045                                 .format_features()
1046                                 .intersects(FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT)
1047                         {
1048                             return Err(
1049                             DescriptorResourceInvalidError::StorageWriteWithoutFormatNotSupported,
1050                         );
1051                         }
1052 
1053                         // VUID-vkCmdDispatch-OpTypeImage-06424
1054                         if binding_reqs.image_format.is_none()
1055                             && !desc_reqs.memory_read.is_empty()
1056                             && !image_view
1057                                 .format_features()
1058                                 .intersects(FormatFeatures::STORAGE_READ_WITHOUT_FORMAT)
1059                         {
1060                             return Err(
1061                             DescriptorResourceInvalidError::StorageReadWithoutFormatNotSupported,
1062                         );
1063                         }
1064                     }
1065                 }
1066 
1067                 /*
1068                    Instruction/Sampler/Image View Validation
1069                    https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap16.html#textures-input-validation
1070                 */
1071 
1072                 // The SPIR-V Image Format is not compatible with the image view’s format.
1073                 if let Some(format) = binding_reqs.image_format {
1074                     if image_view.format() != Some(format) {
1075                         return Err(DescriptorResourceInvalidError::ImageViewFormatMismatch {
1076                             required: format,
1077                             provided: image_view.format(),
1078                         });
1079                     }
1080                 }
1081 
1082                 // Rules for viewType
1083                 if let Some(image_view_type) = binding_reqs.image_view_type {
1084                     if image_view.view_type() != image_view_type {
1085                         return Err(DescriptorResourceInvalidError::ImageViewTypeMismatch {
1086                             required: image_view_type,
1087                             provided: image_view.view_type(),
1088                         });
1089                     }
1090                 }
1091 
1092                 // - If the image was created with VkImageCreateInfo::samples equal to
1093                 //   VK_SAMPLE_COUNT_1_BIT, the instruction must have MS = 0.
1094                 // - If the image was created with VkImageCreateInfo::samples not equal to
1095                 //   VK_SAMPLE_COUNT_1_BIT, the instruction must have MS = 1.
1096                 if binding_reqs.image_multisampled
1097                     != (image_view.image().samples() != SampleCount::Sample1)
1098                 {
1099                     return Err(
1100                         DescriptorResourceInvalidError::ImageViewMultisampledMismatch {
1101                             required: binding_reqs.image_multisampled,
1102                             provided: image_view.image().samples() != SampleCount::Sample1,
1103                         },
1104                     );
1105                 }
1106 
1107                 // - If the Sampled Type of the OpTypeImage does not match the numeric format of the
1108                 //   image, as shown in the SPIR-V Sampled Type column of the
1109                 //   Interpretation of Numeric Format table.
1110                 // - If the signedness of any read or sample operation does not match the signedness of
1111                 //   the image’s format.
1112                 if let Some(scalar_type) = binding_reqs.image_scalar_type {
1113                     let aspects = image_view.subresource_range().aspects;
1114                     let view_scalar_type = ShaderScalarType::from(
1115                         if aspects.intersects(
1116                             ImageAspects::COLOR
1117                                 | ImageAspects::PLANE_0
1118                                 | ImageAspects::PLANE_1
1119                                 | ImageAspects::PLANE_2,
1120                         ) {
1121                             image_view.format().unwrap().type_color().unwrap()
1122                         } else if aspects.intersects(ImageAspects::DEPTH) {
1123                             image_view.format().unwrap().type_depth().unwrap()
1124                         } else if aspects.intersects(ImageAspects::STENCIL) {
1125                             image_view.format().unwrap().type_stencil().unwrap()
1126                         } else {
1127                             // Per `ImageViewBuilder::aspects` and
1128                             // VUID-VkDescriptorImageInfo-imageView-01976
1129                             unreachable!()
1130                         },
1131                     );
1132 
1133                     if scalar_type != view_scalar_type {
1134                         return Err(
1135                             DescriptorResourceInvalidError::ImageViewScalarTypeMismatch {
1136                                 required: scalar_type,
1137                                 provided: view_scalar_type,
1138                             },
1139                         );
1140                     }
1141                 }
1142 
1143                 Ok(())
1144             };
1145 
1146             let check_sampler_common = |index: u32, sampler: &Arc<Sampler>| {
1147                 for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
1148                     .chain(binding_reqs.descriptors.get(&None))
1149                 {
1150                     // VUID-vkCmdDispatch-None-02703
1151                     // VUID-vkCmdDispatch-None-02704
1152                     if desc_reqs.sampler_no_unnormalized_coordinates
1153                         && sampler.unnormalized_coordinates()
1154                     {
1155                         return Err(
1156                         DescriptorResourceInvalidError::SamplerUnnormalizedCoordinatesNotAllowed,
1157                     );
1158                     }
1159 
1160                     // - OpImageFetch, OpImageSparseFetch, OpImage*Gather, and OpImageSparse*Gather must not
1161                     //   be used with a sampler that enables sampler Y′CBCR conversion.
1162                     // - The ConstOffset and Offset operands must not be used with a sampler that enables
1163                     //   sampler Y′CBCR conversion.
1164                     if desc_reqs.sampler_no_ycbcr_conversion
1165                         && sampler.sampler_ycbcr_conversion().is_some()
1166                     {
1167                         return Err(
1168                             DescriptorResourceInvalidError::SamplerYcbcrConversionNotAllowed,
1169                         );
1170                     }
1171 
1172                     /*
1173                         Instruction/Sampler/Image View Validation
1174                         https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap16.html#textures-input-validation
1175                     */
1176 
1177                     // - The SPIR-V instruction is one of the OpImage*Dref* instructions and the sampler
1178                     //   compareEnable is VK_FALSE
1179                     // - The SPIR-V instruction is not one of the OpImage*Dref* instructions and the sampler
1180                     //   compareEnable is VK_TRUE
1181                     if desc_reqs.sampler_compare != sampler.compare().is_some() {
1182                         return Err(DescriptorResourceInvalidError::SamplerCompareMismatch {
1183                             required: desc_reqs.sampler_compare,
1184                             provided: sampler.compare().is_some(),
1185                         });
1186                     }
1187                 }
1188 
1189                 Ok(())
1190             };
1191 
1192             let check_image_view = |index: u32, image_view: &Arc<dyn ImageViewAbstract>| {
1193                 check_image_view_common(index, image_view)?;
1194 
1195                 if let Some(sampler) = layout_binding.immutable_samplers.get(index as usize) {
1196                     check_sampler_common(index, sampler)?;
1197                 }
1198 
1199                 Ok(())
1200             };
1201 
1202             let check_image_view_sampler =
1203                 |index: u32, (image_view, sampler): &(Arc<dyn ImageViewAbstract>, Arc<Sampler>)| {
1204                     check_image_view_common(index, image_view)?;
1205                     check_sampler_common(index, sampler)?;
1206 
1207                     Ok(())
1208                 };
1209 
1210             let check_sampler = |index: u32, sampler: &Arc<Sampler>| {
1211                 check_sampler_common(index, sampler)?;
1212 
1213                 for desc_reqs in (binding_reqs.descriptors.get(&Some(index)).into_iter())
1214                     .chain(binding_reqs.descriptors.get(&None))
1215                 {
1216                     // Check sampler-image compatibility. Only done for separate samplers;
1217                     // combined image samplers are checked when updating the descriptor set.
1218 
1219                     // If the image view isn't actually present in the resources, then just skip it.
1220                     // It will be caught later by check_resources.
1221                     let iter = desc_reqs.sampler_with_images.iter().filter_map(|id| {
1222                         descriptor_set_state
1223                             .descriptor_sets
1224                             .get(&id.set)
1225                             .and_then(|set| set.resources().binding(id.binding))
1226                             .and_then(|res| match res {
1227                                 DescriptorBindingResources::ImageView(elements) => elements
1228                                     .get(id.index as usize)
1229                                     .and_then(|opt| opt.as_ref().map(|opt| (id, opt))),
1230                                 _ => None,
1231                             })
1232                     });
1233 
1234                     for (id, image_view) in iter {
1235                         if let Err(error) = sampler.check_can_sample(image_view.as_ref()) {
1236                             return Err(
1237                                 DescriptorResourceInvalidError::SamplerImageViewIncompatible {
1238                                     image_view_set_num: id.set,
1239                                     image_view_binding_num: id.binding,
1240                                     image_view_index: id.index,
1241                                     error,
1242                                 },
1243                             );
1244                         }
1245                     }
1246                 }
1247 
1248                 Ok(())
1249             };
1250 
1251             let check_none = |index: u32, _: &()| {
1252                 if let Some(sampler) = layout_binding.immutable_samplers.get(index as usize) {
1253                     check_sampler(index, sampler)?;
1254                 }
1255 
1256                 Ok(())
1257             };
1258 
1259             let set_resources = descriptor_set_state
1260                 .descriptor_sets
1261                 .get(&set_num)
1262                 .ok_or(PipelineExecutionError::DescriptorSetNotBound { set_num })?
1263                 .resources();
1264 
1265             let binding_resources = set_resources.binding(binding_num).unwrap();
1266 
1267             match binding_resources {
1268                 DescriptorBindingResources::None(elements) => {
1269                     validate_resources(set_num, binding_num, binding_reqs, elements, check_none)?;
1270                 }
1271                 DescriptorBindingResources::Buffer(elements) => {
1272                     validate_resources(set_num, binding_num, binding_reqs, elements, check_buffer)?;
1273                 }
1274                 DescriptorBindingResources::BufferView(elements) => {
1275                     validate_resources(
1276                         set_num,
1277                         binding_num,
1278                         binding_reqs,
1279                         elements,
1280                         check_buffer_view,
1281                     )?;
1282                 }
1283                 DescriptorBindingResources::ImageView(elements) => {
1284                     validate_resources(
1285                         set_num,
1286                         binding_num,
1287                         binding_reqs,
1288                         elements,
1289                         check_image_view,
1290                     )?;
1291                 }
1292                 DescriptorBindingResources::ImageViewSampler(elements) => {
1293                     validate_resources(
1294                         set_num,
1295                         binding_num,
1296                         binding_reqs,
1297                         elements,
1298                         check_image_view_sampler,
1299                     )?;
1300                 }
1301                 DescriptorBindingResources::Sampler(elements) => {
1302                     validate_resources(
1303                         set_num,
1304                         binding_num,
1305                         binding_reqs,
1306                         elements,
1307                         check_sampler,
1308                     )?;
1309                 }
1310             }
1311         }
1312 
1313         Ok(())
1314     }
1315 
validate_pipeline_push_constants( &self, pipeline_layout: &PipelineLayout, ) -> Result<(), PipelineExecutionError>1316     fn validate_pipeline_push_constants(
1317         &self,
1318         pipeline_layout: &PipelineLayout,
1319     ) -> Result<(), PipelineExecutionError> {
1320         if pipeline_layout.push_constant_ranges().is_empty()
1321             || self.device().enabled_features().maintenance4
1322         {
1323             return Ok(());
1324         }
1325 
1326         // VUID-vkCmdDispatch-maintenance4-06425
1327         let constants_pipeline_layout = self
1328             .builder_state
1329             .push_constants_pipeline_layout
1330             .as_ref()
1331             .ok_or(PipelineExecutionError::PushConstantsMissing)?;
1332 
1333         // VUID-vkCmdDispatch-maintenance4-06425
1334         if pipeline_layout.handle() != constants_pipeline_layout.handle()
1335             && pipeline_layout.push_constant_ranges()
1336                 != constants_pipeline_layout.push_constant_ranges()
1337         {
1338             return Err(PipelineExecutionError::PushConstantsNotCompatible);
1339         }
1340 
1341         let set_bytes = &self.builder_state.push_constants;
1342 
1343         // VUID-vkCmdDispatch-maintenance4-06425
1344         if !pipeline_layout
1345             .push_constant_ranges()
1346             .iter()
1347             .all(|pc_range| set_bytes.contains(pc_range.offset..pc_range.offset + pc_range.size))
1348         {
1349             return Err(PipelineExecutionError::PushConstantsMissing);
1350         }
1351 
1352         Ok(())
1353     }
1354 
validate_pipeline_graphics_dynamic_state( &self, pipeline: &GraphicsPipeline, ) -> Result<(), PipelineExecutionError>1355     fn validate_pipeline_graphics_dynamic_state(
1356         &self,
1357         pipeline: &GraphicsPipeline,
1358     ) -> Result<(), PipelineExecutionError> {
1359         let device = pipeline.device();
1360 
1361         // VUID-vkCmdDraw-commandBuffer-02701
1362         for dynamic_state in pipeline
1363             .dynamic_states()
1364             .filter(|(_, d)| *d)
1365             .map(|(s, _)| s)
1366         {
1367             match dynamic_state {
1368                 DynamicState::BlendConstants => {
1369                     // VUID?
1370                     if self.builder_state.blend_constants.is_none() {
1371                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1372                     }
1373                 }
1374                 DynamicState::ColorWriteEnable => {
1375                     // VUID-vkCmdDraw-attachmentCount-06667
1376                     let enables = self.builder_state.color_write_enable.as_ref().ok_or(PipelineExecutionError::DynamicStateNotSet { dynamic_state })?;
1377 
1378                     // VUID-vkCmdDraw-attachmentCount-06667
1379                     if enables.len() < pipeline.color_blend_state().unwrap().attachments.len() {
1380                         return Err(
1381                             PipelineExecutionError::DynamicColorWriteEnableNotEnoughValues {
1382                                 color_write_enable_count: enables.len() as u32,
1383                                 attachment_count: pipeline
1384                                     .color_blend_state()
1385                                     .unwrap()
1386                                     .attachments
1387                                     .len() as u32,
1388                             },
1389                         );
1390                     }
1391                 }
1392                 DynamicState::CullMode => {
1393                     // VUID?
1394                     if self.builder_state.cull_mode.is_none() {
1395                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1396                     }
1397                 }
1398                 DynamicState::DepthBias => {
1399                     // VUID?
1400                     if self.builder_state.depth_bias.is_none() {
1401                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1402                     }
1403                 }
1404                 DynamicState::DepthBiasEnable => {
1405                     // VUID-vkCmdDraw-None-04877
1406                     if self.builder_state.depth_bias_enable.is_none() {
1407                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1408                     }
1409                 }
1410                 DynamicState::DepthBounds => {
1411                     // VUID?
1412                     if self.builder_state.depth_bounds.is_none() {
1413                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1414                     }
1415                 }
1416                 DynamicState::DepthBoundsTestEnable => {
1417                     // VUID?
1418                     if self.builder_state.depth_bounds_test_enable.is_none() {
1419                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1420                     }
1421                 }
1422                 DynamicState::DepthCompareOp => {
1423                     // VUID?
1424                     if self.builder_state.depth_compare_op.is_none() {
1425                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1426                     }
1427                 }
1428                 DynamicState::DepthTestEnable => {
1429                     // VUID?
1430                     if self.builder_state.depth_test_enable.is_none() {
1431                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1432                     }
1433                 }
1434                 DynamicState::DepthWriteEnable => {
1435                     // VUID?
1436                     if self.builder_state.depth_write_enable.is_none() {
1437                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1438                     }
1439 
1440                     // TODO: Check if the depth buffer is writable
1441                 }
1442                 DynamicState::DiscardRectangle => {
1443                     let discard_rectangle_count =
1444                         match pipeline.discard_rectangle_state().unwrap().rectangles {
1445                             PartialStateMode::Dynamic(count) => count,
1446                             _ => unreachable!(),
1447                         };
1448 
1449                     for num in 0..discard_rectangle_count {
1450                         // VUID?
1451                         if !self.builder_state.discard_rectangle.contains_key(&num) {
1452                             return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1453                         }
1454                     }
1455                 }
1456                 DynamicState::ExclusiveScissor => todo!(),
1457                 DynamicState::FragmentShadingRate => todo!(),
1458                 DynamicState::FrontFace => {
1459                     // VUID?
1460                     if self.builder_state.front_face.is_none() {
1461                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1462                     }
1463                 }
1464                 DynamicState::LineStipple => {
1465                     // VUID?
1466                     if self.builder_state.line_stipple.is_none() {
1467                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1468                     }
1469                 }
1470                 DynamicState::LineWidth => {
1471                     // VUID?
1472                     if self.builder_state.line_width.is_none() {
1473                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1474                     }
1475                 }
1476                 DynamicState::LogicOp => {
1477                     // VUID-vkCmdDraw-logicOp-04878
1478                     if self.builder_state.logic_op.is_none() {
1479                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1480                     }
1481                 }
1482                 DynamicState::PatchControlPoints => {
1483                     // VUID-vkCmdDraw-None-04875
1484                     if self.builder_state.patch_control_points.is_none() {
1485                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1486                     }
1487                 }
1488                 DynamicState::PrimitiveRestartEnable => {
1489                     // VUID-vkCmdDraw-None-04879
1490                     let primitive_restart_enable =
1491                         if let Some(enable) = self.builder_state.primitive_restart_enable {
1492                             enable
1493                         } else {
1494                             return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1495                         };
1496 
1497                     if primitive_restart_enable {
1498                         let topology = match pipeline.input_assembly_state().topology {
1499                             PartialStateMode::Fixed(topology) => topology,
1500                             PartialStateMode::Dynamic(_) => {
1501                                 if let Some(topology) = self.builder_state.primitive_topology {
1502                                     topology
1503                                 } else {
1504                                     return Err(PipelineExecutionError::DynamicStateNotSet {
1505                                         dynamic_state: DynamicState::PrimitiveTopology,
1506                                     });
1507                                 }
1508                             }
1509                         };
1510 
1511                         match topology {
1512                             PrimitiveTopology::PointList
1513                             | PrimitiveTopology::LineList
1514                             | PrimitiveTopology::TriangleList
1515                             | PrimitiveTopology::LineListWithAdjacency
1516                             | PrimitiveTopology::TriangleListWithAdjacency => {
1517                                 // VUID?
1518                                 if !device.enabled_features().primitive_topology_list_restart {
1519                                     return Err(PipelineExecutionError::RequirementNotMet {
1520                                         required_for: "The bound pipeline sets \
1521                                             `DynamicState::PrimitiveRestartEnable` and the \
1522                                             current primitive topology is \
1523                                             `PrimitiveTopology::*List`",
1524                                         requires_one_of: RequiresOneOf {
1525                                             features: &["primitive_topology_list_restart"],
1526                                             ..Default::default()
1527                                         },
1528                                     });
1529                                 }
1530                             }
1531                             PrimitiveTopology::PatchList => {
1532                                 // VUID?
1533                                 if !device
1534                                     .enabled_features()
1535                                     .primitive_topology_patch_list_restart
1536                                 {
1537                                     return Err(PipelineExecutionError::RequirementNotMet {
1538                                         required_for: "The bound pipeline sets \
1539                                             `DynamicState::PrimitiveRestartEnable` and the \
1540                                             current primitive topology is \
1541                                             `PrimitiveTopology::PatchList`",
1542                                         requires_one_of: RequiresOneOf {
1543                                             features: &["primitive_topology_patch_list_restart"],
1544                                             ..Default::default()
1545                                         },
1546                                     });
1547                                 }
1548                             }
1549                             _ => (),
1550                         }
1551                     }
1552                 }
1553                 DynamicState::PrimitiveTopology => {
1554                     // VUID-vkCmdDraw-primitiveTopology-03420
1555                     let topology = if let Some(topology) = self.builder_state.primitive_topology {
1556                         topology
1557                     } else {
1558                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1559                     };
1560 
1561                     if pipeline.shader(ShaderStage::TessellationControl).is_some() {
1562                         // VUID?
1563                         if !matches!(topology, PrimitiveTopology::PatchList) {
1564                             return Err(PipelineExecutionError::DynamicPrimitiveTopologyInvalid {
1565                                 topology,
1566                             });
1567                         }
1568                     } else {
1569                         // VUID?
1570                         if matches!(topology, PrimitiveTopology::PatchList) {
1571                             return Err(PipelineExecutionError::DynamicPrimitiveTopologyInvalid {
1572                                 topology,
1573                             });
1574                         }
1575                     }
1576 
1577                     let required_topology_class = match pipeline.input_assembly_state().topology {
1578                         PartialStateMode::Dynamic(topology_class) => topology_class,
1579                         _ => unreachable!(),
1580                     };
1581 
1582                     // VUID-vkCmdDraw-primitiveTopology-03420
1583                     if topology.class() != required_topology_class {
1584                         return Err(
1585                             PipelineExecutionError::DynamicPrimitiveTopologyClassMismatch {
1586                                 provided_class: topology.class(),
1587                                 required_class: required_topology_class,
1588                             },
1589                         );
1590                     }
1591 
1592                     // TODO: check that the topology matches the geometry shader
1593                 }
1594                 DynamicState::RasterizerDiscardEnable => {
1595                     // VUID-vkCmdDraw-None-04876
1596                     if self.builder_state.rasterizer_discard_enable.is_none() {
1597                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1598                     }
1599                 }
1600                 DynamicState::RayTracingPipelineStackSize => unreachable!(
1601                     "RayTracingPipelineStackSize dynamic state should not occur on a graphics pipeline"
1602                 ),
1603                 DynamicState::SampleLocations => todo!(),
1604                 DynamicState::Scissor => {
1605                     for num in 0..pipeline.viewport_state().unwrap().count().unwrap() {
1606                         // VUID?
1607                         if !self.builder_state.scissor.contains_key(&num) {
1608                             return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1609                         }
1610                     }
1611                 }
1612                 DynamicState::ScissorWithCount => {
1613                     // VUID-vkCmdDraw-scissorCount-03418
1614                     // VUID-vkCmdDraw-viewportCount-03419
1615                     let scissor_count = self.builder_state.scissor_with_count.as_ref().ok_or(PipelineExecutionError::DynamicStateNotSet { dynamic_state })?.len() as u32;
1616 
1617                     // Check if the counts match, but only if the viewport count is fixed.
1618                     // If the viewport count is also dynamic, then the
1619                     // DynamicState::ViewportWithCount match arm will handle it.
1620                     if let Some(viewport_count) = pipeline.viewport_state().unwrap().count() {
1621                         // VUID-vkCmdDraw-scissorCount-03418
1622                         if viewport_count != scissor_count {
1623                             return Err(
1624                                 PipelineExecutionError::DynamicViewportScissorCountMismatch {
1625                                     viewport_count,
1626                                     scissor_count,
1627                                 },
1628                             );
1629                         }
1630                     }
1631                 }
1632                 DynamicState::StencilCompareMask => {
1633                     let state = self.builder_state.stencil_compare_mask;
1634 
1635                     // VUID?
1636                     if state.front.is_none() || state.back.is_none() {
1637                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1638                     }
1639                 }
1640                 DynamicState::StencilOp => {
1641                     let state = self.builder_state.stencil_op;
1642 
1643                     // VUID?
1644                     if state.front.is_none() || state.back.is_none() {
1645                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1646                     }
1647                 }
1648                 DynamicState::StencilReference => {
1649                     let state = self.builder_state.stencil_reference;
1650 
1651                     // VUID?
1652                     if state.front.is_none() || state.back.is_none() {
1653                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1654                     }
1655                 }
1656                 DynamicState::StencilTestEnable => {
1657                     // VUID?
1658                     if self.builder_state.stencil_test_enable.is_none() {
1659                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1660                     }
1661 
1662                     // TODO: Check if the stencil buffer is writable
1663                 }
1664                 DynamicState::StencilWriteMask => {
1665                     let state = self.builder_state.stencil_write_mask;
1666 
1667                     // VUID?
1668                     if state.front.is_none() || state.back.is_none() {
1669                         return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1670                     }
1671                 }
1672                 DynamicState::VertexInput => todo!(),
1673                 DynamicState::VertexInputBindingStride => todo!(),
1674                 DynamicState::Viewport => {
1675                     for num in 0..pipeline.viewport_state().unwrap().count().unwrap() {
1676                         // VUID?
1677                         if !self.builder_state.viewport.contains_key(&num) {
1678                             return Err(PipelineExecutionError::DynamicStateNotSet { dynamic_state });
1679                         }
1680                     }
1681                 }
1682                 DynamicState::ViewportCoarseSampleOrder => todo!(),
1683                 DynamicState::ViewportShadingRatePalette => todo!(),
1684                 DynamicState::ViewportWithCount => {
1685                     // VUID-vkCmdDraw-viewportCount-03417
1686                     let viewport_count = self.builder_state.viewport_with_count.as_ref().ok_or(PipelineExecutionError::DynamicStateNotSet { dynamic_state })?.len() as u32;
1687 
1688                     let scissor_count = if let Some(scissor_count) =
1689                         pipeline.viewport_state().unwrap().count()
1690                     {
1691                         // The scissor count is fixed.
1692                         scissor_count
1693                     } else {
1694                         // VUID-vkCmdDraw-viewportCount-03419
1695                         // The scissor count is also dynamic.
1696                         self.builder_state.scissor_with_count.as_ref().ok_or(PipelineExecutionError::DynamicStateNotSet { dynamic_state })?.len() as u32
1697                     };
1698 
1699                     // VUID-vkCmdDraw-viewportCount-03417
1700                     // VUID-vkCmdDraw-viewportCount-03419
1701                     if viewport_count != scissor_count {
1702                         return Err(
1703                             PipelineExecutionError::DynamicViewportScissorCountMismatch {
1704                                 viewport_count,
1705                                 scissor_count,
1706                             },
1707                         );
1708                     }
1709 
1710                     // TODO: VUID-vkCmdDrawIndexed-primitiveFragmentShadingRateWithMultipleViewports-04552
1711                     // If the primitiveFragmentShadingRateWithMultipleViewports limit is not supported,
1712                     // the bound graphics pipeline was created with the
1713                     // VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT dynamic state enabled, and any of the
1714                     // shader stages of the bound graphics pipeline write to the PrimitiveShadingRateKHR
1715                     // built-in, then vkCmdSetViewportWithCountEXT must have been called in the current
1716                     // command buffer prior to this drawing command, and the viewportCount parameter of
1717                     // vkCmdSetViewportWithCountEXT must be 1
1718                 }
1719                 DynamicState::ViewportWScaling => todo!(),
1720                 DynamicState::TessellationDomainOrigin => todo!(),
1721                 DynamicState::DepthClampEnable => todo!(),
1722                 DynamicState::PolygonMode => todo!(),
1723                 DynamicState::RasterizationSamples => todo!(),
1724                 DynamicState::SampleMask => todo!(),
1725                 DynamicState::AlphaToCoverageEnable => todo!(),
1726                 DynamicState::AlphaToOneEnable => todo!(),
1727                 DynamicState::LogicOpEnable => todo!(),
1728                 DynamicState::ColorBlendEnable => todo!(),
1729                 DynamicState::ColorBlendEquation => todo!(),
1730                 DynamicState::ColorWriteMask => todo!(),
1731                 DynamicState::RasterizationStream => todo!(),
1732                 DynamicState::ConservativeRasterizationMode => todo!(),
1733                 DynamicState::ExtraPrimitiveOverestimationSize => todo!(),
1734                 DynamicState::DepthClipEnable => todo!(),
1735                 DynamicState::SampleLocationsEnable => todo!(),
1736                 DynamicState::ColorBlendAdvanced => todo!(),
1737                 DynamicState::ProvokingVertexMode => todo!(),
1738                 DynamicState::LineRasterizationMode => todo!(),
1739                 DynamicState::LineStippleEnable => todo!(),
1740                 DynamicState::DepthClipNegativeOneToOne => todo!(),
1741                 DynamicState::ViewportWScalingEnable => todo!(),
1742                 DynamicState::ViewportSwizzle => todo!(),
1743                 DynamicState::CoverageToColorEnable => todo!(),
1744                 DynamicState::CoverageToColorLocation => todo!(),
1745                 DynamicState::CoverageModulationMode => todo!(),
1746                 DynamicState::CoverageModulationTableEnable => todo!(),
1747                 DynamicState::CoverageModulationTable => todo!(),
1748                 DynamicState::ShadingRateImageEnable => todo!(),
1749                 DynamicState::RepresentativeFragmentTestEnable => todo!(),
1750                 DynamicState::CoverageReductionMode => todo!(),
1751             }
1752         }
1753 
1754         Ok(())
1755     }
1756 
validate_pipeline_graphics_render_pass( &self, pipeline: &GraphicsPipeline, render_pass_state: &RenderPassState, ) -> Result<(), PipelineExecutionError>1757     fn validate_pipeline_graphics_render_pass(
1758         &self,
1759         pipeline: &GraphicsPipeline,
1760         render_pass_state: &RenderPassState,
1761     ) -> Result<(), PipelineExecutionError> {
1762         // VUID?
1763         if render_pass_state.contents != SubpassContents::Inline {
1764             return Err(PipelineExecutionError::ForbiddenWithSubpassContents {
1765                 subpass_contents: render_pass_state.contents,
1766             });
1767         }
1768 
1769         match (&render_pass_state.render_pass, pipeline.render_pass()) {
1770             (
1771                 RenderPassStateType::BeginRenderPass(state),
1772                 PipelineRenderPassType::BeginRenderPass(pipeline_subpass),
1773             ) => {
1774                 // VUID-vkCmdDraw-renderPass-02684
1775                 if !pipeline_subpass
1776                     .render_pass()
1777                     .is_compatible_with(state.subpass.render_pass())
1778                 {
1779                     return Err(PipelineExecutionError::PipelineRenderPassNotCompatible);
1780                 }
1781 
1782                 // VUID-vkCmdDraw-subpass-02685
1783                 if pipeline_subpass.index() != state.subpass.index() {
1784                     return Err(PipelineExecutionError::PipelineSubpassMismatch {
1785                         pipeline: pipeline_subpass.index(),
1786                         current: state.subpass.index(),
1787                     });
1788                 }
1789             }
1790             (
1791                 RenderPassStateType::BeginRendering(_),
1792                 PipelineRenderPassType::BeginRendering(pipeline_rendering_info),
1793             ) => {
1794                 // VUID-vkCmdDraw-viewMask-06178
1795                 if pipeline_rendering_info.view_mask != render_pass_state.rendering_info.view_mask {
1796                     return Err(PipelineExecutionError::PipelineViewMaskMismatch {
1797                         pipeline_view_mask: pipeline_rendering_info.view_mask,
1798                         required_view_mask: render_pass_state.rendering_info.view_mask,
1799                     });
1800                 }
1801 
1802                 // VUID-vkCmdDraw-colorAttachmentCount-06179
1803                 if pipeline_rendering_info.color_attachment_formats.len()
1804                     != render_pass_state
1805                         .rendering_info
1806                         .color_attachment_formats
1807                         .len()
1808                 {
1809                     return Err(
1810                         PipelineExecutionError::PipelineColorAttachmentCountMismatch {
1811                             pipeline_count: pipeline_rendering_info.color_attachment_formats.len()
1812                                 as u32,
1813                             required_count: render_pass_state
1814                                 .rendering_info
1815                                 .color_attachment_formats
1816                                 .len() as u32,
1817                         },
1818                     );
1819                 }
1820 
1821                 for (color_attachment_index, required_format, pipeline_format) in render_pass_state
1822                     .rendering_info
1823                     .color_attachment_formats
1824                     .iter()
1825                     .zip(
1826                         pipeline_rendering_info
1827                             .color_attachment_formats
1828                             .iter()
1829                             .copied(),
1830                     )
1831                     .enumerate()
1832                     .filter_map(|(i, (r, p))| r.map(|r| (i as u32, r, p)))
1833                 {
1834                     // VUID-vkCmdDraw-colorAttachmentCount-06180
1835                     if Some(required_format) != pipeline_format {
1836                         return Err(
1837                             PipelineExecutionError::PipelineColorAttachmentFormatMismatch {
1838                                 color_attachment_index,
1839                                 pipeline_format,
1840                                 required_format,
1841                             },
1842                         );
1843                     }
1844                 }
1845 
1846                 if let Some((required_format, pipeline_format)) = render_pass_state
1847                     .rendering_info
1848                     .depth_attachment_format
1849                     .map(|r| (r, pipeline_rendering_info.depth_attachment_format))
1850                 {
1851                     // VUID-vkCmdDraw-pDepthAttachment-06181
1852                     if Some(required_format) != pipeline_format {
1853                         return Err(
1854                             PipelineExecutionError::PipelineDepthAttachmentFormatMismatch {
1855                                 pipeline_format,
1856                                 required_format,
1857                             },
1858                         );
1859                     }
1860                 }
1861 
1862                 if let Some((required_format, pipeline_format)) = render_pass_state
1863                     .rendering_info
1864                     .stencil_attachment_format
1865                     .map(|r| (r, pipeline_rendering_info.stencil_attachment_format))
1866                 {
1867                     // VUID-vkCmdDraw-pStencilAttachment-06182
1868                     if Some(required_format) != pipeline_format {
1869                         return Err(
1870                             PipelineExecutionError::PipelineStencilAttachmentFormatMismatch {
1871                                 pipeline_format,
1872                                 required_format,
1873                             },
1874                         );
1875                     }
1876                 }
1877 
1878                 // VUID-vkCmdDraw-imageView-06172
1879                 // VUID-vkCmdDraw-imageView-06173
1880                 // VUID-vkCmdDraw-imageView-06174
1881                 // VUID-vkCmdDraw-imageView-06175
1882                 // VUID-vkCmdDraw-imageView-06176
1883                 // VUID-vkCmdDraw-imageView-06177
1884                 // TODO:
1885             }
1886             _ => return Err(PipelineExecutionError::PipelineRenderPassTypeMismatch),
1887         }
1888 
1889         // VUID-vkCmdDraw-None-02686
1890         // TODO:
1891 
1892         Ok(())
1893     }
1894 
validate_pipeline_graphics_vertex_buffers( &self, pipeline: &GraphicsPipeline, vertices: Option<(u32, u32)>, instances: Option<(u32, u32)>, ) -> Result<(), PipelineExecutionError>1895     fn validate_pipeline_graphics_vertex_buffers(
1896         &self,
1897         pipeline: &GraphicsPipeline,
1898         vertices: Option<(u32, u32)>,
1899         instances: Option<(u32, u32)>,
1900     ) -> Result<(), PipelineExecutionError> {
1901         let vertex_input = pipeline.vertex_input_state();
1902         let mut vertices_in_buffers: Option<u64> = None;
1903         let mut instances_in_buffers: Option<u64> = None;
1904 
1905         for (&binding_num, binding_desc) in &vertex_input.bindings {
1906             // VUID-vkCmdDraw-None-04007
1907             let vertex_buffer = match self.builder_state.vertex_buffers.get(&binding_num) {
1908                 Some(x) => x,
1909                 None => return Err(PipelineExecutionError::VertexBufferNotBound { binding_num }),
1910             };
1911 
1912             let mut num_elements = vertex_buffer.size() / binding_desc.stride as u64;
1913 
1914             match binding_desc.input_rate {
1915                 VertexInputRate::Vertex => {
1916                     vertices_in_buffers = Some(if let Some(x) = vertices_in_buffers {
1917                         min(x, num_elements)
1918                     } else {
1919                         num_elements
1920                     });
1921                 }
1922                 VertexInputRate::Instance { divisor } => {
1923                     if divisor == 0 {
1924                         // A divisor of 0 means the same instance data is used for all instances,
1925                         // so we can draw any number of instances from a single element.
1926                         // The buffer must contain at least one element though.
1927                         if num_elements != 0 {
1928                             num_elements = u64::MAX;
1929                         }
1930                     } else {
1931                         // If divisor is e.g. 2, we use only half the amount of data from the source
1932                         // buffer, so the number of instances that can be drawn is twice as large.
1933                         num_elements = num_elements.saturating_mul(divisor as u64);
1934                     }
1935 
1936                     instances_in_buffers = Some(if let Some(x) = instances_in_buffers {
1937                         min(x, num_elements)
1938                     } else {
1939                         num_elements
1940                     });
1941                 }
1942             };
1943         }
1944 
1945         if let Some((first_vertex, vertex_count)) = vertices {
1946             let vertices_needed = first_vertex as u64 + vertex_count as u64;
1947 
1948             if let Some(vertices_in_buffers) = vertices_in_buffers {
1949                 // VUID-vkCmdDraw-None-02721
1950                 if vertices_needed > vertices_in_buffers {
1951                     return Err(PipelineExecutionError::VertexBufferVertexRangeOutOfBounds {
1952                         vertices_needed,
1953                         vertices_in_buffers,
1954                     });
1955                 }
1956             }
1957         }
1958 
1959         if let Some((first_instance, instance_count)) = instances {
1960             let instances_needed = first_instance as u64 + instance_count as u64;
1961 
1962             if let Some(instances_in_buffers) = instances_in_buffers {
1963                 // VUID-vkCmdDraw-None-02721
1964                 if instances_needed > instances_in_buffers {
1965                     return Err(
1966                         PipelineExecutionError::VertexBufferInstanceRangeOutOfBounds {
1967                             instances_needed,
1968                             instances_in_buffers,
1969                         },
1970                     );
1971                 }
1972             }
1973 
1974             let view_mask = match pipeline.render_pass() {
1975                 PipelineRenderPassType::BeginRenderPass(subpass) => {
1976                     subpass.render_pass().views_used()
1977                 }
1978                 PipelineRenderPassType::BeginRendering(rendering_info) => rendering_info.view_mask,
1979             };
1980 
1981             if view_mask != 0 {
1982                 let max = pipeline
1983                     .device()
1984                     .physical_device()
1985                     .properties()
1986                     .max_multiview_instance_index
1987                     .unwrap_or(0);
1988 
1989                 let highest_instance = instances_needed.saturating_sub(1);
1990 
1991                 // VUID-vkCmdDraw-maxMultiviewInstanceIndex-02688
1992                 if highest_instance > max as u64 {
1993                     return Err(PipelineExecutionError::MaxMultiviewInstanceIndexExceeded {
1994                         highest_instance,
1995                         max,
1996                     });
1997                 }
1998             }
1999         }
2000 
2001         Ok(())
2002     }
2003 }
2004 
record_descriptor_sets_access( resources_usage_state: &mut ResourcesState, command_index: usize, command_name: &'static str, descriptor_sets_state: &HashMap<PipelineBindPoint, DescriptorSetState>, pipeline: &impl Pipeline, )2005 fn record_descriptor_sets_access(
2006     resources_usage_state: &mut ResourcesState,
2007     command_index: usize,
2008     command_name: &'static str,
2009     descriptor_sets_state: &HashMap<PipelineBindPoint, DescriptorSetState>,
2010     pipeline: &impl Pipeline,
2011 ) {
2012     let descriptor_sets_state = match descriptor_sets_state.get(&pipeline.bind_point()) {
2013         Some(x) => x,
2014         None => return,
2015     };
2016 
2017     for (&(set, binding), binding_reqs) in pipeline.descriptor_binding_requirements() {
2018         let descriptor_type = descriptor_sets_state.pipeline_layout.set_layouts()[set as usize]
2019             .bindings()[&binding]
2020             .descriptor_type;
2021 
2022         // TODO: Should input attachments be handled here or in attachment access?
2023         if descriptor_type == DescriptorType::InputAttachment {
2024             continue;
2025         }
2026 
2027         let use_iter = move |index: u32| {
2028             let (stages_read, stages_write) = [Some(index), None]
2029                 .into_iter()
2030                 .filter_map(|index| binding_reqs.descriptors.get(&index))
2031                 .fold(
2032                     (ShaderStages::empty(), ShaderStages::empty()),
2033                     |(stages_read, stages_write), desc_reqs| {
2034                         (
2035                             stages_read | desc_reqs.memory_read,
2036                             stages_write | desc_reqs.memory_write,
2037                         )
2038                     },
2039                 );
2040             let use_ref = ResourceUseRef {
2041                 command_index,
2042                 command_name,
2043                 resource_in_command: ResourceInCommand::DescriptorSet {
2044                     set,
2045                     binding,
2046                     index,
2047                 },
2048                 secondary_use_ref: None,
2049             };
2050             let stage_access_iter = PipelineStageAccess::iter_descriptor_stages(
2051                 descriptor_type,
2052                 stages_read,
2053                 stages_write,
2054             );
2055             (use_ref, stage_access_iter)
2056         };
2057 
2058         let descriptor_set_state = &descriptor_sets_state.descriptor_sets[&set];
2059 
2060         match descriptor_set_state.resources().binding(binding).unwrap() {
2061             DescriptorBindingResources::None(_) => continue,
2062             DescriptorBindingResources::Buffer(elements) => {
2063                 if matches!(
2064                     descriptor_type,
2065                     DescriptorType::UniformBufferDynamic | DescriptorType::StorageBufferDynamic
2066                 ) {
2067                     let dynamic_offsets = descriptor_set_state.dynamic_offsets();
2068 
2069                     for (index, element) in elements.iter().enumerate() {
2070                         if let Some((buffer, range)) = element {
2071                             let dynamic_offset = dynamic_offsets[index] as DeviceSize;
2072                             let (use_ref, stage_access_iter) = use_iter(index as u32);
2073 
2074                             let mut range = range.clone();
2075                             range.start += buffer.offset() + dynamic_offset;
2076                             range.end += buffer.offset() + dynamic_offset;
2077 
2078                             for stage_access in stage_access_iter {
2079                                 resources_usage_state.record_buffer_access(
2080                                     &use_ref,
2081                                     buffer.buffer(),
2082                                     range.clone(),
2083                                     stage_access,
2084                                 );
2085                             }
2086                         }
2087                     }
2088                 } else {
2089                     for (index, element) in elements.iter().enumerate() {
2090                         if let Some((buffer, range)) = element {
2091                             let (use_ref, stage_access_iter) = use_iter(index as u32);
2092 
2093                             let mut range = range.clone();
2094                             range.start += buffer.offset();
2095                             range.end += buffer.offset();
2096 
2097                             for stage_access in stage_access_iter {
2098                                 resources_usage_state.record_buffer_access(
2099                                     &use_ref,
2100                                     buffer.buffer(),
2101                                     range.clone(),
2102                                     stage_access,
2103                                 );
2104                             }
2105                         }
2106                     }
2107                 }
2108             }
2109             DescriptorBindingResources::BufferView(elements) => {
2110                 for (index, element) in elements.iter().enumerate() {
2111                     if let Some(buffer_view) = element {
2112                         let buffer = buffer_view.buffer();
2113                         let (use_ref, stage_access_iter) = use_iter(index as u32);
2114 
2115                         let mut range = buffer_view.range();
2116                         range.start += buffer.offset();
2117                         range.end += buffer.offset();
2118 
2119                         for stage_access in stage_access_iter {
2120                             resources_usage_state.record_buffer_access(
2121                                 &use_ref,
2122                                 buffer.buffer(),
2123                                 range.clone(),
2124                                 stage_access,
2125                             );
2126                         }
2127                     }
2128                 }
2129             }
2130             DescriptorBindingResources::ImageView(elements) => {
2131                 for (index, element) in elements.iter().enumerate() {
2132                     if let Some(image_view) = element {
2133                         let image = image_view.image();
2134                         let image_inner = image.inner();
2135                         let layout = image
2136                             .descriptor_layouts()
2137                             .expect(
2138                                 "descriptor_layouts must return Some when used in an image view",
2139                             )
2140                             .layout_for(descriptor_type);
2141                         let (use_ref, stage_access_iter) = use_iter(index as u32);
2142 
2143                         let mut subresource_range = image_view.subresource_range().clone();
2144                         subresource_range.array_layers.start += image_inner.first_layer;
2145                         subresource_range.array_layers.end += image_inner.first_layer;
2146                         subresource_range.mip_levels.start += image_inner.first_mipmap_level;
2147                         subresource_range.mip_levels.end += image_inner.first_mipmap_level;
2148 
2149                         for stage_access in stage_access_iter {
2150                             resources_usage_state.record_image_access(
2151                                 &use_ref,
2152                                 image_inner.image,
2153                                 subresource_range.clone(),
2154                                 stage_access,
2155                                 layout,
2156                             );
2157                         }
2158                     }
2159                 }
2160             }
2161             DescriptorBindingResources::ImageViewSampler(elements) => {
2162                 for (index, element) in elements.iter().enumerate() {
2163                     if let Some((image_view, _)) = element {
2164                         let image = image_view.image();
2165                         let image_inner = image.inner();
2166                         let layout = image
2167                             .descriptor_layouts()
2168                             .expect(
2169                                 "descriptor_layouts must return Some when used in an image view",
2170                             )
2171                             .layout_for(descriptor_type);
2172                         let (use_ref, stage_access_iter) = use_iter(index as u32);
2173 
2174                         let mut subresource_range = image_view.subresource_range().clone();
2175                         subresource_range.array_layers.start += image_inner.first_layer;
2176                         subresource_range.array_layers.end += image_inner.first_layer;
2177                         subresource_range.mip_levels.start += image_inner.first_mipmap_level;
2178                         subresource_range.mip_levels.end += image_inner.first_mipmap_level;
2179 
2180                         for stage_access in stage_access_iter {
2181                             resources_usage_state.record_image_access(
2182                                 &use_ref,
2183                                 image_inner.image,
2184                                 subresource_range.clone(),
2185                                 stage_access,
2186                                 layout,
2187                             );
2188                         }
2189                     }
2190                 }
2191             }
2192             DescriptorBindingResources::Sampler(_) => (),
2193         }
2194     }
2195 }
2196 
record_vertex_buffers_access( resources_usage_state: &mut ResourcesState, command_index: usize, command_name: &'static str, vertex_buffers_state: &HashMap<u32, Subbuffer<[u8]>>, pipeline: &GraphicsPipeline, )2197 fn record_vertex_buffers_access(
2198     resources_usage_state: &mut ResourcesState,
2199     command_index: usize,
2200     command_name: &'static str,
2201     vertex_buffers_state: &HashMap<u32, Subbuffer<[u8]>>,
2202     pipeline: &GraphicsPipeline,
2203 ) {
2204     for &binding in pipeline.vertex_input_state().bindings.keys() {
2205         let buffer = &vertex_buffers_state[&binding];
2206         let use_ref = ResourceUseRef {
2207             command_index,
2208             command_name,
2209             resource_in_command: ResourceInCommand::VertexBuffer { binding },
2210             secondary_use_ref: None,
2211         };
2212 
2213         let mut range = 0..buffer.size(); // TODO: take range from draw command
2214         range.start += buffer.offset();
2215         range.end += buffer.offset();
2216         resources_usage_state.record_buffer_access(
2217             &use_ref,
2218             buffer.buffer(),
2219             range,
2220             PipelineStageAccess::VertexAttributeInput_VertexAttributeRead,
2221         );
2222     }
2223 }
2224 
record_index_buffer_access( resources_usage_state: &mut ResourcesState, command_index: usize, command_name: &'static str, index_buffer_state: &Option<(Subbuffer<[u8]>, IndexType)>, )2225 fn record_index_buffer_access(
2226     resources_usage_state: &mut ResourcesState,
2227     command_index: usize,
2228     command_name: &'static str,
2229     index_buffer_state: &Option<(Subbuffer<[u8]>, IndexType)>,
2230 ) {
2231     let buffer = &index_buffer_state.as_ref().unwrap().0;
2232     let use_ref = ResourceUseRef {
2233         command_index,
2234         command_name,
2235         resource_in_command: ResourceInCommand::IndexBuffer,
2236         secondary_use_ref: None,
2237     };
2238 
2239     let mut range = 0..buffer.size(); // TODO: take range from draw command
2240     range.start += buffer.offset();
2241     range.end += buffer.offset();
2242     resources_usage_state.record_buffer_access(
2243         &use_ref,
2244         buffer.buffer(),
2245         range,
2246         PipelineStageAccess::IndexInput_IndexRead,
2247     );
2248 }
2249 
record_indirect_buffer_access( resources_usage_state: &mut ResourcesState, command_index: usize, command_name: &'static str, buffer: &Subbuffer<[u8]>, )2250 fn record_indirect_buffer_access(
2251     resources_usage_state: &mut ResourcesState,
2252     command_index: usize,
2253     command_name: &'static str,
2254     buffer: &Subbuffer<[u8]>,
2255 ) {
2256     let use_ref = ResourceUseRef {
2257         command_index,
2258         command_name,
2259         resource_in_command: ResourceInCommand::IndirectBuffer,
2260         secondary_use_ref: None,
2261     };
2262 
2263     let mut range = 0..buffer.size(); // TODO: take range from draw command
2264     range.start += buffer.offset();
2265     range.end += buffer.offset();
2266     resources_usage_state.record_buffer_access(
2267         &use_ref,
2268         buffer.buffer(),
2269         range,
2270         PipelineStageAccess::DrawIndirect_IndirectCommandRead,
2271     );
2272 }
2273 
record_subpass_attachments_access( resources_usage_state: &mut ResourcesState, command_index: usize, command_name: &'static str, render_pass_state: &RenderPassState, builder_state: &CommandBufferBuilderState, pipeline: &GraphicsPipeline, )2274 fn record_subpass_attachments_access(
2275     resources_usage_state: &mut ResourcesState,
2276     command_index: usize,
2277     command_name: &'static str,
2278     render_pass_state: &RenderPassState,
2279     builder_state: &CommandBufferBuilderState,
2280     pipeline: &GraphicsPipeline,
2281 ) {
2282     if Option::from(pipeline.rasterization_state().rasterizer_discard_enable)
2283         .or(builder_state.rasterizer_discard_enable)
2284         .unwrap()
2285     {
2286         return;
2287     }
2288 
2289     let attachments = match &render_pass_state.attachments {
2290         Some(x) => x,
2291         None => return,
2292     };
2293 
2294     if let Some(attachment_info) = attachments.depth_attachment.as_ref() {
2295         let &RenderPassStateAttachmentInfo {
2296             ref image_view,
2297             image_layout,
2298             ..
2299         } = attachment_info;
2300 
2301         // TODO: check `pipeline.depth_stencil_state` for whether the attachment is going to be
2302         // read and/or written.
2303         let accesses: &'static [PipelineStageAccess] =
2304             match pipeline.fragment_tests_stages().unwrap() {
2305                 FragmentTestsStages::Early => {
2306                     &[PipelineStageAccess::EarlyFragmentTests_DepthStencilAttachmentWrite]
2307                 }
2308                 FragmentTestsStages::Late => {
2309                     &[PipelineStageAccess::LateFragmentTests_DepthStencilAttachmentWrite]
2310                 }
2311                 FragmentTestsStages::EarlyAndLate => &[
2312                     PipelineStageAccess::EarlyFragmentTests_DepthStencilAttachmentWrite,
2313                     PipelineStageAccess::LateFragmentTests_DepthStencilAttachmentWrite,
2314                 ],
2315             };
2316 
2317         let image = image_view.image();
2318         let image_inner = image.inner();
2319         let mut subresource_range = ImageSubresourceRange {
2320             aspects: ImageAspects::DEPTH,
2321             ..image_view.subresource_range().clone()
2322         };
2323         subresource_range.array_layers.start += image_inner.first_layer;
2324         subresource_range.array_layers.end += image_inner.first_layer;
2325         subresource_range.mip_levels.start += image_inner.first_mipmap_level;
2326         subresource_range.mip_levels.end += image_inner.first_mipmap_level;
2327 
2328         let use_ref = ResourceUseRef {
2329             command_index,
2330             command_name,
2331             resource_in_command: ResourceInCommand::DepthStencilAttachment,
2332             secondary_use_ref: None,
2333         };
2334 
2335         for &access in accesses {
2336             resources_usage_state.record_image_access(
2337                 &use_ref,
2338                 image_inner.image,
2339                 subresource_range.clone(),
2340                 access,
2341                 image_layout,
2342             );
2343         }
2344     }
2345 
2346     if let Some(attachment_info) = attachments.stencil_attachment.as_ref() {
2347         let &RenderPassStateAttachmentInfo {
2348             ref image_view,
2349             image_layout,
2350             ..
2351         } = attachment_info;
2352 
2353         // TODO: check `pipeline.depth_stencil_state` for whether the attachment is going to be
2354         // read and/or written.
2355         let accesses: &'static [PipelineStageAccess] =
2356             match pipeline.fragment_tests_stages().unwrap() {
2357                 FragmentTestsStages::Early => {
2358                     &[PipelineStageAccess::EarlyFragmentTests_DepthStencilAttachmentWrite]
2359                 }
2360                 FragmentTestsStages::Late => {
2361                     &[PipelineStageAccess::LateFragmentTests_DepthStencilAttachmentWrite]
2362                 }
2363                 FragmentTestsStages::EarlyAndLate => &[
2364                     PipelineStageAccess::EarlyFragmentTests_DepthStencilAttachmentWrite,
2365                     PipelineStageAccess::LateFragmentTests_DepthStencilAttachmentWrite,
2366                 ],
2367             };
2368 
2369         let image = image_view.image();
2370         let image_inner = image.inner();
2371         let mut subresource_range = ImageSubresourceRange {
2372             aspects: ImageAspects::STENCIL,
2373             ..image_view.subresource_range().clone()
2374         };
2375         subresource_range.array_layers.start += image_inner.first_layer;
2376         subresource_range.array_layers.end += image_inner.first_layer;
2377         subresource_range.mip_levels.start += image_inner.first_mipmap_level;
2378         subresource_range.mip_levels.end += image_inner.first_mipmap_level;
2379 
2380         let use_ref = ResourceUseRef {
2381             command_index,
2382             command_name,
2383             resource_in_command: ResourceInCommand::DepthStencilAttachment,
2384             secondary_use_ref: None,
2385         };
2386 
2387         for &access in accesses {
2388             resources_usage_state.record_image_access(
2389                 &use_ref,
2390                 image_inner.image,
2391                 subresource_range.clone(),
2392                 access,
2393                 image_layout,
2394             );
2395         }
2396     }
2397 
2398     for (index, attachment_info) in (attachments.color_attachments.iter().enumerate())
2399         .filter_map(|(i, a)| a.as_ref().map(|a| (i as u32, a)))
2400     {
2401         let &RenderPassStateAttachmentInfo {
2402             ref image_view,
2403             image_layout,
2404             ..
2405         } = attachment_info;
2406 
2407         let image = image_view.image();
2408         let image_inner = image.inner();
2409         let mut subresource_range = image_view.subresource_range().clone();
2410         subresource_range.array_layers.start += image_inner.first_layer;
2411         subresource_range.array_layers.end += image_inner.first_layer;
2412         subresource_range.mip_levels.start += image_inner.first_mipmap_level;
2413         subresource_range.mip_levels.end += image_inner.first_mipmap_level;
2414 
2415         let use_ref = ResourceUseRef {
2416             command_index,
2417             command_name,
2418             resource_in_command: ResourceInCommand::ColorAttachment { index },
2419             secondary_use_ref: None,
2420         };
2421 
2422         // TODO: process only the color attachment indices that the fragment shader actually
2423         // writes to.
2424         // TODO: is it possible to only read a color attachment but not write it?
2425         resources_usage_state.record_image_access(
2426             &use_ref,
2427             image_inner.image,
2428             subresource_range,
2429             PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentWrite,
2430             image_layout,
2431         );
2432     }
2433 }
2434