Partial safe support for queries (#1542)

* Add get_results to UnsafeQueriesRange

* Add copy_query_pool_results command, remove "Unsafe" from names

* Documentation + changelog

* Add reset_query_pool

* Add begin_query and end_query

* Add query feature checks when starting an AutoCommandBufferBuilder

* Add write_timestamp

* Re-exports

* Further safety checks for write_timestamp

* Track query active state in AutoCommandBufferBuilder, further safety checks

* Move query/mod.rs to query.rs as there's no other submodules

* Check for query state when building
This commit is contained in:
Rua 2021-04-10 14:07:59 +02:00 committed by GitHub
parent e8e9c69d83
commit 5577099029
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1960 additions and 533 deletions

View File

@ -3,7 +3,6 @@
Please add new changes at the bottom, preceded by a hyphen -. Please add new changes at the bottom, preceded by a hyphen -.
Breaking changes should be listed first, before other changes, and should be preceded by - **Breaking**. Breaking changes should be listed first, before other changes, and should be preceded by - **Breaking**.
--> -->
- **Breaking** `AutoCommandBuffer` and the `CommandBuffer` trait have been split in two, one for primary and the other for secondary command buffers. `AutoCommandBufferBuilder` remains one type, but has a type parameter for the level of command buffer it will be create, and some of its methods are only implemented for builders that create `PrimaryAutoCommandBuffer`. - **Breaking** `AutoCommandBuffer` and the `CommandBuffer` trait have been split in two, one for primary and the other for secondary command buffers. `AutoCommandBufferBuilder` remains one type, but has a type parameter for the level of command buffer it will be create, and some of its methods are only implemented for builders that create `PrimaryAutoCommandBuffer`.
- **Breaking** `Kind` has been renamed to `CommandBufferLevel`, and for secondary command buffers it now contains a single `CommandBufferInheritance` value. - **Breaking** `Kind` has been renamed to `CommandBufferLevel`, and for secondary command buffers it now contains a single `CommandBufferInheritance` value.
- **Breaking** `CommandBufferInheritance::occlusion_query` and `UnsafeCommandBufferBuilder::begin_query` now take `QueryControlFlags` instead of a boolean. - **Breaking** `CommandBufferInheritance::occlusion_query` and `UnsafeCommandBufferBuilder::begin_query` now take `QueryControlFlags` instead of a boolean.
@ -24,6 +23,14 @@
- `GraphicsPipeline` and `Framebuffer` no longer have a render pass type parameter. - `GraphicsPipeline` and `Framebuffer` no longer have a render pass type parameter.
- `GraphicsPipelineAbstract` and `FramebufferAbstract` have trait methods to retrieve the render pass instead. - `GraphicsPipelineAbstract` and `FramebufferAbstract` have trait methods to retrieve the render pass instead.
- The `ordered_passes_renderpass!` and `single_pass_renderpass!` macros are unchanged externally. - The `ordered_passes_renderpass!` and `single_pass_renderpass!` macros are unchanged externally.
- Support for queries:
- **Breaking** `UnsafeQueryPool`, `UnsafeQuery` and `UnsafeQueriesRange` have `Unsafe` removed from their names.
- **Breaking** `QueriesRange` is now represented with a standard Rust `Range` in its API.
- **Breaking** The secondary command buffer constructors that have parameters for queries will check if the corresponding features are enabled, and return a different error type.
- Removed `OcclusionQueriesPool`, which was incomplete and never did anything useful.
- `get_results` has been added to `QueriesRange`, to copy query results to the CPU.
- The following functions have been added to both `SyncCommandBufferBuilder` and `AutoCommandBufferBuilder`: `begin_query` (still unsafe), `end_query` (safe), `write_timestamp` (still unsafe), `copy_query_pool_results` (safe), `reset_command_pool` (still unsafe).
- Better documentation of everything in the `query` module.
- The deprecated `cause` trait function on Vulkano error types is replaced with `source`. - The deprecated `cause` trait function on Vulkano error types is replaced with `source`.
- Vulkano-shaders: Fixed and refined the generation of the `readonly` descriptor attribute. It should now correctly mark uniforms and sampled images as read-only, but storage buffers and images only if explicitly marked as `readonly` in the shader. - Vulkano-shaders: Fixed and refined the generation of the `readonly` descriptor attribute. It should now correctly mark uniforms and sampled images as read-only, but storage buffers and images only if explicitly marked as `readonly` in the shader.
- Fixed bug in descriptor array layers check when the image is a cubemap. - Fixed bug in descriptor array layers check when the image is a cubemap.

View File

@ -56,6 +56,10 @@ use crate::pipeline::ComputePipelineAbstract;
use crate::pipeline::GraphicsPipelineAbstract; use crate::pipeline::GraphicsPipelineAbstract;
use crate::query::QueryControlFlags; use crate::query::QueryControlFlags;
use crate::query::QueryPipelineStatisticFlags; use crate::query::QueryPipelineStatisticFlags;
use crate::query::QueryPool;
use crate::query::QueryResultElement;
use crate::query::QueryResultFlags;
use crate::query::QueryType;
use crate::render_pass::Framebuffer; use crate::render_pass::Framebuffer;
use crate::render_pass::FramebufferAbstract; use crate::render_pass::FramebufferAbstract;
use crate::render_pass::LoadOp; use crate::render_pass::LoadOp;
@ -66,9 +70,11 @@ use crate::sync::AccessCheckError;
use crate::sync::AccessFlagBits; use crate::sync::AccessFlagBits;
use crate::sync::GpuFuture; use crate::sync::GpuFuture;
use crate::sync::PipelineMemoryAccess; use crate::sync::PipelineMemoryAccess;
use crate::sync::PipelineStage;
use crate::sync::PipelineStages; use crate::sync::PipelineStages;
use crate::VulkanObject; use crate::VulkanObject;
use crate::{OomError, SafeDeref}; use crate::{OomError, SafeDeref};
use fnv::FnvHashMap;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::error; use std::error;
use std::ffi::CStr; use std::ffi::CStr;
@ -76,6 +82,7 @@ use std::fmt;
use std::iter; use std::iter;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem; use std::mem;
use std::ops::Range;
use std::slice; use std::slice;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
@ -90,11 +97,8 @@ pub struct AutoCommandBufferBuilder<L, P = StandardCommandPoolBuilder> {
pool_builder_alloc: P, // Safety: must be dropped after `inner` pool_builder_alloc: P, // Safety: must be dropped after `inner`
state_cacher: StateCacher, state_cacher: StateCacher,
// True if the queue family supports graphics operations. // The queue family that this command buffer is being created for.
graphics_allowed: bool, queue_family_id: u32,
// True if the queue family supports compute operations.
compute_allowed: bool,
// The inheritance for secondary command buffers. // The inheritance for secondary command buffers.
inheritance: Option<CommandBufferInheritance<Box<dyn FramebufferAbstract + Send + Sync>>>, inheritance: Option<CommandBufferInheritance<Box<dyn FramebufferAbstract + Send + Sync>>>,
@ -105,6 +109,9 @@ pub struct AutoCommandBufferBuilder<L, P = StandardCommandPoolBuilder> {
// If we're inside a render pass, contains the render pass state. // If we're inside a render pass, contains the render pass state.
render_pass_state: Option<RenderPassState>, render_pass_state: Option<RenderPassState>,
// If any queries are active, this hashmap contains their state.
query_state: FnvHashMap<vk::QueryType, QueryState>,
_data: PhantomData<L>, _data: PhantomData<L>,
} }
@ -115,6 +122,15 @@ struct RenderPassState {
framebuffer: vk::Framebuffer, // Always null for secondary command buffers framebuffer: vk::Framebuffer, // Always null for secondary command buffers
} }
// The state of an active query.
struct QueryState {
query_pool: vk::QueryPool,
query: u32,
ty: QueryType,
flags: QueryControlFlags,
in_subpass: bool,
}
impl AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, StandardCommandPoolBuilder> { impl AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, StandardCommandPoolBuilder> {
#[inline] #[inline]
pub fn new( pub fn new(
@ -253,10 +269,25 @@ impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBui
query_statistics_flags: QueryPipelineStatisticFlags, query_statistics_flags: QueryPipelineStatisticFlags,
) -> Result< ) -> Result<
AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>, AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
OomError, BeginError,
> { > {
if occlusion_query.is_some() && !device.enabled_features().inherited_queries {
return Err(BeginError::InheritedQueriesFeatureNotEnabled);
}
if query_statistics_flags.count() > 0
&& !device.enabled_features().pipeline_statistics_query
{
return Err(BeginError::PipelineStatisticsQueryFeatureNotEnabled);
}
let level = CommandBufferLevel::secondary(occlusion_query, query_statistics_flags); let level = CommandBufferLevel::secondary(occlusion_query, query_statistics_flags);
AutoCommandBufferBuilder::with_flags(device, queue_family, level, Flags::None) Ok(AutoCommandBufferBuilder::with_flags(
device,
queue_family,
level,
Flags::None,
)?)
} }
/// Same as `secondary_compute_one_time_submit`, but allows specifying how queries are being inherited. /// Same as `secondary_compute_one_time_submit`, but allows specifying how queries are being inherited.
@ -268,10 +299,25 @@ impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBui
query_statistics_flags: QueryPipelineStatisticFlags, query_statistics_flags: QueryPipelineStatisticFlags,
) -> Result< ) -> Result<
AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>, AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
OomError, BeginError,
> { > {
if occlusion_query.is_some() && !device.enabled_features().inherited_queries {
return Err(BeginError::InheritedQueriesFeatureNotEnabled);
}
if query_statistics_flags.count() > 0
&& !device.enabled_features().pipeline_statistics_query
{
return Err(BeginError::PipelineStatisticsQueryFeatureNotEnabled);
}
let level = CommandBufferLevel::secondary(occlusion_query, query_statistics_flags); let level = CommandBufferLevel::secondary(occlusion_query, query_statistics_flags);
AutoCommandBufferBuilder::with_flags(device, queue_family, level, Flags::OneTimeSubmit) Ok(AutoCommandBufferBuilder::with_flags(
device,
queue_family,
level,
Flags::OneTimeSubmit,
)?)
} }
/// Same as `secondary_compute_simultaneous_use`, but allows specifying how queries are being inherited. /// Same as `secondary_compute_simultaneous_use`, but allows specifying how queries are being inherited.
@ -283,10 +329,25 @@ impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBui
query_statistics_flags: QueryPipelineStatisticFlags, query_statistics_flags: QueryPipelineStatisticFlags,
) -> Result< ) -> Result<
AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>, AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
OomError, BeginError,
> { > {
if occlusion_query.is_some() && !device.enabled_features().inherited_queries {
return Err(BeginError::InheritedQueriesFeatureNotEnabled);
}
if query_statistics_flags.count() > 0
&& !device.enabled_features().pipeline_statistics_query
{
return Err(BeginError::PipelineStatisticsQueryFeatureNotEnabled);
}
let level = CommandBufferLevel::secondary(occlusion_query, query_statistics_flags); let level = CommandBufferLevel::secondary(occlusion_query, query_statistics_flags);
AutoCommandBufferBuilder::with_flags(device, queue_family, level, Flags::SimultaneousUse) Ok(AutoCommandBufferBuilder::with_flags(
device,
queue_family,
level,
Flags::SimultaneousUse,
)?)
} }
/// Starts building a secondary graphics command buffer. /// Starts building a secondary graphics command buffer.
@ -367,7 +428,7 @@ impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBui
/// Same as `secondary_graphics`, but allows specifying how queries are being inherited. /// Same as `secondary_graphics`, but allows specifying how queries are being inherited.
#[inline] #[inline]
pub fn secondary_graphics_inherit_queries<R>( pub fn secondary_graphics_inherit_queries(
device: Arc<Device>, device: Arc<Device>,
queue_family: QueueFamily, queue_family: QueueFamily,
subpass: Subpass, subpass: Subpass,
@ -375,8 +436,18 @@ impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBui
query_statistics_flags: QueryPipelineStatisticFlags, query_statistics_flags: QueryPipelineStatisticFlags,
) -> Result< ) -> Result<
AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>, AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
OomError, BeginError,
> { > {
if occlusion_query.is_some() && !device.enabled_features().inherited_queries {
return Err(BeginError::InheritedQueriesFeatureNotEnabled);
}
if query_statistics_flags.count() > 0
&& !device.enabled_features().pipeline_statistics_query
{
return Err(BeginError::PipelineStatisticsQueryFeatureNotEnabled);
}
let level = CommandBufferLevel::Secondary(CommandBufferInheritance { let level = CommandBufferLevel::Secondary(CommandBufferInheritance {
render_pass: Some(CommandBufferInheritanceRenderPass { render_pass: Some(CommandBufferInheritanceRenderPass {
subpass, subpass,
@ -386,12 +457,17 @@ impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBui
query_statistics_flags, query_statistics_flags,
}); });
AutoCommandBufferBuilder::with_flags(device, queue_family, level, Flags::None) Ok(AutoCommandBufferBuilder::with_flags(
device,
queue_family,
level,
Flags::None,
)?)
} }
/// Same as `secondary_graphics_one_time_submit`, but allows specifying how queries are being inherited. /// Same as `secondary_graphics_one_time_submit`, but allows specifying how queries are being inherited.
#[inline] #[inline]
pub fn secondary_graphics_one_time_submit_inherit_queries<R>( pub fn secondary_graphics_one_time_submit_inherit_queries(
device: Arc<Device>, device: Arc<Device>,
queue_family: QueueFamily, queue_family: QueueFamily,
subpass: Subpass, subpass: Subpass,
@ -399,8 +475,18 @@ impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBui
query_statistics_flags: QueryPipelineStatisticFlags, query_statistics_flags: QueryPipelineStatisticFlags,
) -> Result< ) -> Result<
AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>, AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
OomError, BeginError,
> { > {
if occlusion_query.is_some() && !device.enabled_features().inherited_queries {
return Err(BeginError::InheritedQueriesFeatureNotEnabled);
}
if query_statistics_flags.count() > 0
&& !device.enabled_features().pipeline_statistics_query
{
return Err(BeginError::PipelineStatisticsQueryFeatureNotEnabled);
}
let level = CommandBufferLevel::Secondary(CommandBufferInheritance { let level = CommandBufferLevel::Secondary(CommandBufferInheritance {
render_pass: Some(CommandBufferInheritanceRenderPass { render_pass: Some(CommandBufferInheritanceRenderPass {
subpass, subpass,
@ -410,12 +496,17 @@ impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBui
query_statistics_flags, query_statistics_flags,
}); });
AutoCommandBufferBuilder::with_flags(device, queue_family, level, Flags::OneTimeSubmit) Ok(AutoCommandBufferBuilder::with_flags(
device,
queue_family,
level,
Flags::OneTimeSubmit,
)?)
} }
/// Same as `secondary_graphics_simultaneous_use`, but allows specifying how queries are being inherited. /// Same as `secondary_graphics_simultaneous_use`, but allows specifying how queries are being inherited.
#[inline] #[inline]
pub fn secondary_graphics_simultaneous_use_inherit_queries<R>( pub fn secondary_graphics_simultaneous_use_inherit_queries(
device: Arc<Device>, device: Arc<Device>,
queue_family: QueueFamily, queue_family: QueueFamily,
subpass: Subpass, subpass: Subpass,
@ -423,8 +514,18 @@ impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBui
query_statistics_flags: QueryPipelineStatisticFlags, query_statistics_flags: QueryPipelineStatisticFlags,
) -> Result< ) -> Result<
AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>, AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
OomError, BeginError,
> { > {
if occlusion_query.is_some() && !device.enabled_features().inherited_queries {
return Err(BeginError::InheritedQueriesFeatureNotEnabled);
}
if query_statistics_flags.count() > 0
&& !device.enabled_features().pipeline_statistics_query
{
return Err(BeginError::PipelineStatisticsQueryFeatureNotEnabled);
}
let level = CommandBufferLevel::Secondary(CommandBufferInheritance { let level = CommandBufferLevel::Secondary(CommandBufferInheritance {
render_pass: Some(CommandBufferInheritanceRenderPass { render_pass: Some(CommandBufferInheritanceRenderPass {
subpass, subpass,
@ -434,7 +535,12 @@ impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBui
query_statistics_flags, query_statistics_flags,
}); });
AutoCommandBufferBuilder::with_flags(device, queue_family, level, Flags::SimultaneousUse) Ok(AutoCommandBufferBuilder::with_flags(
device,
queue_family,
level,
Flags::SimultaneousUse,
)?)
} }
} }
@ -497,9 +603,9 @@ impl<L> AutoCommandBufferBuilder<L, StandardCommandPoolBuilder> {
inner, inner,
pool_builder_alloc, pool_builder_alloc,
state_cacher: StateCacher::new(), state_cacher: StateCacher::new(),
graphics_allowed: queue_family.supports_graphics(), queue_family_id: queue_family.id(),
compute_allowed: queue_family.supports_compute(),
render_pass_state, render_pass_state,
query_state: FnvHashMap::default(),
inheritance, inheritance,
flags, flags,
_data: PhantomData, _data: PhantomData,
@ -508,6 +614,54 @@ impl<L> AutoCommandBufferBuilder<L, StandardCommandPoolBuilder> {
} }
} }
#[derive(Clone, Copy, Debug)]
pub enum BeginError {
/// Occlusion query inheritance was requested, but the `inherited_queries` feature was not enabled.
InheritedQueriesFeatureNotEnabled,
/// Not enough memory.
OomError(OomError),
/// Pipeline statistics query inheritance was requested, but the `pipeline_statistics_query` feature was not enabled.
PipelineStatisticsQueryFeatureNotEnabled,
}
impl error::Error for BeginError {
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
Self::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for BeginError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
Self::InheritedQueriesFeatureNotEnabled => {
"occlusion query inheritance was requested but the corresponding feature \
wasn't enabled"
}
Self::OomError(_) => "not enough memory available",
Self::PipelineStatisticsQueryFeatureNotEnabled => {
"pipeline statistics query inheritance was requested but the corresponding \
feature wasn't enabled"
}
}
)
}
}
impl From<OomError> for BeginError {
#[inline]
fn from(err: OomError) -> Self {
Self::OomError(err)
}
}
impl<P> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P> impl<P> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P>
where where
P: CommandPoolBuilderAlloc, P: CommandPoolBuilderAlloc,
@ -519,6 +673,10 @@ where
return Err(AutoCommandBufferBuilderContextError::ForbiddenInsideRenderPass.into()); return Err(AutoCommandBufferBuilderContextError::ForbiddenInsideRenderPass.into());
} }
if !self.query_state.is_empty() {
return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into());
}
let submit_state = match self.flags { let submit_state = match self.flags {
Flags::None => SubmitState::ExclusiveUse { Flags::None => SubmitState::ExclusiveUse {
in_use: AtomicBool::new(false), in_use: AtomicBool::new(false),
@ -544,6 +702,10 @@ where
/// Builds the command buffer. /// Builds the command buffer.
#[inline] #[inline]
pub fn build(self) -> Result<SecondaryAutoCommandBuffer<P::Alloc>, BuildError> { pub fn build(self) -> Result<SecondaryAutoCommandBuffer<P::Alloc>, BuildError> {
if !self.query_state.is_empty() {
return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into());
}
let submit_state = match self.flags { let submit_state = match self.flags {
Flags::None => SubmitState::ExclusiveUse { Flags::None => SubmitState::ExclusiveUse {
in_use: AtomicBool::new(false), in_use: AtomicBool::new(false),
@ -609,6 +771,14 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
Ok(()) Ok(())
} }
#[inline]
fn queue_family(&self) -> QueueFamily {
self.device()
.physical_device()
.queue_family_by_id(self.queue_family_id)
.unwrap()
}
/// Adds a command that copies an image to another. /// Adds a command that copies an image to another.
/// ///
/// Copy operations have several restrictions: /// Copy operations have several restrictions:
@ -749,7 +919,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
D: ImageAccess + Send + Sync + 'static, D: ImageAccess + Send + Sync + 'static,
{ {
unsafe { unsafe {
if !self.graphics_allowed { if !self.queue_family().supports_graphics() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
} }
@ -845,7 +1015,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
I: ImageAccess + Send + Sync + 'static, I: ImageAccess + Send + Sync + 'static,
{ {
unsafe { unsafe {
if !self.graphics_allowed && !self.compute_allowed { if !self.queue_family().supports_graphics() && !self.queue_family().supports_compute() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
} }
@ -1020,11 +1190,9 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
} }
} }
/* /// Adds a command that copies from an image to a buffer.
Adds a command that copies from an image to a buffer. // The data layout of the image on the gpu is opaque, as in, it is non of our business how the gpu stores the image.
The data layout of the image on the gpu is opaque, as in, it is non of our business how the gpu stores the image. // This does not matter since the act of copying the image into a buffer converts it to linear form.
This does not matter since the act of copying the image into a buffer converts it to linear form.
*/
pub fn copy_image_to_buffer<S, D, Px>( pub fn copy_image_to_buffer<S, D, Px>(
&mut self, &mut self,
source: S, source: S,
@ -1107,7 +1275,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
name: &'static CStr, name: &'static CStr,
color: [f32; 4], color: [f32; 4],
) -> Result<&mut Self, DebugMarkerError> { ) -> Result<&mut Self, DebugMarkerError> {
if !self.graphics_allowed && self.compute_allowed { if !self.queue_family().supports_graphics() && self.queue_family().supports_compute() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
} }
@ -1126,7 +1294,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
/// Note: you need to enable `VK_EXT_debug_utils` extension when creating an instance. /// Note: you need to enable `VK_EXT_debug_utils` extension when creating an instance.
#[inline] #[inline]
pub fn debug_marker_end(&mut self) -> Result<&mut Self, DebugMarkerError> { pub fn debug_marker_end(&mut self) -> Result<&mut Self, DebugMarkerError> {
if !self.graphics_allowed && self.compute_allowed { if !self.queue_family().supports_graphics() && self.queue_family().supports_compute() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
} }
@ -1148,7 +1316,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
name: &'static CStr, name: &'static CStr,
color: [f32; 4], color: [f32; 4],
) -> Result<&mut Self, DebugMarkerError> { ) -> Result<&mut Self, DebugMarkerError> {
if !self.graphics_allowed && self.compute_allowed { if !self.queue_family().supports_graphics() && self.queue_family().supports_compute() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
} }
@ -1178,7 +1346,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
Doi: Iterator<Item = u32> + Send + Sync + 'static, Doi: Iterator<Item = u32> + Send + Sync + 'static,
{ {
unsafe { unsafe {
if !self.compute_allowed { if !self.queue_family().supports_compute() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
} }
@ -1231,7 +1399,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
Doi: Iterator<Item = u32> + Send + Sync + 'static, Doi: Iterator<Item = u32> + Send + Sync + 'static,
{ {
unsafe { unsafe {
if !self.compute_allowed { if !self.queue_family().supports_compute() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
} }
@ -1316,7 +1484,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
vb_infos.vertex_buffers, vb_infos.vertex_buffers,
)?; )?;
debug_assert!(self.graphics_allowed); debug_assert!(self.queue_family().supports_graphics());
self.inner.draw( self.inner.draw(
vb_infos.vertex_count as u32, vb_infos.vertex_count as u32,
@ -1394,7 +1562,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
vb_infos.vertex_buffers, vb_infos.vertex_buffers,
)?; )?;
debug_assert!(self.graphics_allowed); debug_assert!(self.queue_family().supports_graphics());
self.inner.draw_indirect( self.inner.draw_indirect(
indirect_buffer, indirect_buffer,
@ -1473,7 +1641,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
)?; )?;
// TODO: how to handle an index out of range of the vertex buffers? // TODO: how to handle an index out of range of the vertex buffers?
debug_assert!(self.graphics_allowed); debug_assert!(self.queue_family().supports_graphics());
self.inner.draw_indexed( self.inner.draw_indexed(
ib_infos.num_indices as u32, ib_infos.num_indices as u32,
@ -1564,7 +1732,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
vb_infos.vertex_buffers, vb_infos.vertex_buffers,
)?; )?;
debug_assert!(self.graphics_allowed); debug_assert!(self.queue_family().supports_graphics());
self.inner.draw_indexed_indirect( self.inner.draw_indexed_indirect(
indirect_buffer, indirect_buffer,
@ -1628,6 +1796,184 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
Ok(self) Ok(self)
} }
} }
/// Adds a command that 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<QueryPool>,
query: u32,
flags: QueryControlFlags,
) -> Result<&mut Self, BeginQueryError> {
check_begin_query(self.device(), &query_pool, query, flags)?;
match query_pool.ty() {
QueryType::Occlusion => {
if !self.queue_family().supports_graphics() {
return Err(
AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into(),
);
}
}
QueryType::PipelineStatistics(flags) => {
if flags.is_compute() && !self.queue_family().supports_compute()
|| flags.is_graphics() && !self.queue_family().supports_graphics()
{
return Err(
AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into(),
);
}
}
QueryType::Timestamp => unreachable!(),
}
let ty = query_pool.ty();
let raw_ty = ty.into();
let raw_query_pool = query_pool.internal_object();
if self.query_state.contains_key(&raw_ty) {
return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into());
}
// TODO: validity checks
self.inner.begin_query(query_pool, query, flags);
self.query_state.insert(
raw_ty,
QueryState {
query_pool: raw_query_pool,
query,
ty,
flags,
in_subpass: self.render_pass_state.is_some(),
},
);
Ok(self)
}
/// Adds a command that ends an active query.
pub fn end_query(
&mut self,
query_pool: Arc<QueryPool>,
query: u32,
) -> Result<&mut Self, EndQueryError> {
unsafe {
check_end_query(self.device(), &query_pool, query)?;
let raw_ty = query_pool.ty().into();
let raw_query_pool = query_pool.internal_object();
if !self.query_state.get(&raw_ty).map_or(false, |state| {
state.query_pool == raw_query_pool && state.query == query
}) {
return Err(AutoCommandBufferBuilderContextError::QueryNotActive.into());
}
self.inner.end_query(query_pool, query);
self.query_state.remove(&raw_ty);
}
Ok(self)
}
/// Adds a command that 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<QueryPool>,
query: u32,
stage: PipelineStage,
) -> Result<&mut Self, WriteTimestampError> {
check_write_timestamp(
self.device(),
self.queue_family(),
&query_pool,
query,
stage,
)?;
if !(self.queue_family().supports_graphics()
|| self.queue_family().supports_compute()
|| self.queue_family().explicitly_supports_transfers())
{
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
}
// TODO: validity checks
self.inner.write_timestamp(query_pool, query, stage);
Ok(self)
}
/// Adds a command that copies the results of a range of queries to a buffer on the GPU.
///
/// [`query_pool.ty().data_size()`](crate::query::QueryType::data_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.
///
/// See also [`get_results`](crate::query::QueriesRange::get_results).
pub fn copy_query_pool_results<D, T>(
&mut self,
query_pool: Arc<QueryPool>,
queries: Range<u32>,
destination: D,
flags: QueryResultFlags,
) -> Result<&mut Self, CopyQueryPoolResultsError>
where
D: BufferAccess + TypedBufferAccess<Content = [T]> + Send + Sync + 'static,
T: QueryResultElement,
{
unsafe {
self.ensure_outside_render_pass()?;
let stride = check_copy_query_pool_results(
self.device(),
&query_pool,
queries.clone(),
&destination,
flags,
)?;
self.inner
.copy_query_pool_results(query_pool, queries, destination, stride, flags)?;
}
Ok(self)
}
/// Adds a command to reset 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.
pub unsafe fn reset_query_pool(
&mut self,
query_pool: Arc<QueryPool>,
queries: Range<u32>,
) -> Result<&mut Self, ResetQueryPoolError> {
self.ensure_outside_render_pass()?;
check_reset_query_pool(self.device(), &query_pool, queries.clone())?;
let raw_query_pool = query_pool.internal_object();
if self
.query_state
.values()
.any(|state| state.query_pool == raw_query_pool && queries.contains(&state.query))
{
return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into());
}
// TODO: validity checks
// Do other command buffers actually matter here? Not sure on the Vulkan spec.
self.inner.reset_query_pool(query_pool, queries);
Ok(self)
}
} }
/// Commands that can only be executed on primary command buffers /// Commands that can only be executed on primary command buffers
@ -1657,7 +2003,7 @@ where
I: IntoIterator<Item = ClearValue>, I: IntoIterator<Item = ClearValue>,
{ {
unsafe { unsafe {
if !self.graphics_allowed { if !self.queue_family().supports_graphics() {
return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
} }
@ -1755,7 +2101,11 @@ where
return Err(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass); return Err(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass);
} }
debug_assert!(self.graphics_allowed); if self.query_state.values().any(|state| state.in_subpass) {
return Err(AutoCommandBufferBuilderContextError::QueryIsActive);
}
debug_assert!(self.queue_family().supports_graphics());
self.inner.end_render_pass(); self.inner.end_render_pass();
self.render_pass_state = None; self.render_pass_state = None;
@ -1838,13 +2188,37 @@ where
C: SecondaryCommandBuffer + Send + Sync + 'static, C: SecondaryCommandBuffer + Send + Sync + 'static,
{ {
if let Some(render_pass) = command_buffer.inheritance().render_pass { if let Some(render_pass) = command_buffer.inheritance().render_pass {
// TODO: If support for queries is added, their compatibility should be checked
// here too per vkCmdExecuteCommands specs
self.ensure_inside_render_pass_secondary(&render_pass)?; self.ensure_inside_render_pass_secondary(&render_pass)?;
} else { } else {
self.ensure_outside_render_pass()?; self.ensure_outside_render_pass()?;
} }
for state in self.query_state.values() {
match state.ty {
QueryType::Occlusion => match command_buffer.inheritance().occlusion_query {
Some(inherited_flags) => {
let inherited_flags = vk::QueryControlFlags::from(inherited_flags);
let state_flags = vk::QueryControlFlags::from(state.flags);
if inherited_flags & state_flags != state_flags {
return Err(AutoCommandBufferBuilderContextError::QueryNotInherited);
}
}
None => return Err(AutoCommandBufferBuilderContextError::QueryNotInherited),
},
QueryType::PipelineStatistics(state_flags) => {
let inherited_flags = command_buffer.inheritance().query_statistics_flags;
let inherited_flags = vk::QueryPipelineStatisticFlags::from(inherited_flags);
let state_flags = vk::QueryPipelineStatisticFlags::from(state_flags);
if inherited_flags & state_flags != state_flags {
return Err(AutoCommandBufferBuilderContextError::QueryNotInherited);
}
}
_ => (),
}
}
Ok(()) Ok(())
} }
@ -1913,7 +2287,11 @@ where
return Err(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass); return Err(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass);
} }
debug_assert!(self.graphics_allowed); if self.query_state.values().any(|state| state.in_subpass) {
return Err(AutoCommandBufferBuilderContextError::QueryIsActive);
}
debug_assert!(self.queue_family().supports_graphics());
self.inner.next_subpass(contents); self.inner.next_subpass(contents);
Ok(self) Ok(self)
@ -2440,6 +2818,12 @@ err_gen!(CopyBufferImageError {
SyncCommandBufferBuilderError, SyncCommandBufferBuilderError,
}); });
err_gen!(CopyQueryPoolResultsError {
AutoCommandBufferBuilderContextError,
CheckCopyQueryPoolResultsError,
SyncCommandBufferBuilderError,
});
err_gen!(FillBufferError { err_gen!(FillBufferError {
AutoCommandBufferBuilderContextError, AutoCommandBufferBuilderContextError,
CheckFillBufferError, CheckFillBufferError,
@ -2511,6 +2895,26 @@ err_gen!(ExecuteCommandsError {
SyncCommandBufferBuilderError, SyncCommandBufferBuilderError,
}); });
err_gen!(BeginQueryError {
AutoCommandBufferBuilderContextError,
CheckBeginQueryError,
});
err_gen!(EndQueryError {
AutoCommandBufferBuilderContextError,
CheckEndQueryError,
});
err_gen!(WriteTimestampError {
AutoCommandBufferBuilderContextError,
CheckWriteTimestampError,
});
err_gen!(ResetQueryPoolError {
AutoCommandBufferBuilderContextError,
CheckResetQueryPoolError,
});
err_gen!(UpdateBufferError { err_gen!(UpdateBufferError {
AutoCommandBufferBuilderContextError, AutoCommandBufferBuilderContextError,
CheckUpdateBufferError, CheckUpdateBufferError,
@ -2522,6 +2926,12 @@ pub enum AutoCommandBufferBuilderContextError {
ForbiddenInsideRenderPass, ForbiddenInsideRenderPass,
/// Operation forbidden outside of a render pass. /// Operation forbidden outside of a render pass.
ForbiddenOutsideRenderPass, ForbiddenOutsideRenderPass,
/// Tried to use a secondary command buffer with a specified framebuffer that is
/// incompatible with the current framebuffer.
IncompatibleFramebuffer,
/// Tried to use a graphics pipeline or secondary command buffer whose render pass
/// is incompatible with the current render pass.
IncompatibleRenderPass,
/// The queue family doesn't allow this operation. /// The queue family doesn't allow this operation.
NotSupportedByQueueFamily, NotSupportedByQueueFamily,
/// Tried to end a render pass with subpasses remaining, or tried to go to next subpass with no /// Tried to end a render pass with subpasses remaining, or tried to go to next subpass with no
@ -2532,18 +2942,18 @@ pub enum AutoCommandBufferBuilderContextError {
/// Current subpass index before the failing command. /// Current subpass index before the failing command.
current: u32, current: u32,
}, },
/// Tried to execute a secondary command buffer inside a subpass that only allows inline /// A query is active that conflicts with the current operation.
/// commands, or a draw command in a subpass that only allows secondary command buffers. QueryIsActive,
WrongSubpassType, /// This query was not active.
QueryNotActive,
/// A query is active that is not included in the `inheritance` of the secondary command buffer.
QueryNotInherited,
/// Tried to use a graphics pipeline or secondary command buffer whose subpass index /// Tried to use a graphics pipeline or secondary command buffer whose subpass index
/// didn't match the current subpass index. /// didn't match the current subpass index.
WrongSubpassIndex, WrongSubpassIndex,
/// Tried to use a secondary command buffer with a specified framebuffer that is /// Tried to execute a secondary command buffer inside a subpass that only allows inline
/// incompatible with the current framebuffer. /// commands, or a draw command in a subpass that only allows secondary command buffers.
IncompatibleFramebuffer, WrongSubpassType,
/// Tried to use a graphics pipeline or secondary command buffer whose render pass
/// is incompatible with the current render pass.
IncompatibleRenderPass,
} }
impl error::Error for AutoCommandBufferBuilderContextError {} impl error::Error for AutoCommandBufferBuilderContextError {}
@ -2561,22 +2971,6 @@ impl fmt::Display for AutoCommandBufferBuilderContextError {
AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass => { AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass => {
"operation forbidden outside of a render pass" "operation forbidden outside of a render pass"
} }
AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily => {
"the queue family doesn't allow this operation"
}
AutoCommandBufferBuilderContextError::NumSubpassesMismatch { .. } => {
"tried to end a render pass with subpasses remaining, or tried to go to next \
subpass with no subpass remaining"
}
AutoCommandBufferBuilderContextError::WrongSubpassType => {
"tried to execute a secondary command buffer inside a subpass that only allows \
inline commands, or a draw command in a subpass that only allows secondary \
command buffers"
}
AutoCommandBufferBuilderContextError::WrongSubpassIndex => {
"tried to use a graphics pipeline whose subpass index didn't match the current \
subpass index"
}
AutoCommandBufferBuilderContextError::IncompatibleFramebuffer => { AutoCommandBufferBuilderContextError::IncompatibleFramebuffer => {
"tried to use a secondary command buffer with a specified framebuffer that is \ "tried to use a secondary command buffer with a specified framebuffer that is \
incompatible with the current framebuffer" incompatible with the current framebuffer"
@ -2585,6 +2979,31 @@ impl fmt::Display for AutoCommandBufferBuilderContextError {
"tried to use a graphics pipeline or secondary command buffer whose render pass \ "tried to use a graphics pipeline or secondary command buffer whose render pass \
is incompatible with the current render pass" is incompatible with the current render pass"
} }
AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily => {
"the queue family doesn't allow this operation"
}
AutoCommandBufferBuilderContextError::NumSubpassesMismatch { .. } => {
"tried to end a render pass with subpasses remaining, or tried to go to next \
subpass with no subpass remaining"
}
AutoCommandBufferBuilderContextError::QueryIsActive => {
"a query is active that conflicts with the current operation"
}
AutoCommandBufferBuilderContextError::QueryNotActive => {
"this query was not active"
}
AutoCommandBufferBuilderContextError::QueryNotInherited => {
"a query is active that is not included in the inheritance of the secondary command buffer"
}
AutoCommandBufferBuilderContextError::WrongSubpassIndex => {
"tried to use a graphics pipeline whose subpass index didn't match the current \
subpass index"
}
AutoCommandBufferBuilderContextError::WrongSubpassType => {
"tried to execute a secondary command buffer inside a subpass that only allows \
inline commands, or a draw command in a subpass that only allows secondary \
command buffers"
}
} }
) )
} }

View File

@ -75,6 +75,8 @@
pub use self::auto::AutoCommandBufferBuilder; pub use self::auto::AutoCommandBufferBuilder;
pub use self::auto::AutoCommandBufferBuilderContextError; pub use self::auto::AutoCommandBufferBuilderContextError;
pub use self::auto::BeginError;
pub use self::auto::BeginQueryError;
pub use self::auto::BeginRenderPassError; pub use self::auto::BeginRenderPassError;
pub use self::auto::BlitImageError; pub use self::auto::BlitImageError;
pub use self::auto::BuildError; pub use self::auto::BuildError;
@ -82,6 +84,7 @@ pub use self::auto::ClearColorImageError;
pub use self::auto::CopyBufferError; pub use self::auto::CopyBufferError;
pub use self::auto::CopyBufferImageError; pub use self::auto::CopyBufferImageError;
pub use self::auto::CopyImageError; pub use self::auto::CopyImageError;
pub use self::auto::CopyQueryPoolResultsError;
pub use self::auto::DebugMarkerError; pub use self::auto::DebugMarkerError;
pub use self::auto::DispatchError; pub use self::auto::DispatchError;
pub use self::auto::DispatchIndirectError; pub use self::auto::DispatchIndirectError;
@ -89,11 +92,14 @@ pub use self::auto::DrawError;
pub use self::auto::DrawIndexedError; pub use self::auto::DrawIndexedError;
pub use self::auto::DrawIndexedIndirectError; pub use self::auto::DrawIndexedIndirectError;
pub use self::auto::DrawIndirectError; pub use self::auto::DrawIndirectError;
pub use self::auto::EndQueryError;
pub use self::auto::ExecuteCommandsError; pub use self::auto::ExecuteCommandsError;
pub use self::auto::FillBufferError; pub use self::auto::FillBufferError;
pub use self::auto::PrimaryAutoCommandBuffer; pub use self::auto::PrimaryAutoCommandBuffer;
pub use self::auto::ResetQueryPoolError;
pub use self::auto::SecondaryAutoCommandBuffer; pub use self::auto::SecondaryAutoCommandBuffer;
pub use self::auto::UpdateBufferError; pub use self::auto::UpdateBufferError;
pub use self::auto::WriteTimestampError;
pub use self::state_cacher::StateCacher; pub use self::state_cacher::StateCacher;
pub use self::state_cacher::StateCacherOutcome; pub use self::state_cacher::StateCacherOutcome;
pub use self::traits::CommandBufferExecError; pub use self::traits::CommandBufferExecError;

View File

@ -8,6 +8,7 @@
// according to those terms. // according to those terms.
use crate::buffer::BufferAccess; use crate::buffer::BufferAccess;
use crate::buffer::TypedBufferAccess;
use crate::command_buffer::synced::base::Command; use crate::command_buffer::synced::base::Command;
use crate::command_buffer::synced::base::FinalCommand; use crate::command_buffer::synced::base::FinalCommand;
use crate::command_buffer::synced::base::KeyTy; use crate::command_buffer::synced::base::KeyTy;
@ -37,11 +38,16 @@ use crate::pipeline::viewport::Scissor;
use crate::pipeline::viewport::Viewport; use crate::pipeline::viewport::Viewport;
use crate::pipeline::ComputePipelineAbstract; use crate::pipeline::ComputePipelineAbstract;
use crate::pipeline::GraphicsPipelineAbstract; use crate::pipeline::GraphicsPipelineAbstract;
use crate::query::QueryControlFlags;
use crate::query::QueryPool;
use crate::query::QueryResultElement;
use crate::query::QueryResultFlags;
use crate::render_pass::FramebufferAbstract; use crate::render_pass::FramebufferAbstract;
use crate::sampler::Filter; use crate::sampler::Filter;
use crate::sync::AccessFlagBits; use crate::sync::AccessFlagBits;
use crate::sync::Event; use crate::sync::Event;
use crate::sync::PipelineMemoryAccess; use crate::sync::PipelineMemoryAccess;
use crate::sync::PipelineStage;
use crate::sync::PipelineStages; use crate::sync::PipelineStages;
use crate::SafeDeref; use crate::SafeDeref;
use crate::VulkanObject; use crate::VulkanObject;
@ -49,10 +55,56 @@ use smallvec::SmallVec;
use std::borrow::Cow; use std::borrow::Cow;
use std::ffi::CStr; use std::ffi::CStr;
use std::mem; use std::mem;
use std::ops::Range;
use std::ptr; use std::ptr;
use std::sync::Arc; use std::sync::Arc;
impl SyncCommandBufferBuilder { impl SyncCommandBufferBuilder {
/// Calls `vkCmdBeginQuery` on the builder.
#[inline]
pub unsafe fn begin_query(
&mut self,
query_pool: Arc<QueryPool>,
query: u32,
flags: QueryControlFlags,
) {
struct Cmd {
query_pool: Arc<QueryPool>,
query: u32,
flags: QueryControlFlags,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdBeginQuery"
}
unsafe fn send(&mut self, out: &mut UnsafeCommandBufferBuilder) {
out.begin_query(self.query_pool.query(self.query).unwrap(), self.flags);
}
fn into_final_command(self: Box<Self>) -> Box<dyn FinalCommand + Send + Sync> {
struct Fin(Arc<QueryPool>);
impl FinalCommand for Fin {
fn name(&self) -> &'static str {
"vkCmdBeginQuery"
}
}
Box::new(Fin(self.query_pool))
}
}
self.append_command(
Cmd {
query_pool,
query,
flags,
},
&[],
)
.unwrap();
}
/// Calls `vkBeginRenderPass` on the builder. /// Calls `vkBeginRenderPass` on the builder.
// TODO: it shouldn't be possible to get an error if the framebuffer checked conflicts already // TODO: it shouldn't be possible to get an error if the framebuffer checked conflicts already
// TODO: after begin_render_pass has been called, flushing should be forbidden and an error // TODO: after begin_render_pass has been called, flushing should be forbidden and an error
@ -1205,6 +1257,114 @@ impl SyncCommandBufferBuilder {
Ok(()) Ok(())
} }
/// 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<D, T>(
&mut self,
query_pool: Arc<QueryPool>,
queries: Range<u32>,
destination: D,
stride: usize,
flags: QueryResultFlags,
) -> Result<(), SyncCommandBufferBuilderError>
where
D: BufferAccess + TypedBufferAccess<Content = [T]> + Send + Sync + 'static,
T: QueryResultElement,
{
struct Cmd<D> {
query_pool: Arc<QueryPool>,
queries: Range<u32>,
destination: Option<D>,
stride: usize,
flags: QueryResultFlags,
}
impl<D, T> Command for Cmd<D>
where
D: BufferAccess + TypedBufferAccess<Content = [T]> + Send + Sync + 'static,
T: QueryResultElement,
{
fn name(&self) -> &'static str {
"vkCmdCopyQueryPoolResults"
}
unsafe fn send(&mut self, out: &mut UnsafeCommandBufferBuilder) {
out.copy_query_pool_results(
self.query_pool.queries_range(self.queries.clone()).unwrap(),
self.destination.as_ref().unwrap(),
self.stride,
self.flags,
);
}
fn into_final_command(mut self: Box<Self>) -> Box<dyn FinalCommand + Send + Sync> {
struct Fin<D>(Arc<QueryPool>, D);
impl<D> FinalCommand for Fin<D>
where
D: BufferAccess + Send + Sync + 'static,
{
fn name(&self) -> &'static str {
"vkCmdCopyQueryPoolResults"
}
fn buffer(&self, num: usize) -> &dyn BufferAccess {
assert_eq!(num, 0);
&self.1
}
fn buffer_name(&self, num: usize) -> Cow<'static, str> {
assert_eq!(num, 0);
"destination".into()
}
}
// Note: borrow checker somehow doesn't accept `self.destination`
// without using an Option.
Box::new(Fin(self.query_pool, self.destination.take().unwrap()))
}
fn buffer(&self, num: usize) -> &dyn BufferAccess {
assert_eq!(num, 0);
self.destination.as_ref().unwrap()
}
fn buffer_name(&self, num: usize) -> Cow<'static, str> {
assert_eq!(num, 0);
"destination".into()
}
}
self.append_command(
Cmd {
query_pool,
queries,
destination: Some(destination),
stride,
flags,
},
&[(
KeyTy::Buffer,
Some((
PipelineMemoryAccess {
stages: PipelineStages {
transfer: true,
..PipelineStages::none()
},
access: AccessFlagBits {
transfer_write: true,
..AccessFlagBits::none()
},
exclusive: true,
},
ImageLayout::Undefined,
ImageLayout::Undefined,
)),
)],
)?;
Ok(())
}
/// Calls `vkCmdBeginDebugUtilsLabelEXT` on the builder. /// Calls `vkCmdBeginDebugUtilsLabelEXT` on the builder.
/// ///
/// # Safety /// # Safety
@ -1675,6 +1835,37 @@ impl SyncCommandBufferBuilder {
Ok(()) Ok(())
} }
/// Calls `vkCmdEndQuery` on the builder.
#[inline]
pub unsafe fn end_query(&mut self, query_pool: Arc<QueryPool>, query: u32) {
struct Cmd {
query_pool: Arc<QueryPool>,
query: u32,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdEndQuery"
}
unsafe fn send(&mut self, out: &mut UnsafeCommandBufferBuilder) {
out.end_query(self.query_pool.query(self.query).unwrap());
}
fn into_final_command(self: Box<Self>) -> Box<dyn FinalCommand + Send + Sync> {
struct Fin(Arc<QueryPool>);
impl FinalCommand for Fin {
fn name(&self) -> &'static str {
"vkCmdEndQuery"
}
}
Box::new(Fin(self.query_pool))
}
}
self.append_command(Cmd { query_pool, query }, &[]).unwrap();
}
/// Calls `vkCmdEndRenderPass` on the builder. /// Calls `vkCmdEndRenderPass` on the builder.
#[inline] #[inline]
pub unsafe fn end_render_pass(&mut self) { pub unsafe fn end_render_pass(&mut self) {
@ -1916,6 +2107,45 @@ impl SyncCommandBufferBuilder {
self.append_command(Cmd { event, stages }, &[]).unwrap(); self.append_command(Cmd { event, stages }, &[]).unwrap();
} }
/// Calls `vkCmdResetQueryPool` on the builder.
#[inline]
pub unsafe fn reset_query_pool(&mut self, query_pool: Arc<QueryPool>, queries: Range<u32>) {
struct Cmd {
query_pool: Arc<QueryPool>,
queries: Range<u32>,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdResetQueryPool"
}
unsafe fn send(&mut self, out: &mut UnsafeCommandBufferBuilder) {
out.reset_query_pool(self.query_pool.queries_range(self.queries.clone()).unwrap());
}
fn into_final_command(self: Box<Self>) -> Box<dyn FinalCommand + Send + Sync> {
struct Fin(Arc<QueryPool>);
impl FinalCommand for Fin {
fn name(&self) -> &'static str {
"vkCmdResetQueryPool"
}
}
Box::new(Fin(self.query_pool))
}
}
self.append_command(
Cmd {
query_pool,
queries,
},
&[],
)
.unwrap();
}
/// Calls `vkCmdSetBlendConstants` on the builder. /// Calls `vkCmdSetBlendConstants` on the builder.
#[inline] #[inline]
pub unsafe fn set_blend_constants(&mut self, constants: [f32; 4]) { pub unsafe fn set_blend_constants(&mut self, constants: [f32; 4]) {
@ -2310,6 +2540,51 @@ impl SyncCommandBufferBuilder {
) )
.unwrap(); .unwrap();
} }
/// Calls `vkCmdWriteTimestamp` on the builder.
#[inline]
pub unsafe fn write_timestamp(
&mut self,
query_pool: Arc<QueryPool>,
query: u32,
stage: PipelineStage,
) {
struct Cmd {
query_pool: Arc<QueryPool>,
query: u32,
stage: PipelineStage,
}
impl Command for Cmd {
fn name(&self) -> &'static str {
"vkCmdWriteTimestamp"
}
unsafe fn send(&mut self, out: &mut UnsafeCommandBufferBuilder) {
out.write_timestamp(self.query_pool.query(self.query).unwrap(), self.stage);
}
fn into_final_command(self: Box<Self>) -> Box<dyn FinalCommand + Send + Sync> {
struct Fin(Arc<QueryPool>);
impl FinalCommand for Fin {
fn name(&self) -> &'static str {
"vkCmdWriteTimestamp"
}
}
Box::new(Fin(self.query_pool))
}
}
self.append_command(
Cmd {
query_pool,
query,
stage,
},
&[],
)
.unwrap();
}
} }
pub struct SyncCommandBufferBuilderBindDescriptorSets<'b> { pub struct SyncCommandBufferBuilderBindDescriptorSets<'b> {

View File

@ -9,6 +9,7 @@
use crate::buffer::BufferAccess; use crate::buffer::BufferAccess;
use crate::buffer::BufferInner; use crate::buffer::BufferInner;
use crate::buffer::TypedBufferAccess;
use crate::check_errors; use crate::check_errors;
use crate::command_buffer::pool::UnsafeCommandPoolAlloc; use crate::command_buffer::pool::UnsafeCommandPoolAlloc;
use crate::command_buffer::CommandBufferInheritance; use crate::command_buffer::CommandBufferInheritance;
@ -31,13 +32,16 @@ use crate::pipeline::viewport::Scissor;
use crate::pipeline::viewport::Viewport; use crate::pipeline::viewport::Viewport;
use crate::pipeline::ComputePipelineAbstract; use crate::pipeline::ComputePipelineAbstract;
use crate::pipeline::GraphicsPipelineAbstract; use crate::pipeline::GraphicsPipelineAbstract;
use crate::query::QueriesRange;
use crate::query::Query;
use crate::query::QueryControlFlags; use crate::query::QueryControlFlags;
use crate::query::UnsafeQueriesRange; use crate::query::QueryResultElement;
use crate::query::UnsafeQuery; use crate::query::QueryResultFlags;
use crate::render_pass::FramebufferAbstract; use crate::render_pass::FramebufferAbstract;
use crate::sampler::Filter; use crate::sampler::Filter;
use crate::sync::AccessFlagBits; use crate::sync::AccessFlagBits;
use crate::sync::Event; use crate::sync::Event;
use crate::sync::PipelineStage;
use crate::sync::PipelineStages; use crate::sync::PipelineStages;
use crate::vk; use crate::vk;
use crate::OomError; use crate::OomError;
@ -166,11 +170,8 @@ impl UnsafeCommandBufferBuilder {
.. ..
}) => { }) => {
let ps: vk::QueryPipelineStatisticFlagBits = query_statistics_flags.into(); let ps: vk::QueryPipelineStatisticFlagBits = query_statistics_flags.into();
debug_assert!(ps == 0 || device.enabled_features().pipeline_statistics_query);
let (oqe, qf) = match occlusion_query { let (oqe, qf) = match occlusion_query {
Some(flags) => { Some(flags) => {
debug_assert!(device.enabled_features().inherited_queries);
let qf = if flags.precise { let qf = if flags.precise {
vk::QUERY_CONTROL_PRECISE_BIT vk::QUERY_CONTROL_PRECISE_BIT
} else { } else {
@ -230,7 +231,7 @@ impl UnsafeCommandBufferBuilder {
/// Calls `vkCmdBeginQuery` on the builder. /// Calls `vkCmdBeginQuery` on the builder.
#[inline] #[inline]
pub unsafe fn begin_query(&mut self, query: UnsafeQuery, flags: QueryControlFlags) { pub unsafe fn begin_query(&mut self, query: Query, flags: QueryControlFlags) {
let vk = self.device().pointers(); let vk = self.device().pointers();
let cmd = self.internal_object(); let cmd = self.internal_object();
let flags = if flags.precise { let flags = if flags.precise {
@ -980,29 +981,34 @@ impl UnsafeCommandBufferBuilder {
/// Calls `vkCmdCopyQueryPoolResults` on the builder. /// Calls `vkCmdCopyQueryPoolResults` on the builder.
#[inline] #[inline]
pub unsafe fn copy_query_pool_results( pub unsafe fn copy_query_pool_results<D, T>(
&mut self, &mut self,
queries: UnsafeQueriesRange, queries: QueriesRange,
destination: &dyn BufferAccess, destination: D,
stride: usize, stride: usize,
) { flags: QueryResultFlags,
) where
D: BufferAccess + TypedBufferAccess<Content = [T]>,
T: QueryResultElement,
{
let destination = destination.inner(); let destination = destination.inner();
let range = queries.range();
debug_assert!(destination.offset < destination.buffer.size()); debug_assert!(destination.offset < destination.buffer.size());
debug_assert!(destination.buffer.usage_transfer_destination()); debug_assert!(destination.buffer.usage_transfer_destination());
debug_assert!(destination.offset % std::mem::size_of::<T>() == 0);
let flags = 0; // FIXME: debug_assert!(stride % std::mem::size_of::<T>() == 0);
let vk = self.device().pointers(); let vk = self.device().pointers();
let cmd = self.internal_object(); let cmd = self.internal_object();
vk.CmdCopyQueryPoolResults( vk.CmdCopyQueryPoolResults(
cmd, cmd,
queries.pool().internal_object(), queries.pool().internal_object(),
queries.first_index(), range.start,
queries.count(), range.end - range.start,
destination.buffer.internal_object(), destination.buffer.internal_object(),
destination.offset as vk::DeviceSize, destination.offset as vk::DeviceSize,
stride as vk::DeviceSize, stride as vk::DeviceSize,
flags, vk::QueryResultFlags::from(flags) | T::FLAG,
); );
} }
@ -1140,7 +1146,7 @@ impl UnsafeCommandBufferBuilder {
/// Calls `vkCmdEndQuery` on the builder. /// Calls `vkCmdEndQuery` on the builder.
#[inline] #[inline]
pub unsafe fn end_query(&mut self, query: UnsafeQuery) { pub unsafe fn end_query(&mut self, query: Query) {
let vk = self.device().pointers(); let vk = self.device().pointers();
let cmd = self.internal_object(); let cmd = self.internal_object();
vk.CmdEndQuery(cmd, query.pool().internal_object(), query.index()); vk.CmdEndQuery(cmd, query.pool().internal_object(), query.index());
@ -1284,14 +1290,15 @@ impl UnsafeCommandBufferBuilder {
/// Calls `vkCmdResetQueryPool` on the builder. /// Calls `vkCmdResetQueryPool` on the builder.
#[inline] #[inline]
pub unsafe fn reset_query_pool(&mut self, queries: UnsafeQueriesRange) { pub unsafe fn reset_query_pool(&mut self, queries: QueriesRange) {
let range = queries.range();
let vk = self.device().pointers(); let vk = self.device().pointers();
let cmd = self.internal_object(); let cmd = self.internal_object();
vk.CmdResetQueryPool( vk.CmdResetQueryPool(
cmd, cmd,
queries.pool().internal_object(), queries.pool().internal_object(),
queries.first_index(), range.start,
queries.count(), range.end - range.start,
); );
} }
@ -1477,12 +1484,12 @@ impl UnsafeCommandBufferBuilder {
/// Calls `vkCmdWriteTimestamp` on the builder. /// Calls `vkCmdWriteTimestamp` on the builder.
#[inline] #[inline]
pub unsafe fn write_timestamp(&mut self, query: UnsafeQuery, stages: PipelineStages) { pub unsafe fn write_timestamp(&mut self, query: Query, stage: PipelineStage) {
let vk = self.device().pointers(); let vk = self.device().pointers();
let cmd = self.internal_object(); let cmd = self.internal_object();
vk.CmdWriteTimestamp( vk.CmdWriteTimestamp(
cmd, cmd,
stages.into_vulkan_bits(), stage as u32,
query.pool().internal_object(), query.pool().internal_object(),
query.index(), query.index(),
); );

View File

@ -24,6 +24,11 @@ pub use self::fill_buffer::{check_fill_buffer, CheckFillBufferError};
pub use self::index_buffer::{check_index_buffer, CheckIndexBuffer, CheckIndexBufferError}; pub use self::index_buffer::{check_index_buffer, CheckIndexBuffer, CheckIndexBufferError};
pub use self::indirect_buffer::{check_indirect_buffer, CheckIndirectBufferError}; pub use self::indirect_buffer::{check_indirect_buffer, CheckIndirectBufferError};
pub use self::push_constants::{check_push_constants_validity, CheckPushConstantsValidityError}; pub use self::push_constants::{check_push_constants_validity, CheckPushConstantsValidityError};
pub use self::query::{
check_begin_query, check_copy_query_pool_results, check_end_query, check_reset_query_pool,
check_write_timestamp, CheckBeginQueryError, CheckCopyQueryPoolResultsError,
CheckEndQueryError, CheckResetQueryPoolError, CheckWriteTimestampError,
};
pub use self::update_buffer::{check_update_buffer, CheckUpdateBufferError}; pub use self::update_buffer::{check_update_buffer, CheckUpdateBufferError};
pub use self::vertex_buffers::{check_vertex_buffers, CheckVertexBuffer, CheckVertexBufferError}; pub use self::vertex_buffers::{check_vertex_buffers, CheckVertexBuffer, CheckVertexBufferError};
@ -40,5 +45,6 @@ mod fill_buffer;
mod index_buffer; mod index_buffer;
mod indirect_buffer; mod indirect_buffer;
mod push_constants; mod push_constants;
mod query;
mod update_buffer; mod update_buffer;
mod vertex_buffers; mod vertex_buffers;

View File

@ -0,0 +1,390 @@
// 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.
use crate::buffer::TypedBufferAccess;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::instance::QueueFamily;
use crate::query::GetResultsError;
use crate::query::QueryControlFlags;
use crate::query::QueryPool;
use crate::query::QueryResultElement;
use crate::query::QueryResultFlags;
use crate::query::QueryType;
use crate::sync::PipelineStage;
use crate::VulkanObject;
use std::error;
use std::fmt;
use std::ops::Range;
/// Checks whether a `begin_query` command is valid.
///
/// # Panic
///
/// - Panics if the query pool was not created with `device`.
pub fn check_begin_query(
device: &Device,
query_pool: &QueryPool,
query: u32,
flags: QueryControlFlags,
) -> Result<(), CheckBeginQueryError> {
assert_eq!(
device.internal_object(),
query_pool.device().internal_object(),
);
query_pool
.query(query)
.ok_or(CheckBeginQueryError::OutOfRange)?;
match query_pool.ty() {
QueryType::Occlusion => {
if flags.precise && !device.enabled_features().occlusion_query_precise {
return Err(CheckBeginQueryError::OcclusionQueryPreciseFeatureNotEnabled);
}
}
QueryType::PipelineStatistics(_) => {
if flags.precise {
return Err(CheckBeginQueryError::InvalidFlags);
}
}
QueryType::Timestamp => return Err(CheckBeginQueryError::NotPermitted),
}
Ok(())
}
/// Error that can happen from `check_begin_query`.
#[derive(Debug, Copy, Clone)]
pub enum CheckBeginQueryError {
/// The provided flags are not allowed for this type of query.
InvalidFlags,
/// This operation is not permitted on this query type.
NotPermitted,
/// `QueryControlFlags::precise` was requested, but the `occlusion_query_precise` feature was not enabled.
OcclusionQueryPreciseFeatureNotEnabled,
/// The provided query index is not valid for this pool.
OutOfRange,
}
impl error::Error for CheckBeginQueryError {}
impl fmt::Display for CheckBeginQueryError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
Self::InvalidFlags => {
"the provided flags are not allowed for this type of query"
}
Self::NotPermitted => {
"this operation is not permitted on this query type"
}
Self::OcclusionQueryPreciseFeatureNotEnabled => {
"QueryControlFlags::precise was requested, but the occlusion_query_precise feature was not enabled"
}
Self::OutOfRange => {
"the provided query index is not valid for this pool"
}
}
)
}
}
/// Checks whether a `end_query` command is valid.
///
/// # Panic
///
/// - Panics if the query pool was not created with `device`.
pub fn check_end_query(
device: &Device,
query_pool: &QueryPool,
query: u32,
) -> Result<(), CheckEndQueryError> {
assert_eq!(
device.internal_object(),
query_pool.device().internal_object(),
);
query_pool
.query(query)
.ok_or(CheckEndQueryError::OutOfRange)?;
Ok(())
}
/// Error that can happen from `check_end_query`.
#[derive(Debug, Copy, Clone)]
pub enum CheckEndQueryError {
/// The provided query index is not valid for this pool.
OutOfRange,
}
impl error::Error for CheckEndQueryError {}
impl fmt::Display for CheckEndQueryError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
Self::OutOfRange => {
"the provided query index is not valid for this pool"
}
}
)
}
}
/// Checks whether a `write_timestamp` command is valid.
///
/// # Panic
///
/// - Panics if the query pool was not created with `device`.
pub fn check_write_timestamp(
device: &Device,
queue_family: QueueFamily,
query_pool: &QueryPool,
query: u32,
stage: PipelineStage,
) -> Result<(), CheckWriteTimestampError> {
assert_eq!(
device.internal_object(),
query_pool.device().internal_object(),
);
if !matches!(query_pool.ty(), QueryType::Timestamp) {
return Err(CheckWriteTimestampError::NotPermitted);
}
query_pool
.query(query)
.ok_or(CheckWriteTimestampError::OutOfRange)?;
if queue_family.timestamp_valid_bits().is_none() {
return Err(CheckWriteTimestampError::NoTimestampValidBits);
}
if !queue_family.supports_stage(stage) {
return Err(CheckWriteTimestampError::StageNotSupported);
}
match stage {
PipelineStage::GeometryShader => {
if !device.enabled_features().geometry_shader {
return Err(CheckWriteTimestampError::GeometryShaderFeatureNotEnabled);
}
}
PipelineStage::TessellationControlShader | PipelineStage::TessellationEvaluationShader => {
if !device.enabled_features().tessellation_shader {
return Err(CheckWriteTimestampError::TessellationShaderFeatureNotEnabled);
}
}
_ => (),
}
Ok(())
}
/// Error that can happen from `check_write_timestamp`.
#[derive(Debug, Copy, Clone)]
pub enum CheckWriteTimestampError {
/// The geometry shader stage was requested, but the `geometry_shader` feature was not enabled.
GeometryShaderFeatureNotEnabled,
/// The queue family's `timestamp_valid_bits` value is `None`.
NoTimestampValidBits,
/// This operation is not permitted on this query type.
NotPermitted,
/// The provided query index is not valid for this pool.
OutOfRange,
/// The provided stage is not supported by the queue family.
StageNotSupported,
/// A tessellation shader stage was requested, but the `tessellation_shader` feature was not enabled.
TessellationShaderFeatureNotEnabled,
}
impl error::Error for CheckWriteTimestampError {}
impl fmt::Display for CheckWriteTimestampError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
Self::GeometryShaderFeatureNotEnabled => {
"the geometry shader stage was requested, but the geometry_shader feature was not enabled"
}
Self::NoTimestampValidBits => {
"the queue family's timestamp_valid_bits value is None"
}
Self::NotPermitted => {
"this operation is not permitted on this query type"
}
Self::OutOfRange => {
"the provided query index is not valid for this pool"
}
Self::StageNotSupported => {
"the provided stage is not supported by the queue family"
}
Self::TessellationShaderFeatureNotEnabled => {
"a tessellation shader stage was requested, but the tessellation_shader feature was not enabled"
}
}
)
}
}
/// Checks whether a `copy_query_pool_results` command is valid.
///
/// # Panic
///
/// - Panics if the query pool or buffer was not created with `device`.
pub fn check_copy_query_pool_results<D, T>(
device: &Device,
query_pool: &QueryPool,
queries: Range<u32>,
destination: &D,
flags: QueryResultFlags,
) -> Result<usize, CheckCopyQueryPoolResultsError>
where
D: ?Sized + TypedBufferAccess<Content = [T]>,
T: QueryResultElement,
{
let buffer_inner = destination.inner();
assert_eq!(
device.internal_object(),
buffer_inner.buffer.device().internal_object(),
);
assert_eq!(
device.internal_object(),
query_pool.device().internal_object(),
);
if !buffer_inner.buffer.usage_transfer_destination() {
return Err(CheckCopyQueryPoolResultsError::DestinationMissingTransferUsage);
}
let queries_range = query_pool
.queries_range(queries)
.ok_or(CheckCopyQueryPoolResultsError::OutOfRange)?;
Ok(queries_range.check_query_pool_results::<T>(
buffer_inner.offset,
destination.len(),
flags,
)?)
}
/// Error that can happen from `check_copy_query_pool_results`.
#[derive(Debug, Copy, Clone)]
pub enum CheckCopyQueryPoolResultsError {
/// The buffer is too small for the copy operation.
BufferTooSmall {
/// Required number of elements in the buffer.
required_len: usize,
/// Actual number of elements in the buffer.
actual_len: usize,
},
/// The destination buffer is missing the transfer destination usage.
DestinationMissingTransferUsage,
/// The provided flags are not allowed for this type of query.
InvalidFlags,
/// The provided queries range is not valid for this pool.
OutOfRange,
}
impl From<GetResultsError> for CheckCopyQueryPoolResultsError {
#[inline]
fn from(value: GetResultsError) -> Self {
match value {
GetResultsError::BufferTooSmall {
required_len,
actual_len,
} => CheckCopyQueryPoolResultsError::BufferTooSmall {
required_len,
actual_len,
},
GetResultsError::InvalidFlags => CheckCopyQueryPoolResultsError::InvalidFlags,
GetResultsError::DeviceLost | GetResultsError::OomError(_) => unreachable!(),
}
}
}
impl error::Error for CheckCopyQueryPoolResultsError {}
impl fmt::Display for CheckCopyQueryPoolResultsError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
Self::BufferTooSmall { .. } => {
"the buffer is too small for the copy operation"
}
Self::DestinationMissingTransferUsage => {
"the destination buffer is missing the transfer destination usage"
}
Self::InvalidFlags => {
"the provided flags are not allowed for this type of query"
}
Self::OutOfRange => {
"the provided queries range is not valid for this pool"
}
}
)
}
}
/// Checks whether a `reset_query_pool` command is valid.
///
/// # Panic
///
/// - Panics if the query pool was not created with `device`.
pub fn check_reset_query_pool(
device: &Device,
query_pool: &QueryPool,
queries: Range<u32>,
) -> Result<(), CheckResetQueryPoolError> {
assert_eq!(
device.internal_object(),
query_pool.device().internal_object(),
);
query_pool
.queries_range(queries)
.ok_or(CheckResetQueryPoolError::OutOfRange)?;
Ok(())
}
/// Error that can happen from `check_reset_query_pool`.
#[derive(Debug, Copy, Clone)]
pub enum CheckResetQueryPoolError {
/// The provided queries range is not valid for this pool.
OutOfRange,
}
impl error::Error for CheckResetQueryPoolError {}
impl fmt::Display for CheckResetQueryPoolError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
Self::OutOfRange => {
"the provided queries range is not valid for this pool"
}
}
)
}
}

