bedrock/
query.rs

1use crate::*;
2use derives::{bitflags_newtype, implements};
3
4/// Specify the type of queries managed by a query pool
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum QueryType {
7    /// An occlusion query
8    Occlusion,
9    /// A pipeline statistics query
10    PipelineStatistics(QueryPipelineStatisticFlags),
11    /// A timestamp query
12    Timestamp,
13}
14
15/// Structure specifying parameters of a newly created query pool
16#[repr(transparent)]
17#[derive(Clone)]
18pub struct QueryPoolCreateInfo(VkQueryPoolCreateInfo);
19impl QueryPoolCreateInfo {
20    pub const fn new(query_type: QueryType, count: u32) -> Self {
21        let (qt, ps) = match query_type {
22            QueryType::Occlusion => (VK_QUERY_TYPE_OCCLUSION, 0),
23            QueryType::PipelineStatistics(st) => (VK_QUERY_TYPE_PIPELINE_STATISTICS, st.bits()),
24            QueryType::Timestamp => (VK_QUERY_TYPE_TIMESTAMP, 0),
25        };
26
27        Self(VkQueryPoolCreateInfo {
28            sType: VkQueryPoolCreateInfo::TYPE,
29            pNext: core::ptr::null(),
30            flags: 0,
31            queryType: qt,
32            queryCount: count,
33            pipelineStatistics: ps,
34        })
35    }
36
37    pub const unsafe fn from_raw(raw: VkQueryPoolCreateInfo) -> Self {
38        Self(raw)
39    }
40
41    pub const fn into_raw(self) -> VkQueryPoolCreateInfo {
42        self.0
43    }
44}
45
46/// Opaque handle to a query pool object
47#[derive(VkHandle, VkObject)]
48#[VkObject(type = VkQueryPool::OBJECT_TYPE)]
49pub struct QueryPoolObject<Device: VkHandle<Handle = VkDevice>>(VkQueryPool, Device);
50#[implements]
51impl<Device: VkHandle<Handle = VkDevice>> Drop for QueryPoolObject<Device> {
52    #[inline(always)]
53    fn drop(&mut self) {
54        unsafe {
55            crate::vkfn::destroy_query_pool(self.1.native_ptr(), self.0, core::ptr::null());
56        }
57    }
58}
59unsafe impl<Device: VkHandle<Handle = VkDevice> + Sync> Sync for QueryPoolObject<Device> {}
60unsafe impl<Device: VkHandle<Handle = VkDevice> + Send> Send for QueryPoolObject<Device> {}
61impl<Device: VkHandle<Handle = VkDevice>> DeviceChildHandle for QueryPoolObject<Device> {
62    #[inline(always)]
63    fn device_handle(&self) -> VkDevice {
64        self.1.native_ptr()
65    }
66}
67impl<Device: crate::Device> DeviceChild for QueryPoolObject<Device> {
68    type ConcreteDevice = Device;
69
70    #[inline(always)]
71    fn device(&self) -> &Self::ConcreteDevice {
72        &self.1
73    }
74}
75impl<Device: VkHandle<Handle = VkDevice>> QueryPool for QueryPoolObject<Device> {}
76impl<Device: VkHandle<Handle = VkDevice>> QueryPoolObject<Device> {
77    /// Constructs from raw values
78    /// # Safety
79    /// the resource must be created from the device and not freed anywhere
80    pub const unsafe fn manage(handle: VkQueryPool, parent: Device) -> Self {
81        Self(handle, parent)
82    }
83
84    /// Purges the construct (Drop will not be called for this resource)
85    pub const fn unmanage(self) -> (VkQueryPool, Device) {
86        let h = self.0;
87        let p = unsafe { core::ptr::read(&self.1) };
88        core::mem::forget(self);
89
90        (h, p)
91    }
92}
93impl<Device: VkHandle<Handle = VkDevice> + Clone> QueryPoolObject<&'_ Device> {
94    /// Owning parent object by cloning it.
95    #[inline(always)]
96    pub fn clone_parent(self) -> QueryPoolObject<Device> {
97        let r = QueryPoolObject(self.0, self.1.clone());
98        core::mem::forget(self);
99
100        r
101    }
102}
103impl<Device: crate::Device> QueryPoolObject<Device> {
104    /// Create a new query pool object
105    /// # Failure
106    /// On failure, this command returns
107    ///
108    /// * [`VK_ERROR_OUT_OF_HOST_MEMORY`]
109    /// * [`VK_ERROR_OUT_OF_DEVICE_MEMORY`]
110    #[implements]
111    pub fn new(device: Device, info: &QueryPoolCreateInfo) -> crate::Result<Self> {
112        Ok(unsafe { Self::manage(device.new_query_pool_raw(info, None)?, device) })
113    }
114}
115
116pub enum QueryResult<T> {
117    Err(VkResult),
118    Ready(T),
119    NotReady,
120}
121impl QueryResult<()> {
122    pub(crate) const fn from_vk(r: VkResult) -> Self {
123        match r {
124            VK_NOT_READY => Self::NotReady,
125            r if r.is_err() => Self::Err(r),
126            _ => Self::Ready(()),
127        }
128    }
129}
130impl<T> QueryResult<T> {
131    #[inline(always)]
132    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> QueryResult<U> {
133        match self {
134            Self::Err(r) => QueryResult::Err(r),
135            Self::NotReady => QueryResult::NotReady,
136            Self::Ready(x) => QueryResult::Ready(f(x)),
137        }
138    }
139}
140
141pub trait QueryPool: VkHandle<Handle = VkQueryPool> + DeviceChildHandle {
142    /// Copy results of queries in a query pool to a host memory region
143    /// # Failure
144    /// On failure, this command returns
145    ///
146    /// * [`VK_ERROR_OUT_OF_HOST_MEMORY`]
147    /// * [`VK_ERROR_OUT_OF_DEVICE_MEMORY`]
148    /// * [`VK_ERROR_DEVICE_LOST`]
149    #[implements]
150    #[inline]
151    fn results<T>(
152        &self,
153        offset: u32,
154        sink: &mut [core::mem::MaybeUninit<T>],
155        flags: QueryResultFlags,
156    ) -> QueryResult<()> {
157        unsafe {
158            QueryResult::from_vk(crate::vkfn::get_query_pool_results(
159                self.device_handle(),
160                self.native_ptr(),
161                offset,
162                sink.len() as _,
163                (core::mem::size_of::<T>() * sink.len()) as _,
164                sink.as_mut_ptr() as _,
165                core::mem::size_of::<T>() as _,
166                flags.bits(),
167            ))
168        }
169    }
170
171    /// Copy results of queries in a query pool to a host memory region
172    /// # Failure
173    /// On failure, this command returns
174    ///
175    /// * [`VK_ERROR_OUT_OF_HOST_MEMORY`]
176    /// * [`VK_ERROR_OUT_OF_DEVICE_MEMORY`]
177    /// * [`VK_ERROR_DEVICE_LOST`]
178    #[implements]
179    fn result_array<const N: usize, T>(&self, offset: u32, flags: QueryResultFlags) -> QueryResult<[T; N]> {
180        let mut sink = [const { unsafe { core::mem::MaybeUninit::<T>::uninit().assume_init() } }; N];
181        self.results(
182            offset,
183            unsafe { core::mem::transmute::<&mut [T], &mut [core::mem::MaybeUninit<T>]>(&mut sink[..]) },
184            flags,
185        )
186        .map(move |_| sink)
187    }
188
189    /// Copy results of queries in a query pool to a host memory region
190    /// # Failure
191    /// On failure, this command returns
192    ///
193    /// * [`VK_ERROR_OUT_OF_HOST_MEMORY`]
194    /// * [`VK_ERROR_OUT_OF_DEVICE_MEMORY`]
195    /// * [`VK_ERROR_DEVICE_LOST`]
196    #[implements("alloc")]
197    fn results64_alloc(&self, query_range: core::ops::Range<u32>, flags: QueryResultFlags) -> QueryResult<Vec<u64>> {
198        let mut v = Vec::with_capacity(query_range.len());
199        self.results(
200            query_range.start,
201            &mut v.spare_capacity_mut()[..query_range.len()],
202            flags | QueryResultFlags::WIDE,
203        )
204        .map(|_| {
205            unsafe {
206                v.set_len(query_range.len());
207            }
208            v
209        })
210    }
211
212    /// Copy results of queries in a query pool to a host memory region
213    /// # Failure
214    /// On failure, this command returns
215    ///
216    /// * [`VK_ERROR_OUT_OF_HOST_MEMORY`]
217    /// * [`VK_ERROR_OUT_OF_DEVICE_MEMORY`]
218    /// * [`VK_ERROR_DEVICE_LOST`]
219    #[implements("alloc")]
220    fn results32_alloc(&self, query_range: core::ops::Range<u32>, flags: QueryResultFlags) -> QueryResult<Vec<u32>> {
221        let mut v = Vec::with_capacity(query_range.len());
222        self.results(
223            query_range.start,
224            &mut v.spare_capacity_mut()[..query_range.len()],
225            flags,
226        )
227        .map(move |_| {
228            unsafe {
229                v.set_len(query_range.len());
230            }
231            v
232        })
233    }
234}
235
236/// Bitmask specifying queried pipeline statistics
237#[derive(Debug, Clone, Copy, PartialEq, Eq)]
238#[bitflags_newtype]
239pub struct QueryPipelineStatisticFlags(pub VkQueryPipelineStatisticFlags);
240impl QueryPipelineStatisticFlags {
241    /// Queries managed by the pool will count the number of vertices processed by the input assembly stage
242    pub const INPUT_ASSEMBLY_VERTICES: Self =
243        QueryPipelineStatisticFlags(VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT);
244    /// Queries managed by the pool will count the number of primitives processed by the input assembly state
245    pub const INPUT_ASSEMBLY_PRIMITIVES: Self =
246        QueryPipelineStatisticFlags(VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT);
247    /// Queries managed by the pool will count the number of vertex shader invocations
248    pub const VERTEX_SHADER_INVOCATIONS: Self =
249        QueryPipelineStatisticFlags(VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT);
250    /// Queries managed by the pool will count the number of geometry shader invocations
251    pub const GEOMETRY_SHADER_INVOCATIONS: Self =
252        QueryPipelineStatisticFlags(VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT);
253    /// Queries managed by the pool will count the number of primitives generated by geometry shader invocations
254    pub const GEOMETRY_SHADER_PRIMITIVES: Self =
255        QueryPipelineStatisticFlags(VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT);
256    /// Queries managed by the pool will count the number of primitives processed by the Primitive Clipping stage of the pipeline
257    pub const CLIPPING_INVOCATIONS: Self =
258        QueryPipelineStatisticFlags(VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT);
259    /// Queries managed by the pool will count the number of primitives output by the Primitive Clipping stage of the pipeline
260    pub const CLIPPING_PRIMITIVES: Self =
261        QueryPipelineStatisticFlags(VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT);
262    /// Queries managed by the pool will count the number of fragment shader invocations
263    pub const FRAGMENT_SHADER_INVOCATIONS: Self =
264        QueryPipelineStatisticFlags(VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT);
265    /// Queries managed by the pool will count the number of patches processed by the tessellation control shader
266    pub const TESSELLATION_CONTROL_SHADER_PATCHES: Self =
267        QueryPipelineStatisticFlags(VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT);
268    /// Queries managed by the pool will count the number of invocations of the tessellation evaluation shader
269    pub const TESSELLATION_EVALUATION_SHADER_INVOCATIONS: Self =
270        QueryPipelineStatisticFlags(VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT);
271    /// Queries managed by the pool will count the number of compute shader invocations
272    pub const COMPUTE_SHADER_INVOCATIONS: Self =
273        QueryPipelineStatisticFlags(VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT);
274}
275
276/// Bitmask specifying how and when query results are returned
277#[derive(Debug, Clone, Copy, PartialEq, Eq)]
278#[bitflags_newtype]
279pub struct QueryResultFlags(VkQueryResultFlags);
280impl QueryResultFlags {
281    /// Empty bits
282    pub const EMPTY: Self = QueryResultFlags(0);
283    /// The results will be written as array of 64-bit unsigned integer values
284    pub const WIDE: Self = QueryResultFlags(VK_QUERY_RESULT_64_BIT);
285    /// Vulkan will wait for each query's status to become available before retrieving its results
286    pub const WAIT: Self = QueryResultFlags(VK_QUERY_RESULT_WAIT_BIT);
287    /// The availability status accompanies the results
288    pub const WITH_AVAILABILITY: Self = QueryResultFlags(VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
289    /// Returning partial results is acceptable
290    pub const PARTIAL: Self = QueryResultFlags(VK_QUERY_RESULT_PARTIAL_BIT);
291}