Skip to main content

bedrock/
query.rs

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