View File

@ -7,6 +7,20 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
use crate::check_errors;
use crate::features::{Features, FeaturesFfi};
use crate::instance::limits::Limits;
use crate::instance::loader;
use crate::instance::loader::FunctionPointers;
use crate::instance::loader::Loader;
use crate::instance::loader::LoadingError;
use crate::instance::{InstanceExtensions, RawInstanceExtensions};
use crate::sync::PipelineStage;
use crate::version::Version;
use crate::vk;
use crate::Error;
use crate::OomError;
use crate::VulkanObject;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::borrow::Cow; use std::borrow::Cow;
use std::error; use std::error;
@ -22,21 +36,6 @@ use std::ptr;
use std::slice; use std::slice;
use std::sync::Arc; use std::sync::Arc;
use crate::check_errors;
use crate::instance::limits::Limits;
use crate::instance::loader;
use crate::instance::loader::FunctionPointers;
use crate::instance::loader::Loader;
use crate::instance::loader::LoadingError;
use crate::vk;
use crate::Error;
use crate::OomError;
use crate::VulkanObject;
use crate::features::{Features, FeaturesFfi};
use crate::instance::{InstanceExtensions, RawInstanceExtensions};
use crate::version::Version;
/// An instance of a Vulkan context. This is the main object that should be created by an /// An instance of a Vulkan context. This is the main object that should be created by an
/// application before everything else. /// application before everything else.
/// ///
@ -314,13 +313,13 @@ impl Instance {
}; };
Ok(Arc::new(Instance { Ok(Arc::new(Instance {
instance: instance, instance,
//alloc: None, //alloc: None,
physical_devices: physical_devices, physical_devices,
vk: vk, vk,
extensions: extensions, extensions,
layers: layers, layers,
function_pointers: function_pointers, function_pointers,
})) }))
} }
@ -805,7 +804,7 @@ impl<'a> PhysicalDevice<'a> {
#[inline] #[inline]
pub fn enumerate(instance: &'a Arc<Instance>) -> PhysicalDevicesIter<'a> { pub fn enumerate(instance: &'a Arc<Instance>) -> PhysicalDevicesIter<'a> {
PhysicalDevicesIter { PhysicalDevicesIter {
instance: instance, instance,
current_id: 0, current_id: 0,
} }
} }
@ -828,7 +827,7 @@ impl<'a> PhysicalDevice<'a> {
pub fn from_index(instance: &'a Arc<Instance>, index: usize) -> Option<PhysicalDevice<'a>> { pub fn from_index(instance: &'a Arc<Instance>, index: usize) -> Option<PhysicalDevice<'a>> {
if instance.physical_devices.len() > index { if instance.physical_devices.len() > index {
Some(PhysicalDevice { Some(PhysicalDevice {
instance: instance, instance,
device: index, device: index,
}) })
} else { } else {
@ -931,7 +930,7 @@ impl<'a> PhysicalDevice<'a> {
if (id as usize) < self.infos().queue_families.len() { if (id as usize) < self.infos().queue_families.len() {
Some(QueueFamily { Some(QueueFamily {
physical_device: *self, physical_device: *self,
id: id, id,
}) })
} else { } else {
None None
@ -953,7 +952,7 @@ impl<'a> PhysicalDevice<'a> {
if id < self.infos().memory.memoryTypeCount { if id < self.infos().memory.memoryTypeCount {
Some(MemoryType { Some(MemoryType {
physical_device: *self, physical_device: *self,
id: id, id,
}) })
} else { } else {
None None
@ -975,7 +974,7 @@ impl<'a> PhysicalDevice<'a> {
if id < self.infos().memory.memoryHeapCount { if id < self.infos().memory.memoryHeapCount {
Some(MemoryHeap { Some(MemoryHeap {
physical_device: *self, physical_device: *self,
id: id, id,
}) })
} else { } else {
None None
@ -1149,19 +1148,19 @@ impl<'a> QueueFamily<'a> {
[granularity.width, granularity.height, granularity.depth] [granularity.width, granularity.height, granularity.depth]
} }
/// Returns true if queues of this family can execute graphics operations. /// Returns `true` if queues of this family can execute graphics operations.
#[inline] #[inline]
pub fn supports_graphics(&self) -> bool { pub fn supports_graphics(&self) -> bool {
(self.flags() & vk::QUEUE_GRAPHICS_BIT) != 0 (self.flags() & vk::QUEUE_GRAPHICS_BIT) != 0
} }
/// Returns true if queues of this family can execute compute operations. /// Returns `true` if queues of this family can execute compute operations.
#[inline] #[inline]
pub fn supports_compute(&self) -> bool { pub fn supports_compute(&self) -> bool {
(self.flags() & vk::QUEUE_COMPUTE_BIT) != 0 (self.flags() & vk::QUEUE_COMPUTE_BIT) != 0
} }
/// Returns true if queues of this family can execute transfer operations. /// Returns `true` if queues of this family can execute transfer operations.
/// > **Note**: While all queues that can perform graphics or compute operations can implicitly perform /// > **Note**: While all queues that can perform graphics or compute operations can implicitly perform
/// > transfer operations, graphics & compute queues only optionally indicate support for tranfers. /// > transfer operations, graphics & compute queues only optionally indicate support for tranfers.
/// > Many discrete cards will have one queue family that exclusively sets the VK_QUEUE_TRANSFER_BIT /// > Many discrete cards will have one queue family that exclusively sets the VK_QUEUE_TRANSFER_BIT
@ -1171,12 +1170,18 @@ impl<'a> QueueFamily<'a> {
(self.flags() & vk::QUEUE_TRANSFER_BIT) != 0 (self.flags() & vk::QUEUE_TRANSFER_BIT) != 0
} }
/// Returns true if queues of this family can execute sparse resources binding operations. /// Returns `true` if queues of this family can execute sparse resources binding operations.
#[inline] #[inline]
pub fn supports_sparse_binding(&self) -> bool { pub fn supports_sparse_binding(&self) -> bool {
(self.flags() & vk::QUEUE_SPARSE_BINDING_BIT) != 0 (self.flags() & vk::QUEUE_SPARSE_BINDING_BIT) != 0
} }
/// Returns `true` if the queues of this family support a particular pipeline stage.
#[inline]
pub fn supports_stage(&self, stage: PipelineStage) -> bool {
(self.flags() & stage.required_queue_flags()) != 0
}
/// Internal utility function that returns the flags of this queue family. /// Internal utility function that returns the flags of this queue family.
#[inline] #[inline]
fn flags(&self) -> u32 { fn flags(&self) -> u32 {

687
vulkano/src/query.rs Normal file
View File

@ -0,0 +1,687 @@
// 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::vk;
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: 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 => 0,
};
let pool = unsafe {
let infos = vk::QueryPoolCreateInfo {
sType: vk::STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
pNext: ptr::null(),
flags: 0, // reserved
queryType: ty.into(),
queryCount: num_slots,
pipelineStatistics: statistics,
};
let mut output = MaybeUninit::uninit();
let vk = device.pointers();
check_errors(vk.CreateQueryPool(
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 = vk::QueryPool;
const TYPE: vk::ObjectType = vk::OBJECT_TYPE_QUERY_POOL;
#[inline]
fn internal_object(&self) -> 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 vk = self.device.pointers();
vk.DestroyQueryPool(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().data_size()`](QueryType::data_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 usize,
destination.len(),
flags,
)?;
let result = unsafe {
let vk = self.pool.device.pointers();
check_errors(vk.GetQueryPoolResults(
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 as vk::DeviceSize,
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: usize,
buffer_len: usize,
flags: QueryResultFlags,
) -> Result<usize, GetResultsError>
where
T: QueryResultElement,
{
assert!(buffer_len > 0);
debug_assert!(buffer_start % std::mem::size_of::<T>() == 0);
let count = self.range.end - self.range.start;
let per_query_len = self.pool.ty.data_size() + flags.with_availability as usize;
let required_len = per_query_len * count as usize;
if buffer_len < required_len {
return Err(GetResultsError::BufferTooSmall {
required_len,
actual_len: buffer_len,
});
}
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>())
}
}
/// 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: usize,
/// Actual number of elements in the buffer.
actual_len: usize,
},
/// 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: vk::QueryResultFlags;
}
unsafe impl QueryResultElement for u32 {
const FLAG: vk::QueryResultFlags = 0;
}
unsafe impl QueryResultElement for u64 {
const FLAG: vk::QueryResultFlags = vk::QUERY_RESULT_64_BIT;
}
/// 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 fn data_size(&self) -> usize {
match self {
Self::Occlusion | Self::Timestamp => 1,
Self::PipelineStatistics(flags) => flags.count(),
}
}
}
impl From<QueryType> for vk::QueryType {
#[inline]
fn from(value: QueryType) -> Self {
match value {
QueryType::Occlusion => vk::QUERY_TYPE_OCCLUSION,
QueryType::PipelineStatistics(_) => vk::QUERY_TYPE_PIPELINE_STATISTICS,
QueryType::Timestamp => vk::QUERY_TYPE_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 vk::QueryControlFlags {
#[inline]
fn from(value: QueryControlFlags) -> Self {
let mut result = 0;
if value.precise {
result |= vk::QUERY_CONTROL_PRECISE_BIT;
}
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 fn count(&self) -> usize {
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 usize
+ input_assembly_primitives as usize
+ vertex_shader_invocations as usize
+ geometry_shader_invocations as usize
+ geometry_shader_primitives as usize
+ clipping_invocations as usize
+ clipping_primitives as usize
+ fragment_shader_invocations as usize
+ tessellation_control_shader_patches as usize
+ tessellation_evaluation_shader_invocations as usize
+ compute_shader_invocations as usize
}
/// Returns `true` if any flags referring to compute operations are set to `true`.
#[inline]
pub 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 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 vk::QueryPipelineStatisticFlags {
fn from(value: QueryPipelineStatisticFlags) -> vk::QueryPipelineStatisticFlags {
let mut result = 0;
if value.input_assembly_vertices {
result |= vk::QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT;
}
if value.input_assembly_primitives {
result |= vk::QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT;
}
if value.vertex_shader_invocations {
result |= vk::QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT;
}
if value.geometry_shader_invocations {
result |= vk::QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT;
}
if value.geometry_shader_primitives {
result |= vk::QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT;
}
if value.clipping_invocations {
result |= vk::QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT;
}
if value.clipping_primitives {
result |= vk::QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT;
}
if value.fragment_shader_invocations {
result |= vk::QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT;
}
if value.tessellation_control_shader_patches {
result |= vk::QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT;
}
if value.tessellation_evaluation_shader_invocations {
result |= vk::QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT;
}
if value.compute_shader_invocations {
result |= vk::QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT;
}
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 vk::QueryResultFlags {
#[inline]
fn from(value: QueryResultFlags) -> Self {
let mut result = 0;
if value.wait {
result |= vk::QUERY_RESULT_WAIT_BIT;
}
if value.with_availability {
result |= vk::QUERY_RESULT_WITH_AVAILABILITY_BIT;
}
if value.partial {
result |= vk::QUERY_RESULT_PARTIAL_BIT;
}
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!(),
};
}
}

View File

@ -1,394 +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.
//! This module provides support for 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 std::error;
use std::fmt;
use std::mem::MaybeUninit;
use std::ptr;
use std::sync::Arc;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::check_errors;
use crate::vk;
use crate::Error;
use crate::OomError;
use crate::VulkanObject;
pub struct UnsafeQueryPool {
pool: vk::QueryPool,
device: Arc<Device>,
num_slots: u32,
}
impl UnsafeQueryPool {
/// Builds a new query pool.
pub fn new(
device: Arc<Device>,
ty: QueryType,
num_slots: u32,
) -> Result<UnsafeQueryPool, 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 = MaybeUninit::uninit();
let vk = device.pointers();
check_errors(vk.CreateQueryPool(
device.internal_object(),
&infos,
ptr::null(),
output.as_mut_ptr(),
))?;
output.assume_init()
};
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
}
#[inline]
pub fn query(&self, index: u32) -> Option<UnsafeQuery> {
if index < self.num_slots() {
Some(UnsafeQuery { pool: self, index })
} else {
None
}
}
///
/// # Panic
///
/// Panics if `count` is 0.
#[inline]
pub fn queries_range(&self, first_index: u32, count: u32) -> Option<UnsafeQueriesRange> {
assert!(count >= 1);
if first_index + count < self.num_slots() {
Some(UnsafeQueriesRange {
pool: self,
first: first_index,
count,
})
} else {
None
}
}
}
unsafe impl VulkanObject for UnsafeQueryPool {
type Object = vk::QueryPool;
const TYPE: vk::ObjectType = vk::OBJECT_TYPE_QUERY_POOL;
#[inline]
fn internal_object(&self) -> vk::QueryPool {
self.pool
}
}
unsafe impl DeviceOwned for UnsafeQueryPool {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
pub struct UnsafeQuery<'a> {
pool: &'a UnsafeQueryPool,
index: u32,
}
impl<'a> UnsafeQuery<'a> {
#[inline]
pub fn pool(&self) -> &'a UnsafeQueryPool {
&self.pool
}
#[inline]
pub fn index(&self) -> u32 {
self.index
}
}
pub struct UnsafeQueriesRange<'a> {
pool: &'a UnsafeQueryPool,
first: u32,
count: u32,
}
impl<'a> UnsafeQueriesRange<'a> {
#[inline]
pub fn pool(&self) -> &'a UnsafeQueryPool {
&self.pool
}
#[inline]
pub fn first_index(&self) -> u32 {
self.first
}
#[inline]
pub fn count(&self) -> u32 {
self.count
}
}
#[derive(Debug, Copy, Clone)]
pub enum QueryType {
Occlusion,
PipelineStatistics(QueryPipelineStatisticFlags),
Timestamp,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct QueryControlFlags {
pub precise: bool,
}
#[derive(Clone, Copy, Debug, Default)]
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<vk::QueryPipelineStatisticFlags> 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 {
#[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 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),
}
}
}
pub struct OcclusionQueriesPool {
inner: UnsafeQueryPool,
}
impl OcclusionQueriesPool {
/// See the docs of new().
pub fn raw(device: Arc<Device>, num_slots: u32) -> Result<OcclusionQueriesPool, OomError> {
Ok(OcclusionQueriesPool {
inner: match UnsafeQueryPool::new(device, QueryType::Occlusion, num_slots) {
Ok(q) => q,
Err(QueryPoolCreationError::OomError(err)) => return Err(err),
Err(QueryPoolCreationError::PipelineStatisticsQueryFeatureNotEnabled) => {
unreachable!()
}
},
})
}
/// Builds a new query pool.
///
/// # Panic
///
/// - Panics if the device or host ran out of memory.
///
#[inline]
pub fn new(device: Arc<Device>, num_slots: u32) -> Arc<OcclusionQueriesPool> {
Arc::new(OcclusionQueriesPool::raw(device, num_slots).unwrap())
}
/// Returns the number of slots of that query pool.
#[inline]
pub fn num_slots(&self) -> u32 {
self.inner.num_slots()
}
}
unsafe impl DeviceOwned for OcclusionQueriesPool {
#[inline]
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
#[cfg(test)]
mod tests {
use crate::query::OcclusionQueriesPool;
use crate::query::QueryPipelineStatisticFlags;
use crate::query::QueryPoolCreationError;
use crate::query::QueryType;
use crate::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!(),
};
}
}

View File

@ -120,6 +120,7 @@ pub use self::future::NowFuture;
pub use self::future::SemaphoreSignalFuture; pub use self::future::SemaphoreSignalFuture;
pub use self::pipeline::AccessFlagBits; pub use self::pipeline::AccessFlagBits;
pub use self::pipeline::PipelineMemoryAccess; pub use self::pipeline::PipelineMemoryAccess;
pub use self::pipeline::PipelineStage;
pub use self::pipeline::PipelineStages; pub use self::pipeline::PipelineStages;
pub use self::semaphore::Semaphore; pub use self::semaphore::Semaphore;

View File

@ -11,9 +11,8 @@ use crate::vk;
use std::ops; use std::ops;
macro_rules! pipeline_stages { macro_rules! pipeline_stages {
($($elem:ident => $val:expr,)+) => ( ($($elem:ident, $var:ident => $val:expr, $queue:expr;)+) => (
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub struct PipelineStages { pub struct PipelineStages {
$( $(
pub $elem: bool, pub $elem: bool,
@ -61,27 +60,46 @@ macro_rules! pipeline_stages {
)+ )+
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(u32)]
pub enum PipelineStage {
$(
$var = $val,
)+
}
impl PipelineStage {
#[inline]
pub fn required_queue_flags(&self) -> vk::QueueFlags {
match self {
$(
Self::$var => $queue,
)+
}
}
}
); );
} }
pipeline_stages! { pipeline_stages! {
top_of_pipe => vk::PIPELINE_STAGE_TOP_OF_PIPE_BIT, top_of_pipe, TopOfPipe => vk::PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0;
draw_indirect => vk::PIPELINE_STAGE_DRAW_INDIRECT_BIT, draw_indirect, DrawIndirect => vk::PIPELINE_STAGE_DRAW_INDIRECT_BIT, vk::QUEUE_GRAPHICS_BIT | vk::QUEUE_COMPUTE_BIT;
vertex_input => vk::PIPELINE_STAGE_VERTEX_INPUT_BIT, vertex_input, VertexInput => vk::PIPELINE_STAGE_VERTEX_INPUT_BIT, vk::QUEUE_GRAPHICS_BIT;
vertex_shader => vk::PIPELINE_STAGE_VERTEX_SHADER_BIT, vertex_shader, VertexShader => vk::PIPELINE_STAGE_VERTEX_SHADER_BIT, vk::QUEUE_GRAPHICS_BIT;
tessellation_control_shader => vk::PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT, tessellation_control_shader, TessellationControlShader => vk::PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT, vk::QUEUE_GRAPHICS_BIT;
tessellation_evaluation_shader => vk::PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT, tessellation_evaluation_shader, TessellationEvaluationShader => vk::PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT, vk::QUEUE_GRAPHICS_BIT;
geometry_shader => vk::PIPELINE_STAGE_GEOMETRY_SHADER_BIT, geometry_shader, GeometryShader => vk::PIPELINE_STAGE_GEOMETRY_SHADER_BIT, vk::QUEUE_GRAPHICS_BIT;
fragment_shader => vk::PIPELINE_STAGE_FRAGMENT_SHADER_BIT, fragment_shader, FragmentShader => vk::PIPELINE_STAGE_FRAGMENT_SHADER_BIT, vk::QUEUE_GRAPHICS_BIT;
early_fragment_tests => vk::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, early_fragment_tests, EarlyFragmentTests => vk::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, vk::QUEUE_GRAPHICS_BIT;
late_fragment_tests => vk::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, late_fragment_tests, LateFragmentTests => vk::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, vk::QUEUE_GRAPHICS_BIT;
color_attachment_output => vk::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, color_attachment_output, ColorAttachmentOutput => vk::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::QUEUE_GRAPHICS_BIT;
compute_shader => vk::PIPELINE_STAGE_COMPUTE_SHADER_BIT, compute_shader, ComputeShader => vk::PIPELINE_STAGE_COMPUTE_SHADER_BIT, vk::QUEUE_COMPUTE_BIT;
transfer => vk::PIPELINE_STAGE_TRANSFER_BIT, transfer, Transfer => vk::PIPELINE_STAGE_TRANSFER_BIT, vk::QUEUE_GRAPHICS_BIT | vk::QUEUE_COMPUTE_BIT | vk::QUEUE_TRANSFER_BIT;
bottom_of_pipe => vk::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, bottom_of_pipe, BottomOfPipe => vk::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0;
host => vk::PIPELINE_STAGE_HOST_BIT, host, Host => vk::PIPELINE_STAGE_HOST_BIT, 0;
all_graphics => vk::PIPELINE_STAGE_ALL_GRAPHICS_BIT, all_graphics, AllGraphics => vk::PIPELINE_STAGE_ALL_GRAPHICS_BIT, vk::QUEUE_GRAPHICS_BIT;
all_commands => vk::PIPELINE_STAGE_ALL_COMMANDS_BIT, all_commands, AllCommands => vk::PIPELINE_STAGE_ALL_COMMANDS_BIT, 0;
} }
macro_rules! access_flags { macro_rules! access_flags {