1 // Copyright (c) 2016 The vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9 
10 //! Gather information about rendering, held in query pools.
11 //!
12 //! In Vulkan, queries are not created individually. Instead you manipulate **query pools**, which
13 //! represent a collection of queries. Whenever you use a query, you have to specify both the query
14 //! pool and the slot id within that query pool.
15 
16 use crate::{
17     buffer::BufferContents,
18     device::{Device, DeviceOwned},
19     macros::{impl_id_counter, vulkan_bitflags},
20     DeviceSize, OomError, RequirementNotMet, RequiresOneOf, VulkanError, VulkanObject,
21 };
22 use std::{
23     error::Error,
24     ffi::c_void,
25     fmt::{Display, Error as FmtError, Formatter},
26     mem::{size_of_val, MaybeUninit},
27     num::NonZeroU64,
28     ops::Range,
29     ptr,
30     sync::Arc,
31 };
32 
33 /// A collection of one or more queries of a particular type.
34 #[derive(Debug)]
35 pub struct QueryPool {
36     handle: ash::vk::QueryPool,
37     device: Arc<Device>,
38     id: NonZeroU64,
39 
40     query_type: QueryType,
41     query_count: u32,
42 }
43 
44 impl QueryPool {
45     /// Creates a new `QueryPool`.
46     ///
47     /// # Panics
48     ///
49     /// - Panics if `create_info.query_count` is `0`.
new( device: Arc<Device>, create_info: QueryPoolCreateInfo, ) -> Result<Arc<QueryPool>, QueryPoolCreationError>50     pub fn new(
51         device: Arc<Device>,
52         create_info: QueryPoolCreateInfo,
53     ) -> Result<Arc<QueryPool>, QueryPoolCreationError> {
54         let QueryPoolCreateInfo {
55             query_type,
56             query_count,
57             _ne: _,
58         } = create_info;
59 
60         // VUID-VkQueryPoolCreateInfo-queryCount-02763
61         assert!(query_count != 0);
62 
63         let pipeline_statistics = match query_type {
64             QueryType::PipelineStatistics(flags) => {
65                 // VUID-VkQueryPoolCreateInfo-queryType-00791
66                 if !device.enabled_features().pipeline_statistics_query {
67                     return Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled);
68                 }
69 
70                 // VUID-VkQueryPoolCreateInfo-queryType-00792
71                 flags.into()
72             }
73             QueryType::Occlusion | QueryType::Timestamp => {
74                 ash::vk::QueryPipelineStatisticFlags::empty()
75             }
76         };
77 
78         let create_info = ash::vk::QueryPoolCreateInfo {
79             flags: ash::vk::QueryPoolCreateFlags::empty(),
80             query_type: query_type.into(),
81             query_count,
82             pipeline_statistics,
83             ..Default::default()
84         };
85 
86         let handle = unsafe {
87             let fns = device.fns();
88             let mut output = MaybeUninit::uninit();
89             (fns.v1_0.create_query_pool)(
90                 device.handle(),
91                 &create_info,
92                 ptr::null(),
93                 output.as_mut_ptr(),
94             )
95             .result()
96             .map_err(VulkanError::from)?;
97             output.assume_init()
98         };
99 
100         Ok(Arc::new(QueryPool {
101             handle,
102             device,
103             id: Self::next_id(),
104             query_type,
105             query_count,
106         }))
107     }
108 
109     /// Creates a new `QueryPool` from a raw object handle.
110     ///
111     /// # Safety
112     ///
113     /// - `handle` must be a valid Vulkan object handle created from `device`.
114     /// - `create_info` must match the info used to create the object.
115     #[inline]
from_handle( device: Arc<Device>, handle: ash::vk::QueryPool, create_info: QueryPoolCreateInfo, ) -> Arc<QueryPool>116     pub unsafe fn from_handle(
117         device: Arc<Device>,
118         handle: ash::vk::QueryPool,
119         create_info: QueryPoolCreateInfo,
120     ) -> Arc<QueryPool> {
121         let QueryPoolCreateInfo {
122             query_type,
123             query_count,
124             _ne: _,
125         } = create_info;
126 
127         Arc::new(QueryPool {
128             handle,
129             device,
130             id: Self::next_id(),
131             query_type,
132             query_count,
133         })
134     }
135 
136     /// Returns the query type of the pool.
137     #[inline]
query_type(&self) -> QueryType138     pub fn query_type(&self) -> QueryType {
139         self.query_type
140     }
141 
142     /// Returns the number of query slots of this query pool.
143     #[inline]
query_count(&self) -> u32144     pub fn query_count(&self) -> u32 {
145         self.query_count
146     }
147 
148     /// Returns a reference to a single query slot, or `None` if the index is out of range.
149     #[inline]
query(&self, index: u32) -> Option<Query<'_>>150     pub fn query(&self, index: u32) -> Option<Query<'_>> {
151         if index < self.query_count {
152             Some(Query { pool: self, index })
153         } else {
154             None
155         }
156     }
157 
158     /// Returns a reference to a range of queries, or `None` if out of range.
159     ///
160     /// # Panics
161     ///
162     /// - Panics if the range is empty.
163     #[inline]
queries_range(&self, range: Range<u32>) -> Option<QueriesRange<'_>>164     pub fn queries_range(&self, range: Range<u32>) -> Option<QueriesRange<'_>> {
165         assert!(!range.is_empty());
166 
167         if range.end <= self.query_count {
168             Some(QueriesRange { pool: self, range })
169         } else {
170             None
171         }
172     }
173 }
174 
175 impl Drop for QueryPool {
176     #[inline]
drop(&mut self)177     fn drop(&mut self) {
178         unsafe {
179             let fns = self.device.fns();
180             (fns.v1_0.destroy_query_pool)(self.device.handle(), self.handle, ptr::null());
181         }
182     }
183 }
184 
185 unsafe impl VulkanObject for QueryPool {
186     type Handle = ash::vk::QueryPool;
187 
188     #[inline]
handle(&self) -> Self::Handle189     fn handle(&self) -> Self::Handle {
190         self.handle
191     }
192 }
193 
194 unsafe impl DeviceOwned for QueryPool {
195     #[inline]
device(&self) -> &Arc<Device>196     fn device(&self) -> &Arc<Device> {
197         &self.device
198     }
199 }
200 
201 impl_id_counter!(QueryPool);
202 
203 /// Parameters to create a new `QueryPool`.
204 #[derive(Clone, Debug)]
205 pub struct QueryPoolCreateInfo {
206     /// The type of query that the pool should be for.
207     ///
208     /// There is no default value.
209     pub query_type: QueryType,
210 
211     /// The number of queries to create in the pool.
212     ///
213     /// The default value is `0`, which must be overridden.
214     pub query_count: u32,
215 
216     pub _ne: crate::NonExhaustive,
217 }
218 
219 impl QueryPoolCreateInfo {
220     /// Returns a `QueryPoolCreateInfo` with the specified `query_type`.
221     #[inline]
query_type(query_type: QueryType) -> Self222     pub fn query_type(query_type: QueryType) -> Self {
223         Self {
224             query_type,
225             query_count: 0,
226             _ne: crate::NonExhaustive(()),
227         }
228     }
229 }
230 
231 /// Error that can happen when creating a query pool.
232 #[derive(Clone, Debug, PartialEq, Eq)]
233 pub enum QueryPoolCreationError {
234     /// Not enough memory.
235     OomError(OomError),
236     /// A pipeline statistics pool was requested but the corresponding feature wasn't enabled.
237     PipelineStatisticsQueryFeatureNotEnabled,
238 }
239 
240 impl Error for QueryPoolCreationError {
source(&self) -> Option<&(dyn Error + 'static)>241     fn source(&self) -> Option<&(dyn Error + 'static)> {
242         match self {
243             QueryPoolCreationError::OomError(err) => Some(err),
244             _ => None,
245         }
246     }
247 }
248 
249 impl Display for QueryPoolCreationError {
fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>250     fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
251         write!(
252             f,
253             "{}",
254             match self {
255                 QueryPoolCreationError::OomError(_) => "not enough memory available",
256                 QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled => {
257                     "a pipeline statistics pool was requested but the corresponding feature \
258                     wasn't enabled"
259                 }
260             }
261         )
262     }
263 }
264 
265 impl From<OomError> for QueryPoolCreationError {
from(err: OomError) -> QueryPoolCreationError266     fn from(err: OomError) -> QueryPoolCreationError {
267         QueryPoolCreationError::OomError(err)
268     }
269 }
270 
271 impl From<VulkanError> for QueryPoolCreationError {
from(err: VulkanError) -> QueryPoolCreationError272     fn from(err: VulkanError) -> QueryPoolCreationError {
273         match err {
274             err @ VulkanError::OutOfHostMemory => {
275                 QueryPoolCreationError::OomError(OomError::from(err))
276             }
277             err @ VulkanError::OutOfDeviceMemory => {
278                 QueryPoolCreationError::OomError(OomError::from(err))
279             }
280             _ => panic!("unexpected error: {:?}", err),
281         }
282     }
283 }
284 
285 /// A reference to a single query slot.
286 ///
287 /// This is created through [`QueryPool::query`].
288 #[derive(Clone, Debug)]
289 pub struct Query<'a> {
290     pool: &'a QueryPool,
291     index: u32,
292 }
293 
294 impl<'a> Query<'a> {
295     /// Returns a reference to the query pool.
296     #[inline]
pool(&self) -> &'a QueryPool297     pub fn pool(&self) -> &'a QueryPool {
298         self.pool
299     }
300 
301     /// Returns the index of the query represented.
302     #[inline]
index(&self) -> u32303     pub fn index(&self) -> u32 {
304         self.index
305     }
306 }
307 
308 /// A reference to a range of queries.
309 ///
310 /// This is created through [`QueryPool::queries_range`].
311 #[derive(Clone, Debug)]
312 pub struct QueriesRange<'a> {
313     pool: &'a QueryPool,
314     range: Range<u32>,
315 }
316 
317 impl<'a> QueriesRange<'a> {
318     /// Returns a reference to the query pool.
319     #[inline]
pool(&self) -> &'a QueryPool320     pub fn pool(&self) -> &'a QueryPool {
321         self.pool
322     }
323 
324     /// Returns the range of queries represented.
325     #[inline]
range(&self) -> Range<u32>326     pub fn range(&self) -> Range<u32> {
327         self.range.clone()
328     }
329 
330     /// Copies the results of this range of queries to a buffer on the CPU.
331     ///
332     /// [`self.pool().ty().result_len()`] will be written for each query in the range, plus 1 extra
333     /// element per query if [`WITH_AVAILABILITY`] is enabled. The provided buffer must be large
334     /// enough to hold the data.
335     ///
336     /// `true` is returned if every result was available and written to the buffer. `false`
337     /// is returned if some results were not yet available; these will not be written to the buffer.
338     ///
339     /// See also [`copy_query_pool_results`].
340     ///
341     /// [`self.pool().ty().result_len()`]: QueryType::result_len
342     /// [`WITH_AVAILABILITY`]: QueryResultFlags::WITH_AVAILABILITY
343     /// [`copy_query_pool_results`]: crate::command_buffer::AutoCommandBufferBuilder::copy_query_pool_results
344     #[inline]
get_results<T>( &self, destination: &mut [T], flags: QueryResultFlags, ) -> Result<bool, GetResultsError> where T: QueryResultElement,345     pub fn get_results<T>(
346         &self,
347         destination: &mut [T],
348         flags: QueryResultFlags,
349     ) -> Result<bool, GetResultsError>
350     where
351         T: QueryResultElement,
352     {
353         let stride = self.check_query_pool_results::<T>(
354             destination.as_ptr() as DeviceSize,
355             destination.len() as DeviceSize,
356             flags,
357         )?;
358 
359         let result = unsafe {
360             let fns = self.pool.device.fns();
361             (fns.v1_0.get_query_pool_results)(
362                 self.pool.device.handle(),
363                 self.pool.handle(),
364                 self.range.start,
365                 self.range.end - self.range.start,
366                 size_of_val(destination),
367                 destination.as_mut_ptr() as *mut c_void,
368                 stride,
369                 ash::vk::QueryResultFlags::from(flags) | T::FLAG,
370             )
371         };
372 
373         match result {
374             ash::vk::Result::SUCCESS => Ok(true),
375             ash::vk::Result::NOT_READY => Ok(false),
376             err => Err(VulkanError::from(err).into()),
377         }
378     }
379 
check_query_pool_results<T>( &self, buffer_start: DeviceSize, buffer_len: DeviceSize, flags: QueryResultFlags, ) -> Result<DeviceSize, GetResultsError> where T: QueryResultElement,380     pub(crate) fn check_query_pool_results<T>(
381         &self,
382         buffer_start: DeviceSize,
383         buffer_len: DeviceSize,
384         flags: QueryResultFlags,
385     ) -> Result<DeviceSize, GetResultsError>
386     where
387         T: QueryResultElement,
388     {
389         // VUID-vkGetQueryPoolResults-flags-parameter
390         // VUID-vkCmdCopyQueryPoolResults-flags-parameter
391         flags.validate_device(&self.pool.device)?;
392 
393         assert!(buffer_len > 0);
394 
395         // VUID-vkGetQueryPoolResults-flags-02828
396         // VUID-vkGetQueryPoolResults-flags-00815
397         debug_assert!(buffer_start % std::mem::size_of::<T>() as DeviceSize == 0);
398 
399         let count = self.range.end - self.range.start;
400         let per_query_len = self.pool.query_type.result_len()
401             + flags.intersects(QueryResultFlags::WITH_AVAILABILITY) as DeviceSize;
402         let required_len = per_query_len * count as DeviceSize;
403 
404         // VUID-vkGetQueryPoolResults-dataSize-00817
405         if buffer_len < required_len {
406             return Err(GetResultsError::BufferTooSmall {
407                 required_len: required_len as DeviceSize,
408                 actual_len: buffer_len as DeviceSize,
409             });
410         }
411 
412         match self.pool.query_type {
413             QueryType::Occlusion => (),
414             QueryType::PipelineStatistics(_) => (),
415             QueryType::Timestamp => {
416                 // VUID-vkGetQueryPoolResults-queryType-00818
417                 if flags.intersects(QueryResultFlags::PARTIAL) {
418                     return Err(GetResultsError::InvalidFlags);
419                 }
420             }
421         }
422 
423         Ok(per_query_len * std::mem::size_of::<T>() as DeviceSize)
424     }
425 }
426 
427 /// Error that can happen when calling [`QueriesRange::get_results`].
428 #[derive(Clone, Debug, PartialEq, Eq)]
429 pub enum GetResultsError {
430     /// The connection to the device has been lost.
431     DeviceLost,
432 
433     /// Not enough memory.
434     OomError(OomError),
435 
436     RequirementNotMet {
437         required_for: &'static str,
438         requires_one_of: RequiresOneOf,
439     },
440 
441     /// The buffer is too small for the operation.
442     BufferTooSmall {
443         /// Required number of elements in the buffer.
444         required_len: DeviceSize,
445         /// Actual number of elements in the buffer.
446         actual_len: DeviceSize,
447     },
448 
449     /// The provided flags are not allowed for this type of query.
450     InvalidFlags,
451 }
452 
453 impl Error for GetResultsError {
source(&self) -> Option<&(dyn Error + 'static)>454     fn source(&self) -> Option<&(dyn Error + 'static)> {
455         match self {
456             Self::OomError(err) => Some(err),
457             _ => None,
458         }
459     }
460 }
461 
462 impl Display for GetResultsError {
fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>463     fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
464         match self {
465             Self::OomError(_) => write!(f, "not enough memory available"),
466             Self::DeviceLost => write!(f, "the connection to the device has been lost"),
467             Self::RequirementNotMet {
468                 required_for,
469                 requires_one_of,
470             } => write!(
471                 f,
472                 "a requirement was not met for: {}; requires one of: {}",
473                 required_for, requires_one_of,
474             ),
475             Self::BufferTooSmall { .. } => write!(f, "the buffer is too small for the operation"),
476             Self::InvalidFlags => write!(
477                 f,
478                 "the provided flags are not allowed for this type of query"
479             ),
480         }
481     }
482 }
483 
484 impl From<VulkanError> for GetResultsError {
from(err: VulkanError) -> Self485     fn from(err: VulkanError) -> Self {
486         match err {
487             VulkanError::OutOfHostMemory | VulkanError::OutOfDeviceMemory => {
488                 Self::OomError(OomError::from(err))
489             }
490             VulkanError::DeviceLost => Self::DeviceLost,
491             _ => panic!("unexpected error: {:?}", err),
492         }
493     }
494 }
495 
496 impl From<OomError> for GetResultsError {
from(err: OomError) -> Self497     fn from(err: OomError) -> Self {
498         Self::OomError(err)
499     }
500 }
501 
502 impl From<RequirementNotMet> for GetResultsError {
from(err: RequirementNotMet) -> Self503     fn from(err: RequirementNotMet) -> Self {
504         Self::RequirementNotMet {
505             required_for: err.required_for,
506             requires_one_of: err.requires_one_of,
507         }
508     }
509 }
510 
511 /// A trait for elements of buffers that can be used as a destination for query results.
512 ///
513 /// # Safety
514 /// This is implemented for `u32` and `u64`. Unless you really know what you're doing, you should
515 /// not implement this trait for any other type.
516 pub unsafe trait QueryResultElement: BufferContents + Sized {
517     const FLAG: ash::vk::QueryResultFlags;
518 }
519 
520 unsafe impl QueryResultElement for u32 {
521     const FLAG: ash::vk::QueryResultFlags = ash::vk::QueryResultFlags::empty();
522 }
523 
524 unsafe impl QueryResultElement for u64 {
525     const FLAG: ash::vk::QueryResultFlags = ash::vk::QueryResultFlags::TYPE_64;
526 }
527 
528 /// The type of query that a query pool should perform.
529 #[derive(Debug, Copy, Clone)]
530 pub enum QueryType {
531     /// Tracks the number of samples that pass per-fragment tests (e.g. the depth test).
532     Occlusion,
533     /// Tracks statistics on pipeline invocations and their input data.
534     PipelineStatistics(QueryPipelineStatisticFlags),
535     /// Writes timestamps at chosen points in a command buffer.
536     Timestamp,
537 }
538 
539 impl QueryType {
540     /// Returns the number of [`QueryResultElement`]s that are needed to hold the result of a
541     /// single query of this type.
542     ///
543     /// - For [`Occlusion`] and [`Timestamp`] queries, this returns 1.
544     /// - For [`PipelineStatistics`] queries, this returns the number of statistics flags enabled.
545     ///
546     /// If the results are retrieved with [`WITH_AVAILABILITY`] enabled, then an additional element
547     /// is required per query.
548     ///
549     /// [`Occlusion`]: QueryType::Occlusion
550     /// [`Timestamp`]: QueryType::Timestamp
551     /// [`PipelineStatistics`]: QueryType::PipelineStatistics
552     /// [`WITH_AVAILABILITY`]: QueryResultFlags::WITH_AVAILABILITY
553     #[inline]
result_len(self) -> DeviceSize554     pub const fn result_len(self) -> DeviceSize {
555         match self {
556             Self::Occlusion | Self::Timestamp => 1,
557             Self::PipelineStatistics(flags) => flags.count() as DeviceSize,
558         }
559     }
560 }
561 
562 impl From<QueryType> for ash::vk::QueryType {
563     #[inline]
from(value: QueryType) -> Self564     fn from(value: QueryType) -> Self {
565         match value {
566             QueryType::Occlusion => ash::vk::QueryType::OCCLUSION,
567             QueryType::PipelineStatistics(_) => ash::vk::QueryType::PIPELINE_STATISTICS,
568             QueryType::Timestamp => ash::vk::QueryType::TIMESTAMP,
569         }
570     }
571 }
572 
573 vulkan_bitflags! {
574     #[non_exhaustive]
575 
576     /// Flags that control how a query is to be executed.
577     QueryControlFlags = QueryControlFlags(u32);
578 
579     /// For occlusion queries, specifies that the result must reflect the exact number of
580     /// tests passed. If not enabled, the query may return a result of 1 even if more fragments
581     /// passed the test.
582     PRECISE = PRECISE,
583 }
584 
585 vulkan_bitflags! {
586     #[non_exhaustive]
587 
588     /// For pipeline statistics queries, the statistics that should be gathered.
589     QueryPipelineStatisticFlags impl {
590         /// Returns `true` if `self` contains any flags referring to compute operations.
591         #[inline]
592         pub const fn is_compute(self) -> bool {
593             self.intersects(QueryPipelineStatisticFlags::COMPUTE_SHADER_INVOCATIONS)
594         }
595 
596         /// Returns `true` if `self` contains any flags referring to graphics operations.
597         #[inline]
598         pub const fn is_graphics(self) -> bool {
599             self.intersects(
600                 (QueryPipelineStatisticFlags::INPUT_ASSEMBLY_VERTICES)
601                     .union(QueryPipelineStatisticFlags::INPUT_ASSEMBLY_PRIMITIVES)
602                     .union(QueryPipelineStatisticFlags::VERTEX_SHADER_INVOCATIONS)
603                     .union(QueryPipelineStatisticFlags::GEOMETRY_SHADER_INVOCATIONS)
604                     .union(QueryPipelineStatisticFlags::GEOMETRY_SHADER_PRIMITIVES)
605                     .union(QueryPipelineStatisticFlags::CLIPPING_INVOCATIONS)
606                     .union(QueryPipelineStatisticFlags::CLIPPING_PRIMITIVES)
607                     .union(QueryPipelineStatisticFlags::FRAGMENT_SHADER_INVOCATIONS)
608                     .union(QueryPipelineStatisticFlags::TESSELLATION_CONTROL_SHADER_PATCHES)
609                     .union(QueryPipelineStatisticFlags::TESSELLATION_EVALUATION_SHADER_INVOCATIONS),
610             )
611         }
612     }
613     = QueryPipelineStatisticFlags(u32);
614 
615     /// Count the number of vertices processed by the input assembly.
616     INPUT_ASSEMBLY_VERTICES = INPUT_ASSEMBLY_VERTICES,
617 
618     /// Count the number of primitives processed by the input assembly.
619     INPUT_ASSEMBLY_PRIMITIVES = INPUT_ASSEMBLY_PRIMITIVES,
620 
621     /// Count the number of times a vertex shader is invoked.
622     VERTEX_SHADER_INVOCATIONS = VERTEX_SHADER_INVOCATIONS,
623 
624     /// Count the number of times a geometry shader is invoked.
625     GEOMETRY_SHADER_INVOCATIONS = GEOMETRY_SHADER_INVOCATIONS,
626 
627     /// Count the number of primitives generated by geometry shaders.
628     GEOMETRY_SHADER_PRIMITIVES = GEOMETRY_SHADER_PRIMITIVES,
629 
630     /// Count the number of times the clipping stage is invoked on a primitive.
631     CLIPPING_INVOCATIONS = CLIPPING_INVOCATIONS,
632 
633     /// Count the number of primitives that are output by the clipping stage.
634     CLIPPING_PRIMITIVES = CLIPPING_PRIMITIVES,
635 
636     /// Count the number of times a fragment shader is invoked.
637     FRAGMENT_SHADER_INVOCATIONS = FRAGMENT_SHADER_INVOCATIONS,
638 
639     /// Count the number of patches processed by a tessellation control shader.
640     TESSELLATION_CONTROL_SHADER_PATCHES = TESSELLATION_CONTROL_SHADER_PATCHES,
641 
642     /// Count the number of times a tessellation evaluation shader is invoked.
643     TESSELLATION_EVALUATION_SHADER_INVOCATIONS = TESSELLATION_EVALUATION_SHADER_INVOCATIONS,
644 
645     /// Count the number of times a compute shader is invoked.
646     COMPUTE_SHADER_INVOCATIONS = COMPUTE_SHADER_INVOCATIONS,
647 
648     /* TODO: enable
649     // TODO: document
650     TASK_SHADER_INVOCATIONS = TASK_SHADER_INVOCATIONS_NV {
651         device_extensions: [nv_mesh_shader],
652     },*/
653 
654     /* TODO: enable
655     // TODO: document
656     MESH_SHADER_INVOCATIONS = MESH_SHADER_INVOCATIONS_NV {
657         device_extensions: [nv_mesh_shader],
658     },*/
659 }
660 
661 vulkan_bitflags! {
662     #[non_exhaustive]
663 
664     /// Flags to control how the results of a query should be retrieved.
665     ///
666     /// `VK_QUERY_RESULT_64_BIT` is not included, as it is determined automatically via the
667     /// [`QueryResultElement`] trait.
668     QueryResultFlags = QueryResultFlags(u32);
669 
670     /// Wait for the results to become available before writing the results.
671     WAIT = WAIT,
672 
673     /// Write an additional element to the end of each query's results, indicating the availability
674     /// of the results:
675     /// - Nonzero: The results are available, and have been written to the element(s) preceding.
676     /// - Zero: The results are not yet available, and have not been written.
677     WITH_AVAILABILITY = WITH_AVAILABILITY,
678 
679     /// Allow writing partial results to the buffer, instead of waiting until they are fully
680     /// available.
681     PARTIAL = PARTIAL,
682 
683     /* TODO: enable
684     // TODO: document
685     WITH_STATUS = WITH_STATUS_KHR {
686         device_extensions: [khr_video_queue],
687     },*/
688 }
689 
690 #[cfg(test)]
691 mod tests {
692     use super::QueryPoolCreateInfo;
693     use crate::query::{QueryPipelineStatisticFlags, QueryPool, QueryPoolCreationError, QueryType};
694 
695     #[test]
pipeline_statistics_feature()696     fn pipeline_statistics_feature() {
697         let (device, _) = gfx_dev_and_queue!();
698         let query_type = QueryType::PipelineStatistics(QueryPipelineStatisticFlags::empty());
699         match QueryPool::new(
700             device,
701             QueryPoolCreateInfo {
702                 query_count: 256,
703                 ..QueryPoolCreateInfo::query_type(query_type)
704             },
705         ) {
706             Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled) => (),
707             _ => panic!(),
708         };
709     }
710 }
711