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, SubpassContents,
};
use crate::{
buffer::{sys::Buffer, BufferAccess},
command_buffer::CommandBufferInheritanceRenderingInfo,
device::{Device, DeviceOwned, Queue, QueueFamilyProperties},
format::Format,
image::{sys::Image, ImageAccess, ImageLayout, ImageSubresourceRange},
query::{QueryControlFlags, QueryType},
render_pass::{Framebuffer, Subpass},
sync::{AccessCheckError, AccessFlags, PipelineMemoryAccess, PipelineStages},
DeviceSize, OomError, RequirementNotMet, RequiresOneOf, VulkanObject,
};
use ahash::HashMap;
use parking_lot::{Mutex, MutexGuard};
use std::{
error::Error,
fmt::{Display, Error as FmtError, Formatter},
marker::PhantomData,
ops::Range,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
};
pub struct AutoCommandBufferBuilder<L, A = StandardCommandBufferAllocator>
where
A: CommandBufferAllocator,
{
pub(super) inner: SyncCommandBufferBuilder,
builder_alloc: A::Builder,
queue_family_index: u32,
pub(super) inheritance_info: Option<CommandBufferInheritanceInfo>,
pub(super) usage: CommandBufferUsage,
pub(super) render_pass_state: Option<RenderPassState>,
pub(super) query_state: HashMap<ash::vk::QueryType, QueryState>,
_data: PhantomData<L>,
}
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>,
}
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,
{
#[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,
{
#[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,
{
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 {
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 {
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 {
match render_pass {
CommandBufferInheritanceRenderPassType::BeginRenderPass(render_pass_info) => {
let &CommandBufferInheritanceRenderPassInfo {
ref subpass,
ref framebuffer,
} = render_pass_info;
assert_eq!(device, subpass.render_pass().device().as_ref());
if let Some(framebuffer) = framebuffer {
assert_eq!(device, framebuffer.device().as_ref());
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;
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();
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;
format.validate_device(device)?;
if !unsafe { physical_device.format_properties_unchecked(format) }
.potential_format_features()
.color_attachment
{
return Err(
CommandBufferBeginError::ColorAttachmentFormatUsageNotSupported {
attachment_index,
},
);
}
}
if let Some(format) = depth_attachment_format {
format.validate_device(device)?;
if !format.aspects().depth {
return Err(
CommandBufferBeginError::DepthAttachmentFormatUsageNotSupported,
);
}
if !unsafe { physical_device.format_properties_unchecked(format) }
.potential_format_features()
.depth_stencil_attachment
{
return Err(
CommandBufferBeginError::DepthAttachmentFormatUsageNotSupported,
);
}
}
if let Some(format) = stencil_attachment_format {
format.validate_device(device)?;
if !format.aspects().stencil {
return Err(
CommandBufferBeginError::StencilAttachmentFormatUsageNotSupported,
);
}
if !unsafe { physical_device.format_properties_unchecked(format) }
.potential_format_features()
.depth_stencil_attachment
{
return Err(
CommandBufferBeginError::StencilAttachmentFormatUsageNotSupported,
);
}
}
if let (Some(depth_format), Some(stencil_format)) =
(depth_attachment_format, stencil_attachment_format)
{
if depth_format != stencil_format {
return Err(
CommandBufferBeginError::DepthStencilAttachmentFormatMismatch,
);
}
}
rasterization_samples.validate_device(device)?;
}
}
}
if let Some(control_flags) = occlusion_query {
control_flags.validate_device(device)?;
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()
},
});
}
if control_flags.precise && !device.enabled_features().occlusion_query_precise {
return Err(CommandBufferBeginError::RequirementNotMet {
required_for:
"`inheritance_info.occlusion_query` is `Some(control_flags)`, where `control_flags.precise` is set",
requires_one_of: RequiresOneOf {
features: &["occlusion_query_precise"],
..Default::default()
},
});
}
}
query_statistics_flags.validate_device(device)?;
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);
}
Ok(())
}
}
#[derive(Clone, Copy, Debug)]
pub enum CommandBufferBeginError {
OomError(OomError),
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
ColorAttachmentFormatUsageNotSupported { attachment_index: u32 },
DepthAttachmentFormatUsageNotSupported,
DepthStencilAttachmentFormatMismatch,
FramebufferNotCompatible,
MaxMultiviewViewCountExceeded { view_count: u32, max: u32 },
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,
{
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,
{
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,
})
}
}
#[derive(Clone, Debug)]
pub enum BuildError {
OomError(OomError),
RenderPassActive,
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]
}
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, 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 check_buffer_access(
&self,
buffer: &Buffer,
range: Range<DeviceSize>,
exclusive: bool,
queue: &Queue,
) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
self.inner
.check_buffer_access(buffer, range, exclusive, queue)
}
fn check_image_access(
&self,
image: &Image,
range: Range<DeviceSize>,
exclusive: bool,
expected_layout: ImageLayout,
queue: &Queue,
) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
self.inner
.check_image_access(image, range, exclusive, expected_layout, queue)
}
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, usage: CommandBufferUsage,
inheritance_info: CommandBufferInheritanceInfo,
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 num_buffers(&self) -> usize {
self.inner.num_buffers()
}
fn buffer(
&self,
index: usize,
) -> Option<(
&Arc<dyn BufferAccess>,
Range<DeviceSize>,
PipelineMemoryAccess,
)> {
self.inner.buffer(index)
}
fn num_images(&self) -> usize {
self.inner.num_images()
}
fn image(
&self,
index: usize,
) -> Option<(
&Arc<dyn ImageAccess>,
&ImageSubresourceRange,
PipelineMemoryAccess,
ImageLayout,
ImageLayout,
)> {
self.inner.image(index)
}
}
#[derive(Debug)]
enum SubmitState {
Concurrent,
ExclusiveUse {
in_use: AtomicBool,
},
OneTime {
already_submitted: AtomicBool,
},
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
buffer::{BufferUsage, CpuAccessibleBuffer},
command_buffer::{
synced::SyncCommandBufferBuilderError, BufferCopy, CopyBufferInfoTyped, CopyError,
ExecuteCommandsError,
},
device::{DeviceCreateInfo, QueueCreateInfo},
memory::allocator::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 = CpuAccessibleBuffer::from_iter(
&memory_allocator,
BufferUsage {
transfer_src: true,
..BufferUsage::empty()
},
true,
[1_u32, 2].iter().copied(),
)
.unwrap();
let destination = CpuAccessibleBuffer::from_iter(
&memory_allocator,
BufferUsage {
transfer_dst: true,
..BufferUsage::empty()
},
true,
[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());
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();
builder.execute_commands(secondary.clone()).unwrap();
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();
assert!(matches!(
builder.execute_commands(secondary.clone()),
Err(ExecuteCommandsError::SyncCommandBufferBuilderError(
SyncCommandBufferBuilderError::ExecError(
CommandBufferExecError::ExclusiveAlreadyInUse
)
))
));
std::mem::drop(cb1);
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 = CpuAccessibleBuffer::from_iter(
&memory_allocator,
BufferUsage {
transfer_src: true,
transfer_dst: true,
..BufferUsage::empty()
},
true,
[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 = CpuAccessibleBuffer::from_iter(
&memory_allocator,
BufferUsage {
transfer_src: true,
transfer_dst: true,
..BufferUsage::empty()
},
true,
[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,
})
));
}
}