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