blob: b143b35abdead33d61e7431285184fcb2cb4fbd1 [file] [log] [blame]
// 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 super::{
allocator::{
CommandBufferAlloc, CommandBufferAllocator, CommandBufferBuilderAlloc,
StandardCommandBufferAlloc, StandardCommandBufferAllocator,
},
synced::{CommandBufferBuilderState, SyncCommandBuffer, SyncCommandBufferBuilder},
sys::CommandBufferBeginInfo,
CommandBufferExecError, CommandBufferInheritanceInfo, CommandBufferInheritanceRenderPassInfo,
CommandBufferInheritanceRenderPassType, CommandBufferLevel, CommandBufferResourcesUsage,
CommandBufferState, CommandBufferUsage, PrimaryCommandBufferAbstract, RenderingAttachmentInfo,
SecondaryCommandBufferAbstract, SecondaryCommandBufferResourcesUsage, SubpassContents,
};
use crate::{
command_buffer::CommandBufferInheritanceRenderingInfo,
device::{Device, DeviceOwned, QueueFamilyProperties},
format::{Format, FormatFeatures},
image::ImageAspects,
query::{QueryControlFlags, QueryType},
render_pass::{Framebuffer, Subpass},
OomError, RequirementNotMet, RequiresOneOf, VulkanObject,
};
use ahash::HashMap;
use parking_lot::{Mutex, MutexGuard};
use std::{
error::Error,
fmt::{Display, Error as FmtError, Formatter},
marker::PhantomData,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
};
/// Note that command buffers allocated from `StandardCommandBufferAllocator` don't implement
/// the `Send` and `Sync` traits. If you use this allocator, then the `AutoCommandBufferBuilder`
/// will not implement `Send` and `Sync` either. Once a command buffer is built, however, it *does*
/// implement `Send` and `Sync`.
pub struct AutoCommandBufferBuilder<L, A = StandardCommandBufferAllocator>
where
A: CommandBufferAllocator,
{
pub(super) inner: SyncCommandBufferBuilder,
builder_alloc: A::Builder, // Safety: must be dropped after `inner`
// The index of the queue family that this command buffer is being created for.
queue_family_index: u32,
// The inheritance for secondary command buffers.
// Must be `None` in a primary command buffer and `Some` in a secondary command buffer.
pub(super) inheritance_info: Option<CommandBufferInheritanceInfo>,
// Usage flags passed when creating the command buffer.
pub(super) usage: CommandBufferUsage,
// If we're inside a render pass, contains the render pass state.
pub(super) render_pass_state: Option<RenderPassState>,
// If any queries are active, this hashmap contains their state.
pub(super) query_state: HashMap<ash::vk::QueryType, QueryState>,
_data: PhantomData<L>,
}
// The state of the current render pass.
pub(super) struct RenderPassState {
pub(super) contents: SubpassContents,
pub(super) render_area_offset: [u32; 2],
pub(super) render_area_extent: [u32; 2],
pub(super) render_pass: RenderPassStateType,
pub(super) view_mask: u32,
}
pub(super) enum RenderPassStateType {
BeginRenderPass(BeginRenderPassState),
BeginRendering(BeginRenderingState),
}
impl From<BeginRenderPassState> for RenderPassStateType {
fn from(val: BeginRenderPassState) -> Self {
Self::BeginRenderPass(val)
}
}
impl From<BeginRenderingState> for RenderPassStateType {
fn from(val: BeginRenderingState) -> Self {
Self::BeginRendering(val)
}
}
pub(super) struct BeginRenderPassState {
pub(super) subpass: Subpass,
pub(super) framebuffer: Option<Arc<Framebuffer>>,
}
pub(super) struct BeginRenderingState {
pub(super) attachments: Option<BeginRenderingAttachments>,
pub(super) color_attachment_formats: Vec<Option<Format>>,
pub(super) depth_attachment_format: Option<Format>,
pub(super) stencil_attachment_format: Option<Format>,
pub(super) pipeline_used: bool,
}
pub(super) struct BeginRenderingAttachments {
pub(super) color_attachments: Vec<Option<RenderingAttachmentInfo>>,
pub(super) depth_attachment: Option<RenderingAttachmentInfo>,
pub(super) stencil_attachment: Option<RenderingAttachmentInfo>,
}
// The state of an active query.
pub(super) struct QueryState {
pub(super) query_pool: ash::vk::QueryPool,
pub(super) query: u32,
pub(super) ty: QueryType,
pub(super) flags: QueryControlFlags,
pub(super) in_subpass: bool,
}
impl<A> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, A>
where
A: CommandBufferAllocator,
{
/// Starts recording a primary command buffer.
#[inline]
pub fn primary(
allocator: &A,
queue_family_index: u32,
usage: CommandBufferUsage,
) -> Result<
AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<A::Alloc>, A>,
CommandBufferBeginError,
> {
unsafe {
AutoCommandBufferBuilder::begin(
allocator,
queue_family_index,
CommandBufferLevel::Primary,
CommandBufferBeginInfo {
usage,
inheritance_info: None,
_ne: crate::NonExhaustive(()),
},
)
}
}
}
impl<A> AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, A>
where
A: CommandBufferAllocator,
{
/// Starts recording a secondary command buffer.
#[inline]
pub fn secondary(
allocator: &A,
queue_family_index: u32,
usage: CommandBufferUsage,
inheritance_info: CommandBufferInheritanceInfo,
) -> Result<
AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<A::Alloc>, A>,
CommandBufferBeginError,
> {
unsafe {
AutoCommandBufferBuilder::begin(
allocator,
queue_family_index,
CommandBufferLevel::Secondary,
CommandBufferBeginInfo {
usage,
inheritance_info: Some(inheritance_info),
_ne: crate::NonExhaustive(()),
},
)
}
}
}
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
// Actual constructor. Private.
//
// `begin_info.inheritance_info` must match `level`.
unsafe fn begin(
allocator: &A,
queue_family_index: u32,
level: CommandBufferLevel,
begin_info: CommandBufferBeginInfo,
) -> Result<AutoCommandBufferBuilder<L, A>, CommandBufferBeginError> {
Self::validate_begin(allocator.device(), queue_family_index, level, &begin_info)?;
let &CommandBufferBeginInfo {
usage,
ref inheritance_info,
_ne: _,
} = &begin_info;
let inheritance_info = inheritance_info.clone();
let mut render_pass_state = None;
if let Some(inheritance_info) = &inheritance_info {
let &CommandBufferInheritanceInfo {
ref render_pass,
occlusion_query: _,
query_statistics_flags: _,
_ne: _,
} = inheritance_info;
if let Some(render_pass) = render_pass {
// In a secondary command buffer, we don't know the render area yet, so use a
// dummy value.
let render_area_offset = [0, 0];
let mut render_area_extent = [u32::MAX, u32::MAX];
match render_pass {
CommandBufferInheritanceRenderPassType::BeginRenderPass(info) => {
if let Some(framebuffer) = &info.framebuffer {
// Still not exact, but it's a better upper bound.
render_area_extent = framebuffer.extent();
}
render_pass_state = Some(RenderPassState {
contents: SubpassContents::Inline,
render_area_offset,
render_area_extent,
render_pass: BeginRenderPassState {
subpass: info.subpass.clone(),
framebuffer: info.framebuffer.clone(),
}
.into(),
view_mask: info.subpass.subpass_desc().view_mask,
});
}
CommandBufferInheritanceRenderPassType::BeginRendering(info) => {
render_pass_state = Some(RenderPassState {
contents: SubpassContents::Inline,
render_area_offset,
render_area_extent,
render_pass: BeginRenderingState {
attachments: None,
color_attachment_formats: info.color_attachment_formats.clone(),
depth_attachment_format: info.depth_attachment_format,
stencil_attachment_format: info.stencil_attachment_format,
pipeline_used: false,
}
.into(),
view_mask: info.view_mask,
});
}
}
}
}
let builder_alloc = allocator
.allocate(queue_family_index, level, 1)?
.next()
.expect("requested one command buffer from the command pool, but got zero");
let inner = SyncCommandBufferBuilder::new(builder_alloc.inner(), begin_info)?;
Ok(AutoCommandBufferBuilder {
inner,
builder_alloc,
queue_family_index,
render_pass_state,
query_state: HashMap::default(),
inheritance_info,
usage,
_data: PhantomData,
})
}
fn validate_begin(
device: &Device,
_queue_family_index: u32,
level: CommandBufferLevel,
begin_info: &CommandBufferBeginInfo,
) -> Result<(), CommandBufferBeginError> {
let physical_device = device.physical_device();
let properties = physical_device.properties();
let &CommandBufferBeginInfo {
usage: _,
ref inheritance_info,
_ne: _,
} = &begin_info;
if let Some(inheritance_info) = &inheritance_info {
debug_assert!(level == CommandBufferLevel::Secondary);
let &CommandBufferInheritanceInfo {
ref render_pass,
occlusion_query,
query_statistics_flags,
_ne: _,
} = inheritance_info;
if let Some(render_pass) = render_pass {
// VUID-VkCommandBufferBeginInfo-flags-06000
// VUID-VkCommandBufferBeginInfo-flags-06002
// Ensured by the definition of the `CommandBufferInheritanceRenderPassType` enum.
match render_pass {
CommandBufferInheritanceRenderPassType::BeginRenderPass(render_pass_info) => {
let &CommandBufferInheritanceRenderPassInfo {
ref subpass,
ref framebuffer,
} = render_pass_info;
// VUID-VkCommandBufferInheritanceInfo-commonparent
assert_eq!(device, subpass.render_pass().device().as_ref());
// VUID-VkCommandBufferBeginInfo-flags-06001
// Ensured by how the `Subpass` type is constructed.
if let Some(framebuffer) = framebuffer {
// VUID-VkCommandBufferInheritanceInfo-commonparent
assert_eq!(device, framebuffer.device().as_ref());
// VUID-VkCommandBufferBeginInfo-flags-00055
if !framebuffer
.render_pass()
.is_compatible_with(subpass.render_pass())
{
return Err(CommandBufferBeginError::FramebufferNotCompatible);
}
}
}
CommandBufferInheritanceRenderPassType::BeginRendering(rendering_info) => {
let &CommandBufferInheritanceRenderingInfo {
view_mask,
ref color_attachment_formats,
depth_attachment_format,
stencil_attachment_format,
rasterization_samples,
} = rendering_info;
// VUID-VkCommandBufferInheritanceRenderingInfo-multiview-06008
if view_mask != 0 && !device.enabled_features().multiview {
return Err(CommandBufferBeginError::RequirementNotMet {
required_for: "`inheritance_info.render_pass` is \
`CommandBufferInheritanceRenderPassType::BeginRendering`, \
where `view_mask` is not `0`",
requires_one_of: RequiresOneOf {
features: &["multiview"],
..Default::default()
},
});
}
let view_count = u32::BITS - view_mask.leading_zeros();
// VUID-VkCommandBufferInheritanceRenderingInfo-viewMask-06009
if view_count > properties.max_multiview_view_count.unwrap_or(0) {
return Err(CommandBufferBeginError::MaxMultiviewViewCountExceeded {
view_count,
max: properties.max_multiview_view_count.unwrap_or(0),
});
}
for (attachment_index, format) in color_attachment_formats
.iter()
.enumerate()
.flat_map(|(i, f)| f.map(|f| (i, f)))
{
let attachment_index = attachment_index as u32;
// VUID-VkCommandBufferInheritanceRenderingInfo-pColorAttachmentFormats-parameter
format.validate_device(device)?;
// VUID-VkCommandBufferInheritanceRenderingInfo-pColorAttachmentFormats-06006
// Use unchecked, because all validation has been done above.
if !unsafe { physical_device.format_properties_unchecked(format) }
.potential_format_features()
.intersects(FormatFeatures::COLOR_ATTACHMENT)
{
return Err(
CommandBufferBeginError::ColorAttachmentFormatUsageNotSupported {
attachment_index,
},
);
}
}
if let Some(format) = depth_attachment_format {
// VUID-VkCommandBufferInheritanceRenderingInfo-depthAttachmentFormat-parameter
format.validate_device(device)?;
// VUID-VkCommandBufferInheritanceRenderingInfo-depthAttachmentFormat-06540
if !format.aspects().intersects(ImageAspects::DEPTH) {
return Err(
CommandBufferBeginError::DepthAttachmentFormatUsageNotSupported,
);
}
// VUID-VkCommandBufferInheritanceRenderingInfo-depthAttachmentFormat-06007
// Use unchecked, because all validation has been done above.
if !unsafe { physical_device.format_properties_unchecked(format) }
.potential_format_features()
.intersects(FormatFeatures::DEPTH_STENCIL_ATTACHMENT)
{
return Err(
CommandBufferBeginError::DepthAttachmentFormatUsageNotSupported,
);
}
}
if let Some(format) = stencil_attachment_format {
// VUID-VkCommandBufferInheritanceRenderingInfo-stencilAttachmentFormat-parameter
format.validate_device(device)?;
// VUID-VkCommandBufferInheritanceRenderingInfo-stencilAttachmentFormat-06541
if !format.aspects().intersects(ImageAspects::STENCIL) {
return Err(
CommandBufferBeginError::StencilAttachmentFormatUsageNotSupported,
);
}
// VUID-VkCommandBufferInheritanceRenderingInfo-stencilAttachmentFormat-06199
// Use unchecked, because all validation has been done above.
if !unsafe { physical_device.format_properties_unchecked(format) }
.potential_format_features()
.intersects(FormatFeatures::DEPTH_STENCIL_ATTACHMENT)
{
return Err(
CommandBufferBeginError::StencilAttachmentFormatUsageNotSupported,
);
}
}
if let (Some(depth_format), Some(stencil_format)) =
(depth_attachment_format, stencil_attachment_format)
{
// VUID-VkCommandBufferInheritanceRenderingInfo-depthAttachmentFormat-06200
if depth_format != stencil_format {
return Err(
CommandBufferBeginError::DepthStencilAttachmentFormatMismatch,
);
}
}
// VUID-VkCommandBufferInheritanceRenderingInfo-rasterizationSamples-parameter
rasterization_samples.validate_device(device)?;
}
}
}
if let Some(control_flags) = occlusion_query {
// VUID-VkCommandBufferInheritanceInfo-queryFlags-00057
control_flags.validate_device(device)?;
// VUID-VkCommandBufferInheritanceInfo-occlusionQueryEnable-00056
// VUID-VkCommandBufferInheritanceInfo-queryFlags-02788
if !device.enabled_features().inherited_queries {
return Err(CommandBufferBeginError::RequirementNotMet {
required_for: "`inheritance_info.occlusion_query` is `Some`",
requires_one_of: RequiresOneOf {
features: &["inherited_queries"],
..Default::default()
},
});
}
// VUID-vkBeginCommandBuffer-commandBuffer-00052
if control_flags.intersects(QueryControlFlags::PRECISE)
&& !device.enabled_features().occlusion_query_precise
{
return Err(CommandBufferBeginError::RequirementNotMet {
required_for: "`inheritance_info.occlusion_query` is \
`Some(control_flags)`, where `control_flags` contains \
`QueryControlFlags::PRECISE`",
requires_one_of: RequiresOneOf {
features: &["occlusion_query_precise"],
..Default::default()
},
});
}
}
// VUID-VkCommandBufferInheritanceInfo-pipelineStatistics-02789
query_statistics_flags.validate_device(device)?;
// VUID-VkCommandBufferInheritanceInfo-pipelineStatistics-00058
if query_statistics_flags.count() > 0
&& !device.enabled_features().pipeline_statistics_query
{
return Err(CommandBufferBeginError::RequirementNotMet {
required_for: "`inheritance_info.query_statistics_flags` is not empty",
requires_one_of: RequiresOneOf {
features: &["pipeline_statistics_query"],
..Default::default()
},
});
}
} else {
debug_assert!(level == CommandBufferLevel::Primary);
// VUID-vkBeginCommandBuffer-commandBuffer-02840
// Ensured by the definition of the `CommandBufferUsage` enum.
}
Ok(())
}
}
/// Error that can happen when beginning recording of a command buffer.
#[derive(Clone, Copy, Debug)]
pub enum CommandBufferBeginError {
/// Not enough memory.
OomError(OomError),
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
/// A color attachment has a format that does not support that usage.
ColorAttachmentFormatUsageNotSupported { attachment_index: u32 },
/// The depth attachment has a format that does not support that usage.
DepthAttachmentFormatUsageNotSupported,
/// The depth and stencil attachments have different formats.
DepthStencilAttachmentFormatMismatch,
/// The framebuffer is not compatible with the render pass.
FramebufferNotCompatible,
/// The `max_multiview_view_count` limit has been exceeded.
MaxMultiviewViewCountExceeded { view_count: u32, max: u32 },
/// The stencil attachment has a format that does not support that usage.
StencilAttachmentFormatUsageNotSupported,
}
impl Error for CommandBufferBeginError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::OomError(err) => Some(err),
_ => None,
}
}
}
impl Display for CommandBufferBeginError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::OomError(_) => write!(f, "not enough memory available"),
Self::RequirementNotMet {
required_for,
requires_one_of,
} => write!(
f,
"a requirement was not met for: {}; requires one of: {}",
required_for, requires_one_of,
),
Self::ColorAttachmentFormatUsageNotSupported { attachment_index } => write!(
f,
"color attachment {} has a format that does not support that usage",
attachment_index,
),
Self::DepthAttachmentFormatUsageNotSupported => write!(
f,
"the depth attachment has a format that does not support that usage",
),
Self::DepthStencilAttachmentFormatMismatch => write!(
f,
"the depth and stencil attachments have different formats",
),
Self::FramebufferNotCompatible => {
write!(f, "the framebuffer is not compatible with the render pass")
}
Self::MaxMultiviewViewCountExceeded { .. } => {
write!(f, "the `max_multiview_view_count` limit has been exceeded")
}
Self::StencilAttachmentFormatUsageNotSupported => write!(
f,
"the stencil attachment has a format that does not support that usage",
),
}
}
}
impl From<OomError> for CommandBufferBeginError {
fn from(err: OomError) -> Self {
Self::OomError(err)
}
}
impl From<RequirementNotMet> for CommandBufferBeginError {
fn from(err: RequirementNotMet) -> Self {
Self::RequirementNotMet {
required_for: err.required_for,
requires_one_of: err.requires_one_of,
}
}
}
impl<A> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<A::Alloc>, A>
where
A: CommandBufferAllocator,
{
/// Builds the command buffer.
pub fn build(self) -> Result<PrimaryAutoCommandBuffer<A::Alloc>, BuildError> {
if self.render_pass_state.is_some() {
return Err(BuildError::RenderPassActive);
}
if !self.query_state.is_empty() {
return Err(BuildError::QueryActive);
}
Ok(PrimaryAutoCommandBuffer {
inner: self.inner.build()?,
_alloc: self.builder_alloc.into_alloc(),
usage: self.usage,
state: Mutex::new(Default::default()),
})
}
}
impl<A> AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<A::Alloc>, A>
where
A: CommandBufferAllocator,
{
/// Builds the command buffer.
pub fn build(self) -> Result<SecondaryAutoCommandBuffer<A::Alloc>, BuildError> {
if !self.query_state.is_empty() {
return Err(BuildError::QueryActive);
}
let submit_state = match self.usage {
CommandBufferUsage::MultipleSubmit => SubmitState::ExclusiveUse {
in_use: AtomicBool::new(false),
},
CommandBufferUsage::SimultaneousUse => SubmitState::Concurrent,
CommandBufferUsage::OneTimeSubmit => SubmitState::OneTime {
already_submitted: AtomicBool::new(false),
},
};
Ok(SecondaryAutoCommandBuffer {
inner: self.inner.build()?,
_alloc: self.builder_alloc.into_alloc(),
usage: self.usage,
inheritance_info: self.inheritance_info.unwrap(),
submit_state,
})
}
}
/// Error that can happen when building a command buffer.
#[derive(Clone, Debug)]
pub enum BuildError {
OomError(OomError),
/// A render pass is still active on the command buffer.
RenderPassActive,
/// A query is still active on the command buffer.
QueryActive,
}
impl Error for BuildError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::OomError(err) => Some(err),
_ => None,
}
}
}
impl Display for BuildError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::OomError(_) => write!(f, "out of memory"),
Self::RenderPassActive => {
write!(f, "a render pass is still active on the command buffer")
}
Self::QueryActive => write!(f, "a query is still active on the command buffer"),
}
}
}
impl From<OomError> for BuildError {
fn from(err: OomError) -> Self {
Self::OomError(err)
}
}
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
pub(super) fn queue_family_properties(&self) -> &QueueFamilyProperties {
&self.device().physical_device().queue_family_properties()[self.queue_family_index as usize]
}
/// Returns the binding/setting state.
pub fn state(&self) -> CommandBufferBuilderState<'_> {
self.inner.state()
}
}
unsafe impl<L, A> DeviceOwned for AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
pub struct PrimaryAutoCommandBuffer<A = StandardCommandBufferAlloc> {
inner: SyncCommandBuffer,
_alloc: A, // Safety: must be dropped after `inner`
usage: CommandBufferUsage,
state: Mutex<CommandBufferState>,
}
unsafe impl<A> DeviceOwned for PrimaryAutoCommandBuffer<A> {
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
unsafe impl<A> VulkanObject for PrimaryAutoCommandBuffer<A> {
type Handle = ash::vk::CommandBuffer;
fn handle(&self) -> Self::Handle {
self.inner.as_ref().handle()
}
}
unsafe impl<A> PrimaryCommandBufferAbstract for PrimaryAutoCommandBuffer<A>
where
A: CommandBufferAlloc,
{
fn usage(&self) -> CommandBufferUsage {
self.usage
}
fn state(&self) -> MutexGuard<'_, CommandBufferState> {
self.state.lock()
}
fn resources_usage(&self) -> &CommandBufferResourcesUsage {
self.inner.resources_usage()
}
}
pub struct SecondaryAutoCommandBuffer<A = StandardCommandBufferAlloc> {
inner: SyncCommandBuffer,
_alloc: A, // Safety: must be dropped after `inner`
usage: CommandBufferUsage,
inheritance_info: CommandBufferInheritanceInfo,
// Tracks usage of the command buffer on the GPU.
submit_state: SubmitState,
}
unsafe impl<A> VulkanObject for SecondaryAutoCommandBuffer<A> {
type Handle = ash::vk::CommandBuffer;
fn handle(&self) -> Self::Handle {
self.inner.as_ref().handle()
}
}
unsafe impl<A> DeviceOwned for SecondaryAutoCommandBuffer<A> {
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
unsafe impl<A> SecondaryCommandBufferAbstract for SecondaryAutoCommandBuffer<A>
where
A: CommandBufferAlloc,
{
fn usage(&self) -> CommandBufferUsage {
self.usage
}
fn inheritance_info(&self) -> &CommandBufferInheritanceInfo {
&self.inheritance_info
}
fn lock_record(&self) -> Result<(), CommandBufferExecError> {
match self.submit_state {
SubmitState::OneTime {
ref already_submitted,
} => {
let was_already_submitted = already_submitted.swap(true, Ordering::SeqCst);
if was_already_submitted {
return Err(CommandBufferExecError::OneTimeSubmitAlreadySubmitted);
}
}
SubmitState::ExclusiveUse { ref in_use } => {
let already_in_use = in_use.swap(true, Ordering::SeqCst);
if already_in_use {
return Err(CommandBufferExecError::ExclusiveAlreadyInUse);
}
}
SubmitState::Concurrent => (),
};
Ok(())
}
unsafe fn unlock(&self) {
match self.submit_state {
SubmitState::OneTime {
ref already_submitted,
} => {
debug_assert!(already_submitted.load(Ordering::SeqCst));
}
SubmitState::ExclusiveUse { ref in_use } => {
let old_val = in_use.swap(false, Ordering::SeqCst);
debug_assert!(old_val);
}
SubmitState::Concurrent => (),
};
}
fn resources_usage(&self) -> &SecondaryCommandBufferResourcesUsage {
self.inner.secondary_resources_usage()
}
}
// Whether the command buffer can be submitted.
#[derive(Debug)]
enum SubmitState {
// The command buffer was created with the "SimultaneousUse" flag. Can always be submitted at
// any time.
Concurrent,
// The command buffer can only be submitted once simultaneously.
ExclusiveUse {
// True if the command buffer is current in use by the GPU.
in_use: AtomicBool,
},
// The command buffer can only ever be submitted once.
OneTime {
// True if the command buffer has already been submitted once and can be no longer be
// submitted.
already_submitted: AtomicBool,
},
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
buffer::{Buffer, BufferCreateInfo, BufferUsage},
command_buffer::{
synced::SyncCommandBufferBuilderError, BufferCopy, CopyBufferInfoTyped, CopyError,
ExecuteCommandsError,
},
device::{DeviceCreateInfo, QueueCreateInfo},
memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
sync::GpuFuture,
};
#[test]
fn copy_buffer_dimensions() {
let instance = instance!();
let physical_device = match instance.enumerate_physical_devices().unwrap().next() {
Some(p) => p,
None => return,
};
let (device, mut queues) = Device::new(
physical_device,
DeviceCreateInfo {
queue_create_infos: vec![QueueCreateInfo {
queue_family_index: 0,
..Default::default()
}],
..Default::default()
},
)
.unwrap();
let queue = queues.next().unwrap();
let memory_allocator = StandardMemoryAllocator::new_default(device.clone());
let source = Buffer::from_iter(
&memory_allocator,
BufferCreateInfo {
usage: BufferUsage::TRANSFER_SRC,
..Default::default()
},
AllocationCreateInfo {
usage: MemoryUsage::Upload,
..Default::default()
},
[1_u32, 2].iter().copied(),
)
.unwrap();
let destination = Buffer::from_iter(
&memory_allocator,
BufferCreateInfo {
usage: BufferUsage::TRANSFER_DST,
..Default::default()
},
AllocationCreateInfo {
usage: MemoryUsage::Upload,
..Default::default()
},
[0_u32, 10, 20, 3, 4].iter().copied(),
)
.unwrap();
let cb_allocator = StandardCommandBufferAllocator::new(device, Default::default());
let mut cbb = AutoCommandBufferBuilder::primary(
&cb_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)
.unwrap();
cbb.copy_buffer(CopyBufferInfoTyped {
regions: [BufferCopy {
src_offset: 0,
dst_offset: 1,
size: 2,
..Default::default()
}]
.into(),
..CopyBufferInfoTyped::buffers(source, destination.clone())
})
.unwrap();
let cb = cbb.build().unwrap();
let future = cb
.execute(queue)
.unwrap()
.then_signal_fence_and_flush()
.unwrap();
future.wait(None).unwrap();
let result = destination.read().unwrap();
assert_eq!(*result, [0_u32, 1, 2, 3, 4]);
}
#[test]
fn secondary_nonconcurrent_conflict() {
let (device, queue) = gfx_dev_and_queue!();
let cb_allocator = StandardCommandBufferAllocator::new(device, Default::default());
// Make a secondary CB that doesn't support simultaneous use.
let builder = AutoCommandBufferBuilder::secondary(
&cb_allocator,
queue.queue_family_index(),
CommandBufferUsage::MultipleSubmit,
Default::default(),
)
.unwrap();
let secondary = Arc::new(builder.build().unwrap());
{
let mut builder = AutoCommandBufferBuilder::primary(
&cb_allocator,
queue.queue_family_index(),
CommandBufferUsage::SimultaneousUse,
)
.unwrap();
// Add the secondary a first time
builder.execute_commands(secondary.clone()).unwrap();
// Recording the same non-concurrent secondary command buffer twice into the same
// primary is an error.
assert!(matches!(
builder.execute_commands(secondary.clone()),
Err(ExecuteCommandsError::SyncCommandBufferBuilderError(
SyncCommandBufferBuilderError::ExecError(
CommandBufferExecError::ExclusiveAlreadyInUse
)
))
));
}
{
let mut builder = AutoCommandBufferBuilder::primary(
&cb_allocator,
queue.queue_family_index(),
CommandBufferUsage::SimultaneousUse,
)
.unwrap();
builder.execute_commands(secondary.clone()).unwrap();
let cb1 = builder.build().unwrap();
let mut builder = AutoCommandBufferBuilder::primary(
&cb_allocator,
queue.queue_family_index(),
CommandBufferUsage::SimultaneousUse,
)
.unwrap();
// Recording the same non-concurrent secondary command buffer into multiple
// primaries is an error.
assert!(matches!(
builder.execute_commands(secondary.clone()),
Err(ExecuteCommandsError::SyncCommandBufferBuilderError(
SyncCommandBufferBuilderError::ExecError(
CommandBufferExecError::ExclusiveAlreadyInUse
)
))
));
std::mem::drop(cb1);
// Now that the first cb is dropped, we should be able to record.
builder.execute_commands(secondary).unwrap();
}
}
#[test]
fn buffer_self_copy_overlapping() {
let (device, queue) = gfx_dev_and_queue!();
let memory_allocator = StandardMemoryAllocator::new_default(device.clone());
let source = Buffer::from_iter(
&memory_allocator,
BufferCreateInfo {
usage: BufferUsage::TRANSFER_SRC | BufferUsage::TRANSFER_DST,
..Default::default()
},
AllocationCreateInfo {
usage: MemoryUsage::Upload,
..Default::default()
},
[0_u32, 1, 2, 3].iter().copied(),
)
.unwrap();
let cb_allocator = StandardCommandBufferAllocator::new(device, Default::default());
let mut builder = AutoCommandBufferBuilder::primary(
&cb_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)
.unwrap();
builder
.copy_buffer(CopyBufferInfoTyped {
regions: [BufferCopy {
src_offset: 0,
dst_offset: 2,
size: 2,
..Default::default()
}]
.into(),
..CopyBufferInfoTyped::buffers(source.clone(), source.clone())
})
.unwrap();
let cb = builder.build().unwrap();
let future = cb
.execute(queue)
.unwrap()
.then_signal_fence_and_flush()
.unwrap();
future.wait(None).unwrap();
let result = source.read().unwrap();
assert_eq!(*result, [0_u32, 1, 0, 1]);
}
#[test]
fn buffer_self_copy_not_overlapping() {
let (device, queue) = gfx_dev_and_queue!();
let memory_allocator = StandardMemoryAllocator::new_default(device.clone());
let source = Buffer::from_iter(
&memory_allocator,
BufferCreateInfo {
usage: BufferUsage::TRANSFER_SRC | BufferUsage::TRANSFER_DST,
..Default::default()
},
AllocationCreateInfo {
usage: MemoryUsage::Upload,
..Default::default()
},
[0_u32, 1, 2, 3].iter().copied(),
)
.unwrap();
let cb_allocator = StandardCommandBufferAllocator::new(device, Default::default());
let mut builder = AutoCommandBufferBuilder::primary(
&cb_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)
.unwrap();
assert!(matches!(
builder.copy_buffer(CopyBufferInfoTyped {
regions: [BufferCopy {
src_offset: 0,
dst_offset: 1,
size: 2,
..Default::default()
}]
.into(),
..CopyBufferInfoTyped::buffers(source.clone(), source)
}),
Err(CopyError::OverlappingRegions {
src_region_index: 0,
dst_region_index: 0,
})
));
}
}