// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // 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 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, // 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, // If any queries are active, this hashmap contains their state. pub(super) query_state: HashMap, _data: PhantomData, } // 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 for RenderPassStateType { fn from(val: BeginRenderPassState) -> Self { Self::BeginRenderPass(val) } } impl From for RenderPassStateType { fn from(val: BeginRenderingState) -> Self { Self::BeginRendering(val) } } pub(super) struct BeginRenderPassState { pub(super) subpass: Subpass, pub(super) framebuffer: Option>, } pub(super) struct BeginRenderingState { pub(super) attachments: Option, pub(super) color_attachment_formats: Vec>, pub(super) depth_attachment_format: Option, pub(super) stencil_attachment_format: Option, pub(super) pipeline_used: bool, } pub(super) struct BeginRenderingAttachments { pub(super) color_attachments: Vec>, pub(super) depth_attachment: Option, pub(super) stencil_attachment: Option, } // 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 AutoCommandBufferBuilder where A: CommandBufferAllocator, { /// Starts recording a primary command buffer. #[inline] pub fn primary( allocator: &A, queue_family_index: u32, usage: CommandBufferUsage, ) -> Result< AutoCommandBufferBuilder, A>, CommandBufferBeginError, > { unsafe { AutoCommandBufferBuilder::begin( allocator, queue_family_index, CommandBufferLevel::Primary, CommandBufferBeginInfo { usage, inheritance_info: None, _ne: crate::NonExhaustive(()), }, ) } } } impl AutoCommandBufferBuilder 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, A>, CommandBufferBeginError, > { unsafe { AutoCommandBufferBuilder::begin( allocator, queue_family_index, CommandBufferLevel::Secondary, CommandBufferBeginInfo { usage, inheritance_info: Some(inheritance_info), _ne: crate::NonExhaustive(()), }, ) } } } impl AutoCommandBufferBuilder 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, 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 for CommandBufferBeginError { fn from(err: OomError) -> Self { Self::OomError(err) } } impl From for CommandBufferBeginError { fn from(err: RequirementNotMet) -> Self { Self::RequirementNotMet { required_for: err.required_for, requires_one_of: err.requires_one_of, } } } impl AutoCommandBufferBuilder, A> where A: CommandBufferAllocator, { /// Builds the command buffer. pub fn build(self) -> Result, 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 AutoCommandBufferBuilder, A> where A: CommandBufferAllocator, { /// Builds the command buffer. pub fn build(self) -> Result, 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 for BuildError { fn from(err: OomError) -> Self { Self::OomError(err) } } impl AutoCommandBufferBuilder 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 DeviceOwned for AutoCommandBufferBuilder where A: CommandBufferAllocator, { fn device(&self) -> &Arc { self.inner.device() } } pub struct PrimaryAutoCommandBuffer { inner: SyncCommandBuffer, _alloc: A, // Safety: must be dropped after `inner` usage: CommandBufferUsage, state: Mutex, } unsafe impl DeviceOwned for PrimaryAutoCommandBuffer { fn device(&self) -> &Arc { self.inner.device() } } unsafe impl VulkanObject for PrimaryAutoCommandBuffer { type Handle = ash::vk::CommandBuffer; fn handle(&self) -> Self::Handle { self.inner.as_ref().handle() } } unsafe impl PrimaryCommandBufferAbstract for PrimaryAutoCommandBuffer 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 { 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 VulkanObject for SecondaryAutoCommandBuffer { type Handle = ash::vk::CommandBuffer; fn handle(&self) -> Self::Handle { self.inner.as_ref().handle() } } unsafe impl DeviceOwned for SecondaryAutoCommandBuffer { fn device(&self) -> &Arc { self.inner.device() } } unsafe impl SecondaryCommandBufferAbstract for SecondaryAutoCommandBuffer 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, }) )); } }