1use crate::{
2 command_buffer::{
3 auto::{RenderPassStateType, Resource, ResourceUseRef2},
4 sys::{CommandBuffer, RecordingCommandBuffer},
5 AutoCommandBufferBuilder, CommandBufferInheritanceRenderPassType, CommandBufferLevel,
6 ResourceInCommand, SecondaryCommandBufferAbstract, SecondaryCommandBufferBufferUsage,
7 SecondaryCommandBufferImageUsage, SecondaryCommandBufferResourcesUsage, SubpassContents,
8 },
9 device::{DeviceOwned, QueueFlags},
10 query::QueryType,
11 Requires, RequiresAllOf, RequiresOneOf, SafeDeref, ValidationError, VulkanObject,
12};
13use smallvec::{smallvec, SmallVec};
14use std::{cmp::min, iter, sync::Arc};
15
16impl<L> AutoCommandBufferBuilder<L> {
21 pub fn execute_commands(
27 &mut self,
28 command_buffer: Arc<dyn SecondaryCommandBufferAbstract>,
29 ) -> Result<&mut Self, Box<ValidationError>> {
30 let command_buffer = DropUnlockCommandBuffer::new(command_buffer)?;
31 self.validate_execute_commands(iter::once(&**command_buffer))?;
32
33 Ok(unsafe { self.execute_commands_locked(smallvec![command_buffer]) })
34 }
35
36 pub fn execute_commands_from_vec(
43 &mut self,
44 command_buffers: Vec<Arc<dyn SecondaryCommandBufferAbstract>>,
45 ) -> Result<&mut Self, Box<ValidationError>> {
46 let command_buffers: SmallVec<[_; 4]> = command_buffers
47 .into_iter()
48 .map(DropUnlockCommandBuffer::new)
49 .collect::<Result<_, _>>()?;
50
51 self.validate_execute_commands(command_buffers.iter().map(|cb| &***cb))?;
52
53 Ok(unsafe { self.execute_commands_locked(command_buffers) })
54 }
55
56 fn validate_execute_commands<'a>(
57 &self,
58 command_buffers: impl Iterator<Item = &'a dyn SecondaryCommandBufferAbstract> + Clone,
59 ) -> Result<(), Box<ValidationError>> {
60 self.inner
61 .validate_execute_commands(command_buffers.clone().map(|cb| cb.as_raw()))?;
62
63 if let Some(render_pass_state) = &self.builder_state.render_pass {
64 if render_pass_state.contents != SubpassContents::SecondaryCommandBuffers {
65 return Err(Box::new(ValidationError {
66 problem: "a render pass instance is active, but its current subpass contents \
67 is `SubpassContents::SecondaryCommandBuffers`"
68 .into(),
69 vuids: &[
70 "VUID-vkCmdExecuteCommands-contents-06018",
71 "VUID-vkCmdExecuteCommands-flags-06024",
72 ],
73 ..Default::default()
74 }));
75 }
76 }
77
78 if !self.builder_state.queries.is_empty()
79 && !self.device().enabled_features().inherited_queries
80 {
81 return Err(Box::new(ValidationError {
82 problem: "a query is active".into(),
83 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceFeature(
84 "inherited_queries",
85 )])]),
86 vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-00101"],
87 ..Default::default()
88 }));
89 }
90
91 for (command_buffer_index, command_buffer) in command_buffers.enumerate() {
92 let inheritance_info = command_buffer.inheritance_info();
93
94 if let Some(render_pass_state) = &self.builder_state.render_pass {
95 let inheritance_render_pass =
96 inheritance_info.render_pass.as_ref().ok_or_else(|| {
97 Box::new(ValidationError {
98 problem: format!(
99 "a render pass instance is active, but \
100 `command_buffers[{}].inheritance_info().render_pass` is `None`",
101 command_buffer_index,
102 )
103 .into(),
104 vuids: &["VUID-vkCmdExecuteCommands-pCommandBuffers-00096"],
105 ..Default::default()
106 })
107 })?;
108
109 match (&render_pass_state.render_pass, inheritance_render_pass) {
110 (
111 RenderPassStateType::BeginRenderPass(state),
112 CommandBufferInheritanceRenderPassType::BeginRenderPass(inheritance_info),
113 ) => {
114 if !inheritance_info
115 .subpass
116 .render_pass()
117 .is_compatible_with(state.subpass.render_pass())
118 {
119 return Err(Box::new(ValidationError {
120 context: format!(
121 "command_buffers[{}].inheritance_info().render_pass\
122 .subpass.render_pass()",
123 command_buffer_index
124 )
125 .into(),
126 problem: "is not compatible with the current render pass instance"
127 .into(),
128 vuids: &["VUID-vkCmdExecuteCommands-pBeginInfo-06020"],
129 ..Default::default()
130 }));
131 }
132
133 if inheritance_info.subpass.index() != state.subpass.index() {
134 return Err(Box::new(ValidationError {
135 context: format!(
136 "command_buffers[{}].inheritance_info().render_pass\
137 .subpass.index()",
138 command_buffer_index
139 )
140 .into(),
141 problem: "is not equal to the index of the current \
142 subpass instance"
143 .into(),
144 vuids: &["VUID-vkCmdExecuteCommands-pCommandBuffers-06019"],
145 ..Default::default()
146 }));
147 }
148
149 if let Some(framebuffer) = &inheritance_info.framebuffer {
150 if framebuffer != state.framebuffer.as_ref().unwrap() {
151 return Err(Box::new(ValidationError {
152 context: format!(
153 "command_buffers[{}].inheritance_info().render_pass\
154 .framebuffer",
155 command_buffer_index
156 )
157 .into(),
158 problem: "is `Some`, but is not equal to the framebuffer of \
159 the current render pass instance"
160 .into(),
161 vuids: &["VUID-vkCmdExecuteCommands-pCommandBuffers-00099"],
162 ..Default::default()
163 }));
164 }
165 }
166 }
167 (
168 RenderPassStateType::BeginRendering(_),
169 CommandBufferInheritanceRenderPassType::BeginRendering(inheritance_info),
170 ) => {
171 let attachments = render_pass_state.attachments.as_ref().unwrap();
172
173 if inheritance_info.color_attachment_formats.len()
174 != attachments.color_attachments.len()
175 {
176 return Err(Box::new(ValidationError {
177 context: format!(
178 "command_buffers[{}].inheritance_info().render_pass\
179 .color_attachment_formats.len()",
180 command_buffer_index
181 )
182 .into(),
183 problem: "is not equal to the number of color attachments in the \
184 current subpass instance"
185 .into(),
186 vuids: &["VUID-vkCmdExecuteCommands-colorAttachmentCount-06027"],
187 ..Default::default()
188 }));
189 }
190
191 for (color_attachment_index, image_view, inherited_format) in attachments
192 .color_attachments
193 .iter()
194 .zip(inheritance_info.color_attachment_formats.iter().copied())
195 .enumerate()
196 .filter_map(|(i, (a, f))| {
197 a.as_ref().map(|a| (i as u32, &a.image_view, f))
198 })
199 {
200 let required_format = image_view.format();
201
202 if Some(required_format) != inherited_format {
203 return Err(Box::new(ValidationError {
204 context: format!(
205 "command_buffers[{}].inheritance_info().render_pass\
206 .color_attachment_formats[{}]",
207 command_buffer_index, color_attachment_index
208 )
209 .into(),
210 problem: "is not equal to the format of the \
211 corresponding color attachment in the current subpass \
212 instance"
213 .into(),
214 vuids: &["VUID-vkCmdExecuteCommands-imageView-06028"],
215 ..Default::default()
216 }));
217 }
218
219 if image_view.image().samples()
220 != inheritance_info.rasterization_samples
221 {
222 return Err(Box::new(ValidationError {
223 context: format!(
224 "command_buffers[{}].inheritance_info().render_pass\
225 .rasterization_samples",
226 command_buffer_index,
227 )
228 .into(),
229 problem: "is not equal to the number of samples of the \
230 attachments in the current subpass instance"
231 .into(),
232 vuids: &["VUID-vkCmdExecuteCommands-pNext-06035"],
233 ..Default::default()
234 }));
235 }
236 }
237
238 if let Some((image_view, format)) = attachments
239 .depth_attachment
240 .as_ref()
241 .map(|a| (&a.image_view, inheritance_info.depth_attachment_format))
242 {
243 if Some(image_view.format()) != format {
244 return Err(Box::new(ValidationError {
245 context: format!(
246 "command_buffers[{}].inheritance_info().render_pass\
247 .depth_attachment_format",
248 command_buffer_index
249 )
250 .into(),
251 problem: "is not equal to the format of the \
252 depth attachment in the current subpass instance"
253 .into(),
254 vuids: &["VUID-vkCmdExecuteCommands-pDepthAttachment-06029"],
255 ..Default::default()
256 }));
257 }
258
259 if image_view.image().samples()
260 != inheritance_info.rasterization_samples
261 {
262 return Err(Box::new(ValidationError {
263 context: format!(
264 "command_buffers[{}].inheritance_info().render_pass\
265 .rasterization_samples",
266 command_buffer_index,
267 )
268 .into(),
269 problem: "is not equal to the number of samples of the \
270 attachments in the current subpass instance"
271 .into(),
272 vuids: &["VUID-vkCmdExecuteCommands-pNext-06036"],
273 ..Default::default()
274 }));
275 }
276 }
277
278 if let Some((image_view, format)) = attachments
279 .stencil_attachment
280 .as_ref()
281 .map(|a| (&a.image_view, inheritance_info.stencil_attachment_format))
282 {
283 if Some(image_view.format()) != format {
284 return Err(Box::new(ValidationError {
285 context: format!(
286 "command_buffers[{}].inheritance_info().render_pass\
287 .stencil_attachment_format",
288 command_buffer_index
289 )
290 .into(),
291 problem: "is not equal to the format of the \
292 stencil attachment in the current subpass instance"
293 .into(),
294 vuids: &["VUID-vkCmdExecuteCommands-pStencilAttachment-06030"],
295 ..Default::default()
296 }));
297 }
298
299 if image_view.image().samples()
300 != inheritance_info.rasterization_samples
301 {
302 return Err(Box::new(ValidationError {
303 context: format!(
304 "command_buffers[{}].inheritance_info().render_pass\
305 .rasterization_samples",
306 command_buffer_index,
307 )
308 .into(),
309 problem: "is not equal to the number of samples of the \
310 attachments in the current subpass instance"
311 .into(),
312 vuids: &["VUID-vkCmdExecuteCommands-pNext-06037"],
313 ..Default::default()
314 }));
315 }
316 }
317
318 if inheritance_info.view_mask != render_pass_state.rendering_info.view_mask
319 {
320 return Err(Box::new(ValidationError {
321 context: format!(
322 "command_buffers[{}].inheritance_info().render_pass\
323 .view_mask",
324 command_buffer_index,
325 )
326 .into(),
327 problem: "is not equal to the `view_mask` of the current subpass \
328 instance"
329 .into(),
330 vuids: &["VUID-vkCmdExecuteCommands-viewMask-06031"],
331 ..Default::default()
332 }));
333 }
334 }
335 (
336 RenderPassStateType::BeginRenderPass(_),
337 CommandBufferInheritanceRenderPassType::BeginRendering(_),
338 ) => {
339 return Err(Box::new(ValidationError {
340 context: format!(
341 "command_buffers[{}].inheritance_info().render_pass",
342 command_buffer_index
343 )
344 .into(),
345 problem: "is `CommandBufferInheritanceRenderPassType::\
346 BeginRendering`, but the current render pass instance was begun \
347 with `begin_render_pass`"
348 .into(),
349 ..Default::default()
351 }));
352 }
353 (
354 RenderPassStateType::BeginRendering(_),
355 CommandBufferInheritanceRenderPassType::BeginRenderPass(_),
356 ) => {
357 return Err(Box::new(ValidationError {
358 context: format!(
359 "command_buffers[{}].inheritance_info().render_pass",
360 command_buffer_index
361 )
362 .into(),
363 problem: "is `CommandBufferInheritanceRenderPassType::\
364 BeginRenderPass`, but the current render pass instance was begun \
365 with `begin_rendering`"
366 .into(),
367 vuids: &["VUID-vkCmdExecuteCommands-pBeginInfo-06025"],
368 ..Default::default()
369 }));
370 }
371 }
372
373 } else {
379 if inheritance_info.render_pass.is_some() {
380 return Err(Box::new(ValidationError {
381 context: format!(
382 "command_buffers[{}].inheritance_info().render_pass",
383 command_buffer_index
384 )
385 .into(),
386 problem: "is `Some`, but a render pass instance is not active".into(),
387 vuids: &["VUID-vkCmdExecuteCommands-pCommandBuffers-00100"],
388 ..Default::default()
389 }));
390 }
391 }
392
393 for state in self.builder_state.queries.values() {
394 match state.query_pool.query_type() {
395 QueryType::Occlusion => {
396 let inherited_flags =
397 inheritance_info.occlusion_query.ok_or_else(|| {
398 Box::new(ValidationError {
399 context: format!(
400 "command_buffers[{}].inheritance_info().occlusion_query",
401 command_buffer_index
402 )
403 .into(),
404 problem:
405 "is `None`, but an occlusion query is currently active"
406 .into(),
407 vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-00102"],
408 ..Default::default()
409 })
410 })?;
411
412 if !inherited_flags.contains(state.flags) {
413 return Err(Box::new(ValidationError {
414 context: format!(
415 "command_buffers[{}].inheritance_info().occlusion_query",
416 command_buffer_index
417 )
418 .into(),
419 problem: "is not a superset of the flags of the active \
420 occlusion query"
421 .into(),
422 vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-00103"],
423 ..Default::default()
424 }));
425 }
426 }
427 QueryType::PipelineStatistics => {
428 let inherited_flags = inheritance_info.pipeline_statistics;
429
430 if !inherited_flags.contains(state.query_pool.pipeline_statistics()) {
431 return Err(Box::new(ValidationError {
432 context: format!(
433 "command_buffers[{}].inheritance_info().pipeline_statistics",
434 command_buffer_index
435 )
436 .into(),
437 problem: "is not a superset of the flags of the active \
438 `PipelineStatistics` query"
439 .into(),
440 vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-00104"],
441 ..Default::default()
442 }));
443 }
444 }
445 _ => (),
446 }
447 }
448 }
449
450 Ok(())
457 }
458
459 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
460 pub unsafe fn execute_commands_unchecked(
461 &mut self,
462 command_buffers: SmallVec<[Arc<dyn SecondaryCommandBufferAbstract>; 4]>,
463 ) -> &mut Self {
464 unsafe {
465 self.execute_commands_locked(
466 command_buffers
467 .into_iter()
468 .map(DropUnlockCommandBuffer::new)
469 .collect::<Result<_, _>>()
470 .unwrap(),
471 )
472 }
473 }
474
475 unsafe fn execute_commands_locked(
476 &mut self,
477 command_buffers: SmallVec<[DropUnlockCommandBuffer; 4]>,
478 ) -> &mut Self {
479 self.builder_state.reset_non_render_pass_states();
481
482 self.add_command(
483 "execute_commands",
484 command_buffers
485 .iter()
486 .enumerate()
487 .flat_map(|(index, command_buffer)| {
488 let index = index as u32;
489 let SecondaryCommandBufferResourcesUsage { buffers, images } =
490 command_buffer.resources_usage();
491
492 (buffers.iter().map(move |usage| {
493 let &SecondaryCommandBufferBufferUsage {
494 use_ref,
495 ref buffer,
496 ref range,
497 memory_access,
498 } = usage;
499
500 (
501 ResourceUseRef2 {
502 resource_in_command: ResourceInCommand::SecondaryCommandBuffer {
503 index,
504 },
505 secondary_use_ref: Some(use_ref.into()),
506 },
507 Resource::Buffer {
508 buffer: buffer.clone(),
509 range: range.clone(),
510 memory_access,
511 },
512 )
513 }))
514 .chain(images.iter().map(move |usage| {
515 let &SecondaryCommandBufferImageUsage {
516 use_ref,
517 ref image,
518 ref subresource_range,
519 memory_access,
520 start_layout,
521 end_layout,
522 } = usage;
523
524 (
525 ResourceUseRef2 {
526 resource_in_command: ResourceInCommand::SecondaryCommandBuffer {
527 index,
528 },
529 secondary_use_ref: Some(use_ref.into()),
530 },
531 Resource::Image {
532 image: image.clone(),
533 subresource_range: subresource_range.clone(),
534 memory_access,
535 start_layout,
536 end_layout,
537 },
538 )
539 }))
540 })
541 .collect(),
542 move |out: &mut RecordingCommandBuffer| {
543 unsafe { out.execute_commands_locked(&command_buffers) };
544 },
545 );
546
547 self
548 }
549}
550
551impl RecordingCommandBuffer {
552 #[inline]
553 pub unsafe fn execute_commands(
554 &mut self,
555 command_buffers: &[&CommandBuffer],
556 ) -> Result<&mut Self, Box<ValidationError>> {
557 self.validate_execute_commands(command_buffers.iter().copied())?;
558
559 Ok(unsafe { self.execute_commands_unchecked(command_buffers) })
560 }
561
562 fn validate_execute_commands<'a>(
563 &self,
564 command_buffers: impl Iterator<Item = &'a CommandBuffer>,
565 ) -> Result<(), Box<ValidationError>> {
566 if self.level() != CommandBufferLevel::Primary {
567 return Err(Box::new(ValidationError {
568 problem: "this command buffer is not a primary command buffer".into(),
569 vuids: &["VUID-vkCmdExecuteCommands-bufferlevel"],
570 ..Default::default()
571 }));
572 }
573
574 if !self
575 .queue_family_properties()
576 .queue_flags
577 .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
578 {
579 return Err(Box::new(ValidationError {
580 problem: "the queue family of the command buffer does not support \
581 transfer, graphics or compute operations"
582 .into(),
583 vuids: &["VUID-vkCmdExecuteCommands-commandBuffer-cmdpool"],
584 ..Default::default()
585 }));
586 }
587
588 for (command_buffer_index, command_buffer) in command_buffers.enumerate() {
589 assert_eq!(self.device(), command_buffer.device());
591
592 if command_buffer.level() != CommandBufferLevel::Secondary {
593 return Err(Box::new(ValidationError {
594 context: format!("command_buffers[{}]", command_buffer_index).into(),
595 problem: "is not a secondary command buffer".into(),
596 vuids: &["VUID-vkCmdExecuteCommands-pCommandBuffers-00088"],
597 ..Default::default()
598 }));
599 }
600
601 }
604
605 Ok(())
615 }
616
617 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
618 pub unsafe fn execute_commands_unchecked(
619 &mut self,
620 command_buffers: &[&CommandBuffer],
621 ) -> &mut Self {
622 if command_buffers.is_empty() {
623 return self;
624 }
625
626 let command_buffers_vk: SmallVec<[_; 4]> =
627 command_buffers.iter().map(|cb| cb.handle()).collect();
628
629 let fns = self.device().fns();
630 unsafe {
631 (fns.v1_0.cmd_execute_commands)(
632 self.handle(),
633 command_buffers_vk.len() as u32,
634 command_buffers_vk.as_ptr(),
635 )
636 };
637
638 self.usage = command_buffers
641 .iter()
642 .map(|cb| cb.usage())
643 .fold(self.usage, min);
644
645 self
646 }
647
648 unsafe fn execute_commands_locked(
649 &mut self,
650 command_buffers: &[DropUnlockCommandBuffer],
651 ) -> &mut Self {
652 if command_buffers.is_empty() {
653 return self;
654 }
655
656 let command_buffers_vk: SmallVec<[_; 4]> =
657 command_buffers.iter().map(|cb| cb.handle()).collect();
658
659 let fns = self.device().fns();
660 unsafe {
661 (fns.v1_0.cmd_execute_commands)(
662 self.handle(),
663 command_buffers_vk.len() as u32,
664 command_buffers_vk.as_ptr(),
665 )
666 };
667
668 self.usage = command_buffers
671 .iter()
672 .map(|cb| cb.usage())
673 .fold(self.usage, min);
674
675 self
676 }
677}
678
679struct DropUnlockCommandBuffer(Arc<dyn SecondaryCommandBufferAbstract>);
680
681impl DropUnlockCommandBuffer {
682 fn new(
683 command_buffer: Arc<dyn SecondaryCommandBufferAbstract>,
684 ) -> Result<Self, Box<ValidationError>> {
685 command_buffer.lock_record()?;
686 Ok(Self(command_buffer))
687 }
688}
689
690impl std::ops::Deref for DropUnlockCommandBuffer {
691 type Target = Arc<dyn SecondaryCommandBufferAbstract>;
692
693 fn deref(&self) -> &Self::Target {
694 &self.0
695 }
696}
697
698unsafe impl SafeDeref for DropUnlockCommandBuffer {}
699
700impl Drop for DropUnlockCommandBuffer {
701 fn drop(&mut self) {
702 unsafe {
703 self.unlock();
704 }
705 }
706}