diff --git a/examples/src/bin/occlusion-query.rs b/examples/src/bin/occlusion-query.rs index affa2c3e..2e2fc334 100644 --- a/examples/src/bin/occlusion-query.rs +++ b/examples/src/bin/occlusion-query.rs @@ -25,7 +25,9 @@ use vulkano::pipeline::graphics::input_assembly::InputAssemblyState; use vulkano::pipeline::graphics::vertex_input::BuffersDefinition; use vulkano::pipeline::graphics::viewport::{Viewport, ViewportState}; use vulkano::pipeline::GraphicsPipeline; -use vulkano::query::{QueryControlFlags, QueryPool, QueryResultFlags, QueryType}; +use vulkano::query::{ + QueryControlFlags, QueryPool, QueryPoolCreateInfo, QueryResultFlags, QueryType, +}; use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass}; use vulkano::swapchain::{ self, AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError, @@ -187,7 +189,14 @@ fn main() { let triangle3 = vertex_buffer.slice(6..9).unwrap(); // Create a query pool for occlusion queries, with 3 slots. - let query_pool = QueryPool::new(device.clone(), QueryType::Occlusion, 3).unwrap(); + let query_pool = QueryPool::new( + device.clone(), + QueryPoolCreateInfo { + query_count: 3, + ..QueryPoolCreateInfo::query_type(QueryType::Occlusion) + }, + ) + .unwrap(); // Create a buffer on the CPU to hold the results of the three queries. // Query results are always represented as either `u32` or `u64`. diff --git a/vulkano/src/command_buffer/auto.rs b/vulkano/src/command_buffer/auto.rs index 827fcc5c..7f0390e9 100644 --- a/vulkano/src/command_buffer/auto.rs +++ b/vulkano/src/command_buffer/auto.rs @@ -3054,7 +3054,7 @@ impl AutoCommandBufferBuilder { ) -> Result<&mut Self, BeginQueryError> { check_begin_query(self.device(), &query_pool, query, flags)?; - match query_pool.ty() { + match query_pool.query_type() { QueryType::Occlusion => { if !self.queue_family().supports_graphics() { return Err( @@ -3074,7 +3074,7 @@ impl AutoCommandBufferBuilder { QueryType::Timestamp => unreachable!(), } - let ty = query_pool.ty(); + let ty = query_pool.query_type(); let raw_ty = ty.into(); let raw_query_pool = query_pool.internal_object(); if self.query_state.contains_key(&raw_ty) { @@ -3106,7 +3106,7 @@ impl AutoCommandBufferBuilder { unsafe { check_end_query(self.device(), &query_pool, query)?; - let raw_ty = query_pool.ty().into(); + let raw_ty = query_pool.query_type().into(); let raw_query_pool = query_pool.internal_object(); if !self.query_state.get(&raw_ty).map_or(false, |state| { state.query_pool == raw_query_pool && state.query == query diff --git a/vulkano/src/command_buffer/validity/query.rs b/vulkano/src/command_buffer/validity/query.rs index 52781f4a..793883e0 100644 --- a/vulkano/src/command_buffer/validity/query.rs +++ b/vulkano/src/command_buffer/validity/query.rs @@ -43,7 +43,7 @@ pub fn check_begin_query( .query(query) .ok_or(CheckBeginQueryError::OutOfRange)?; - match query_pool.ty() { + match query_pool.query_type() { QueryType::Occlusion => { if flags.precise && !device.enabled_features().occlusion_query_precise { return Err(CheckBeginQueryError::OcclusionQueryPreciseFeatureNotEnabled); @@ -162,7 +162,7 @@ pub fn check_write_timestamp( query_pool.device().internal_object(), ); - if !matches!(query_pool.ty(), QueryType::Timestamp) { + if !matches!(query_pool.query_type(), QueryType::Timestamp) { return Err(CheckWriteTimestampError::NotPermitted); } diff --git a/vulkano/src/query.rs b/vulkano/src/query.rs index 47ce50ce..355701e4 100644 --- a/vulkano/src/query.rs +++ b/vulkano/src/query.rs @@ -13,44 +13,59 @@ //! 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::check_errors; -use crate::device::Device; -use crate::device::DeviceOwned; -use crate::DeviceSize; -use crate::Error; -use crate::OomError; -use crate::Success; -use crate::VulkanObject; -use std::error; -use std::ffi::c_void; -use std::fmt; -use std::mem::MaybeUninit; -use std::ops::Range; -use std::ptr; -use std::sync::Arc; +use crate::{ + check_errors, + device::{Device, DeviceOwned}, + DeviceSize, Error, OomError, Success, VulkanObject, +}; +use std::{ + error, + ffi::c_void, + fmt, + hash::{Hash, Hasher}, + mem::MaybeUninit, + ops::Range, + ptr, + sync::Arc, +}; /// A collection of one or more queries of a particular type. #[derive(Debug)] pub struct QueryPool { - pool: ash::vk::QueryPool, + handle: ash::vk::QueryPool, device: Arc, - num_slots: u32, - ty: QueryType, + + query_type: QueryType, + query_count: u32, } impl QueryPool { - /// Builds a new query pool. + /// Creates a new `QueryPool`. + /// + /// # Panics + /// + /// - Panics if `create_info.query_count` is `0`. pub fn new( device: Arc, - ty: QueryType, - num_slots: u32, + create_info: QueryPoolCreateInfo, ) -> Result, QueryPoolCreationError> { - let statistics = match ty { + 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 => { @@ -58,20 +73,20 @@ impl QueryPool { } }; - let pool = unsafe { - let infos = ash::vk::QueryPoolCreateInfo { - flags: ash::vk::QueryPoolCreateFlags::empty(), - query_type: ty.into(), - query_count: num_slots, - pipeline_statistics: statistics, - ..Default::default() - }; + let create_info = ash::vk::QueryPoolCreateInfo { + flags: ash::vk::QueryPoolCreateFlags::empty(), + query_type: query_type.into(), + query_count, + pipeline_statistics, + ..Default::default() + }; - let mut output = MaybeUninit::uninit(); + let handle = unsafe { let fns = device.fns(); + let mut output = MaybeUninit::uninit(); check_errors(fns.v1_0.create_query_pool( device.internal_object(), - &infos, + &create_info, ptr::null(), output.as_mut_ptr(), ))?; @@ -79,29 +94,30 @@ impl QueryPool { }; Ok(Arc::new(QueryPool { - pool, + handle, device, - num_slots, - ty, + + query_type, + query_count, })) } - /// Returns the [`QueryType`] that this query pool was created with. + /// Returns the query type of the pool. #[inline] - pub fn ty(&self) -> QueryType { - self.ty + pub fn query_type(&self) -> QueryType { + self.query_type } /// Returns the number of query slots of this query pool. #[inline] - pub fn num_slots(&self) -> u32 { - self.num_slots + 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 { - if index < self.num_slots() { + if index < self.query_count { Some(Query { pool: self, index }) } else { None @@ -117,7 +133,7 @@ impl QueryPool { pub fn queries_range(&self, range: Range) -> Option { assert!(!range.is_empty()); - if range.end <= self.num_slots() { + if range.end <= self.query_count { Some(QueriesRange { pool: self, range }) } else { None @@ -125,12 +141,23 @@ impl QueryPool { } } +impl Drop for QueryPool { + #[inline] + fn drop(&mut self) { + unsafe { + let fns = self.device.fns(); + fns.v1_0 + .destroy_query_pool(self.device.internal_object(), self.handle, ptr::null()); + } + } +} + unsafe impl VulkanObject for QueryPool { type Object = ash::vk::QueryPool; #[inline] fn internal_object(&self) -> ash::vk::QueryPool { - self.pool + self.handle } } @@ -141,13 +168,47 @@ unsafe impl DeviceOwned for QueryPool { } } -impl Drop for QueryPool { +impl PartialEq for QueryPool { #[inline] - fn drop(&mut self) { - unsafe { - let fns = self.device.fns(); - fns.v1_0 - .destroy_query_pool(self.device.internal_object(), self.pool, ptr::null()); + fn eq(&self, other: &Self) -> bool { + self.handle == other.handle && self.device() == other.device() + } +} + +impl Eq for QueryPool {} + +impl Hash for QueryPool { + #[inline] + fn hash(&self, state: &mut H) { + self.handle.hash(state); + self.device().hash(state); + } +} + +/// 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(()), } } } @@ -310,7 +371,8 @@ impl<'a> QueriesRange<'a> { debug_assert!(buffer_start % std::mem::size_of::() as DeviceSize == 0); let count = self.range.end - self.range.start; - let per_query_len = self.pool.ty.result_size() + flags.with_availability as DeviceSize; + let per_query_len = + self.pool.query_type.result_size() + flags.with_availability as DeviceSize; let required_len = per_query_len * count as DeviceSize; if buffer_len < required_len { @@ -320,7 +382,7 @@ impl<'a> QueriesRange<'a> { }); } - match self.pool.ty { + match self.pool.query_type { QueryType::Occlusion => (), QueryType::PipelineStatistics(_) => (), QueryType::Timestamp => { @@ -670,6 +732,7 @@ impl From for ash::vk::QueryResultFlags { #[cfg(test)] mod tests { + use super::QueryPoolCreateInfo; use crate::query::QueryPipelineStatisticFlags; use crate::query::QueryPool; use crate::query::QueryPoolCreationError; @@ -678,9 +741,14 @@ mod tests { #[test] fn pipeline_statistics_feature() { let (device, _) = gfx_dev_and_queue!(); - - let ty = QueryType::PipelineStatistics(QueryPipelineStatisticFlags::none()); - match QueryPool::new(device, ty, 256) { + let query_type = QueryType::PipelineStatistics(QueryPipelineStatisticFlags::none()); + match QueryPool::new( + device, + QueryPoolCreateInfo { + query_count: 256, + ..QueryPoolCreateInfo::query_type(query_type) + }, + ) { Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled) => (), _ => panic!(), };