vulkano/command_buffer/commands/
query.rs

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
10use 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
25/// # Commands related to queries.
26impl<L, A> AutoCommandBufferBuilder<L, A>
27where
28    A: CommandBufferAllocator,
29{
30    /// Begins a query.
31    ///
32    /// The query will be active until [`end_query`](Self::end_query) is called for the same query.
33    ///
34    /// # Safety
35    ///
36    /// The query must be unavailable, ensured by calling
37    /// [`reset_query_pool`](Self::reset_query_pool).
38    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        // VUID-vkCmdBeginQuery-None-00807
87        // Not checked, therefore unsafe.
88        // TODO: add check.
89
90        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    /// Ends an active query.
122    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    /// Writes a timestamp to a timestamp query.
194    ///
195    /// # Safety
196    ///
197    /// The query must be unavailable, ensured by calling
198    /// [`reset_query_pool`](Self::reset_query_pool).
199    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        // VUID-vkCmdWriteTimestamp2-queryPool-03862
235        // VUID-vkCmdWriteTimestamp2-None-03864
236        // Not checked, therefore unsafe.
237        // TODO: add check.
238
239        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    /// Copies the results of a range of queries to a buffer on the GPU.
261    ///
262    /// [`query_pool.ty().result_len()`] elements will be written for each query in the range, plus
263    /// 1 extra element per query if [`QueryResultFlags::WITH_AVAILABILITY`] is enabled.
264    /// The provided buffer must be large enough to hold the data.
265    ///
266    /// See also [`get_results`].
267    ///
268    /// [`query_pool.ty().result_len()`]: crate::query::QueryType::result_len
269    /// [`QueryResultFlags::WITH_AVAILABILITY`]: crate::query::QueryResultFlags::WITH_AVAILABILITY
270    /// [`get_results`]: crate::query::QueryPool::get_results
271    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(), // TODO:
330                    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    /// Resets a range of queries on a query pool.
349    ///
350    /// The affected queries will be marked as "unavailable" after this command runs, and will no
351    /// longer return any results. They will be ready to have new results recorded for them.
352    ///
353    /// # Safety
354    /// The queries in the specified range must not be active in another command buffer.
355    // TODO: Do other command buffers actually matter here? Not sure on the Vulkan spec.
356    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        // VUID-vkCmdBeginQuery-commonparent
462        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        // VUID-vkCmdEndQuery-commonparent
615        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        // VUID-vkCmdWriteTimestamp2-commonparent
692        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        // VUID-vkCmdCopyQueryPoolResults-commonparent
937        assert_eq!(device, destination.buffer().device());
938        assert_eq!(device, query_pool.device());
939
940        // VUID-vkCmdCopyQueryPoolResults-flags-00822
941        // VUID-vkCmdCopyQueryPoolResults-flags-00823
942        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        // VUID-vkCmdResetQueryPool-commonparent
1071        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}