1use crate::{
11 buffer::{BufferUsage, Subbuffer},
12 command_buffer::{
13 allocator::CommandBufferAllocator,
14 auto::{QueryState, Resource},
15 sys::UnsafeCommandBufferBuilder,
16 AutoCommandBufferBuilder, ResourceInCommand,
17 },
18 device::{DeviceOwned, QueueFlags},
19 query::{QueryControlFlags, QueryPool, QueryResultElement, QueryResultFlags, QueryType},
20 sync::{PipelineStage, PipelineStageAccessFlags, PipelineStages},
21 DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, Version, VulkanObject,
22};
23use std::{ops::Range, sync::Arc};
24
25impl<L, A> AutoCommandBufferBuilder<L, A>
27where
28 A: CommandBufferAllocator,
29{
30 pub unsafe fn begin_query(
39 &mut self,
40 query_pool: Arc<QueryPool>,
41 query: u32,
42 flags: QueryControlFlags,
43 ) -> Result<&mut Self, Box<ValidationError>> {
44 self.validate_begin_query(&query_pool, query, flags)?;
45
46 Ok(self.begin_query_unchecked(query_pool, query, flags))
47 }
48
49 fn validate_begin_query(
50 &self,
51 query_pool: &QueryPool,
52 query: u32,
53 flags: QueryControlFlags,
54 ) -> Result<(), Box<ValidationError>> {
55 self.inner.validate_begin_query(query_pool, query, flags)?;
56
57 if self
58 .builder_state
59 .queries
60 .contains_key(&query_pool.query_type().into())
61 {
62 return Err(Box::new(ValidationError {
63 problem: "a query with the same type as `query_pool.query_type()` is \
64 already active"
65 .into(),
66 vuids: &["VUID-vkCmdBeginQuery-queryPool-01922"],
67 ..Default::default()
68 }));
69 }
70
71 if let Some(render_pass_state) = &self.builder_state.render_pass {
72 if query + render_pass_state.rendering_info.view_mask.count_ones()
73 > query_pool.query_count()
74 {
75 return Err(Box::new(ValidationError {
76 problem: "a render subpass with a non-zero `view_mask` is active, but \
77 `query` + the number of views in `view_mask` is greater than \
78 `query_pool.query_count()`"
79 .into(),
80 vuids: &["VUID-vkCmdBeginQuery-query-00808"],
81 ..Default::default()
82 }));
83 }
84 }
85
86 Ok(())
91 }
92
93 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
94 pub unsafe fn begin_query_unchecked(
95 &mut self,
96 query_pool: Arc<QueryPool>,
97 query: u32,
98 flags: QueryControlFlags,
99 ) -> &mut Self {
100 self.builder_state.queries.insert(
101 query_pool.query_type().into(),
102 QueryState {
103 query_pool: query_pool.clone(),
104 query,
105 flags,
106 in_subpass: self.builder_state.render_pass.is_some(),
107 },
108 );
109
110 self.add_command(
111 "begin_query",
112 Default::default(),
113 move |out: &mut UnsafeCommandBufferBuilder<A>| {
114 out.begin_query_unchecked(&query_pool, query, flags);
115 },
116 );
117
118 self
119 }
120
121 pub fn end_query(
123 &mut self,
124 query_pool: Arc<QueryPool>,
125 query: u32,
126 ) -> Result<&mut Self, Box<ValidationError>> {
127 self.validate_end_query(&query_pool, query)?;
128
129 unsafe { Ok(self.end_query_unchecked(query_pool, query)) }
130 }
131
132 fn validate_end_query(
133 &self,
134 query_pool: &QueryPool,
135 query: u32,
136 ) -> Result<(), Box<ValidationError>> {
137 self.inner.validate_end_query(query_pool, query)?;
138
139 if !self
140 .builder_state
141 .queries
142 .get(&query_pool.query_type().into())
143 .map_or(false, |state| {
144 *state.query_pool == *query_pool && state.query == query
145 })
146 {
147 return Err(Box::new(ValidationError {
148 problem: "no query with the same type as `query_pool.query_type()` is active"
149 .into(),
150 vuids: &["VUID-vkCmdEndQuery-None-01923"],
151 ..Default::default()
152 }));
153 }
154
155 if let Some(render_pass_state) = &self.builder_state.render_pass {
156 if query + render_pass_state.rendering_info.view_mask.count_ones()
157 > query_pool.query_count()
158 {
159 return Err(Box::new(ValidationError {
160 problem: "a render subpass with a non-zero `view_mask` is active, but \
161 `query` + the number of views in `view_mask` is greater than \
162 `query_pool.query_count()`"
163 .into(),
164 vuids: &["VUID-vkCmdEndQuery-query-00812"],
165 ..Default::default()
166 }));
167 }
168 }
169
170 Ok(())
171 }
172
173 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
174 pub unsafe fn end_query_unchecked(
175 &mut self,
176 query_pool: Arc<QueryPool>,
177 query: u32,
178 ) -> &mut Self {
179 let raw_ty = query_pool.query_type().into();
180 self.builder_state.queries.remove(&raw_ty);
181
182 self.add_command(
183 "end_query",
184 Default::default(),
185 move |out: &mut UnsafeCommandBufferBuilder<A>| {
186 out.end_query_unchecked(&query_pool, query);
187 },
188 );
189
190 self
191 }
192
193 pub unsafe fn write_timestamp(
200 &mut self,
201 query_pool: Arc<QueryPool>,
202 query: u32,
203 stage: PipelineStage,
204 ) -> Result<&mut Self, Box<ValidationError>> {
205 self.validate_write_timestamp(&query_pool, query, stage)?;
206
207 Ok(self.write_timestamp_unchecked(query_pool, query, stage))
208 }
209
210 fn validate_write_timestamp(
211 &self,
212 query_pool: &QueryPool,
213 query: u32,
214 stage: PipelineStage,
215 ) -> Result<(), Box<ValidationError>> {
216 self.inner
217 .validate_write_timestamp(query_pool, query, stage)?;
218
219 if let Some(render_pass_state) = &self.builder_state.render_pass {
220 if query + render_pass_state.rendering_info.view_mask.count_ones()
221 > query_pool.query_count()
222 {
223 return Err(Box::new(ValidationError {
224 problem: "a render subpass with a non-zero `view_mask` is active, but \
225 `query` + the number of views in `view_mask` is greater than \
226 `query_pool.query_count()`"
227 .into(),
228 vuids: &["VUID-vkCmdWriteTimestamp2-query-03865"],
229 ..Default::default()
230 }));
231 }
232 }
233
234 Ok(())
240 }
241
242 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
243 pub unsafe fn write_timestamp_unchecked(
244 &mut self,
245 query_pool: Arc<QueryPool>,
246 query: u32,
247 stage: PipelineStage,
248 ) -> &mut Self {
249 self.add_command(
250 "write_timestamp",
251 Default::default(),
252 move |out: &mut UnsafeCommandBufferBuilder<A>| {
253 out.write_timestamp_unchecked(&query_pool, query, stage);
254 },
255 );
256
257 self
258 }
259
260 pub fn copy_query_pool_results<T>(
272 &mut self,
273 query_pool: Arc<QueryPool>,
274 queries: Range<u32>,
275 destination: Subbuffer<[T]>,
276 flags: QueryResultFlags,
277 ) -> Result<&mut Self, Box<ValidationError>>
278 where
279 T: QueryResultElement,
280 {
281 self.validate_copy_query_pool_results(&query_pool, queries.clone(), &destination, flags)?;
282
283 unsafe {
284 Ok(self.copy_query_pool_results_unchecked(query_pool, queries, destination, flags))
285 }
286 }
287
288 fn validate_copy_query_pool_results<T>(
289 &self,
290 query_pool: &QueryPool,
291 queries: Range<u32>,
292 destination: &Subbuffer<[T]>,
293 flags: QueryResultFlags,
294 ) -> Result<(), Box<ValidationError>>
295 where
296 T: QueryResultElement,
297 {
298 self.inner
299 .validate_copy_query_pool_results(query_pool, queries, destination, flags)?;
300
301 if self.builder_state.render_pass.is_some() {
302 return Err(Box::new(ValidationError {
303 problem: "a render pass instance is active".into(),
304 vuids: &["VUID-vkCmdCopyQueryPoolResults-renderpass"],
305 ..Default::default()
306 }));
307 }
308
309 Ok(())
310 }
311
312 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
313 pub unsafe fn copy_query_pool_results_unchecked<T>(
314 &mut self,
315 query_pool: Arc<QueryPool>,
316 queries: Range<u32>,
317 destination: Subbuffer<[T]>,
318 flags: QueryResultFlags,
319 ) -> &mut Self
320 where
321 T: QueryResultElement,
322 {
323 self.add_command(
324 "copy_query_pool_results",
325 [(
326 ResourceInCommand::Destination.into(),
327 Resource::Buffer {
328 buffer: destination.as_bytes().clone(),
329 range: 0..destination.size(), memory_access: PipelineStageAccessFlags::Copy_TransferWrite,
331 },
332 )]
333 .into_iter()
334 .collect(),
335 move |out: &mut UnsafeCommandBufferBuilder<A>| {
336 out.copy_query_pool_results_unchecked(
337 &query_pool,
338 queries.clone(),
339 &destination,
340 flags,
341 );
342 },
343 );
344
345 self
346 }
347
348 pub unsafe fn reset_query_pool(
357 &mut self,
358 query_pool: Arc<QueryPool>,
359 queries: Range<u32>,
360 ) -> Result<&mut Self, Box<ValidationError>> {
361 self.validate_reset_query_pool(&query_pool, queries.clone())?;
362
363 Ok(self.reset_query_pool_unchecked(query_pool, queries))
364 }
365
366 fn validate_reset_query_pool(
367 &self,
368 query_pool: &QueryPool,
369 queries: Range<u32>,
370 ) -> Result<(), Box<ValidationError>> {
371 self.inner
372 .validate_reset_query_pool(query_pool, queries.clone())?;
373
374 if self.builder_state.render_pass.is_some() {
375 return Err(Box::new(ValidationError {
376 problem: "a render pass instance is active".into(),
377 vuids: &["VUID-vkCmdResetQueryPool-renderpass"],
378 ..Default::default()
379 }));
380 }
381
382 if self
383 .builder_state
384 .queries
385 .values()
386 .any(|state| *state.query_pool == *query_pool && queries.contains(&state.query))
387 {
388 return Err(Box::new(ValidationError {
389 problem: "one of the `queries` in `query_pool` is currently active".into(),
390 vuids: &["VUID-vkCmdResetQueryPool-None-02841"],
391 ..Default::default()
392 }));
393 }
394
395 Ok(())
396 }
397
398 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
399 pub unsafe fn reset_query_pool_unchecked(
400 &mut self,
401 query_pool: Arc<QueryPool>,
402 queries: Range<u32>,
403 ) -> &mut Self {
404 self.add_command(
405 "reset_query_pool",
406 Default::default(),
407 move |out: &mut UnsafeCommandBufferBuilder<A>| {
408 out.reset_query_pool_unchecked(&query_pool, queries.clone());
409 },
410 );
411
412 self
413 }
414}
415
416impl<A> UnsafeCommandBufferBuilder<A>
417where
418 A: CommandBufferAllocator,
419{
420 pub unsafe fn begin_query(
421 &mut self,
422 query_pool: &QueryPool,
423 query: u32,
424 flags: QueryControlFlags,
425 ) -> Result<&mut Self, Box<ValidationError>> {
426 self.validate_begin_query(query_pool, query, flags)?;
427
428 Ok(self.begin_query_unchecked(query_pool, query, flags))
429 }
430
431 fn validate_begin_query(
432 &self,
433 query_pool: &QueryPool,
434 query: u32,
435 flags: QueryControlFlags,
436 ) -> Result<(), Box<ValidationError>> {
437 let queue_family_properties = self.queue_family_properties();
438
439 if !queue_family_properties.queue_flags.intersects(
440 QueueFlags::GRAPHICS
441 | QueueFlags::COMPUTE
442 | QueueFlags::VIDEO_DECODE
443 | QueueFlags::VIDEO_ENCODE,
444 ) {
445 return Err(Box::new(ValidationError {
446 problem: "the queue family of the command buffer does not support \
447 graphics, compute, video decode or video encode operations"
448 .into(),
449 vuids: &["VUID-vkCmdBeginQuery-commandBuffer-cmdpool"],
450 ..Default::default()
451 }));
452 }
453
454 let device = self.device();
455
456 flags.validate_device(device).map_err(|err| {
457 err.add_context("flags")
458 .set_vuids(&["VUID-vkCmdBeginQuery-flags-parameter"])
459 })?;
460
461 assert_eq!(device, query_pool.device());
463
464 if query > query_pool.query_count() {
465 return Err(Box::new(ValidationError {
466 problem: "`query` is greater than `query_pool.query_count()`".into(),
467 vuids: &["VUID-vkCmdBeginQuery-query-00802"],
468 ..Default::default()
469 }));
470 }
471
472 match query_pool.query_type() {
473 QueryType::Occlusion => {
474 if !queue_family_properties
475 .queue_flags
476 .intersects(QueueFlags::GRAPHICS)
477 {
478 return Err(Box::new(ValidationError {
479 problem: "`query_pool.query_type()` is `QueryType::Occlusion`, but \
480 the queue family of the command buffer does not support \
481 graphics operations"
482 .into(),
483 vuids: &["VUID-vkCmdBeginQuery-queryType-00803"],
484 ..Default::default()
485 }));
486 }
487 }
488 QueryType::PipelineStatistics(statistic_flags) => {
489 if statistic_flags.is_graphics()
490 && !queue_family_properties
491 .queue_flags
492 .intersects(QueueFlags::GRAPHICS)
493 {
494 return Err(Box::new(ValidationError {
495 context: "query_pool.query_type()".into(),
496 problem: "is `QueryType::PipelineStatistics`, and the \
497 pipeline statistics flags include a graphics flag, but \
498 the queue family of the command buffer does not support \
499 graphics operations"
500 .into(),
501 vuids: &["VUID-vkCmdBeginQuery-queryType-00804"],
502 ..Default::default()
503 }));
504 }
505
506 if statistic_flags.is_compute()
507 && !queue_family_properties
508 .queue_flags
509 .intersects(QueueFlags::COMPUTE)
510 {
511 return Err(Box::new(ValidationError {
512 context: "query_pool.query_type()".into(),
513 problem: "is `QueryType::PipelineStatistics`, and the \
514 pipeline statistics flags include a compute flag, but \
515 the queue family of the command buffer does not support \
516 compute operations"
517 .into(),
518 vuids: &["VUID-vkCmdBeginQuery-queryType-00805"],
519 ..Default::default()
520 }));
521 }
522 }
523 QueryType::Timestamp
524 | QueryType::AccelerationStructureCompactedSize
525 | QueryType::AccelerationStructureSerializationSize
526 | QueryType::AccelerationStructureSerializationBottomLevelPointers
527 | QueryType::AccelerationStructureSize => {
528 return Err(Box::new(ValidationError {
529 context: "query_pool.query_type()".into(),
530 problem: "is not allowed for this command".into(),
531 vuids: &[
532 "VUID-vkCmdBeginQuery-queryType-02804",
533 "VUID-vkCmdBeginQuery-queryType-04728",
534 "VUID-vkCmdBeginQuery-queryType-06741",
535 ],
536 ..Default::default()
537 }));
538 }
539 }
540
541 if flags.intersects(QueryControlFlags::PRECISE) {
542 if !device.enabled_features().occlusion_query_precise {
543 return Err(Box::new(ValidationError {
544 context: "flags".into(),
545 problem: "contains `QueryControlFlags::PRECISE`".into(),
546 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
547 "occlusion_query_precise",
548 )])]),
549 vuids: &["VUID-vkCmdBeginQuery-queryType-00800"],
550 }));
551 }
552
553 if !matches!(query_pool.query_type(), QueryType::Occlusion) {
554 return Err(Box::new(ValidationError {
555 problem: "`flags` contains `QueryControlFlags::PRECISE`, but \
556 `query_pool.query_type()` is not `QueryType::Occlusion`"
557 .into(),
558 vuids: &["VUID-vkCmdBeginQuery-queryType-00800"],
559 ..Default::default()
560 }));
561 }
562 }
563
564 Ok(())
565 }
566
567 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
568 pub unsafe fn begin_query_unchecked(
569 &mut self,
570 query_pool: &QueryPool,
571 query: u32,
572 flags: QueryControlFlags,
573 ) -> &mut Self {
574 let fns = self.device().fns();
575 (fns.v1_0.cmd_begin_query)(self.handle(), query_pool.handle(), query, flags.into());
576
577 self
578 }
579
580 pub unsafe fn end_query(
581 &mut self,
582 query_pool: &QueryPool,
583 query: u32,
584 ) -> Result<&mut Self, Box<ValidationError>> {
585 self.validate_end_query(query_pool, query)?;
586
587 Ok(self.end_query_unchecked(query_pool, query))
588 }
589
590 fn validate_end_query(
591 &self,
592 query_pool: &QueryPool,
593 query: u32,
594 ) -> Result<(), Box<ValidationError>> {
595 let queue_family_properties = self.queue_family_properties();
596
597 if !queue_family_properties.queue_flags.intersects(
598 QueueFlags::GRAPHICS
599 | QueueFlags::COMPUTE
600 | QueueFlags::VIDEO_DECODE
601 | QueueFlags::VIDEO_ENCODE,
602 ) {
603 return Err(Box::new(ValidationError {
604 problem: "the queue family of the command buffer does not support \
605 graphics, compute, video decode or video encode operations"
606 .into(),
607 vuids: &["VUID-vkCmdEndQuery-commandBuffer-cmdpool"],
608 ..Default::default()
609 }));
610 }
611
612 let device = self.device();
613
614 assert_eq!(device, query_pool.device());
616
617 if query > query_pool.query_count() {
618 return Err(Box::new(ValidationError {
619 problem: "`query` is greater than `query_pool.query_count()`".into(),
620 vuids: &["VUID-vkCmdEndQuery-query-00810"],
621 ..Default::default()
622 }));
623 }
624
625 Ok(())
626 }
627
628 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
629 pub unsafe fn end_query_unchecked(&mut self, query_pool: &QueryPool, query: u32) -> &mut Self {
630 let fns = self.device().fns();
631 (fns.v1_0.cmd_end_query)(self.handle(), query_pool.handle(), query);
632
633 self
634 }
635
636 pub unsafe fn write_timestamp(
637 &mut self,
638 query_pool: &QueryPool,
639 query: u32,
640 stage: PipelineStage,
641 ) -> Result<&mut Self, Box<ValidationError>> {
642 self.validate_write_timestamp(query_pool, query, stage)?;
643
644 Ok(self.write_timestamp_unchecked(query_pool, query, stage))
645 }
646
647 fn validate_write_timestamp(
648 &self,
649 query_pool: &QueryPool,
650 query: u32,
651 stage: PipelineStage,
652 ) -> Result<(), Box<ValidationError>> {
653 let queue_family_properties = self.queue_family_properties();
654
655 if !queue_family_properties.queue_flags.intersects(
656 QueueFlags::TRANSFER
657 | QueueFlags::GRAPHICS
658 | QueueFlags::COMPUTE
659 | QueueFlags::VIDEO_DECODE
660 | QueueFlags::VIDEO_ENCODE,
661 ) {
662 return Err(Box::new(ValidationError {
663 problem: "the queue family of the command buffer does not support \
664 transfer, graphics, compute, video decode or video encode operations"
665 .into(),
666 vuids: &["VUID-vkCmdWriteTimestamp2-commandBuffer-cmdpool"],
667 ..Default::default()
668 }));
669 }
670
671 let device = self.device();
672
673 stage.validate_device(device).map_err(|err| {
674 err.add_context("stage")
675 .set_vuids(&["VUID-vkCmdWriteTimestamp2-stage-parameter"])
676 })?;
677
678 if !device.enabled_features().synchronization2
679 && PipelineStages::from(stage).contains_flags2()
680 {
681 return Err(Box::new(ValidationError {
682 context: "stage".into(),
683 problem: "is a stage flag from `VkPipelineStageFlagBits2`".into(),
684 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
685 "synchronization2",
686 )])]),
687 ..Default::default()
688 }));
689 }
690
691 assert_eq!(device, query_pool.device());
693
694 if !PipelineStages::from(queue_family_properties.queue_flags).contains_enum(stage) {
695 return Err(Box::new(ValidationError {
696 context: "stage".into(),
697 problem: "is not supported by the queue family of the command buffer".into(),
698 vuids: &["VUID-vkCmdWriteTimestamp2-stage-03860"],
699 ..Default::default()
700 }));
701 }
702
703 match stage {
704 PipelineStage::GeometryShader => {
705 if !device.enabled_features().geometry_shader {
706 return Err(Box::new(ValidationError {
707 context: "stage".into(),
708 problem: "is `PipelineStage::GeometryShader`".into(),
709 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
710 "geometry_shadere",
711 )])]),
712 vuids: &["VUID-vkCmdWriteTimestamp2-stage-03929"],
713 }));
714 }
715 }
716 PipelineStage::TessellationControlShader
717 | PipelineStage::TessellationEvaluationShader => {
718 if !device.enabled_features().tessellation_shader {
719 return Err(Box::new(ValidationError {
720 context: "stage".into(),
721 problem: "is `PipelineStage::TessellationControlShader` or \
722 `PipelineStage::TessellationEvaluationShader`"
723 .into(),
724 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
725 "tessellation_shader",
726 )])]),
727 vuids: &["VUID-vkCmdWriteTimestamp2-stage-03930"],
728 }));
729 }
730 }
731 PipelineStage::ConditionalRendering => {
732 if !device.enabled_features().conditional_rendering {
733 return Err(Box::new(ValidationError {
734 context: "stage".into(),
735 problem: "is `PipelineStage::ConditionalRendering`".into(),
736 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
737 "conditional_rendering",
738 )])]),
739 vuids: &["VUID-vkCmdWriteTimestamp2-stage-03931"],
740 }));
741 }
742 }
743 PipelineStage::FragmentDensityProcess => {
744 if !device.enabled_features().fragment_density_map {
745 return Err(Box::new(ValidationError {
746 context: "stage".into(),
747 problem: "is `PipelineStage::FragmentDensityProcess`".into(),
748 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
749 "fragment_density_map",
750 )])]),
751 vuids: &["VUID-vkCmdWriteTimestamp2-stage-03932"],
752 }));
753 }
754 }
755 PipelineStage::TransformFeedback => {
756 if !device.enabled_features().transform_feedback {
757 return Err(Box::new(ValidationError {
758 context: "stage".into(),
759 problem: "is `PipelineStage::TransformFeedback`".into(),
760 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
761 "transform_feedback",
762 )])]),
763 vuids: &["VUID-vkCmdWriteTimestamp2-stage-03933"],
764 }));
765 }
766 }
767 PipelineStage::MeshShader => {
768 if !device.enabled_features().mesh_shader {
769 return Err(Box::new(ValidationError {
770 context: "stage".into(),
771 problem: "is `PipelineStage::MeshShader`".into(),
772 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
773 "mesh_shader",
774 )])]),
775 vuids: &["VUID-vkCmdWriteTimestamp2-stage-03934"],
776 }));
777 }
778 }
779 PipelineStage::TaskShader => {
780 if !device.enabled_features().task_shader {
781 return Err(Box::new(ValidationError {
782 context: "stage".into(),
783 problem: "is `PipelineStage::TaskShader`".into(),
784 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
785 "task_shader",
786 )])]),
787 vuids: &["VUID-vkCmdWriteTimestamp2-stage-03935"],
788 }));
789 }
790 }
791 PipelineStage::FragmentShadingRateAttachment => {
792 if !(device.enabled_features().attachment_fragment_shading_rate
793 || device.enabled_features().shading_rate_image)
794 {
795 return Err(Box::new(ValidationError {
796 context: "stage".into(),
797 problem: "is `PipelineStage::FragmentShadingRateAttachment`".into(),
798 requires_one_of: RequiresOneOf(&[
799 RequiresAllOf(&[Requires::Feature("attachment_fragment_shading_rate")]),
800 RequiresAllOf(&[Requires::Feature("shading_rate_image")]),
801 ]),
802 vuids: &["VUID-vkCmdWriteTimestamp2-shadingRateImage-07316"],
803 }));
804 }
805 }
806 PipelineStage::SubpassShading => {
807 if !device.enabled_features().subpass_shading {
808 return Err(Box::new(ValidationError {
809 context: "stage".into(),
810 problem: "is `PipelineStage::SubpassShading`".into(),
811 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
812 "subpass_shading",
813 )])]),
814 vuids: &["VUID-vkCmdWriteTimestamp2-stage-04957"],
815 }));
816 }
817 }
818 PipelineStage::InvocationMask => {
819 if !device.enabled_features().invocation_mask {
820 return Err(Box::new(ValidationError {
821 context: "stage".into(),
822 problem: "is `PipelineStage::InvocationMask`".into(),
823 requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
824 "invocation_mask",
825 )])]),
826 vuids: &["VUID-vkCmdWriteTimestamp2-stage-04995"],
827 }));
828 }
829 }
830 _ => (),
831 }
832
833 if !matches!(query_pool.query_type(), QueryType::Timestamp) {
834 return Err(Box::new(ValidationError {
835 context: "query_pool.query_type()".into(),
836 problem: "is not `QueryType::Timestamp`".into(),
837 vuids: &["VUID-vkCmdWriteTimestamp2-queryPool-03861"],
838 ..Default::default()
839 }));
840 }
841
842 if queue_family_properties.timestamp_valid_bits.is_none() {
843 return Err(Box::new(ValidationError {
844 problem: "the `timestamp_valid_bits` value of the queue family properties of \
845 the command buffer is `None`"
846 .into(),
847 vuids: &["VUID-vkCmdWriteTimestamp2-timestampValidBits-03863"],
848 ..Default::default()
849 }));
850 }
851
852 if query > query_pool.query_count() {
853 return Err(Box::new(ValidationError {
854 problem: "`query` is greater than `query_pool.query_count()`".into(),
855 vuids: &["VUID-vkCmdWriteTimestamp2-query-04903"],
856 ..Default::default()
857 }));
858 }
859
860 Ok(())
861 }
862
863 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
864 pub unsafe fn write_timestamp_unchecked(
865 &mut self,
866 query_pool: &QueryPool,
867 query: u32,
868 stage: PipelineStage,
869 ) -> &mut Self {
870 let fns = self.device().fns();
871
872 if self.device().enabled_features().synchronization2 {
873 if self.device().api_version() >= Version::V1_3 {
874 (fns.v1_3.cmd_write_timestamp2)(
875 self.handle(),
876 stage.into(),
877 query_pool.handle(),
878 query,
879 );
880 } else {
881 (fns.khr_synchronization2.cmd_write_timestamp2_khr)(
882 self.handle(),
883 stage.into(),
884 query_pool.handle(),
885 query,
886 );
887 }
888 } else {
889 (fns.v1_0.cmd_write_timestamp)(self.handle(), stage.into(), query_pool.handle(), query);
890 }
891
892 self
893 }
894
895 pub unsafe fn copy_query_pool_results<T>(
896 &mut self,
897 query_pool: &QueryPool,
898 queries: Range<u32>,
899 destination: &Subbuffer<[T]>,
900 flags: QueryResultFlags,
901 ) -> Result<&mut Self, Box<ValidationError>>
902 where
903 T: QueryResultElement,
904 {
905 self.validate_copy_query_pool_results(query_pool, queries.clone(), destination, flags)?;
906
907 Ok(self.copy_query_pool_results_unchecked(query_pool, queries, destination, flags))
908 }
909
910 fn validate_copy_query_pool_results<T>(
911 &self,
912 query_pool: &QueryPool,
913 queries: Range<u32>,
914 destination: &Subbuffer<[T]>,
915 flags: QueryResultFlags,
916 ) -> Result<(), Box<ValidationError>>
917 where
918 T: QueryResultElement,
919 {
920 if !self
921 .queue_family_properties()
922 .queue_flags
923 .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
924 {
925 return Err(Box::new(ValidationError {
926 problem: "the queue family of the command buffer does not support \
927 graphics or compute operations"
928 .into(),
929 vuids: &["VUID-vkCmdCopyQueryPoolResults-commandBuffer-cmdpool"],
930 ..Default::default()
931 }));
932 }
933
934 let device = self.device();
935
936 assert_eq!(device, destination.buffer().device());
938 assert_eq!(device, query_pool.device());
939
940 debug_assert!(destination.offset() % std::mem::size_of::<T>() as DeviceSize == 0);
943
944 if queries.end < queries.start {
945 return Err(Box::new(ValidationError {
946 context: "queries".into(),
947 problem: "`end` is less than `start`".into(),
948 ..Default::default()
949 }));
950 }
951
952 if queries.end > query_pool.query_count() {
953 return Err(Box::new(ValidationError {
954 problem: "`queries.end` is greater than `query_pool.query_count()`".into(),
955 vuids: &[
956 "VUID-vkCmdCopyQueryPoolResults-firstQuery-00820",
957 "VUID-vkCmdCopyQueryPoolResults-firstQuery-00821",
958 ],
959 ..Default::default()
960 }));
961 }
962
963 let count = queries.end - queries.start;
964 let per_query_len = query_pool.query_type().result_len()
965 + flags.intersects(QueryResultFlags::WITH_AVAILABILITY) as DeviceSize;
966 let required_len = per_query_len * count as DeviceSize;
967
968 if destination.len() < required_len {
969 return Err(Box::new(ValidationError {
970 problem: "`destination` is smaller than the size required to write the results"
971 .into(),
972 vuids: &["VUID-vkCmdCopyQueryPoolResults-dstBuffer-00824"],
973 ..Default::default()
974 }));
975 }
976
977 if !destination
978 .buffer()
979 .usage()
980 .intersects(BufferUsage::TRANSFER_DST)
981 {
982 return Err(Box::new(ValidationError {
983 context: "destination.usage()".into(),
984 problem: "does not contain `BufferUsage::TRANSFER_DST`".into(),
985 vuids: &["VUID-vkCmdCopyQueryPoolResults-dstBuffer-00825"],
986 ..Default::default()
987 }));
988 }
989
990 if matches!(query_pool.query_type(), QueryType::Timestamp)
991 && flags.intersects(QueryResultFlags::PARTIAL)
992 {
993 return Err(Box::new(ValidationError {
994 problem: "`query_pool.query_type()` is `QueryType::Timestamp`, but \
995 `flags` contains `QueryResultFlags::PARTIAL`"
996 .into(),
997 vuids: &["VUID-vkCmdCopyQueryPoolResults-queryType-00827"],
998 ..Default::default()
999 }));
1000 }
1001
1002 Ok(())
1003 }
1004
1005 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1006 pub unsafe fn copy_query_pool_results_unchecked<T>(
1007 &mut self,
1008 query_pool: &QueryPool,
1009 queries: Range<u32>,
1010 destination: &Subbuffer<[T]>,
1011 flags: QueryResultFlags,
1012 ) -> &mut Self
1013 where
1014 T: QueryResultElement,
1015 {
1016 let per_query_len = query_pool.query_type().result_len()
1017 + flags.intersects(QueryResultFlags::WITH_AVAILABILITY) as DeviceSize;
1018 let stride = per_query_len * std::mem::size_of::<T>() as DeviceSize;
1019
1020 let fns = self.device().fns();
1021 (fns.v1_0.cmd_copy_query_pool_results)(
1022 self.handle(),
1023 query_pool.handle(),
1024 queries.start,
1025 queries.end - queries.start,
1026 destination.buffer().handle(),
1027 destination.offset(),
1028 stride,
1029 ash::vk::QueryResultFlags::from(flags) | T::FLAG,
1030 );
1031
1032 self
1033 }
1034
1035 pub unsafe fn reset_query_pool(
1036 &mut self,
1037 query_pool: &QueryPool,
1038 queries: Range<u32>,
1039 ) -> Result<&mut Self, Box<ValidationError>> {
1040 self.validate_reset_query_pool(query_pool, queries.clone())?;
1041
1042 Ok(self.reset_query_pool_unchecked(query_pool, queries))
1043 }
1044
1045 fn validate_reset_query_pool(
1046 &self,
1047 query_pool: &QueryPool,
1048 queries: Range<u32>,
1049 ) -> Result<(), Box<ValidationError>> {
1050 let queue_family_properties = self.queue_family_properties();
1051
1052 if !queue_family_properties.queue_flags.intersects(
1053 QueueFlags::GRAPHICS
1054 | QueueFlags::COMPUTE
1055 | QueueFlags::VIDEO_DECODE
1056 | QueueFlags::VIDEO_ENCODE
1057 | QueueFlags::OPTICAL_FLOW,
1058 ) {
1059 return Err(Box::new(ValidationError {
1060 problem: "the queue family of the command buffer does not support \
1061 graphics, compute, video decode, video encode or optical flow operations"
1062 .into(),
1063 vuids: &["VUID-vkCmdResetQueryPool-commandBuffer-cmdpool"],
1064 ..Default::default()
1065 }));
1066 }
1067
1068 let device = self.device();
1069
1070 assert_eq!(device, query_pool.device());
1072
1073 if queries.end < queries.start {
1074 return Err(Box::new(ValidationError {
1075 context: "queries".into(),
1076 problem: "`end` is less than `start`".into(),
1077 ..Default::default()
1078 }));
1079 }
1080
1081 if queries.end > query_pool.query_count() {
1082 return Err(Box::new(ValidationError {
1083 problem: "`queries.end` is greater than `query_pool.query_count()`".into(),
1084 vuids: &[
1085 "VUID-vkCmdResetQueryPool-firstQuery-00796",
1086 "VUID-vkCmdResetQueryPool-firstQuery-00797",
1087 ],
1088 ..Default::default()
1089 }));
1090 }
1091
1092 Ok(())
1093 }
1094
1095 #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1096 pub unsafe fn reset_query_pool_unchecked(
1097 &mut self,
1098 query_pool: &QueryPool,
1099 queries: Range<u32>,
1100 ) -> &mut Self {
1101 let fns = self.device().fns();
1102 (fns.v1_0.cmd_reset_query_pool)(
1103 self.handle(),
1104 query_pool.handle(),
1105 queries.start,
1106 queries.end - queries.start,
1107 );
1108
1109 self
1110 }
1111}