mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-22 06:45:23 +00:00
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:
parent
e8e9c69d83
commit
5577099029
@ -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.
|
||||||
|
@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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> {
|
||||||
|
@ -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(),
|
||||||
);
|
);
|
||||||
|
@ -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;
|
||||||
|
390
vulkano/src/command_buffer/validity/query.rs
Normal file
390
vulkano/src/command_buffer/validity/query.rs
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -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
687
vulkano/src/query.rs
Normal 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!(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -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!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user