| // Copyright (c) 2016 The vulkano developers |
| // Licensed under the Apache License, Version 2.0 |
| // <LICENSE-APACHE or |
| // https://www.apache.org/licenses/LICENSE-2.0> or the MIT |
| // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, |
| // at your option. All files in the project carrying such |
| // notice may not be copied, modified, or distributed except |
| // according to those terms. |
| |
| //! Gather information about rendering, held in query pools. |
| //! |
| //! In Vulkan, queries are not created individually. Instead you manipulate **query pools**, which |
| //! represent a collection of queries. Whenever you use a query, you have to specify both the query |
| //! pool and the slot id within that query pool. |
| |
| use crate::{ |
| buffer::BufferContents, |
| device::{Device, DeviceOwned}, |
| macros::{impl_id_counter, vulkan_bitflags}, |
| DeviceSize, OomError, RequirementNotMet, RequiresOneOf, VulkanError, VulkanObject, |
| }; |
| use std::{ |
| error::Error, |
| ffi::c_void, |
| fmt::{Display, Error as FmtError, Formatter}, |
| mem::{size_of_val, MaybeUninit}, |
| num::NonZeroU64, |
| ops::Range, |
| ptr, |
| sync::Arc, |
| }; |
| |
| /// A collection of one or more queries of a particular type. |
| #[derive(Debug)] |
| pub struct QueryPool { |
| handle: ash::vk::QueryPool, |
| device: Arc<Device>, |
| id: NonZeroU64, |
| |
| query_type: QueryType, |
| query_count: u32, |
| } |
| |
| impl QueryPool { |
| /// Creates a new `QueryPool`. |
| /// |
| /// # Panics |
| /// |
| /// - Panics if `create_info.query_count` is `0`. |
| pub fn new( |
| device: Arc<Device>, |
| create_info: QueryPoolCreateInfo, |
| ) -> Result<Arc<QueryPool>, QueryPoolCreationError> { |
| let QueryPoolCreateInfo { |
| query_type, |
| query_count, |
| _ne: _, |
| } = create_info; |
| |
| // VUID-VkQueryPoolCreateInfo-queryCount-02763 |
| assert!(query_count != 0); |
| |
| let pipeline_statistics = match query_type { |
| QueryType::PipelineStatistics(flags) => { |
| // VUID-VkQueryPoolCreateInfo-queryType-00791 |
| if !device.enabled_features().pipeline_statistics_query { |
| return Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled); |
| } |
| |
| // VUID-VkQueryPoolCreateInfo-queryType-00792 |
| flags.into() |
| } |
| QueryType::Occlusion | QueryType::Timestamp => { |
| ash::vk::QueryPipelineStatisticFlags::empty() |
| } |
| }; |
| |
| let create_info = ash::vk::QueryPoolCreateInfo { |
| flags: ash::vk::QueryPoolCreateFlags::empty(), |
| query_type: query_type.into(), |
| query_count, |
| pipeline_statistics, |
| ..Default::default() |
| }; |
| |
| let handle = unsafe { |
| let fns = device.fns(); |
| let mut output = MaybeUninit::uninit(); |
| (fns.v1_0.create_query_pool)( |
| device.handle(), |
| &create_info, |
| ptr::null(), |
| output.as_mut_ptr(), |
| ) |
| .result() |
| .map_err(VulkanError::from)?; |
| output.assume_init() |
| }; |
| |
| Ok(Arc::new(QueryPool { |
| handle, |
| device, |
| id: Self::next_id(), |
| query_type, |
| query_count, |
| })) |
| } |
| |
| /// Creates a new `QueryPool` from a raw object handle. |
| /// |
| /// # Safety |
| /// |
| /// - `handle` must be a valid Vulkan object handle created from `device`. |
| /// - `create_info` must match the info used to create the object. |
| #[inline] |
| pub unsafe fn from_handle( |
| device: Arc<Device>, |
| handle: ash::vk::QueryPool, |
| create_info: QueryPoolCreateInfo, |
| ) -> Arc<QueryPool> { |
| let QueryPoolCreateInfo { |
| query_type, |
| query_count, |
| _ne: _, |
| } = create_info; |
| |
| Arc::new(QueryPool { |
| handle, |
| device, |
| id: Self::next_id(), |
| query_type, |
| query_count, |
| }) |
| } |
| |
| /// Returns the query type of the pool. |
| #[inline] |
| pub fn query_type(&self) -> QueryType { |
| self.query_type |
| } |
| |
| /// Returns the number of query slots of this query pool. |
| #[inline] |
| pub fn query_count(&self) -> u32 { |
| self.query_count |
| } |
| |
| /// Returns a reference to a single query slot, or `None` if the index is out of range. |
| #[inline] |
| pub fn query(&self, index: u32) -> Option<Query<'_>> { |
| if index < self.query_count { |
| Some(Query { pool: self, index }) |
| } else { |
| None |
| } |
| } |
| |
| /// Returns a reference to a range of queries, or `None` if out of range. |
| /// |
| /// # Panics |
| /// |
| /// - Panics if the range is empty. |
| #[inline] |
| pub fn queries_range(&self, range: Range<u32>) -> Option<QueriesRange<'_>> { |
| assert!(!range.is_empty()); |
| |
| if range.end <= self.query_count { |
| Some(QueriesRange { pool: self, range }) |
| } else { |
| None |
| } |
| } |
| } |
| |
| impl Drop for QueryPool { |
| #[inline] |
| fn drop(&mut self) { |
| unsafe { |
| let fns = self.device.fns(); |
| (fns.v1_0.destroy_query_pool)(self.device.handle(), self.handle, ptr::null()); |
| } |
| } |
| } |
| |
| unsafe impl VulkanObject for QueryPool { |
| type Handle = ash::vk::QueryPool; |
| |
| #[inline] |
| fn handle(&self) -> Self::Handle { |
| self.handle |
| } |
| } |
| |
| unsafe impl DeviceOwned for QueryPool { |
| #[inline] |
| fn device(&self) -> &Arc<Device> { |
| &self.device |
| } |
| } |
| |
| impl_id_counter!(QueryPool); |
| |
| /// Parameters to create a new `QueryPool`. |
| #[derive(Clone, Debug)] |
| pub struct QueryPoolCreateInfo { |
| /// The type of query that the pool should be for. |
| /// |
| /// There is no default value. |
| pub query_type: QueryType, |
| |
| /// The number of queries to create in the pool. |
| /// |
| /// The default value is `0`, which must be overridden. |
| pub query_count: u32, |
| |
| pub _ne: crate::NonExhaustive, |
| } |
| |
| impl QueryPoolCreateInfo { |
| /// Returns a `QueryPoolCreateInfo` with the specified `query_type`. |
| #[inline] |
| pub fn query_type(query_type: QueryType) -> Self { |
| Self { |
| query_type, |
| query_count: 0, |
| _ne: crate::NonExhaustive(()), |
| } |
| } |
| } |
| |
| /// Error that can happen when creating a query pool. |
| #[derive(Clone, Debug, PartialEq, Eq)] |
| pub enum QueryPoolCreationError { |
| /// Not enough memory. |
| OomError(OomError), |
| /// A pipeline statistics pool was requested but the corresponding feature wasn't enabled. |
| PipelineStatisticsQueryFeatureNotEnabled, |
| } |
| |
| impl Error for QueryPoolCreationError { |
| fn source(&self) -> Option<&(dyn Error + 'static)> { |
| match self { |
| QueryPoolCreationError::OomError(err) => Some(err), |
| _ => None, |
| } |
| } |
| } |
| |
| impl Display for QueryPoolCreationError { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { |
| write!( |
| f, |
| "{}", |
| match self { |
| QueryPoolCreationError::OomError(_) => "not enough memory available", |
| QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled => { |
| "a pipeline statistics pool was requested but the corresponding feature \ |
| wasn't enabled" |
| } |
| } |
| ) |
| } |
| } |
| |
| impl From<OomError> for QueryPoolCreationError { |
| fn from(err: OomError) -> QueryPoolCreationError { |
| QueryPoolCreationError::OomError(err) |
| } |
| } |
| |
| impl From<VulkanError> for QueryPoolCreationError { |
| fn from(err: VulkanError) -> QueryPoolCreationError { |
| match err { |
| err @ VulkanError::OutOfHostMemory => { |
| QueryPoolCreationError::OomError(OomError::from(err)) |
| } |
| err @ VulkanError::OutOfDeviceMemory => { |
| QueryPoolCreationError::OomError(OomError::from(err)) |
| } |
| _ => panic!("unexpected error: {:?}", err), |
| } |
| } |
| } |
| |
| /// A reference to a single query slot. |
| /// |
| /// This is created through [`QueryPool::query`]. |
| #[derive(Clone, Debug)] |
| pub struct Query<'a> { |
| pool: &'a QueryPool, |
| index: u32, |
| } |
| |
| impl<'a> Query<'a> { |
| /// Returns a reference to the query pool. |
| #[inline] |
| pub fn pool(&self) -> &'a QueryPool { |
| self.pool |
| } |
| |
| /// Returns the index of the query represented. |
| #[inline] |
| pub fn index(&self) -> u32 { |
| self.index |
| } |
| } |
| |
| /// A reference to a range of queries. |
| /// |
| /// This is created through [`QueryPool::queries_range`]. |
| #[derive(Clone, Debug)] |
| pub struct QueriesRange<'a> { |
| pool: &'a QueryPool, |
| range: Range<u32>, |
| } |
| |
| impl<'a> QueriesRange<'a> { |
| /// Returns a reference to the query pool. |
| #[inline] |
| pub fn pool(&self) -> &'a QueryPool { |
| self.pool |
| } |
| |
| /// Returns the range of queries represented. |
| #[inline] |
| pub fn range(&self) -> Range<u32> { |
| self.range.clone() |
| } |
| |
| /// Copies the results of this range of queries to a buffer on the CPU. |
| /// |
| /// [`self.pool().ty().result_len()`] will be written for each query in the range, plus 1 extra |
| /// element per query if [`WITH_AVAILABILITY`] is enabled. The provided buffer must be large |
| /// enough to hold the data. |
| /// |
| /// `true` is returned if every result was available and written to the buffer. `false` |
| /// is returned if some results were not yet available; these will not be written to the buffer. |
| /// |
| /// See also [`copy_query_pool_results`]. |
| /// |
| /// [`self.pool().ty().result_len()`]: QueryType::result_len |
| /// [`WITH_AVAILABILITY`]: QueryResultFlags::WITH_AVAILABILITY |
| /// [`copy_query_pool_results`]: crate::command_buffer::AutoCommandBufferBuilder::copy_query_pool_results |
| #[inline] |
| pub fn get_results<T>( |
| &self, |
| destination: &mut [T], |
| flags: QueryResultFlags, |
| ) -> Result<bool, GetResultsError> |
| where |
| T: QueryResultElement, |
| { |
| let stride = self.check_query_pool_results::<T>( |
| destination.as_ptr() as DeviceSize, |
| destination.len() as DeviceSize, |
| flags, |
| )?; |
| |
| let result = unsafe { |
| let fns = self.pool.device.fns(); |
| (fns.v1_0.get_query_pool_results)( |
| self.pool.device.handle(), |
| self.pool.handle(), |
| self.range.start, |
| self.range.end - self.range.start, |
| size_of_val(destination), |
| destination.as_mut_ptr() as *mut c_void, |
| stride, |
| ash::vk::QueryResultFlags::from(flags) | T::FLAG, |
| ) |
| }; |
| |
| match result { |
| ash::vk::Result::SUCCESS => Ok(true), |
| ash::vk::Result::NOT_READY => Ok(false), |
| err => Err(VulkanError::from(err).into()), |
| } |
| } |
| |
| pub(crate) fn check_query_pool_results<T>( |
| &self, |
| buffer_start: DeviceSize, |
| buffer_len: DeviceSize, |
| flags: QueryResultFlags, |
| ) -> Result<DeviceSize, GetResultsError> |
| where |
| T: QueryResultElement, |
| { |
| // VUID-vkGetQueryPoolResults-flags-parameter |
| // VUID-vkCmdCopyQueryPoolResults-flags-parameter |
| flags.validate_device(&self.pool.device)?; |
| |
| assert!(buffer_len > 0); |
| |
| // VUID-vkGetQueryPoolResults-flags-02828 |
| // VUID-vkGetQueryPoolResults-flags-00815 |
| debug_assert!(buffer_start % std::mem::size_of::<T>() as DeviceSize == 0); |
| |
| let count = self.range.end - self.range.start; |
| let per_query_len = self.pool.query_type.result_len() |
| + flags.intersects(QueryResultFlags::WITH_AVAILABILITY) as DeviceSize; |
| let required_len = per_query_len * count as DeviceSize; |
| |
| // VUID-vkGetQueryPoolResults-dataSize-00817 |
| if buffer_len < required_len { |
| return Err(GetResultsError::BufferTooSmall { |
| required_len: required_len as DeviceSize, |
| actual_len: buffer_len as DeviceSize, |
| }); |
| } |
| |
| match self.pool.query_type { |
| QueryType::Occlusion => (), |
| QueryType::PipelineStatistics(_) => (), |
| QueryType::Timestamp => { |
| // VUID-vkGetQueryPoolResults-queryType-00818 |
| if flags.intersects(QueryResultFlags::PARTIAL) { |
| return Err(GetResultsError::InvalidFlags); |
| } |
| } |
| } |
| |
| Ok(per_query_len * std::mem::size_of::<T>() as DeviceSize) |
| } |
| } |
| |
| /// Error that can happen when calling [`QueriesRange::get_results`]. |
| #[derive(Clone, Debug, PartialEq, Eq)] |
| pub enum GetResultsError { |
| /// The connection to the device has been lost. |
| DeviceLost, |
| |
| /// Not enough memory. |
| OomError(OomError), |
| |
| RequirementNotMet { |
| required_for: &'static str, |
| requires_one_of: RequiresOneOf, |
| }, |
| |
| /// The buffer is too small for the operation. |
| BufferTooSmall { |
| /// Required number of elements in the buffer. |
| required_len: DeviceSize, |
| /// Actual number of elements in the buffer. |
| actual_len: DeviceSize, |
| }, |
| |
| /// The provided flags are not allowed for this type of query. |
| InvalidFlags, |
| } |
| |
| impl Error for GetResultsError { |
| fn source(&self) -> Option<&(dyn Error + 'static)> { |
| match self { |
| Self::OomError(err) => Some(err), |
| _ => None, |
| } |
| } |
| } |
| |
| impl Display for GetResultsError { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { |
| match self { |
| Self::OomError(_) => write!(f, "not enough memory available"), |
| Self::DeviceLost => write!(f, "the connection to the device has been lost"), |
| Self::RequirementNotMet { |
| required_for, |
| requires_one_of, |
| } => write!( |
| f, |
| "a requirement was not met for: {}; requires one of: {}", |
| required_for, requires_one_of, |
| ), |
| Self::BufferTooSmall { .. } => write!(f, "the buffer is too small for the operation"), |
| Self::InvalidFlags => write!( |
| f, |
| "the provided flags are not allowed for this type of query" |
| ), |
| } |
| } |
| } |
| |
| impl From<VulkanError> for GetResultsError { |
| fn from(err: VulkanError) -> Self { |
| match err { |
| VulkanError::OutOfHostMemory | VulkanError::OutOfDeviceMemory => { |
| Self::OomError(OomError::from(err)) |
| } |
| VulkanError::DeviceLost => Self::DeviceLost, |
| _ => panic!("unexpected error: {:?}", err), |
| } |
| } |
| } |
| |
| impl From<OomError> for GetResultsError { |
| fn from(err: OomError) -> Self { |
| Self::OomError(err) |
| } |
| } |
| |
| impl From<RequirementNotMet> for GetResultsError { |
| fn from(err: RequirementNotMet) -> Self { |
| Self::RequirementNotMet { |
| required_for: err.required_for, |
| requires_one_of: err.requires_one_of, |
| } |
| } |
| } |
| |
| /// A trait for elements of buffers that can be used as a destination for query results. |
| /// |
| /// # Safety |
| /// This is implemented for `u32` and `u64`. Unless you really know what you're doing, you should |
| /// not implement this trait for any other type. |
| pub unsafe trait QueryResultElement: BufferContents + Sized { |
| const FLAG: ash::vk::QueryResultFlags; |
| } |
| |
| unsafe impl QueryResultElement for u32 { |
| const FLAG: ash::vk::QueryResultFlags = ash::vk::QueryResultFlags::empty(); |
| } |
| |
| unsafe impl QueryResultElement for u64 { |
| const FLAG: ash::vk::QueryResultFlags = ash::vk::QueryResultFlags::TYPE_64; |
| } |
| |
| /// The type of query that a query pool should perform. |
| #[derive(Debug, Copy, Clone)] |
| pub enum QueryType { |
| /// Tracks the number of samples that pass per-fragment tests (e.g. the depth test). |
| Occlusion, |
| /// Tracks statistics on pipeline invocations and their input data. |
| PipelineStatistics(QueryPipelineStatisticFlags), |
| /// Writes timestamps at chosen points in a command buffer. |
| Timestamp, |
| } |
| |
| impl QueryType { |
| /// Returns the number of [`QueryResultElement`]s that are needed to hold the result of a |
| /// single query of this type. |
| /// |
| /// - For [`Occlusion`] and [`Timestamp`] queries, this returns 1. |
| /// - For [`PipelineStatistics`] queries, this returns the number of statistics flags enabled. |
| /// |
| /// If the results are retrieved with [`WITH_AVAILABILITY`] enabled, then an additional element |
| /// is required per query. |
| /// |
| /// [`Occlusion`]: QueryType::Occlusion |
| /// [`Timestamp`]: QueryType::Timestamp |
| /// [`PipelineStatistics`]: QueryType::PipelineStatistics |
| /// [`WITH_AVAILABILITY`]: QueryResultFlags::WITH_AVAILABILITY |
| #[inline] |
| pub const fn result_len(self) -> DeviceSize { |
| match self { |
| Self::Occlusion | Self::Timestamp => 1, |
| Self::PipelineStatistics(flags) => flags.count() as DeviceSize, |
| } |
| } |
| } |
| |
| impl From<QueryType> for ash::vk::QueryType { |
| #[inline] |
| fn from(value: QueryType) -> Self { |
| match value { |
| QueryType::Occlusion => ash::vk::QueryType::OCCLUSION, |
| QueryType::PipelineStatistics(_) => ash::vk::QueryType::PIPELINE_STATISTICS, |
| QueryType::Timestamp => ash::vk::QueryType::TIMESTAMP, |
| } |
| } |
| } |
| |
| vulkan_bitflags! { |
| #[non_exhaustive] |
| |
| /// Flags that control how a query is to be executed. |
| QueryControlFlags = QueryControlFlags(u32); |
| |
| /// For occlusion queries, specifies that the result must reflect the exact number of |
| /// tests passed. If not enabled, the query may return a result of 1 even if more fragments |
| /// passed the test. |
| PRECISE = PRECISE, |
| } |
| |
| vulkan_bitflags! { |
| #[non_exhaustive] |
| |
| /// For pipeline statistics queries, the statistics that should be gathered. |
| QueryPipelineStatisticFlags impl { |
| /// Returns `true` if `self` contains any flags referring to compute operations. |
| #[inline] |
| pub const fn is_compute(self) -> bool { |
| self.intersects(QueryPipelineStatisticFlags::COMPUTE_SHADER_INVOCATIONS) |
| } |
| |
| /// Returns `true` if `self` contains any flags referring to graphics operations. |
| #[inline] |
| pub const fn is_graphics(self) -> bool { |
| self.intersects( |
| (QueryPipelineStatisticFlags::INPUT_ASSEMBLY_VERTICES) |
| .union(QueryPipelineStatisticFlags::INPUT_ASSEMBLY_PRIMITIVES) |
| .union(QueryPipelineStatisticFlags::VERTEX_SHADER_INVOCATIONS) |
| .union(QueryPipelineStatisticFlags::GEOMETRY_SHADER_INVOCATIONS) |
| .union(QueryPipelineStatisticFlags::GEOMETRY_SHADER_PRIMITIVES) |
| .union(QueryPipelineStatisticFlags::CLIPPING_INVOCATIONS) |
| .union(QueryPipelineStatisticFlags::CLIPPING_PRIMITIVES) |
| .union(QueryPipelineStatisticFlags::FRAGMENT_SHADER_INVOCATIONS) |
| .union(QueryPipelineStatisticFlags::TESSELLATION_CONTROL_SHADER_PATCHES) |
| .union(QueryPipelineStatisticFlags::TESSELLATION_EVALUATION_SHADER_INVOCATIONS), |
| ) |
| } |
| } |
| = QueryPipelineStatisticFlags(u32); |
| |
| /// Count the number of vertices processed by the input assembly. |
| INPUT_ASSEMBLY_VERTICES = INPUT_ASSEMBLY_VERTICES, |
| |
| /// Count the number of primitives processed by the input assembly. |
| INPUT_ASSEMBLY_PRIMITIVES = INPUT_ASSEMBLY_PRIMITIVES, |
| |
| /// Count the number of times a vertex shader is invoked. |
| VERTEX_SHADER_INVOCATIONS = VERTEX_SHADER_INVOCATIONS, |
| |
| /// Count the number of times a geometry shader is invoked. |
| GEOMETRY_SHADER_INVOCATIONS = GEOMETRY_SHADER_INVOCATIONS, |
| |
| /// Count the number of primitives generated by geometry shaders. |
| GEOMETRY_SHADER_PRIMITIVES = GEOMETRY_SHADER_PRIMITIVES, |
| |
| /// Count the number of times the clipping stage is invoked on a primitive. |
| CLIPPING_INVOCATIONS = CLIPPING_INVOCATIONS, |
| |
| /// Count the number of primitives that are output by the clipping stage. |
| CLIPPING_PRIMITIVES = CLIPPING_PRIMITIVES, |
| |
| /// Count the number of times a fragment shader is invoked. |
| FRAGMENT_SHADER_INVOCATIONS = FRAGMENT_SHADER_INVOCATIONS, |
| |
| /// Count the number of patches processed by a tessellation control shader. |
| TESSELLATION_CONTROL_SHADER_PATCHES = TESSELLATION_CONTROL_SHADER_PATCHES, |
| |
| /// Count the number of times a tessellation evaluation shader is invoked. |
| TESSELLATION_EVALUATION_SHADER_INVOCATIONS = TESSELLATION_EVALUATION_SHADER_INVOCATIONS, |
| |
| /// Count the number of times a compute shader is invoked. |
| COMPUTE_SHADER_INVOCATIONS = COMPUTE_SHADER_INVOCATIONS, |
| |
| /* TODO: enable |
| // TODO: document |
| TASK_SHADER_INVOCATIONS = TASK_SHADER_INVOCATIONS_NV { |
| device_extensions: [nv_mesh_shader], |
| },*/ |
| |
| /* TODO: enable |
| // TODO: document |
| MESH_SHADER_INVOCATIONS = MESH_SHADER_INVOCATIONS_NV { |
| device_extensions: [nv_mesh_shader], |
| },*/ |
| } |
| |
| vulkan_bitflags! { |
| #[non_exhaustive] |
| |
| /// Flags to control how the results of a query should be retrieved. |
| /// |
| /// `VK_QUERY_RESULT_64_BIT` is not included, as it is determined automatically via the |
| /// [`QueryResultElement`] trait. |
| QueryResultFlags = QueryResultFlags(u32); |
| |
| /// Wait for the results to become available before writing the results. |
| WAIT = WAIT, |
| |
| /// Write an additional element to the end of each query's results, indicating the availability |
| /// of the results: |
| /// - Nonzero: The results are available, and have been written to the element(s) preceding. |
| /// - Zero: The results are not yet available, and have not been written. |
| WITH_AVAILABILITY = WITH_AVAILABILITY, |
| |
| /// Allow writing partial results to the buffer, instead of waiting until they are fully |
| /// available. |
| PARTIAL = PARTIAL, |
| |
| /* TODO: enable |
| // TODO: document |
| WITH_STATUS = WITH_STATUS_KHR { |
| device_extensions: [khr_video_queue], |
| },*/ |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::QueryPoolCreateInfo; |
| use crate::query::{QueryPipelineStatisticFlags, QueryPool, QueryPoolCreationError, QueryType}; |
| |
| #[test] |
| fn pipeline_statistics_feature() { |
| let (device, _) = gfx_dev_and_queue!(); |
| let query_type = QueryType::PipelineStatistics(QueryPipelineStatisticFlags::empty()); |
| match QueryPool::new( |
| device, |
| QueryPoolCreateInfo { |
| query_count: 256, |
| ..QueryPoolCreateInfo::query_type(query_type) |
| }, |
| ) { |
| Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled) => (), |
| _ => panic!(), |
| }; |
| } |
| } |