diff options
Diffstat (limited to 'src/command_buffer/commands/query.rs')
-rw-r--r-- | src/command_buffer/commands/query.rs | 1024 |
1 files changed, 1024 insertions, 0 deletions
diff --git a/src/command_buffer/commands/query.rs b/src/command_buffer/commands/query.rs new file mode 100644 index 0000000..75adb76 --- /dev/null +++ b/src/command_buffer/commands/query.rs @@ -0,0 +1,1024 @@ +// Copyright (c) 2022 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. + +use crate::{ + buffer::{BufferUsage, Subbuffer}, + command_buffer::{ + allocator::CommandBufferAllocator, + auto::QueryState, + synced::{Command, Resource, SyncCommandBufferBuilder, SyncCommandBufferBuilderError}, + sys::UnsafeCommandBufferBuilder, + AutoCommandBufferBuilder, ResourceInCommand, ResourceUseRef, + }, + device::{DeviceOwned, QueueFlags}, + query::{ + QueriesRange, Query, QueryControlFlags, QueryPool, QueryResultElement, QueryResultFlags, + QueryType, + }, + sync::{AccessFlags, PipelineMemoryAccess, PipelineStage, PipelineStages}, + DeviceSize, RequirementNotMet, RequiresOneOf, Version, VulkanObject, +}; +use std::{ + error::Error, + fmt::{Display, Error as FmtError, Formatter}, + mem::size_of, + ops::Range, + sync::Arc, +}; + +/// # Commands related to queries. +impl<L, A> AutoCommandBufferBuilder<L, A> +where + A: CommandBufferAllocator, +{ + /// Begins a query. + /// + /// The query will be active until [`end_query`](Self::end_query) is called for the same query. + /// + /// # Safety + /// + /// The query must be unavailable, ensured by calling + /// [`reset_query_pool`](Self::reset_query_pool). + pub unsafe fn begin_query( + &mut self, + query_pool: Arc<QueryPool>, + query: u32, + flags: QueryControlFlags, + ) -> Result<&mut Self, QueryError> { + self.validate_begin_query(&query_pool, query, flags)?; + + let ty = query_pool.query_type(); + let raw_query_pool = query_pool.handle(); + + self.inner.begin_query(query_pool, query, flags); + self.query_state.insert( + ty.into(), + QueryState { + query_pool: raw_query_pool, + query, + ty, + flags, + in_subpass: self.render_pass_state.is_some(), + }, + ); + + Ok(self) + } + + fn validate_begin_query( + &self, + query_pool: &QueryPool, + query: u32, + flags: QueryControlFlags, + ) -> Result<(), QueryError> { + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdBeginQuery-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(QueryError::NotSupportedByQueueFamily); + } + + let device = self.device(); + + // VUID-vkCmdBeginQuery-flags-parameter + flags.validate_device(device)?; + + // VUID-vkCmdBeginQuery-commonparent + assert_eq!(device, query_pool.device()); + + // VUID-vkCmdBeginQuery-query-00802 + query_pool.query(query).ok_or(QueryError::OutOfRange)?; + + match query_pool.query_type() { + QueryType::Occlusion => { + // VUID-vkCmdBeginQuery-commandBuffer-cmdpool + // // VUID-vkCmdBeginQuery-queryType-00803 + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(QueryError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdBeginQuery-queryType-00800 + if flags.intersects(QueryControlFlags::PRECISE) + && !device.enabled_features().occlusion_query_precise + { + return Err(QueryError::RequirementNotMet { + required_for: "`flags` contains `QueryControlFlags::PRECISE`", + requires_one_of: RequiresOneOf { + features: &["occlusion_query_precise"], + ..Default::default() + }, + }); + } + } + QueryType::PipelineStatistics(statistic_flags) => { + // VUID-vkCmdBeginQuery-commandBuffer-cmdpool + // VUID-vkCmdBeginQuery-queryType-00804 + // VUID-vkCmdBeginQuery-queryType-00805 + if statistic_flags.is_compute() + && !queue_family_properties + .queue_flags + .intersects(QueueFlags::COMPUTE) + || statistic_flags.is_graphics() + && !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS) + { + return Err(QueryError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdBeginQuery-queryType-00800 + if flags.intersects(QueryControlFlags::PRECISE) { + return Err(QueryError::InvalidFlags); + } + } + // VUID-vkCmdBeginQuery-queryType-02804 + QueryType::Timestamp => return Err(QueryError::NotPermitted), + } + + // VUID-vkCmdBeginQuery-queryPool-01922 + if self + .query_state + .contains_key(&query_pool.query_type().into()) + { + return Err(QueryError::QueryIsActive); + } + + if let Some(render_pass_state) = &self.render_pass_state { + // VUID-vkCmdBeginQuery-query-00808 + if query + render_pass_state.view_mask.count_ones() > query_pool.query_count() { + return Err(QueryError::OutOfRangeMultiview); + } + } + + // VUID-vkCmdBeginQuery-None-00807 + // Not checked, therefore unsafe. + // TODO: add check. + + Ok(()) + } + + /// Ends an active query. + pub fn end_query( + &mut self, + query_pool: Arc<QueryPool>, + query: u32, + ) -> Result<&mut Self, QueryError> { + self.validate_end_query(&query_pool, query)?; + + unsafe { + let raw_ty = query_pool.query_type().into(); + self.inner.end_query(query_pool, query); + self.query_state.remove(&raw_ty); + } + + Ok(self) + } + + fn validate_end_query(&self, query_pool: &QueryPool, query: u32) -> Result<(), QueryError> { + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdEndQuery-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(QueryError::NotSupportedByQueueFamily); + } + + let device = self.device(); + + // VUID-vkCmdEndQuery-commonparent + assert_eq!(device, query_pool.device()); + + // VUID-vkCmdEndQuery-None-01923 + if !self + .query_state + .get(&query_pool.query_type().into()) + .map_or(false, |state| { + state.query_pool == query_pool.handle() && state.query == query + }) + { + return Err(QueryError::QueryNotActive); + } + + // VUID-vkCmdEndQuery-query-00810 + query_pool.query(query).ok_or(QueryError::OutOfRange)?; + + if let Some(render_pass_state) = &self.render_pass_state { + // VUID-vkCmdEndQuery-query-00812 + if query + render_pass_state.view_mask.count_ones() > query_pool.query_count() { + return Err(QueryError::OutOfRangeMultiview); + } + } + + Ok(()) + } + + /// Writes a timestamp to a timestamp query. + /// + /// # Safety + /// + /// The query must be unavailable, ensured by calling + /// [`reset_query_pool`](Self::reset_query_pool). + pub unsafe fn write_timestamp( + &mut self, + query_pool: Arc<QueryPool>, + query: u32, + stage: PipelineStage, + ) -> Result<&mut Self, QueryError> { + self.validate_write_timestamp(&query_pool, query, stage)?; + + self.inner.write_timestamp(query_pool, query, stage); + + Ok(self) + } + + fn validate_write_timestamp( + &self, + query_pool: &QueryPool, + query: u32, + stage: PipelineStage, + ) -> Result<(), QueryError> { + let device = self.device(); + + if !device.enabled_features().synchronization2 && PipelineStages::from(stage).is_2() { + return Err(QueryError::RequirementNotMet { + required_for: "`stage` has flags set from `VkPipelineStageFlagBits2`", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdWriteTimestamp2-stage-parameter + stage.validate_device(device)?; + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdWriteTimestamp2-commandBuffer-cmdpool + if !queue_family_properties.queue_flags.intersects( + QueueFlags::TRANSFER + | QueueFlags::GRAPHICS + | QueueFlags::COMPUTE + | QueueFlags::VIDEO_DECODE + | QueueFlags::VIDEO_ENCODE, + ) { + return Err(QueryError::NotSupportedByQueueFamily); + } + + let device = self.device(); + + // VUID-vkCmdWriteTimestamp2-commonparent + assert_eq!(device, query_pool.device()); + + // VUID-vkCmdWriteTimestamp2-stage-03860 + if !PipelineStages::from(queue_family_properties.queue_flags).contains_enum(stage) { + return Err(QueryError::StageNotSupported); + } + + match stage { + PipelineStage::GeometryShader => { + // VUID-vkCmdWriteTimestamp2-stage-03929 + if !device.enabled_features().geometry_shader { + return Err(QueryError::RequirementNotMet { + required_for: "`stage` is `PipelineStage::GeometryShader`", + requires_one_of: RequiresOneOf { + features: &["geometry_shadere"], + ..Default::default() + }, + }); + } + } + PipelineStage::TessellationControlShader + | PipelineStage::TessellationEvaluationShader => { + // VUID-vkCmdWriteTimestamp2-stage-03930 + if !device.enabled_features().tessellation_shader { + return Err(QueryError::RequirementNotMet { + required_for: "`stage` is `PipelineStage::TessellationControlShader` or \ + `PipelineStage::TessellationEvaluationShader`", + requires_one_of: RequiresOneOf { + features: &["tessellation_shader"], + ..Default::default() + }, + }); + } + } + PipelineStage::ConditionalRendering => { + // VUID-vkCmdWriteTimestamp2-stage-03931 + if !device.enabled_features().conditional_rendering { + return Err(QueryError::RequirementNotMet { + required_for: "`stage` is `PipelineStage::ConditionalRendering`", + requires_one_of: RequiresOneOf { + features: &["conditional_rendering"], + ..Default::default() + }, + }); + } + } + PipelineStage::FragmentDensityProcess => { + // VUID-vkCmdWriteTimestamp2-stage-03932 + if !device.enabled_features().fragment_density_map { + return Err(QueryError::RequirementNotMet { + required_for: "`stage` is `PipelineStage::FragmentDensityProcess`", + requires_one_of: RequiresOneOf { + features: &["fragment_density_map"], + ..Default::default() + }, + }); + } + } + PipelineStage::TransformFeedback => { + // VUID-vkCmdWriteTimestamp2-stage-03933 + if !device.enabled_features().transform_feedback { + return Err(QueryError::RequirementNotMet { + required_for: "`stage` is `PipelineStage::TransformFeedback`", + requires_one_of: RequiresOneOf { + features: &["transform_feedback"], + ..Default::default() + }, + }); + } + } + PipelineStage::MeshShader => { + // VUID-vkCmdWriteTimestamp2-stage-03934 + if !device.enabled_features().mesh_shader { + return Err(QueryError::RequirementNotMet { + required_for: "`stage` is `PipelineStage::MeshShader`", + requires_one_of: RequiresOneOf { + features: &["mesh_shader"], + ..Default::default() + }, + }); + } + } + PipelineStage::TaskShader => { + // VUID-vkCmdWriteTimestamp2-stage-03935 + if !device.enabled_features().task_shader { + return Err(QueryError::RequirementNotMet { + required_for: "`stage` is `PipelineStage::TaskShader`", + requires_one_of: RequiresOneOf { + features: &["task_shader"], + ..Default::default() + }, + }); + } + } + PipelineStage::FragmentShadingRateAttachment => { + // VUID-vkCmdWriteTimestamp2-shadingRateImage-07316 + if !(device.enabled_features().attachment_fragment_shading_rate + || device.enabled_features().shading_rate_image) + { + return Err(QueryError::RequirementNotMet { + required_for: "`stage` is `PipelineStage::FragmentShadingRateAttachment`", + requires_one_of: RequiresOneOf { + features: &["attachment_fragment_shading_rate", "shading_rate_image"], + ..Default::default() + }, + }); + } + } + PipelineStage::SubpassShading => { + // VUID-vkCmdWriteTimestamp2-stage-04957 + if !device.enabled_features().subpass_shading { + return Err(QueryError::RequirementNotMet { + required_for: "`stage` is `PipelineStage::SubpassShading`", + requires_one_of: RequiresOneOf { + features: &["subpass_shading"], + ..Default::default() + }, + }); + } + } + PipelineStage::InvocationMask => { + // VUID-vkCmdWriteTimestamp2-stage-04995 + if !device.enabled_features().invocation_mask { + return Err(QueryError::RequirementNotMet { + required_for: "`stage` is `PipelineStage::InvocationMask`", + requires_one_of: RequiresOneOf { + features: &["invocation_mask"], + ..Default::default() + }, + }); + } + } + _ => (), + } + + // VUID-vkCmdWriteTimestamp2-queryPool-03861 + if !matches!(query_pool.query_type(), QueryType::Timestamp) { + return Err(QueryError::NotPermitted); + } + + // VUID-vkCmdWriteTimestamp2-timestampValidBits-03863 + if queue_family_properties.timestamp_valid_bits.is_none() { + return Err(QueryError::NoTimestampValidBits); + } + + // VUID-vkCmdWriteTimestamp2-query-04903 + query_pool.query(query).ok_or(QueryError::OutOfRange)?; + + if let Some(render_pass_state) = &self.render_pass_state { + // VUID-vkCmdWriteTimestamp2-query-03865 + if query + render_pass_state.view_mask.count_ones() > query_pool.query_count() { + return Err(QueryError::OutOfRangeMultiview); + } + } + + // VUID-vkCmdWriteTimestamp2-queryPool-03862 + // VUID-vkCmdWriteTimestamp2-None-03864 + // Not checked, therefore unsafe. + // TODO: add check. + + Ok(()) + } + + /// Copies the results of a range of queries to a buffer on the GPU. + /// + /// [`query_pool.ty().result_len()`] elements will be written for each query in the range, plus + /// 1 extra element per query if [`QueryResultFlags::WITH_AVAILABILITY`] is enabled. + /// The provided buffer must be large enough to hold the data. + /// + /// See also [`get_results`]. + /// + /// [`query_pool.ty().result_len()`]: crate::query::QueryType::result_len + /// [`QueryResultFlags::WITH_AVAILABILITY`]: crate::query::QueryResultFlags::WITH_AVAILABILITY + /// [`get_results`]: crate::query::QueriesRange::get_results + pub fn copy_query_pool_results<T>( + &mut self, + query_pool: Arc<QueryPool>, + queries: Range<u32>, + destination: Subbuffer<[T]>, + flags: QueryResultFlags, + ) -> Result<&mut Self, QueryError> + where + T: QueryResultElement, + { + self.validate_copy_query_pool_results(&query_pool, queries.clone(), &destination, flags)?; + + unsafe { + let per_query_len = query_pool.query_type().result_len() + + flags.intersects(QueryResultFlags::WITH_AVAILABILITY) as DeviceSize; + let stride = per_query_len * std::mem::size_of::<T>() as DeviceSize; + self.inner + .copy_query_pool_results(query_pool, queries, destination, stride, flags)?; + } + + Ok(self) + } + + fn validate_copy_query_pool_results<T>( + &self, + query_pool: &QueryPool, + queries: Range<u32>, + destination: &Subbuffer<[T]>, + flags: QueryResultFlags, + ) -> Result<(), QueryError> + where + T: QueryResultElement, + { + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdCopyQueryPoolResults-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(QueryError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdCopyQueryPoolResults-renderpass + if self.render_pass_state.is_some() { + return Err(QueryError::ForbiddenInsideRenderPass); + } + + let device = self.device(); + + // VUID-vkCmdCopyQueryPoolResults-commonparent + assert_eq!(device, destination.buffer().device()); + assert_eq!(device, query_pool.device()); + + assert!(destination.len() > 0); + + // VUID-vkCmdCopyQueryPoolResults-flags-00822 + // VUID-vkCmdCopyQueryPoolResults-flags-00823 + debug_assert!(destination.offset() % std::mem::size_of::<T>() as DeviceSize == 0); + + // VUID-vkCmdCopyQueryPoolResults-firstQuery-00820 + // VUID-vkCmdCopyQueryPoolResults-firstQuery-00821 + query_pool + .queries_range(queries.clone()) + .ok_or(QueryError::OutOfRange)?; + + let count = queries.end - queries.start; + let per_query_len = query_pool.query_type().result_len() + + flags.intersects(QueryResultFlags::WITH_AVAILABILITY) as DeviceSize; + let required_len = per_query_len * count as DeviceSize; + + // VUID-vkCmdCopyQueryPoolResults-dstBuffer-00824 + if destination.len() < required_len { + return Err(QueryError::BufferTooSmall { + required_len, + actual_len: destination.len(), + }); + } + + // VUID-vkCmdCopyQueryPoolResults-dstBuffer-00825 + if !destination + .buffer() + .usage() + .intersects(BufferUsage::TRANSFER_DST) + { + return Err(QueryError::DestinationMissingUsage); + } + + // VUID-vkCmdCopyQueryPoolResults-queryType-00827 + if matches!(query_pool.query_type(), QueryType::Timestamp) + && flags.intersects(QueryResultFlags::PARTIAL) + { + return Err(QueryError::InvalidFlags); + } + + Ok(()) + } + + /// Resets a range of queries on a query pool. + /// + /// The affected queries will be marked as "unavailable" after this command runs, and will no + /// longer return any results. They will be ready to have new results recorded for them. + /// + /// # Safety + /// The queries in the specified range must not be active in another command buffer. + // TODO: Do other command buffers actually matter here? Not sure on the Vulkan spec. + pub unsafe fn reset_query_pool( + &mut self, + query_pool: Arc<QueryPool>, + queries: Range<u32>, + ) -> Result<&mut Self, QueryError> { + self.validate_reset_query_pool(&query_pool, queries.clone())?; + + self.inner.reset_query_pool(query_pool, queries); + + Ok(self) + } + + fn validate_reset_query_pool( + &self, + query_pool: &QueryPool, + queries: Range<u32>, + ) -> Result<(), QueryError> { + // VUID-vkCmdResetQueryPool-renderpass + if self.render_pass_state.is_some() { + return Err(QueryError::ForbiddenInsideRenderPass); + } + + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdResetQueryPool-commandBuffer-cmdpool + if !queue_family_properties + .queue_flags + .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + { + return Err(QueryError::NotSupportedByQueueFamily); + } + + let device = self.device(); + + // VUID-vkCmdResetQueryPool-commonparent + assert_eq!(device, query_pool.device()); + + // VUID-vkCmdResetQueryPool-firstQuery-00796 + // VUID-vkCmdResetQueryPool-firstQuery-00797 + query_pool + .queries_range(queries.clone()) + .ok_or(QueryError::OutOfRange)?; + + // VUID-vkCmdResetQueryPool-None-02841 + if self + .query_state + .values() + .any(|state| state.query_pool == query_pool.handle() && queries.contains(&state.query)) + { + return Err(QueryError::QueryIsActive); + } + + Ok(()) + } +} + +impl SyncCommandBufferBuilder { + /// Calls `vkCmdBeginQuery` on the builder. + #[inline] + pub unsafe fn begin_query( + &mut self, + query_pool: Arc<QueryPool>, + query: u32, + flags: QueryControlFlags, + ) { + struct Cmd { + query_pool: Arc<QueryPool>, + query: u32, + flags: QueryControlFlags, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "begin_query" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.begin_query(self.query_pool.query(self.query).unwrap(), self.flags); + } + } + + self.commands.push(Box::new(Cmd { + query_pool, + query, + flags, + })); + } + + /// Calls `vkCmdEndQuery` on the builder. + #[inline] + pub unsafe fn end_query(&mut self, query_pool: Arc<QueryPool>, query: u32) { + struct Cmd { + query_pool: Arc<QueryPool>, + query: u32, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "end_query" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.end_query(self.query_pool.query(self.query).unwrap()); + } + } + + self.commands.push(Box::new(Cmd { query_pool, query })); + } + + /// Calls `vkCmdWriteTimestamp` on the builder. + #[inline] + pub unsafe fn write_timestamp( + &mut self, + query_pool: Arc<QueryPool>, + query: u32, + stage: PipelineStage, + ) { + struct Cmd { + query_pool: Arc<QueryPool>, + query: u32, + stage: PipelineStage, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "write_timestamp" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.write_timestamp(self.query_pool.query(self.query).unwrap(), self.stage); + } + } + + self.commands.push(Box::new(Cmd { + query_pool, + query, + stage, + })); + } + + /// Calls `vkCmdCopyQueryPoolResults` on the builder. + /// + /// # Safety + /// `stride` must be at least the number of bytes that will be written by each query. + pub unsafe fn copy_query_pool_results( + &mut self, + query_pool: Arc<QueryPool>, + queries: Range<u32>, + destination: Subbuffer<[impl QueryResultElement]>, + stride: DeviceSize, + flags: QueryResultFlags, + ) -> Result<(), SyncCommandBufferBuilderError> { + struct Cmd<T> { + query_pool: Arc<QueryPool>, + queries: Range<u32>, + destination: Subbuffer<[T]>, + stride: DeviceSize, + flags: QueryResultFlags, + } + + impl<T> Command for Cmd<T> + where + T: QueryResultElement, + { + fn name(&self) -> &'static str { + "copy_query_pool_results" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.copy_query_pool_results( + self.query_pool.queries_range(self.queries.clone()).unwrap(), + &self.destination, + self.stride, + self.flags, + ); + } + } + + let command_index = self.commands.len(); + let command_name = "copy_query_pool_results"; + let resources = [( + ResourceUseRef { + command_index, + command_name, + resource_in_command: ResourceInCommand::Destination, + secondary_use_ref: None, + }, + Resource::Buffer { + buffer: destination.as_bytes().clone(), + range: 0..destination.size(), // TODO: + memory: PipelineMemoryAccess { + stages: PipelineStages::ALL_TRANSFER, + access: AccessFlags::TRANSFER_WRITE, + exclusive: true, + }, + }, + )]; + + for resource in &resources { + self.check_resource_conflicts(resource)?; + } + + self.commands.push(Box::new(Cmd { + query_pool, + queries, + destination, + stride, + flags, + })); + + for resource in resources { + self.add_resource(resource); + } + + Ok(()) + } + + /// Calls `vkCmdResetQueryPool` on the builder. + #[inline] + pub unsafe fn reset_query_pool(&mut self, query_pool: Arc<QueryPool>, queries: Range<u32>) { + struct Cmd { + query_pool: Arc<QueryPool>, + queries: Range<u32>, + } + + impl Command for Cmd { + fn name(&self) -> &'static str { + "reset_query_pool" + } + + unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { + out.reset_query_pool(self.query_pool.queries_range(self.queries.clone()).unwrap()); + } + } + + self.commands.push(Box::new(Cmd { + query_pool, + queries, + })); + } +} + +impl UnsafeCommandBufferBuilder { + /// Calls `vkCmdBeginQuery` on the builder. + #[inline] + pub unsafe fn begin_query(&mut self, query: Query<'_>, flags: QueryControlFlags) { + let fns = self.device.fns(); + (fns.v1_0.cmd_begin_query)( + self.handle, + query.pool().handle(), + query.index(), + flags.into(), + ); + } + + /// Calls `vkCmdEndQuery` on the builder. + #[inline] + pub unsafe fn end_query(&mut self, query: Query<'_>) { + let fns = self.device.fns(); + (fns.v1_0.cmd_end_query)(self.handle, query.pool().handle(), query.index()); + } + + /// Calls `vkCmdWriteTimestamp` on the builder. + #[inline] + pub unsafe fn write_timestamp(&mut self, query: Query<'_>, stage: PipelineStage) { + let fns = self.device.fns(); + + if self.device.enabled_features().synchronization2 { + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.cmd_write_timestamp2)( + self.handle, + stage.into(), + query.pool().handle(), + query.index(), + ); + } else { + debug_assert!(self.device.enabled_extensions().khr_synchronization2); + (fns.khr_synchronization2.cmd_write_timestamp2_khr)( + self.handle, + stage.into(), + query.pool().handle(), + query.index(), + ); + } + } else { + (fns.v1_0.cmd_write_timestamp)( + self.handle, + stage.into(), + query.pool().handle(), + query.index(), + ); + } + } + + /// Calls `vkCmdCopyQueryPoolResults` on the builder. + pub unsafe fn copy_query_pool_results<T>( + &mut self, + queries: QueriesRange<'_>, + destination: &Subbuffer<[T]>, + stride: DeviceSize, + flags: QueryResultFlags, + ) where + T: QueryResultElement, + { + let range = queries.range(); + debug_assert!(destination.offset() < destination.buffer().size()); + debug_assert!(destination + .buffer() + .usage() + .intersects(BufferUsage::TRANSFER_DST)); + debug_assert!(destination.offset() % size_of::<T>() as DeviceSize == 0); + debug_assert!(stride % size_of::<T>() as DeviceSize == 0); + + let fns = self.device.fns(); + (fns.v1_0.cmd_copy_query_pool_results)( + self.handle, + queries.pool().handle(), + range.start, + range.end - range.start, + destination.buffer().handle(), + destination.offset(), + stride, + ash::vk::QueryResultFlags::from(flags) | T::FLAG, + ); + } + + /// Calls `vkCmdResetQueryPool` on the builder. + #[inline] + pub unsafe fn reset_query_pool(&mut self, queries: QueriesRange<'_>) { + let range = queries.range(); + let fns = self.device.fns(); + (fns.v1_0.cmd_reset_query_pool)( + self.handle, + queries.pool().handle(), + range.start, + range.end - range.start, + ); + } +} + +/// Error that can happen when recording a query command. +#[derive(Clone, Debug)] +pub enum QueryError { + SyncCommandBufferBuilderError(SyncCommandBufferBuilderError), + + RequirementNotMet { + required_for: &'static str, + requires_one_of: RequiresOneOf, + }, + + /// The buffer is too small for the copy operation. + BufferTooSmall { + /// Required number of elements in the buffer. + required_len: DeviceSize, + /// Actual number of elements in the buffer. + actual_len: DeviceSize, + }, + + /// The destination buffer is missing the `transfer_dst` usage. + DestinationMissingUsage, + + /// Operation forbidden inside of a render pass. + ForbiddenInsideRenderPass, + + /// The provided flags are not allowed for this type of query. + InvalidFlags, + + /// The queue family's `timestamp_valid_bits` value is `None`. + NoTimestampValidBits, + + /// This operation is not permitted on this query type. + NotPermitted, + + /// The queue family doesn't allow this operation. + NotSupportedByQueueFamily, + + /// The provided query index is not valid for this pool. + OutOfRange, + + /// The provided query index plus the number of views in the current render subpass is greater + /// than the number of queries in the pool. + OutOfRangeMultiview, + + /// A query is active that conflicts with the current operation. + QueryIsActive, + + /// This query was not active. + QueryNotActive, + + /// The provided stage is not supported by the queue family. + StageNotSupported, +} + +impl Error for QueryError {} + +impl Display for QueryError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + match self { + Self::SyncCommandBufferBuilderError(_) => write!(f, "a SyncCommandBufferBuilderError"), + 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 copy operation") + } + Self::DestinationMissingUsage => write!( + f, + "the destination buffer is missing the `transfer_dst` usage", + ), + Self::ForbiddenInsideRenderPass => { + write!(f, "operation forbidden inside of a render pass") + } + Self::InvalidFlags => write!( + f, + "the provided flags are not allowed for this type of query", + ), + Self::NoTimestampValidBits => { + write!(f, "the queue family's timestamp_valid_bits value is None") + } + Self::NotPermitted => write!(f, "this operation is not permitted on this query type"), + Self::NotSupportedByQueueFamily => { + write!(f, "the queue family doesn't allow this operation") + } + Self::OutOfRange => write!(f, "the provided query index is not valid for this pool"), + Self::OutOfRangeMultiview => write!( + f, + "the provided query index plus the number of views in the current render subpass \ + is greater than the number of queries in the pool", + ), + Self::QueryIsActive => write!( + f, + "a query is active that conflicts with the current operation" + ), + Self::QueryNotActive => write!(f, "this query was not active"), + Self::StageNotSupported => { + write!(f, "the provided stage is not supported by the queue family") + } + } + } +} + +impl From<SyncCommandBufferBuilderError> for QueryError { + fn from(err: SyncCommandBufferBuilderError) -> Self { + Self::SyncCommandBufferBuilderError(err) + } +} + +impl From<RequirementNotMet> for QueryError { + fn from(err: RequirementNotMet) -> Self { + Self::RequirementNotMet { + required_for: err.required_for, + requires_one_of: err.requires_one_of, + } + } +} |