// Copyright (c) 2022 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // 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 AutoCommandBufferBuilder 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, 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, 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, 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( &mut self, query_pool: Arc, queries: Range, 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::() as DeviceSize; self.inner .copy_query_pool_results(query_pool, queries, destination, stride, flags)?; } Ok(self) } fn validate_copy_query_pool_results( &self, query_pool: &QueryPool, queries: Range, 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::() 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, queries: Range, ) -> 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, ) -> 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, query: u32, flags: QueryControlFlags, ) { struct Cmd { query_pool: Arc, 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, query: u32) { struct Cmd { query_pool: Arc, 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, query: u32, stage: PipelineStage, ) { struct Cmd { query_pool: Arc, 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, queries: Range, destination: Subbuffer<[impl QueryResultElement]>, stride: DeviceSize, flags: QueryResultFlags, ) -> Result<(), SyncCommandBufferBuilderError> { struct Cmd { query_pool: Arc, queries: Range, destination: Subbuffer<[T]>, stride: DeviceSize, flags: QueryResultFlags, } impl Command for Cmd 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, queries: Range) { struct Cmd { query_pool: Arc, queries: Range, } 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( &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::() as DeviceSize == 0); debug_assert!(stride % size_of::() 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 for QueryError { fn from(err: SyncCommandBufferBuilderError) -> Self { Self::SyncCommandBufferBuilderError(err) } } impl From for QueryError { fn from(err: RequirementNotMet) -> Self { Self::RequirementNotMet { required_for: err.required_for, requires_one_of: err.requires_one_of, } } }