aboutsummaryrefslogtreecommitdiff
path: root/src/query.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/query.rs')
-rw-r--r--src/query.rs688
1 files changed, 0 insertions, 688 deletions
diff --git a/src/query.rs b/src/query.rs
deleted file mode 100644
index 44caada..0000000
--- a/src/query.rs
+++ /dev/null
@@ -1,688 +0,0 @@
-// 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::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;
-
-/// A collection of one or more queries of a particular type.
-#[derive(Debug)]
-pub struct QueryPool {
- pool: ash::vk::QueryPool,
- device: Arc<Device>,
- num_slots: u32,
- ty: QueryType,
-}
-
-impl QueryPool {
- /// Builds a new query pool.
- pub fn new(
- device: Arc<Device>,
- ty: QueryType,
- num_slots: u32,
- ) -> Result<QueryPool, QueryPoolCreationError> {
- let statistics = match ty {
- QueryType::PipelineStatistics(flags) => {
- if !device.enabled_features().pipeline_statistics_query {
- return Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled);
- }
-
- flags.into()
- }
- QueryType::Occlusion | QueryType::Timestamp => {
- ash::vk::QueryPipelineStatisticFlags::empty()
- }
- };
-
- 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 mut output = MaybeUninit::uninit();
- let fns = device.fns();
- check_errors(fns.v1_0.create_query_pool(
- device.internal_object(),
- &infos,
- ptr::null(),
- output.as_mut_ptr(),
- ))?;
- output.assume_init()
- };
-
- Ok(QueryPool {
- pool,
- device,
- num_slots,
- ty,
- })
- }
-
- /// Returns the [`QueryType`] that this query pool was created with.
- #[inline]
- pub fn ty(&self) -> QueryType {
- self.ty
- }
-
- /// Returns the number of query slots of this query pool.
- #[inline]
- pub fn num_slots(&self) -> u32 {
- self.num_slots
- }
-
- /// 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.num_slots() {
- Some(Query { pool: self, index })
- } else {
- None
- }
- }
-
- /// Returns a reference to a range of queries, or `None` if out of range.
- ///
- /// # Panic
- ///
- /// 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.num_slots() {
- Some(QueriesRange { pool: self, range })
- } else {
- None
- }
- }
-}
-
-unsafe impl VulkanObject for QueryPool {
- type Object = ash::vk::QueryPool;
-
- #[inline]
- fn internal_object(&self) -> ash::vk::QueryPool {
- self.pool
- }
-}
-
-unsafe impl DeviceOwned for QueryPool {
- #[inline]
- fn device(&self) -> &Arc<Device> {
- &self.device
- }
-}
-
-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.pool, ptr::null());
- }
- }
-}
-
-/// 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::Error for QueryPoolCreationError {
- #[inline]
- fn source(&self) -> Option<&(dyn error::Error + 'static)> {
- match *self {
- QueryPoolCreationError::OomError(ref err) => Some(err),
- _ => None,
- }
- }
-}
-
-impl fmt::Display for QueryPoolCreationError {
- #[inline]
- fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- write!(
- fmt,
- "{}",
- 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 {
- #[inline]
- fn from(err: OomError) -> QueryPoolCreationError {
- QueryPoolCreationError::OomError(err)
- }
-}
-
-impl From<Error> for QueryPoolCreationError {
- #[inline]
- fn from(err: Error) -> QueryPoolCreationError {
- match err {
- err @ Error::OutOfHostMemory => QueryPoolCreationError::OomError(OomError::from(err)),
- err @ Error::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_size()`](QueryType::result_size) 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.
- ///
- /// `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`](crate::command_buffer::AutoCommandBufferBuilder::copy_query_pool_results).
- 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();
- check_errors(fns.v1_0.get_query_pool_results(
- self.pool.device.internal_object(),
- self.pool.internal_object(),
- self.range.start,
- self.range.end - self.range.start,
- std::mem::size_of_val(destination),
- destination.as_mut_ptr() as *mut c_void,
- stride,
- ash::vk::QueryResultFlags::from(flags) | T::FLAG,
- ))?
- };
-
- Ok(match result {
- Success::Success => true,
- Success::NotReady => false,
- s => panic!("unexpected success value: {:?}", s),
- })
- }
-
- pub(crate) fn check_query_pool_results<T>(
- &self,
- buffer_start: DeviceSize,
- buffer_len: DeviceSize,
- flags: QueryResultFlags,
- ) -> Result<DeviceSize, GetResultsError>
- where
- T: QueryResultElement,
- {
- assert!(buffer_len > 0);
- 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.ty.result_size() + flags.with_availability as DeviceSize;
- let required_len = per_query_len * count as DeviceSize;
-
- if buffer_len < required_len {
- return Err(GetResultsError::BufferTooSmall {
- required_len: required_len as DeviceSize,
- actual_len: buffer_len as DeviceSize,
- });
- }
-
- match self.pool.ty {
- QueryType::Occlusion => (),
- QueryType::PipelineStatistics(_) => (),
- QueryType::Timestamp => {
- if flags.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 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 connection to the device has been lost.
- DeviceLost,
- /// The provided flags are not allowed for this type of query.
- InvalidFlags,
- /// Not enough memory.
- OomError(OomError),
-}
-
-impl From<Error> for GetResultsError {
- #[inline]
- fn from(err: Error) -> Self {
- match err {
- Error::OutOfHostMemory | Error::OutOfDeviceMemory => {
- Self::OomError(OomError::from(err))
- }
- Error::DeviceLost => Self::DeviceLost,
- _ => panic!("unexpected error: {:?}", err),
- }
- }
-}
-
-impl From<OomError> for GetResultsError {
- #[inline]
- fn from(err: OomError) -> Self {
- Self::OomError(err)
- }
-}
-
-impl fmt::Display for GetResultsError {
- #[inline]
- fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- write!(
- fmt,
- "{}",
- match *self {
- Self::BufferTooSmall { .. } => {
- "the buffer is too small for the operation"
- }
- Self::DeviceLost => "the connection to the device has been lost",
- Self::InvalidFlags => {
- "the provided flags are not allowed for this type of query"
- }
- Self::OomError(_) => "not enough memory available",
- }
- )
- }
-}
-
-impl error::Error for GetResultsError {
- #[inline]
- fn source(&self) -> Option<&(dyn error::Error + 'static)> {
- match *self {
- Self::OomError(ref err) => Some(err),
- _ => None,
- }
- }
-}
-
-/// 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 {
- 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 [`QueryResultFlags::with_availability`] enabled, then
- /// an additional element is required per query.
- #[inline]
- pub const fn result_size(&self) -> DeviceSize {
- match self {
- Self::Occlusion | Self::Timestamp => 1,
- Self::PipelineStatistics(flags) => flags.count(),
- }
- }
-}
-
-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,
- }
- }
-}
-
-/// Flags that control how a query is to be executed.
-#[derive(Clone, Copy, Debug, Default)]
-pub struct QueryControlFlags {
- /// 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.
- pub precise: bool,
-}
-
-impl From<QueryControlFlags> for ash::vk::QueryControlFlags {
- #[inline]
- fn from(value: QueryControlFlags) -> Self {
- let mut result = ash::vk::QueryControlFlags::empty();
- if value.precise {
- result |= ash::vk::QueryControlFlags::PRECISE;
- }
- result
- }
-}
-
-/// For pipeline statistics queries, the statistics that should be gathered.
-#[derive(Clone, Copy, Debug, Default)]
-pub struct QueryPipelineStatisticFlags {
- /// Count the number of vertices processed by the input assembly.
- pub input_assembly_vertices: bool,
- /// Count the number of primitives processed by the input assembly.
- pub input_assembly_primitives: bool,
- /// Count the number of times a vertex shader is invoked.
- pub vertex_shader_invocations: bool,
- /// Count the number of times a geometry shader is invoked.
- pub geometry_shader_invocations: bool,
- /// Count the number of primitives generated by geometry shaders.
- pub geometry_shader_primitives: bool,
- /// Count the number of times the clipping stage is invoked on a primitive.
- pub clipping_invocations: bool,
- /// Count the number of primitives that are output by the clipping stage.
- pub clipping_primitives: bool,
- /// Count the number of times a fragment shader is invoked.
- pub fragment_shader_invocations: bool,
- /// Count the number of patches processed by a tessellation control shader.
- pub tessellation_control_shader_patches: bool,
- /// Count the number of times a tessellation evaluation shader is invoked.
- pub tessellation_evaluation_shader_invocations: bool,
- /// Count the number of times a compute shader is invoked.
- pub compute_shader_invocations: bool,
-}
-
-impl QueryPipelineStatisticFlags {
- #[inline]
- pub fn none() -> QueryPipelineStatisticFlags {
- QueryPipelineStatisticFlags {
- input_assembly_vertices: false,
- input_assembly_primitives: false,
- vertex_shader_invocations: false,
- geometry_shader_invocations: false,
- geometry_shader_primitives: false,
- clipping_invocations: false,
- clipping_primitives: false,
- fragment_shader_invocations: false,
- tessellation_control_shader_patches: false,
- tessellation_evaluation_shader_invocations: false,
- compute_shader_invocations: false,
- }
- }
-
- /// Returns the number of flags that are set to `true`.
- #[inline]
- pub const fn count(&self) -> DeviceSize {
- let &Self {
- input_assembly_vertices,
- input_assembly_primitives,
- vertex_shader_invocations,
- geometry_shader_invocations,
- geometry_shader_primitives,
- clipping_invocations,
- clipping_primitives,
- fragment_shader_invocations,
- tessellation_control_shader_patches,
- tessellation_evaluation_shader_invocations,
- compute_shader_invocations,
- } = self;
- input_assembly_vertices as DeviceSize
- + input_assembly_primitives as DeviceSize
- + vertex_shader_invocations as DeviceSize
- + geometry_shader_invocations as DeviceSize
- + geometry_shader_primitives as DeviceSize
- + clipping_invocations as DeviceSize
- + clipping_primitives as DeviceSize
- + fragment_shader_invocations as DeviceSize
- + tessellation_control_shader_patches as DeviceSize
- + tessellation_evaluation_shader_invocations as DeviceSize
- + compute_shader_invocations as DeviceSize
- }
-
- /// Returns `true` if any flags referring to compute operations are set to `true`.
- #[inline]
- pub const fn is_compute(&self) -> bool {
- let &Self {
- compute_shader_invocations,
- ..
- } = self;
- compute_shader_invocations
- }
-
- /// Returns `true` if any flags referring to graphics operations are set to `true`.
- #[inline]
- pub const fn is_graphics(&self) -> bool {
- let &Self {
- input_assembly_vertices,
- input_assembly_primitives,
- vertex_shader_invocations,
- geometry_shader_invocations,
- geometry_shader_primitives,
- clipping_invocations,
- clipping_primitives,
- fragment_shader_invocations,
- tessellation_control_shader_patches,
- tessellation_evaluation_shader_invocations,
- ..
- } = self;
- input_assembly_vertices
- || input_assembly_primitives
- || vertex_shader_invocations
- || geometry_shader_invocations
- || geometry_shader_primitives
- || clipping_invocations
- || clipping_primitives
- || fragment_shader_invocations
- || tessellation_control_shader_patches
- || tessellation_evaluation_shader_invocations
- }
-}
-
-impl From<QueryPipelineStatisticFlags> for ash::vk::QueryPipelineStatisticFlags {
- fn from(value: QueryPipelineStatisticFlags) -> ash::vk::QueryPipelineStatisticFlags {
- let mut result = ash::vk::QueryPipelineStatisticFlags::empty();
- if value.input_assembly_vertices {
- result |= ash::vk::QueryPipelineStatisticFlags::INPUT_ASSEMBLY_VERTICES;
- }
- if value.input_assembly_primitives {
- result |= ash::vk::QueryPipelineStatisticFlags::INPUT_ASSEMBLY_PRIMITIVES;
- }
- if value.vertex_shader_invocations {
- result |= ash::vk::QueryPipelineStatisticFlags::VERTEX_SHADER_INVOCATIONS;
- }
- if value.geometry_shader_invocations {
- result |= ash::vk::QueryPipelineStatisticFlags::GEOMETRY_SHADER_INVOCATIONS;
- }
- if value.geometry_shader_primitives {
- result |= ash::vk::QueryPipelineStatisticFlags::GEOMETRY_SHADER_PRIMITIVES;
- }
- if value.clipping_invocations {
- result |= ash::vk::QueryPipelineStatisticFlags::CLIPPING_INVOCATIONS;
- }
- if value.clipping_primitives {
- result |= ash::vk::QueryPipelineStatisticFlags::CLIPPING_PRIMITIVES;
- }
- if value.fragment_shader_invocations {
- result |= ash::vk::QueryPipelineStatisticFlags::FRAGMENT_SHADER_INVOCATIONS;
- }
- if value.tessellation_control_shader_patches {
- result |= ash::vk::QueryPipelineStatisticFlags::TESSELLATION_CONTROL_SHADER_PATCHES;
- }
- if value.tessellation_evaluation_shader_invocations {
- result |=
- ash::vk::QueryPipelineStatisticFlags::TESSELLATION_EVALUATION_SHADER_INVOCATIONS;
- }
- if value.compute_shader_invocations {
- result |= ash::vk::QueryPipelineStatisticFlags::COMPUTE_SHADER_INVOCATIONS;
- }
- result
- }
-}
-
-/// 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.
-#[derive(Clone, Copy, Debug, Default)]
-pub struct QueryResultFlags {
- /// Wait for the results to become available before writing the results.
- pub wait: bool,
- /// 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.
- pub with_availability: bool,
- /// Allow writing partial results to the buffer, instead of waiting until they are fully
- /// available.
- pub partial: bool,
-}
-
-impl From<QueryResultFlags> for ash::vk::QueryResultFlags {
- #[inline]
- fn from(value: QueryResultFlags) -> Self {
- let mut result = ash::vk::QueryResultFlags::empty();
- if value.wait {
- result |= ash::vk::QueryResultFlags::WAIT;
- }
- if value.with_availability {
- result |= ash::vk::QueryResultFlags::WITH_AVAILABILITY;
- }
- if value.partial {
- result |= ash::vk::QueryResultFlags::PARTIAL;
- }
- result
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::query::QueryPipelineStatisticFlags;
- use crate::query::QueryPool;
- use crate::query::QueryPoolCreationError;
- use crate::query::QueryType;
-
- #[test]
- fn pipeline_statistics_feature() {
- let (device, _) = gfx_dev_and_queue!();
-
- let ty = QueryType::PipelineStatistics(QueryPipelineStatisticFlags::none());
- match QueryPool::new(device, ty, 256) {
- Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled) => (),
- _ => panic!(),
- };
- }
-}