diff --git a/vulkano/src/query.rs b/vulkano/src/query.rs index 1a224757a..514ed4f04 100644 --- a/vulkano/src/query.rs +++ b/vulkano/src/query.rs @@ -13,6 +13,8 @@ //! 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 std::error; +use std::fmt; use std::mem; use std::ptr; use std::sync::Arc; @@ -20,15 +22,221 @@ use std::sync::Arc; use device::Device; use check_errors; +use Error; use OomError; +use SafeDeref; use VulkanObject; use VulkanPointers; use vk; -pub struct OcclusionQueriesPool { +pub struct UnsafeQueryPool

> where P: SafeDeref { pool: vk::QueryPool, + device: P, num_slots: u32, - device: Arc, +} + +impl

UnsafeQueryPool

where P: SafeDeref { + /// Builds a new query pool. + pub fn new(device: P, ty: QueryType, num_slots: u32) + -> Result, QueryPoolCreationError> + { + let (vk_ty, statistics) = match ty { + QueryType::Occlusion => (vk::QUERY_TYPE_OCCLUSION, 0), + QueryType::Timestamp => (vk::QUERY_TYPE_TIMESTAMP, 0), + QueryType::PipelineStatistics(flags) => { + if !device.enabled_features().pipeline_statistics_query { + return Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled); + } + + (vk::QUERY_TYPE_PIPELINE_STATISTICS, flags.into()) + }, + }; + + let pool = unsafe { + let infos = vk::QueryPoolCreateInfo { + sType: vk::STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, + pNext: ptr::null(), + flags: 0, // reserved + queryType: vk_ty, + queryCount: num_slots, + pipelineStatistics: statistics, + }; + + let mut output = mem::uninitialized(); + let vk = device.pointers(); + try!(check_errors(vk.CreateQueryPool(device.internal_object(), &infos, + ptr::null(), &mut output))); + output + }; + + Ok(UnsafeQueryPool { + pool: pool, + device: device, + num_slots: num_slots, + }) + } + + /// Returns the number of slots of that query pool. + #[inline] + pub fn num_slots(&self) -> u32 { + self.num_slots + } + + /// Returns the device used to create the pool. + #[inline] + pub fn device(&self) -> &P { + &self.device + } +} + +#[derive(Debug, Copy, Clone)] +pub enum QueryType { + Occlusion, + PipelineStatistics(QueryPipelineStatisticFlags), + Timestamp, +} + +#[derive(Debug, Copy, Clone)] +pub struct QueryPipelineStatisticFlags { + pub input_assembly_vertices: bool, + pub input_assembly_primitives: bool, + pub vertex_shader_invocations: bool, + pub geometry_shader_invocations: bool, + pub geometry_shader_primitives: bool, + pub clipping_invocations: bool, + pub clipping_primitives: bool, + pub fragment_shader_invocations: bool, + pub tessellation_control_shader_patches: bool, + pub tessellation_evaluation_shader_invocations: bool, + 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, + } + } +} + +impl Into for QueryPipelineStatisticFlags { + fn into(self) -> vk::QueryPipelineStatisticFlags { + let mut result = 0; + if self.input_assembly_vertices { + result |= vk::QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT; + } + if self.input_assembly_primitives { + result |= vk::QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT; + } + if self.vertex_shader_invocations { + result |= vk::QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT; + } + if self.geometry_shader_invocations { + result |= vk::QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT; + } + if self.geometry_shader_primitives { + result |= vk::QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT; + } + if self.clipping_invocations { + result |= vk::QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT; + } + if self.clipping_primitives { + result |= vk::QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT; + } + if self.fragment_shader_invocations { + result |= vk::QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT; + } + if self.tessellation_control_shader_patches { + result |= vk::QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT; + } + if self.tessellation_evaluation_shader_invocations { + result |= vk::QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT; + } + if self.compute_shader_invocations { + result |= vk::QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT; + } + result + } +} + +impl

Drop for UnsafeQueryPool

where P: SafeDeref { + #[inline] + fn drop(&mut self) { + unsafe { + let vk = self.device.pointers(); + vk.DestroyQueryPool(self.device.internal_object(), self.pool, ptr::null()); + } + } +} + +/// Error that can happen when creating a buffer. +#[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 description(&self) -> &str { + match *self { + QueryPoolCreationError::OomError(_) => "not enough memory available", + QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled => { + "a pipeline statistics pool was requested but the corresponding feature \ + wasn't enabled" + }, + } + } + + #[inline] + fn cause(&self) -> Option<&error::Error> { + 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, "{}", error::Error::description(self)) + } +} + +impl From for QueryPoolCreationError { + #[inline] + fn from(err: OomError) -> QueryPoolCreationError { + QueryPoolCreationError::OomError(err) + } +} + +impl From 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) + } + } +} + +pub struct OcclusionQueriesPool { + inner: UnsafeQueryPool, } impl OcclusionQueriesPool { @@ -36,28 +244,14 @@ impl OcclusionQueriesPool { pub fn raw(device: &Arc, num_slots: u32) -> Result { - let vk = device.pointers(); - - let pool = unsafe { - let infos = vk::QueryPoolCreateInfo { - sType: vk::STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, - pNext: ptr::null(), - flags: 0, // reserved - queryType: vk::QUERY_TYPE_OCCLUSION, - queryCount: num_slots, - pipelineStatistics: 0, - }; - - let mut output = mem::uninitialized(); - try!(check_errors(vk.CreateQueryPool(device.internal_object(), &infos, - ptr::null(), &mut output))); - output - }; - Ok(OcclusionQueriesPool { - pool: pool, - num_slots: num_slots, - device: device.clone(), + inner: match UnsafeQueryPool::new(device.clone(), QueryType::Occlusion, num_slots) { + Ok(q) => q, + Err(QueryPoolCreationError::OomError(err)) => return Err(err), + Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled) => { + unreachable!() + }, + } }) } @@ -77,32 +271,38 @@ impl OcclusionQueriesPool { /// Returns the number of slots of that query pool. #[inline] pub fn num_slots(&self) -> u32 { - self.num_slots + self.inner.num_slots() } /// Returns the device that was used to create this pool. #[inline] pub fn device(&self) -> &Arc { - &self.device - } -} - -impl Drop for OcclusionQueriesPool { - fn drop(&mut self) { - unsafe { - let vk = self.device.pointers(); - vk.DestroyQueryPool(self.device.internal_object(), self.pool, ptr::null()); - } + self.inner.device() } } #[cfg(test)] mod tests { use query::OcclusionQueriesPool; + use query::QueryPipelineStatisticFlags; + use query::QueryPoolCreationError; + use query::QueryType; + use query::UnsafeQueryPool; #[test] fn occlusion_create() { let (device, _) = gfx_dev_and_queue!(); let _ = OcclusionQueriesPool::new(&device, 256); } + + #[test] + fn pipeline_statistics_feature() { + let (device, _) = gfx_dev_and_queue!(); + + let ty = QueryType::PipelineStatistics(QueryPipelineStatisticFlags::none()); + match UnsafeQueryPool::new(device, ty, 256) { + Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled) => (), + _ => panic!() + }; + } }