diff options
Diffstat (limited to 'src/command_buffer/auto.rs')
-rw-r--r-- | src/command_buffer/auto.rs | 3315 |
1 files changed, 791 insertions, 2524 deletions
diff --git a/src/command_buffer/auto.rs b/src/command_buffer/auto.rs index 64d0d2d..b143b35 100644 --- a/src/command_buffer/auto.rs +++ b/src/command_buffer/auto.rs @@ -7,2330 +7,795 @@ // notice may not be copied, modified, or distributed except // according to those terms. -use crate::buffer::BufferAccess; -use crate::buffer::TypedBufferAccess; -use crate::command_buffer::pool::standard::StandardCommandPoolAlloc; -use crate::command_buffer::pool::standard::StandardCommandPoolBuilder; -use crate::command_buffer::pool::CommandPool; -use crate::command_buffer::pool::CommandPoolBuilderAlloc; -use crate::command_buffer::synced::SyncCommandBuffer; -use crate::command_buffer::synced::SyncCommandBufferBuilder; -use crate::command_buffer::synced::SyncCommandBufferBuilderError; -use crate::command_buffer::sys::UnsafeCommandBuffer; -use crate::command_buffer::sys::UnsafeCommandBufferBuilderBufferImageCopy; -use crate::command_buffer::sys::UnsafeCommandBufferBuilderColorImageClear; -use crate::command_buffer::sys::UnsafeCommandBufferBuilderImageBlit; -use crate::command_buffer::sys::UnsafeCommandBufferBuilderImageCopy; -use crate::command_buffer::validity::*; -use crate::command_buffer::CommandBufferExecError; -use crate::command_buffer::CommandBufferInheritance; -use crate::command_buffer::CommandBufferInheritanceRenderPass; -use crate::command_buffer::CommandBufferLevel; -use crate::command_buffer::CommandBufferUsage; -use crate::command_buffer::DispatchIndirectCommand; -use crate::command_buffer::DrawIndexedIndirectCommand; -use crate::command_buffer::DrawIndirectCommand; -use crate::command_buffer::DynamicState; -use crate::command_buffer::ImageUninitializedSafe; -use crate::command_buffer::PrimaryCommandBuffer; -use crate::command_buffer::SecondaryCommandBuffer; -use crate::command_buffer::StateCacher; -use crate::command_buffer::StateCacherOutcome; -use crate::command_buffer::SubpassContents; -use crate::descriptor_set::DescriptorSetWithOffsets; -use crate::descriptor_set::DescriptorSetsCollection; -use crate::device::physical::QueueFamily; -use crate::device::Device; -use crate::device::DeviceOwned; -use crate::device::Queue; -use crate::format::ClearValue; -use crate::format::FormatTy; -use crate::format::Pixel; -use crate::image::ImageAccess; -use crate::image::ImageAspect; -use crate::image::ImageAspects; -use crate::image::ImageLayout; -use crate::pipeline::depth_stencil::StencilFaces; -use crate::pipeline::input_assembly::Index; -use crate::pipeline::layout::PipelineLayout; -use crate::pipeline::vertex::VertexSource; -use crate::pipeline::ComputePipelineAbstract; -use crate::pipeline::GraphicsPipelineAbstract; -use crate::pipeline::PipelineBindPoint; -use crate::query::QueryControlFlags; -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::FramebufferAbstract; -use crate::render_pass::LoadOp; -use crate::render_pass::RenderPass; -use crate::render_pass::Subpass; -use crate::sampler::Filter; -use crate::sync::AccessCheckError; -use crate::sync::AccessFlags; -use crate::sync::GpuFuture; -use crate::sync::PipelineMemoryAccess; -use crate::sync::PipelineStage; -use crate::sync::PipelineStages; -use crate::DeviceSize; -use crate::VulkanObject; -use crate::{OomError, SafeDeref}; -use fnv::FnvHashMap; -use std::error; -use std::ffi::CStr; -use std::fmt; -use std::iter; -use std::marker::PhantomData; -use std::mem; -use std::ops::Range; -use std::slice; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering; -use std::sync::Arc; - -/// Note that command buffers allocated from the default command pool (`Arc<StandardCommandPool>`) -/// don't implement the `Send` and `Sync` traits. If you use this pool, 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, P = StandardCommandPoolBuilder> { - inner: SyncCommandBufferBuilder, - pool_builder_alloc: P, // Safety: must be dropped after `inner` - state_cacher: StateCacher, - - // The queue family that this command buffer is being created for. - queue_family_id: u32, +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. - inheritance: Option<CommandBufferInheritance<Box<dyn FramebufferAbstract + Send + Sync>>>, + // 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. - usage: CommandBufferUsage, + pub(super) usage: CommandBufferUsage, // If we're inside a render pass, contains the render pass state. - render_pass_state: Option<RenderPassState>, + pub(super) render_pass_state: Option<RenderPassState>, // If any queries are active, this hashmap contains their state. - query_state: FnvHashMap<ash::vk::QueryType, QueryState>, + pub(super) query_state: HashMap<ash::vk::QueryType, QueryState>, _data: PhantomData<L>, } -// The state of the current render pass, specifying the pass, subpass index and its intended contents. -struct RenderPassState { - subpass: (Arc<RenderPass>, u32), - contents: SubpassContents, - framebuffer: ash::vk::Framebuffer, // Always null for secondary command buffers +// 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, } -// The state of an active query. -struct QueryState { - query_pool: ash::vk::QueryPool, - query: u32, - ty: QueryType, - flags: QueryControlFlags, - in_subpass: bool, +pub(super) enum RenderPassStateType { + BeginRenderPass(BeginRenderPassState), + BeginRendering(BeginRenderingState), } -impl AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, StandardCommandPoolBuilder> { - /// Starts building a primary command buffer. - #[inline] - pub fn primary( - device: Arc<Device>, - queue_family: QueueFamily, - usage: CommandBufferUsage, - ) -> Result< - AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, StandardCommandPoolBuilder>, - OomError, - > { - AutoCommandBufferBuilder::with_level( - device, - queue_family, - usage, - CommandBufferLevel::primary(), - ) - } -} - -impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder> { - /// Starts building a secondary compute command buffer. - #[inline] - pub fn secondary_compute( - device: Arc<Device>, - queue_family: QueueFamily, - usage: CommandBufferUsage, - ) -> Result< - AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>, - OomError, - > { - let level = CommandBufferLevel::secondary(None, QueryPipelineStatisticFlags::none()); - AutoCommandBufferBuilder::with_level(device, queue_family, usage, level) - } - - /// Same as `secondary_compute`, but allows specifying how queries are being inherited. - #[inline] - pub fn secondary_compute_inherit_queries( - device: Arc<Device>, - queue_family: QueueFamily, - usage: CommandBufferUsage, - occlusion_query: Option<QueryControlFlags>, - query_statistics_flags: QueryPipelineStatisticFlags, - ) -> Result< - AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>, - 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); - Ok(AutoCommandBufferBuilder::with_level( - device, - queue_family, - usage, - level, - )?) - } - - /// Starts building a secondary graphics command buffer. - #[inline] - pub fn secondary_graphics( - device: Arc<Device>, - queue_family: QueueFamily, - usage: CommandBufferUsage, - subpass: Subpass, - ) -> Result< - AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>, - OomError, - > { - let level = CommandBufferLevel::Secondary(CommandBufferInheritance { - render_pass: Some(CommandBufferInheritanceRenderPass { - subpass, - framebuffer: None::<Arc<Framebuffer<()>>>, - }), - occlusion_query: None, - query_statistics_flags: QueryPipelineStatisticFlags::none(), - }); - - AutoCommandBufferBuilder::with_level(device, queue_family, usage, level) - } - - /// Same as `secondary_graphics`, but allows specifying how queries are being inherited. - #[inline] - pub fn secondary_graphics_inherit_queries( - device: Arc<Device>, - queue_family: QueueFamily, - usage: CommandBufferUsage, - subpass: Subpass, - occlusion_query: Option<QueryControlFlags>, - query_statistics_flags: QueryPipelineStatisticFlags, - ) -> Result< - AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>, - 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 { - render_pass: Some(CommandBufferInheritanceRenderPass { - subpass, - framebuffer: None::<Arc<Framebuffer<()>>>, - }), - occlusion_query, - query_statistics_flags, - }); - - Ok(AutoCommandBufferBuilder::with_level( - device, - queue_family, - usage, - level, - )?) +impl From<BeginRenderPassState> for RenderPassStateType { + fn from(val: BeginRenderPassState) -> Self { + Self::BeginRenderPass(val) } } -impl<L> AutoCommandBufferBuilder<L, StandardCommandPoolBuilder> { - // Actual constructor. Private. - fn with_level<F>( - device: Arc<Device>, - queue_family: QueueFamily, - usage: CommandBufferUsage, - level: CommandBufferLevel<F>, - ) -> Result<AutoCommandBufferBuilder<L, StandardCommandPoolBuilder>, OomError> - where - F: FramebufferAbstract + Clone + Send + Sync + 'static, - { - let (inheritance, render_pass_state) = match &level { - CommandBufferLevel::Primary => (None, None), - CommandBufferLevel::Secondary(inheritance) => { - let (render_pass, render_pass_state) = match inheritance.render_pass.as_ref() { - Some(CommandBufferInheritanceRenderPass { - subpass, - framebuffer, - }) => { - let render_pass = CommandBufferInheritanceRenderPass { - subpass: Subpass::from(subpass.render_pass().clone(), subpass.index()) - .unwrap(), - framebuffer: framebuffer - .as_ref() - .map(|f| Box::new(f.clone()) as Box<_>), - }; - let render_pass_state = RenderPassState { - subpass: (subpass.render_pass().clone(), subpass.index()), - contents: SubpassContents::Inline, - framebuffer: ash::vk::Framebuffer::null(), // Only needed for primary command buffers - }; - (Some(render_pass), Some(render_pass_state)) - } - None => (None, None), - }; - - ( - Some(CommandBufferInheritance { - render_pass, - occlusion_query: inheritance.occlusion_query, - query_statistics_flags: inheritance.query_statistics_flags, - }), - render_pass_state, - ) - } - }; - - unsafe { - let pool = Device::standard_command_pool(&device, queue_family); - let pool_builder_alloc = pool - .alloc(!matches!(level, CommandBufferLevel::Primary), 1)? - .next() - .expect("Requested one command buffer from the command pool, but got zero."); - let inner = SyncCommandBufferBuilder::new(pool_builder_alloc.inner(), level, usage)?; - - Ok(AutoCommandBufferBuilder { - inner, - pool_builder_alloc, - state_cacher: StateCacher::new(), - queue_family_id: queue_family.id(), - render_pass_state, - query_state: FnvHashMap::default(), - inheritance, - usage, - _data: PhantomData, - }) - } +impl From<BeginRenderingState> for RenderPassStateType { + fn from(val: BeginRenderingState) -> Self { + Self::BeginRendering(val) } } -#[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, +pub(super) struct BeginRenderPassState { + pub(super) subpass: Subpass, + pub(super) framebuffer: Option<Arc<Framebuffer>>, } -impl error::Error for BeginError { - #[inline] - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - Self::OomError(ref err) => Some(err), - _ => None, - } - } +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, } -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" - } - } - ) - } +pub(super) struct BeginRenderingAttachments { + pub(super) color_attachments: Vec<Option<RenderingAttachmentInfo>>, + pub(super) depth_attachment: Option<RenderingAttachmentInfo>, + pub(super) stencil_attachment: Option<RenderingAttachmentInfo>, } -impl From<OomError> for BeginError { - #[inline] - fn from(err: OomError) -> Self { - Self::OomError(err) - } +// 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<P> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P> +impl<A> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, A> where - P: CommandPoolBuilderAlloc, + A: CommandBufferAllocator, { - /// Builds the command buffer. + /// Starts recording a primary command buffer. #[inline] - pub fn build(self) -> Result<PrimaryAutoCommandBuffer<P::Alloc>, BuildError> { - if self.render_pass_state.is_some() { - return Err(AutoCommandBufferBuilderContextError::ForbiddenInsideRenderPass.into()); - } - - if !self.query_state.is_empty() { - return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into()); + 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(()), + }, + ) } - - 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(PrimaryAutoCommandBuffer { - inner: self.inner.build()?, - pool_alloc: self.pool_builder_alloc.into_alloc(), - submit_state, - }) } } -impl<P> AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<P::Alloc>, P> +impl<A> AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, A> where - P: CommandPoolBuilderAlloc, + A: CommandBufferAllocator, { - /// Builds the command buffer. - #[inline] - 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.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()?, - pool_alloc: self.pool_builder_alloc.into_alloc(), - inheritance: self.inheritance.unwrap(), - submit_state, - }) - } -} - -impl<L, P> AutoCommandBufferBuilder<L, P> { - #[inline] - fn ensure_outside_render_pass(&self) -> Result<(), AutoCommandBufferBuilderContextError> { - if self.render_pass_state.is_some() { - return Err(AutoCommandBufferBuilderContextError::ForbiddenInsideRenderPass); - } - - Ok(()) - } - - #[inline] - fn ensure_inside_render_pass_inline<Gp>( - &self, - pipeline: &Gp, - ) -> Result<(), AutoCommandBufferBuilderContextError> - where - Gp: ?Sized + GraphicsPipelineAbstract, - { - let render_pass_state = self - .render_pass_state - .as_ref() - .ok_or(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass)?; - - // Subpass must be for inline commands - if render_pass_state.contents != SubpassContents::Inline { - return Err(AutoCommandBufferBuilderContextError::WrongSubpassType); - } - - // Subpasses must be the same. - if pipeline.subpass().index() != render_pass_state.subpass.1 { - return Err(AutoCommandBufferBuilderContextError::WrongSubpassIndex); - } - - // Render passes must be compatible. - if !pipeline - .subpass() - .render_pass() - .desc() - .is_compatible_with_desc(&render_pass_state.subpass.0.desc()) - { - return Err(AutoCommandBufferBuilderContextError::IncompatibleRenderPass); - } - - Ok(()) - } - + /// Starts recording a secondary command buffer. #[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. - /// - /// Copy operations have several restrictions: - /// - /// - Copy operations are only allowed on queue families that support transfer, graphics, or - /// compute operations. - /// - The number of samples in the source and destination images must be equal. - /// - The size of the uncompressed element format of the source image must be equal to the - /// compressed element format of the destination. - /// - If you copy between depth, stencil or depth-stencil images, the format of both images - /// must match exactly. - /// - For two-dimensional images, the Z coordinate must be 0 for the image offsets and 1 for - /// the extent. Same for the Y coordinate for one-dimensional images. - /// - For non-array images, the base array layer must be 0 and the number of layers must be 1. - /// - /// If `layer_count` is greater than 1, the copy will happen between each individual layer as - /// if they were separate images. - /// - /// # Panic - /// - /// - Panics if the source or the destination was not created with `device`. - /// - pub fn copy_image<S, D>( - &mut self, - source: S, - source_offset: [i32; 3], - source_base_array_layer: u32, - source_mip_level: u32, - destination: D, - destination_offset: [i32; 3], - destination_base_array_layer: u32, - destination_mip_level: u32, - extent: [u32; 3], - layer_count: u32, - ) -> Result<&mut Self, CopyImageError> - where - S: ImageAccess + Send + Sync + 'static, - D: ImageAccess + Send + Sync + 'static, - { + pub fn secondary( + allocator: &A, + queue_family_index: u32, + usage: CommandBufferUsage, + inheritance_info: CommandBufferInheritanceInfo, + ) -> Result< + AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<A::Alloc>, A>, + CommandBufferBeginError, + > { unsafe { - self.ensure_outside_render_pass()?; - - check_copy_image( - self.device(), - &source, - source_offset, - source_base_array_layer, - source_mip_level, - &destination, - destination_offset, - destination_base_array_layer, - destination_mip_level, - extent, - layer_count, - )?; - - let copy = UnsafeCommandBufferBuilderImageCopy { - // TODO: Allowing choosing a subset of the image aspects, but note that if color - // is included, neither depth nor stencil may. - aspects: ImageAspects { - color: source.has_color(), - depth: !source.has_color() && source.has_depth() && destination.has_depth(), - stencil: !source.has_color() - && source.has_stencil() - && destination.has_stencil(), - ..ImageAspects::none() + AutoCommandBufferBuilder::begin( + allocator, + queue_family_index, + CommandBufferLevel::Secondary, + CommandBufferBeginInfo { + usage, + inheritance_info: Some(inheritance_info), + _ne: crate::NonExhaustive(()), }, - source_mip_level, - destination_mip_level, - source_base_array_layer, - destination_base_array_layer, - layer_count, - source_offset, - destination_offset, - extent, - }; - - // TODO: Allow choosing layouts, but note that only Transfer*Optimal and General are - // valid. - self.inner.copy_image( - source, - ImageLayout::TransferSrcOptimal, - destination, - ImageLayout::TransferDstOptimal, - iter::once(copy), - )?; - Ok(self) + ) } } +} - /// Adds a command that blits an image to another. - /// - /// A *blit* is similar to an image copy operation, except that the portion of the image that - /// is transferred can be resized. You choose an area of the source and an area of the - /// destination, and the implementation will resize the area of the source so that it matches - /// the size of the area of the destination before writing it. - /// - /// Blit operations have several restrictions: - /// - /// - Blit operations are only allowed on queue families that support graphics operations. - /// - The format of the source and destination images must support blit operations, which - /// depends on the Vulkan implementation. Vulkan guarantees that some specific formats must - /// always be supported. See tables 52 to 61 of the specifications. - /// - Only single-sampled images are allowed. - /// - You can only blit between two images whose formats belong to the same type. The types - /// are: floating-point, signed integers, unsigned integers, depth-stencil. - /// - If you blit between depth, stencil or depth-stencil images, the format of both images - /// must match exactly. - /// - If you blit between depth, stencil or depth-stencil images, only the `Nearest` filter is - /// allowed. - /// - For two-dimensional images, the Z coordinate must be 0 for the top-left offset and 1 for - /// the bottom-right offset. Same for the Y coordinate for one-dimensional images. - /// - For non-array images, the base array layer must be 0 and the number of layers must be 1. - /// - /// If `layer_count` is greater than 1, the blit will happen between each individual layer as - /// if they were separate images. - /// - /// # Panic - /// - /// - Panics if the source or the destination was not created with `device`. - /// - pub fn blit_image<S, D>( - &mut self, - source: S, - source_top_left: [i32; 3], - source_bottom_right: [i32; 3], - source_base_array_layer: u32, - source_mip_level: u32, - destination: D, - destination_top_left: [i32; 3], - destination_bottom_right: [i32; 3], - destination_base_array_layer: u32, - destination_mip_level: u32, - layer_count: u32, - filter: Filter, - ) -> Result<&mut Self, BlitImageError> - where - S: ImageAccess + Send + Sync + 'static, - D: ImageAccess + Send + Sync + 'static, - { - unsafe { - if !self.queue_family().supports_graphics() { - return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); - } +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(); + } - self.ensure_outside_render_pass()?; - - check_blit_image( - self.device(), - &source, - source_top_left, - source_bottom_right, - source_base_array_layer, - source_mip_level, - &destination, - destination_top_left, - destination_bottom_right, - destination_base_array_layer, - destination_mip_level, - layer_count, - filter, - )?; - - let blit = UnsafeCommandBufferBuilderImageBlit { - // TODO: - aspects: if source.has_color() { - ImageAspects { - color: true, - ..ImageAspects::none() + 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, + }); } - } else { - unimplemented!() - }, - source_mip_level, - destination_mip_level, - source_base_array_layer, - destination_base_array_layer, - layer_count, - source_top_left, - source_bottom_right, - destination_top_left, - destination_bottom_right, - }; - - self.inner.blit_image( - source, - ImageLayout::TransferSrcOptimal, - destination, // TODO: let choose layout - ImageLayout::TransferDstOptimal, - iter::once(blit), - filter, - )?; - Ok(self) - } - } - - /// Adds a command that clears all the layers and mipmap levels of a color image with a - /// specific value. - /// - /// # Panic - /// - /// Panics if `color` is not a color value. - /// - pub fn clear_color_image<I>( - &mut self, - image: I, - color: ClearValue, - ) -> Result<&mut Self, ClearColorImageError> - where - I: ImageAccess + Send + Sync + 'static, - { - let layers = image.dimensions().array_layers(); - let levels = image.mipmap_levels(); - - self.clear_color_image_dimensions(image, 0, layers, 0, levels, color) - } - - /// Adds a command that clears a color image with a specific value. - /// - /// # Panic - /// - /// - Panics if `color` is not a color value. - /// - pub fn clear_color_image_dimensions<I>( - &mut self, - image: I, - first_layer: u32, - num_layers: u32, - first_mipmap: u32, - num_mipmaps: u32, - color: ClearValue, - ) -> Result<&mut Self, ClearColorImageError> - where - I: ImageAccess + Send + Sync + 'static, - { - unsafe { - if !self.queue_family().supports_graphics() && !self.queue_family().supports_compute() { - return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); + 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, + }); + } + } } - - self.ensure_outside_render_pass()?; - check_clear_color_image( - self.device(), - &image, - first_layer, - num_layers, - first_mipmap, - num_mipmaps, - )?; - - match color { - ClearValue::Float(_) | ClearValue::Int(_) | ClearValue::Uint(_) => {} - _ => panic!("The clear color is not a color value"), - }; - - let region = UnsafeCommandBufferBuilderColorImageClear { - base_mip_level: first_mipmap, - level_count: num_mipmaps, - base_array_layer: first_layer, - layer_count: num_layers, - }; - - // TODO: let choose layout - self.inner.clear_color_image( - image, - ImageLayout::TransferDstOptimal, - color, - iter::once(region), - )?; - Ok(self) - } - } - - /// Adds a command that copies from a buffer to another. - /// - /// This command will copy from the source to the destination. If their size is not equal, then - /// the amount of data copied is equal to the smallest of the two. - #[inline] - pub fn copy_buffer<S, D, T>( - &mut self, - source: S, - destination: D, - ) -> Result<&mut Self, CopyBufferError> - where - S: TypedBufferAccess<Content = T> + Send + Sync + 'static, - D: TypedBufferAccess<Content = T> + Send + Sync + 'static, - T: ?Sized, - { - unsafe { - self.ensure_outside_render_pass()?; - let infos = check_copy_buffer(self.device(), &source, &destination)?; - self.inner - .copy_buffer(source, destination, iter::once((0, 0, infos.copy_size)))?; - Ok(self) - } - } - - /// Adds a command that copies a range from the source to the destination buffer. - /// Panics if out of bounds. - #[inline] - pub fn copy_buffer_dimensions<S, D, T>( - &mut self, - source: S, - source_offset: DeviceSize, - destination: D, - destination_offset: DeviceSize, - count: DeviceSize, - ) -> Result<&mut Self, CopyBufferError> - where - S: TypedBufferAccess<Content = [T]> + Send + Sync + 'static, - D: TypedBufferAccess<Content = [T]> + Send + Sync + 'static, - { - self.ensure_outside_render_pass()?; - - let _infos = check_copy_buffer(self.device(), &source, &destination)?; - debug_assert!(source_offset + count <= source.len()); - debug_assert!(destination_offset + count <= destination.len()); - - let size = std::mem::size_of::<T>() as DeviceSize; - unsafe { - self.inner.copy_buffer( - source, - destination, - iter::once(( - source_offset * size, - destination_offset * size, - count * size, - )), - )?; } - Ok(self) - } - /// Adds a command that copies from a buffer to an image. - pub fn copy_buffer_to_image<S, D, Px>( - &mut self, - source: S, - destination: D, - ) -> Result<&mut Self, CopyBufferImageError> - where - S: TypedBufferAccess<Content = [Px]> + Send + Sync + 'static, - D: ImageAccess + Send + Sync + 'static, - Px: Pixel, - { - self.ensure_outside_render_pass()?; - - let dims = destination.dimensions().width_height_depth(); - self.copy_buffer_to_image_dimensions(source, destination, [0, 0, 0], dims, 0, 1, 0) - } + let builder_alloc = allocator + .allocate(queue_family_index, level, 1)? + .next() + .expect("requested one command buffer from the command pool, but got zero"); - /// Adds a command that copies from a buffer to an image. - pub fn copy_buffer_to_image_dimensions<S, D, Px>( - &mut self, - source: S, - destination: D, - offset: [u32; 3], - size: [u32; 3], - first_layer: u32, - num_layers: u32, - mipmap: u32, - ) -> Result<&mut Self, CopyBufferImageError> - where - S: TypedBufferAccess<Content = [Px]> + Send + Sync + 'static, - D: ImageAccess + Send + Sync + 'static, - Px: Pixel, - { - unsafe { - self.ensure_outside_render_pass()?; - - check_copy_buffer_image( - self.device(), - &source, - &destination, - CheckCopyBufferImageTy::BufferToImage, - offset, - size, - first_layer, - num_layers, - mipmap, - )?; - - let copy = UnsafeCommandBufferBuilderBufferImageCopy { - buffer_offset: 0, - buffer_row_length: 0, - buffer_image_height: 0, - image_aspect: if destination.has_color() { - ImageAspect::Color - } else { - unimplemented!() - }, - image_mip_level: mipmap, - image_base_array_layer: first_layer, - image_layer_count: num_layers, - image_offset: [offset[0] as i32, offset[1] as i32, offset[2] as i32], - image_extent: size, - }; - - self.inner.copy_buffer_to_image( - source, - destination, - ImageLayout::TransferDstOptimal, // TODO: let choose layout - iter::once(copy), - )?; - Ok(self) - } - } - - /// 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. - // 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>( - &mut self, - source: S, - destination: D, - ) -> Result<&mut Self, CopyBufferImageError> - where - S: ImageAccess + Send + Sync + 'static, - D: TypedBufferAccess<Content = [Px]> + Send + Sync + 'static, - Px: Pixel, - { - self.ensure_outside_render_pass()?; - - let dims = source.dimensions().width_height_depth(); - self.copy_image_to_buffer_dimensions(source, destination, [0, 0, 0], dims, 0, 1, 0) - } - - /// Adds a command that copies from an image to a buffer. - pub fn copy_image_to_buffer_dimensions<S, D, Px>( - &mut self, - source: S, - destination: D, - offset: [u32; 3], - size: [u32; 3], - first_layer: u32, - num_layers: u32, - mipmap: u32, - ) -> Result<&mut Self, CopyBufferImageError> - where - S: ImageAccess + Send + Sync + 'static, - D: TypedBufferAccess<Content = [Px]> + Send + Sync + 'static, - Px: Pixel, - { - unsafe { - self.ensure_outside_render_pass()?; - - check_copy_buffer_image( - self.device(), - &destination, - &source, - CheckCopyBufferImageTy::ImageToBuffer, - offset, - size, - first_layer, - num_layers, - mipmap, - )?; - - let copy = UnsafeCommandBufferBuilderBufferImageCopy { - buffer_offset: 0, - buffer_row_length: 0, - buffer_image_height: 0, - // TODO: Allow the user to choose aspect - image_aspect: if source.has_color() { - ImageAspect::Color - } else if source.has_depth() { - ImageAspect::Depth - } else if source.has_stencil() { - ImageAspect::Stencil - } else { - unimplemented!() - }, - image_mip_level: mipmap, - image_base_array_layer: first_layer, - image_layer_count: num_layers, - image_offset: [offset[0] as i32, offset[1] as i32, offset[2] as i32], - image_extent: size, - }; - - self.inner.copy_image_to_buffer( - source, - ImageLayout::TransferSrcOptimal, - destination, // TODO: let choose layout - iter::once(copy), - )?; - Ok(self) - } - } - - /// Open a command buffer debug label region. - /// - /// Note: you need to enable `VK_EXT_debug_utils` extension when creating an instance. - #[inline] - pub fn debug_marker_begin( - &mut self, - name: &'static CStr, - color: [f32; 4], - ) -> Result<&mut Self, DebugMarkerError> { - if !self.queue_family().supports_graphics() && self.queue_family().supports_compute() { - return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); - } - - check_debug_marker_color(color)?; - - unsafe { - self.inner.debug_marker_begin(name.into(), color); - } + let inner = SyncCommandBufferBuilder::new(builder_alloc.inner(), begin_info)?; - Ok(self) + Ok(AutoCommandBufferBuilder { + inner, + builder_alloc, + queue_family_index, + render_pass_state, + query_state: HashMap::default(), + inheritance_info, + usage, + _data: PhantomData, + }) } - /// Close a command buffer label region. - /// - /// Note: you need to open a command buffer label region first with `debug_marker_begin`. - /// Note: you need to enable `VK_EXT_debug_utils` extension when creating an instance. - #[inline] - pub fn debug_marker_end(&mut self) -> Result<&mut Self, DebugMarkerError> { - if !self.queue_family().supports_graphics() && self.queue_family().supports_compute() { - return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); - } - - // TODO: validate that debug_marker_begin with same name was sent earlier - - unsafe { - self.inner.debug_marker_end(); - } - - Ok(self) - } + 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() + }, + }); + } - /// Insert a label into a command buffer. - /// - /// Note: you need to enable `VK_EXT_debug_utils` extension when creating an instance. - #[inline] - pub fn debug_marker_insert( - &mut self, - name: &'static CStr, - color: [f32; 4], - ) -> Result<&mut Self, DebugMarkerError> { - if !self.queue_family().supports_graphics() && self.queue_family().supports_compute() { - return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); - } + let view_count = u32::BITS - view_mask.leading_zeros(); - check_debug_marker_color(color)?; + // 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), + }); + } - unsafe { - self.inner.debug_marker_insert(name.into(), color); - } + 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, + }, + ); + } + } - Ok(self) - } + if let Some(format) = depth_attachment_format { + // VUID-VkCommandBufferInheritanceRenderingInfo-depthAttachmentFormat-parameter + format.validate_device(device)?; - /// Perform a single compute operation using a compute pipeline. - #[inline] - pub fn dispatch<Cp, S, Pc>( - &mut self, - group_counts: [u32; 3], - pipeline: Cp, - descriptor_sets: S, - push_constants: Pc, - ) -> Result<&mut Self, DispatchError> - where - Cp: ComputePipelineAbstract + Send + Sync + 'static + Clone, // TODO: meh for Clone - S: DescriptorSetsCollection, - { - let descriptor_sets = descriptor_sets.into_vec(); + // VUID-VkCommandBufferInheritanceRenderingInfo-depthAttachmentFormat-06540 + if !format.aspects().intersects(ImageAspects::DEPTH) { + return Err( + CommandBufferBeginError::DepthAttachmentFormatUsageNotSupported, + ); + } - unsafe { - if !self.queue_family().supports_compute() { - return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); - } + // 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, + ); + } + } - self.ensure_outside_render_pass()?; - check_push_constants_validity(pipeline.layout(), &push_constants)?; - check_descriptor_sets_validity(pipeline.layout(), &descriptor_sets)?; - check_dispatch(pipeline.device(), group_counts)?; + if let Some(format) = stencil_attachment_format { + // VUID-VkCommandBufferInheritanceRenderingInfo-stencilAttachmentFormat-parameter + format.validate_device(device)?; - if let StateCacherOutcome::NeedChange = - self.state_cacher.bind_compute_pipeline(&pipeline) - { - self.inner.bind_pipeline_compute(pipeline.clone()); - } + // VUID-VkCommandBufferInheritanceRenderingInfo-stencilAttachmentFormat-06541 + if !format.aspects().intersects(ImageAspects::STENCIL) { + return Err( + CommandBufferBeginError::StencilAttachmentFormatUsageNotSupported, + ); + } - set_push_constants(&mut self.inner, pipeline.layout(), push_constants); - bind_descriptor_sets( - &mut self.inner, - &mut self.state_cacher, - PipelineBindPoint::Compute, - pipeline.layout(), - descriptor_sets, - )?; - - self.inner.dispatch(group_counts); - Ok(self) - } - } + // 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, + ); + } + } - /// Perform multiple compute operations using a compute pipeline. One dispatch is performed for - /// each `vulkano::command_buffer::DispatchIndirectCommand` struct in `indirect_buffer`. - #[inline] - pub fn dispatch_indirect<Inb, Cp, S, Pc>( - &mut self, - indirect_buffer: Inb, - pipeline: Cp, - descriptor_sets: S, - push_constants: Pc, - ) -> Result<&mut Self, DispatchIndirectError> - where - Inb: BufferAccess - + TypedBufferAccess<Content = [DispatchIndirectCommand]> - + Send - + Sync - + 'static, - Cp: ComputePipelineAbstract + Send + Sync + 'static + Clone, // TODO: meh for Clone - S: DescriptorSetsCollection, - { - let descriptor_sets = descriptor_sets.into_vec(); + 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, + ); + } + } - unsafe { - if !self.queue_family().supports_compute() { - return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); + // VUID-VkCommandBufferInheritanceRenderingInfo-rasterizationSamples-parameter + rasterization_samples.validate_device(device)?; + } + } } - self.ensure_outside_render_pass()?; - check_indirect_buffer(self.device(), &indirect_buffer)?; - check_push_constants_validity(pipeline.layout(), &push_constants)?; - check_descriptor_sets_validity(pipeline.layout(), &descriptor_sets)?; + 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() + }, + }); + } - if let StateCacherOutcome::NeedChange = - self.state_cacher.bind_compute_pipeline(&pipeline) - { - self.inner.bind_pipeline_compute(pipeline.clone()); + // 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() + }, + }); + } } - set_push_constants(&mut self.inner, pipeline.layout(), push_constants); - bind_descriptor_sets( - &mut self.inner, - &mut self.state_cacher, - PipelineBindPoint::Compute, - pipeline.layout(), - descriptor_sets, - )?; - - self.inner.dispatch_indirect(indirect_buffer)?; - Ok(self) - } - } - - /// Perform a single draw operation using a graphics pipeline. - /// - /// `vertex_buffer` is a set of vertex and/or instance buffers used to provide input. - /// - /// All data in `vertex_buffer` is used for the draw operation. To use only some data in the - /// buffer, wrap it in a `vulkano::buffer::BufferSlice`. - #[inline] - pub fn draw<V, Gp, S, Pc>( - &mut self, - pipeline: Gp, - dynamic: &DynamicState, - vertex_buffers: V, - descriptor_sets: S, - push_constants: Pc, - ) -> Result<&mut Self, DrawError> - where - Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone - S: DescriptorSetsCollection, - { - let descriptor_sets = descriptor_sets.into_vec(); - - unsafe { - // TODO: must check that pipeline is compatible with render pass + // VUID-VkCommandBufferInheritanceInfo-pipelineStatistics-02789 + query_statistics_flags.validate_device(device)?; - self.ensure_inside_render_pass_inline(&pipeline)?; - check_dynamic_state_validity(&pipeline, dynamic)?; - check_push_constants_validity(pipeline.layout(), &push_constants)?; - check_descriptor_sets_validity(pipeline.layout(), &descriptor_sets)?; - let vb_infos = check_vertex_buffers(&pipeline, vertex_buffers)?; - - if let StateCacherOutcome::NeedChange = - self.state_cacher.bind_graphics_pipeline(&pipeline) + // VUID-VkCommandBufferInheritanceInfo-pipelineStatistics-00058 + if query_statistics_flags.count() > 0 + && !device.enabled_features().pipeline_statistics_query { - self.inner.bind_pipeline_graphics(pipeline.clone()); + 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); - let dynamic = self.state_cacher.dynamic_state(dynamic); - - set_push_constants(&mut self.inner, pipeline.layout(), push_constants); - set_state(&mut self.inner, &dynamic); - bind_descriptor_sets( - &mut self.inner, - &mut self.state_cacher, - PipelineBindPoint::Graphics, - pipeline.layout(), - descriptor_sets, - )?; - bind_vertex_buffers( - &mut self.inner, - &mut self.state_cacher, - vb_infos.vertex_buffers, - )?; - - debug_assert!(self.queue_family().supports_graphics()); - - self.inner.draw( - vb_infos.vertex_count as u32, - vb_infos.instance_count as u32, - 0, - 0, - ); - Ok(self) + // VUID-vkBeginCommandBuffer-commandBuffer-02840 + // Ensured by the definition of the `CommandBufferUsage` enum. } - } - - /// Perform multiple draw operations using a graphics pipeline. - /// - /// One draw is performed for each [`DrawIndirectCommand`] struct in `indirect_buffer`. - /// The maximum number of draw commands in the buffer is limited by the - /// [`max_draw_indirect_count`](crate::device::Properties::max_draw_indirect_count) limit. - /// This limit is 1 unless the - /// [`multi_draw_indirect`](crate::device::Features::multi_draw_indirect) feature has been - /// enabled. - /// - /// `vertex_buffer` is a set of vertex and/or instance buffers used to provide input. It is - /// used for every draw operation. - /// - /// All data in `vertex_buffer` is used for every draw operation. To use only some data in the - /// buffer, wrap it in a `vulkano::buffer::BufferSlice`. - #[inline] - pub fn draw_indirect<V, Gp, S, Pc, Inb>( - &mut self, - pipeline: Gp, - dynamic: &DynamicState, - vertex_buffers: V, - indirect_buffer: Inb, - descriptor_sets: S, - push_constants: Pc, - ) -> Result<&mut Self, DrawIndirectError> - where - Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone - S: DescriptorSetsCollection, - Inb: BufferAccess - + TypedBufferAccess<Content = [DrawIndirectCommand]> - + Send - + Sync - + 'static, - { - let descriptor_sets = descriptor_sets.into_vec(); - - unsafe { - // TODO: must check that pipeline is compatible with render pass - - self.ensure_inside_render_pass_inline(&pipeline)?; - check_indirect_buffer(self.device(), &indirect_buffer)?; - check_dynamic_state_validity(&pipeline, dynamic)?; - check_push_constants_validity(pipeline.layout(), &push_constants)?; - check_descriptor_sets_validity(pipeline.layout(), &descriptor_sets)?; - let vb_infos = check_vertex_buffers(&pipeline, vertex_buffers)?; - - let requested = indirect_buffer.len() as u32; - let limit = self - .device() - .physical_device() - .properties() - .max_draw_indirect_count; - - if requested > limit { - return Err( - CheckIndirectBufferError::MaxDrawIndirectCountLimitExceeded { - limit, - requested, - } - .into(), - ); - } - - if let StateCacherOutcome::NeedChange = - self.state_cacher.bind_graphics_pipeline(&pipeline) - { - self.inner.bind_pipeline_graphics(pipeline.clone()); - } - let dynamic = self.state_cacher.dynamic_state(dynamic); - - set_push_constants(&mut self.inner, pipeline.layout(), push_constants); - set_state(&mut self.inner, &dynamic); - bind_descriptor_sets( - &mut self.inner, - &mut self.state_cacher, - PipelineBindPoint::Graphics, - pipeline.layout(), - descriptor_sets, - )?; - bind_vertex_buffers( - &mut self.inner, - &mut self.state_cacher, - vb_infos.vertex_buffers, - )?; - - debug_assert!(self.queue_family().supports_graphics()); - - self.inner.draw_indirect( - indirect_buffer, - requested, - mem::size_of::<DrawIndirectCommand>() as u32, - )?; - Ok(self) - } + Ok(()) } +} - /// Perform a single draw operation using a graphics pipeline, using an index buffer. - /// - /// `vertex_buffer` is a set of vertex and/or instance buffers used to provide input. - /// `index_buffer` is a buffer containing indices into the vertex buffer that should be - /// processed in order. - /// - /// All data in `vertex_buffer` and `index_buffer` is used for the draw operation. To use - /// only some data in the buffer, wrap it in a `vulkano::buffer::BufferSlice`. - #[inline] - pub fn draw_indexed<V, Gp, S, Pc, Ib, I>( - &mut self, - pipeline: Gp, - dynamic: &DynamicState, - vertex_buffers: V, - index_buffer: Ib, - descriptor_sets: S, - push_constants: Pc, - ) -> Result<&mut Self, DrawIndexedError> - where - Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone - S: DescriptorSetsCollection, - Ib: BufferAccess + TypedBufferAccess<Content = [I]> + Send + Sync + 'static, - I: Index + 'static, - { - let descriptor_sets = descriptor_sets.into_vec(); - - unsafe { - // TODO: must check that pipeline is compatible with render pass - - self.ensure_inside_render_pass_inline(&pipeline)?; - let ib_infos = check_index_buffer(self.device(), &index_buffer)?; - check_dynamic_state_validity(&pipeline, dynamic)?; - check_push_constants_validity(pipeline.layout(), &push_constants)?; - check_descriptor_sets_validity(pipeline.layout(), &descriptor_sets)?; - let vb_infos = check_vertex_buffers(&pipeline, vertex_buffers)?; - - if let StateCacherOutcome::NeedChange = - self.state_cacher.bind_graphics_pipeline(&pipeline) - { - self.inner.bind_pipeline_graphics(pipeline.clone()); - } - - if let StateCacherOutcome::NeedChange = - self.state_cacher.bind_index_buffer(&index_buffer, I::ty()) - { - self.inner.bind_index_buffer(index_buffer, I::ty())?; - } - - let dynamic = self.state_cacher.dynamic_state(dynamic); - - set_push_constants(&mut self.inner, pipeline.layout(), push_constants); - set_state(&mut self.inner, &dynamic); - bind_descriptor_sets( - &mut self.inner, - &mut self.state_cacher, - PipelineBindPoint::Graphics, - pipeline.layout(), - descriptor_sets, - )?; - bind_vertex_buffers( - &mut self.inner, - &mut self.state_cacher, - vb_infos.vertex_buffers, - )?; - // TODO: how to handle an index out of range of the vertex buffers? - - debug_assert!(self.queue_family().supports_graphics()); - - self.inner.draw_indexed( - ib_infos.num_indices as u32, - vb_infos.instance_count as u32, - 0, - 0, - 0, - ); - Ok(self) - } - } +/// Error that can happen when beginning recording of a command buffer. +#[derive(Clone, Copy, Debug)] +pub enum CommandBufferBeginError { + /// Not enough memory. + OomError(OomError), - /// Perform multiple draw operations using a graphics pipeline, using an index buffer. - /// - /// One draw is performed for each [`DrawIndirectCommand`] struct in `indirect_buffer`. - /// The maximum number of draw commands in the buffer is limited by the - /// [`max_draw_indirect_count`](crate::device::Properties::max_draw_indirect_count) limit. - /// This limit is 1 unless the - /// [`multi_draw_indirect`](crate::device::Features::multi_draw_indirect) feature has been - /// enabled. - /// - /// `vertex_buffer` is a set of vertex and/or instance buffers used to provide input. - /// `index_buffer` is a buffer containing indices into the vertex buffer that should be - /// processed in order. - /// - /// All data in `vertex_buffer` and `index_buffer` is used for every draw operation. To use - /// only some data in the buffer, wrap it in a `vulkano::buffer::BufferSlice`. - #[inline] - pub fn draw_indexed_indirect<V, Gp, S, Pc, Ib, Inb, I>( - &mut self, - pipeline: Gp, - dynamic: &DynamicState, - vertex_buffers: V, - index_buffer: Ib, - indirect_buffer: Inb, - descriptor_sets: S, - push_constants: Pc, - ) -> Result<&mut Self, DrawIndexedIndirectError> - where - Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone - S: DescriptorSetsCollection, - Ib: BufferAccess + TypedBufferAccess<Content = [I]> + Send + Sync + 'static, - Inb: BufferAccess - + TypedBufferAccess<Content = [DrawIndexedIndirectCommand]> - + Send - + Sync - + 'static, - I: Index + 'static, - { - let descriptor_sets = descriptor_sets.into_vec(); + RequirementNotMet { + required_for: &'static str, + requires_one_of: RequiresOneOf, + }, - unsafe { - // TODO: must check that pipeline is compatible with render pass - - self.ensure_inside_render_pass_inline(&pipeline)?; - let ib_infos = check_index_buffer(self.device(), &index_buffer)?; - check_indirect_buffer(self.device(), &indirect_buffer)?; - check_dynamic_state_validity(&pipeline, dynamic)?; - check_push_constants_validity(pipeline.layout(), &push_constants)?; - check_descriptor_sets_validity(pipeline.layout(), &descriptor_sets)?; - let vb_infos = check_vertex_buffers(&pipeline, vertex_buffers)?; - - let requested = indirect_buffer.len() as u32; - let limit = self - .device() - .physical_device() - .properties() - .max_draw_indirect_count; - - if requested > limit { - return Err( - CheckIndirectBufferError::MaxDrawIndirectCountLimitExceeded { - limit, - requested, - } - .into(), - ); - } + /// A color attachment has a format that does not support that usage. + ColorAttachmentFormatUsageNotSupported { attachment_index: u32 }, - if let StateCacherOutcome::NeedChange = - self.state_cacher.bind_graphics_pipeline(&pipeline) - { - self.inner.bind_pipeline_graphics(pipeline.clone()); - } + /// The depth attachment has a format that does not support that usage. + DepthAttachmentFormatUsageNotSupported, - if let StateCacherOutcome::NeedChange = - self.state_cacher.bind_index_buffer(&index_buffer, I::ty()) - { - self.inner.bind_index_buffer(index_buffer, I::ty())?; - } + /// The depth and stencil attachments have different formats. + DepthStencilAttachmentFormatMismatch, - let dynamic = self.state_cacher.dynamic_state(dynamic); - - set_push_constants(&mut self.inner, pipeline.layout(), push_constants); - set_state(&mut self.inner, &dynamic); - bind_descriptor_sets( - &mut self.inner, - &mut self.state_cacher, - PipelineBindPoint::Graphics, - pipeline.layout(), - descriptor_sets, - )?; - bind_vertex_buffers( - &mut self.inner, - &mut self.state_cacher, - vb_infos.vertex_buffers, - )?; - - debug_assert!(self.queue_family().supports_graphics()); - - self.inner.draw_indexed_indirect( - indirect_buffer, - requested, - mem::size_of::<DrawIndexedIndirectCommand>() as u32, - )?; - Ok(self) - } - } + /// The framebuffer is not compatible with the render pass. + FramebufferNotCompatible, - /// Adds a command that writes the content of a buffer. - /// - /// This function is similar to the `memset` function in C. The `data` parameter is a number - /// that will be repeatedly written through the entire buffer. - /// - /// > **Note**: This function is technically safe because buffers can only contain integers or - /// > floating point numbers, which are always valid whatever their memory representation is. - /// > But unless your buffer actually contains only 32-bits integers, you are encouraged to use - /// > this function only for zeroing the content of a buffer by passing `0` for the data. - // TODO: not safe because of signalling NaNs - #[inline] - pub fn fill_buffer<B>(&mut self, buffer: B, data: u32) -> Result<&mut Self, FillBufferError> - where - B: BufferAccess + Send + Sync + 'static, - { - unsafe { - self.ensure_outside_render_pass()?; - check_fill_buffer(self.device(), &buffer)?; - self.inner.fill_buffer(buffer, data); - Ok(self) - } - } + /// The `max_multiview_view_count` limit has been exceeded. + MaxMultiviewViewCountExceeded { view_count: u32, max: u32 }, - /// Adds a command that writes data to a buffer. - /// - /// If `data` is larger than the buffer, only the part of `data` that fits is written. If the - /// buffer is larger than `data`, only the start of the buffer is written. - #[inline] - pub fn update_buffer<B, D, Dd>( - &mut self, - buffer: B, - data: Dd, - ) -> Result<&mut Self, UpdateBufferError> - where - B: TypedBufferAccess<Content = D> + Send + Sync + 'static, - D: ?Sized, - Dd: SafeDeref<Target = D> + Send + Sync + 'static, - { - unsafe { - self.ensure_outside_render_pass()?; - check_update_buffer(self.device(), &buffer, data.deref())?; - - let size_of_data = mem::size_of_val(data.deref()) as DeviceSize; - if buffer.size() >= size_of_data { - self.inner.update_buffer(buffer, data); - } else { - unimplemented!() // TODO: - //self.inner.update_buffer(buffer.slice(0 .. size_of_data), data); - } + /// The stencil attachment has a format that does not support that usage. + StencilAttachmentFormatUsageNotSupported, +} - Ok(self) +impl Error for CommandBufferBeginError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::OomError(err) => Some(err), + _ => None, } } +} - /// 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(), - ); - } +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") } - 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::MaxMultiviewViewCountExceeded { .. } => { + write!(f, "the `max_multiview_view_count` limit has been exceeded") } - - 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()); + Self::StencilAttachmentFormatUsageNotSupported => write!( + f, + "the stencil attachment has a format that does not support that usage", + ), } - - // 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().result_size()`](crate::query::QueryType::result_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) +impl From<OomError> for CommandBufferBeginError { + fn from(err: OomError) -> Self { + Self::OomError(err) } +} - /// 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()); +impl From<RequirementNotMet> for CommandBufferBeginError { + fn from(err: RequirementNotMet) -> Self { + Self::RequirementNotMet { + required_for: err.required_for, + requires_one_of: err.requires_one_of, } - - // 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 -impl<P> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P> +impl<A> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<A::Alloc>, A> where - P: CommandPoolBuilderAlloc, + A: CommandBufferAllocator, { - /// Adds a command that enters a render pass. - /// - /// If `contents` is `SubpassContents::SecondaryCommandBuffers`, then you will only be able to - /// add secondary command buffers while you're inside the first subpass of the render pass. - /// If it is `SubpassContents::Inline`, you will only be able to add inline draw commands and - /// not secondary command buffers. - /// - /// C must contain exactly one clear value for each attachment in the framebuffer. - /// - /// You must call this before you can add draw commands. - #[inline] - pub fn begin_render_pass<F, I>( - &mut self, - framebuffer: F, - contents: SubpassContents, - clear_values: I, - ) -> Result<&mut Self, BeginRenderPassError> - where - F: FramebufferAbstract + Clone + Send + Sync + 'static, - I: IntoIterator<Item = ClearValue>, - { - unsafe { - if !self.queue_family().supports_graphics() { - return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); - } - - self.ensure_outside_render_pass()?; - - let clear_values = framebuffer - .render_pass() - .desc() - .convert_clear_values(clear_values); - let clear_values = clear_values.collect::<Vec<_>>().into_iter(); // TODO: necessary for Send + Sync ; needs an API rework of convert_clear_values - let mut clear_values_copy = clear_values.clone().enumerate(); // TODO: Proper errors for clear value errors instead of panics - - for (atch_i, atch_desc) in framebuffer - .render_pass() - .desc() - .attachments() - .into_iter() - .enumerate() - { - match clear_values_copy.next() { - Some((clear_i, clear_value)) => { - if atch_desc.load == LoadOp::Clear { - match clear_value { - ClearValue::None => panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: None", - clear_i, atch_i, atch_desc.format.ty()), - ClearValue::Float(_) => if atch_desc.format.ty() != FormatTy::Float { - panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Float", - clear_i, atch_i, atch_desc.format.ty()); - } - ClearValue::Int(_) => if atch_desc.format.ty() != FormatTy::Sint { - panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Int", - clear_i, atch_i, atch_desc.format.ty()); - } - ClearValue::Uint(_) => if atch_desc.format.ty() != FormatTy::Uint { - panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Uint", - clear_i, atch_i, atch_desc.format.ty()); - } - ClearValue::Depth(_) => if atch_desc.format.ty() != FormatTy::Depth { - panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Depth", - clear_i, atch_i, atch_desc.format.ty()); - } - ClearValue::Stencil(_) => if atch_desc.format.ty() != FormatTy::Stencil { - panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Stencil", - clear_i, atch_i, atch_desc.format.ty()); - } - ClearValue::DepthStencil(_) => if atch_desc.format.ty() != FormatTy::DepthStencil { - panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: DepthStencil", - clear_i, atch_i, atch_desc.format.ty()); - } - } - } else { - if clear_value != ClearValue::None { - panic!("Bad ClearValue! index: {}, attachment index: {}, expected: None, got: {:?}", - clear_i, atch_i, clear_value); - } - } - } - None => panic!("Not enough clear values"), - } - } - - if clear_values_copy.count() != 0 { - panic!("Too many clear values") - } - - if let Some(multiview_desc) = framebuffer.render_pass().desc().multiview() { - // When multiview is enabled, at the beginning of each subpass all non-render pass state is undefined - self.state_cacher.invalidate(); - - // ensure that the framebuffer is compatible with the render pass multiview configuration - if multiview_desc - .view_masks - .iter() - .chain(multiview_desc.correlation_masks.iter()) - .map(|&mask| 32 - mask.leading_zeros()) // calculates the highest used layer index of the mask - .any(|highest_used_layer| highest_used_layer > framebuffer.layers()) - { - panic!("A multiview mask references more layers than exist in the framebuffer"); - } - } - - let framebuffer_object = FramebufferAbstract::inner(&framebuffer).internal_object(); - self.inner - .begin_render_pass(framebuffer.clone(), contents, clear_values)?; - self.render_pass_state = Some(RenderPassState { - subpass: (framebuffer.render_pass().clone(), 0), - contents, - framebuffer: framebuffer_object, - }); - Ok(self) - } - } - - /// Adds a command that ends the current render pass. - /// - /// This must be called after you went through all the subpasses and before you can build - /// the command buffer or add further commands. - #[inline] - pub fn end_render_pass(&mut self) -> Result<&mut Self, AutoCommandBufferBuilderContextError> { - unsafe { - if let Some(render_pass_state) = self.render_pass_state.as_ref() { - let (ref rp, index) = render_pass_state.subpass; - - if rp.desc().subpasses().len() as u32 != index + 1 { - return Err(AutoCommandBufferBuilderContextError::NumSubpassesMismatch { - actual: rp.desc().subpasses().len() as u32, - current: index, - }); - } - } else { - return Err(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass); - } - - 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.render_pass_state = None; - Ok(self) - } - } - - /// Adds a command that executes a secondary command buffer. - /// - /// If the `flags` that `command_buffer` was created with are more restrictive than those of - /// `self`, then `self` will be restricted to match. E.g. executing a secondary command buffer - /// with `Flags::OneTimeSubmit` will set `self`'s flags to `Flags::OneTimeSubmit` also. - pub fn execute_commands<C>( - &mut self, - command_buffer: C, - ) -> Result<&mut Self, ExecuteCommandsError> - where - C: SecondaryCommandBuffer + Send + Sync + 'static, - { - self.check_command_buffer(&command_buffer)?; - let secondary_usage = command_buffer.inner().usage(); - - unsafe { - let mut builder = self.inner.execute_commands(); - builder.add(command_buffer); - builder.submit()?; - } - - // Secondary command buffer could leave the primary in any state. - self.state_cacher.invalidate(); - - // If the secondary is non-concurrent or one-time use, that restricts the primary as well. - self.usage = std::cmp::min(self.usage, secondary_usage); - - Ok(self) - } - - /// Adds a command that multiple secondary command buffers in a vector. - /// - /// This requires that the secondary command buffers do not have resource conflicts; an error - /// will be returned if there are any. Use `execute_commands` if you want to ensure that - /// resource conflicts are automatically resolved. - // TODO ^ would be nice if this just worked without errors - pub fn execute_commands_from_vec<C>( - &mut self, - command_buffers: Vec<C>, - ) -> Result<&mut Self, ExecuteCommandsError> - where - C: SecondaryCommandBuffer + Send + Sync + 'static, - { - for command_buffer in &command_buffers { - self.check_command_buffer(command_buffer)?; + /// Builds the command buffer. + pub fn build(self) -> Result<PrimaryAutoCommandBuffer<A::Alloc>, BuildError> { + if self.render_pass_state.is_some() { + return Err(BuildError::RenderPassActive); } - let mut secondary_usage = CommandBufferUsage::SimultaneousUse; // Most permissive usage - unsafe { - let mut builder = self.inner.execute_commands(); - for command_buffer in command_buffers { - secondary_usage = std::cmp::min(secondary_usage, command_buffer.inner().usage()); - builder.add(command_buffer); - } - builder.submit()?; + if !self.query_state.is_empty() { + return Err(BuildError::QueryActive); } - // Secondary command buffer could leave the primary in any state. - self.state_cacher.invalidate(); - - // If the secondary is non-concurrent or one-time use, that restricts the primary as well. - self.usage = std::cmp::min(self.usage, secondary_usage); + Ok(PrimaryAutoCommandBuffer { + inner: self.inner.build()?, + _alloc: self.builder_alloc.into_alloc(), + usage: self.usage, - Ok(self) + state: Mutex::new(Default::default()), + }) } +} - // Helper function for execute_commands - fn check_command_buffer<C>( - &self, - command_buffer: &C, - ) -> Result<(), AutoCommandBufferBuilderContextError> - where - C: SecondaryCommandBuffer + Send + Sync + 'static, - { - if let Some(render_pass) = command_buffer.inheritance().render_pass { - self.ensure_inside_render_pass_secondary(&render_pass)?; - } else { - self.ensure_outside_render_pass()?; +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); } - for state in self.query_state.values() { - match state.ty { - QueryType::Occlusion => match command_buffer.inheritance().occlusion_query { - Some(inherited_flags) => { - let inherited_flags = ash::vk::QueryControlFlags::from(inherited_flags); - let state_flags = ash::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 = - ash::vk::QueryPipelineStatisticFlags::from(inherited_flags); - let state_flags = ash::vk::QueryPipelineStatisticFlags::from(state_flags); - - if inherited_flags & state_flags != state_flags { - return Err(AutoCommandBufferBuilderContextError::QueryNotInherited); - } - } - _ => (), - } - } + 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(()) + Ok(SecondaryAutoCommandBuffer { + inner: self.inner.build()?, + _alloc: self.builder_alloc.into_alloc(), + usage: self.usage, + inheritance_info: self.inheritance_info.unwrap(), + submit_state, + }) } +} - #[inline] - fn ensure_inside_render_pass_secondary( - &self, - render_pass: &CommandBufferInheritanceRenderPass<&dyn FramebufferAbstract>, - ) -> Result<(), AutoCommandBufferBuilderContextError> { - let render_pass_state = self - .render_pass_state - .as_ref() - .ok_or(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass)?; - - if render_pass_state.contents != SubpassContents::SecondaryCommandBuffers { - return Err(AutoCommandBufferBuilderContextError::WrongSubpassType); - } +/// Error that can happen when building a command buffer. +#[derive(Clone, Debug)] +pub enum BuildError { + OomError(OomError), - // Subpasses must be the same. - if render_pass.subpass.index() != render_pass_state.subpass.1 { - return Err(AutoCommandBufferBuilderContextError::WrongSubpassIndex); - } + /// A render pass is still active on the command buffer. + RenderPassActive, - // Render passes must be compatible. - if !render_pass - .subpass - .render_pass() - .desc() - .is_compatible_with_desc(render_pass_state.subpass.0.desc()) - { - return Err(AutoCommandBufferBuilderContextError::IncompatibleRenderPass); - } + /// A query is still active on the command buffer. + QueryActive, +} - // Framebuffer, if present on the secondary command buffer, must be the - // same as the one in the current render pass. - if let Some(framebuffer) = render_pass.framebuffer { - if FramebufferAbstract::inner(framebuffer).internal_object() - != render_pass_state.framebuffer - { - return Err(AutoCommandBufferBuilderContextError::IncompatibleFramebuffer); - } +impl Error for BuildError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::OomError(err) => Some(err), + _ => None, } - - Ok(()) } +} - /// Adds a command that jumps to the next subpass of the current render pass. - #[inline] - pub fn next_subpass( - &mut self, - contents: SubpassContents, - ) -> Result<&mut Self, AutoCommandBufferBuilderContextError> { - unsafe { - if let Some(render_pass_state) = self.render_pass_state.as_mut() { - let (ref rp, ref mut index) = render_pass_state.subpass; - - if *index + 1 >= rp.desc().subpasses().len() as u32 { - return Err(AutoCommandBufferBuilderContextError::NumSubpassesMismatch { - actual: rp.desc().subpasses().len() as u32, - current: *index, - }); - } else { - *index += 1; - render_pass_state.contents = contents; - } - - if let Some(multiview) = rp.desc().multiview() { - // When multiview is enabled, at the beginning of each subpass all non-render pass state is undefined - self.state_cacher.invalidate(); - } - } else { - return Err(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass); - } - - if self.query_state.values().any(|state| state.in_subpass) { - return Err(AutoCommandBufferBuilderContextError::QueryIsActive); +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") } - - debug_assert!(self.queue_family().supports_graphics()); - - self.inner.next_subpass(contents); - Ok(self) + Self::QueryActive => write!(f, "a query is still active on the command buffer"), } } } -impl<P> AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<P::Alloc>, P> where - P: CommandPoolBuilderAlloc -{ -} - -unsafe impl<L, P> DeviceOwned for AutoCommandBufferBuilder<L, P> { - #[inline] - fn device(&self) -> &Arc<Device> { - self.inner.device() - } -} - -// Shortcut function to set the push constants. -unsafe fn set_push_constants<Pc>( - destination: &mut SyncCommandBufferBuilder, - pipeline_layout: &Arc<PipelineLayout>, - push_constants: Pc, -) { - for range in pipeline_layout.push_constant_ranges() { - debug_assert_eq!(range.offset % 4, 0); - debug_assert_eq!(range.size % 4, 0); - - let data = slice::from_raw_parts( - (&push_constants as *const Pc as *const u8).offset(range.offset as isize), - range.size as usize, - ); - - destination.push_constants::<[u8]>( - pipeline_layout.clone(), - range.stages, - range.offset as u32, - range.size as u32, - data, - ); +impl From<OomError> for BuildError { + fn from(err: OomError) -> Self { + Self::OomError(err) } } -// Shortcut function to change the state of the pipeline. -unsafe fn set_state(destination: &mut SyncCommandBufferBuilder, dynamic: &DynamicState) { - if let Some(line_width) = dynamic.line_width { - destination.set_line_width(line_width); - } - - if let Some(ref viewports) = dynamic.viewports { - destination.set_viewport(0, viewports.iter().cloned().collect::<Vec<_>>().into_iter()); - // TODO: don't collect - } - - if let Some(ref scissors) = dynamic.scissors { - destination.set_scissor(0, scissors.iter().cloned().collect::<Vec<_>>().into_iter()); - // TODO: don't collect - } - - if let Some(compare_mask) = dynamic.compare_mask { - destination.set_stencil_compare_mask(StencilFaces::Front, compare_mask.front); - destination.set_stencil_compare_mask(StencilFaces::Back, compare_mask.back); - } - - if let Some(write_mask) = dynamic.write_mask { - destination.set_stencil_write_mask(StencilFaces::Front, write_mask.front); - destination.set_stencil_write_mask(StencilFaces::Back, write_mask.back); +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] } - if let Some(reference) = dynamic.reference { - destination.set_stencil_reference(StencilFaces::Front, reference.front); - destination.set_stencil_reference(StencilFaces::Back, reference.back); + /// Returns the binding/setting state. + pub fn state(&self) -> CommandBufferBuilderState<'_> { + self.inner.state() } } -// Shortcut function to bind vertex buffers. -unsafe fn bind_vertex_buffers( - destination: &mut SyncCommandBufferBuilder, - state_cacher: &mut StateCacher, - vertex_buffers: Vec<Box<dyn BufferAccess + Send + Sync>>, -) -> Result<(), SyncCommandBufferBuilderError> { - let binding_range = { - let mut compare = state_cacher.bind_vertex_buffers(); - for vb in vertex_buffers.iter() { - compare.add(vb); - } - match compare.compare() { - Some(r) => r, - None => return Ok(()), - } - }; - - let first_binding = binding_range.start; - let num_bindings = binding_range.end - binding_range.start; - - let mut binder = destination.bind_vertex_buffers(); - for vb in vertex_buffers - .into_iter() - .skip(first_binding as usize) - .take(num_bindings as usize) - { - binder.add(vb); - } - binder.submit(first_binding)?; - Ok(()) -} - -unsafe fn bind_descriptor_sets( - destination: &mut SyncCommandBufferBuilder, - state_cacher: &mut StateCacher, - pipeline_bind_point: PipelineBindPoint, - pipeline_layout: &Arc<PipelineLayout>, - descriptor_sets: Vec<DescriptorSetWithOffsets>, -) -> Result<(), SyncCommandBufferBuilderError> { - let first_binding = { - let mut compare = state_cacher.bind_descriptor_sets(pipeline_bind_point); - for descriptor_set in descriptor_sets.iter() { - compare.add(descriptor_set); - } - compare.compare() - }; - - let first_binding = match first_binding { - None => return Ok(()), - Some(fb) => fb, - }; - - let mut sets_binder = destination.bind_descriptor_sets(); - for set in descriptor_sets.into_iter().skip(first_binding as usize) { - sets_binder.add(set); +unsafe impl<L, A> DeviceOwned for AutoCommandBufferBuilder<L, A> +where + A: CommandBufferAllocator, +{ + fn device(&self) -> &Arc<Device> { + self.inner.device() } - sets_binder.submit(pipeline_bind_point, pipeline_layout.clone(), first_binding)?; - Ok(()) } -pub struct PrimaryAutoCommandBuffer<P = StandardCommandPoolAlloc> { +pub struct PrimaryAutoCommandBuffer<A = StandardCommandBufferAlloc> { inner: SyncCommandBuffer, - pool_alloc: P, // Safety: must be dropped after `inner` + _alloc: A, // Safety: must be dropped after `inner` + usage: CommandBufferUsage, - // Tracks usage of the command buffer on the GPU. - submit_state: SubmitState, + state: Mutex<CommandBufferState>, } -unsafe impl<P> DeviceOwned for PrimaryAutoCommandBuffer<P> { - #[inline] +unsafe impl<A> DeviceOwned for PrimaryAutoCommandBuffer<A> { fn device(&self) -> &Arc<Device> { self.inner.device() } } -unsafe impl<P> PrimaryCommandBuffer for PrimaryAutoCommandBuffer<P> { - #[inline] - fn inner(&self) -> &UnsafeCommandBuffer { - self.inner.as_ref() - } - - #[inline] - fn lock_submit( - &self, - future: &dyn GpuFuture, - queue: &Queue, - ) -> 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 => (), - }; - - let err = match self.inner.lock_submit(future, queue) { - Ok(()) => return Ok(()), - Err(err) => err, - }; - - // If `self.inner.lock_submit()` failed, we revert action. - match self.submit_state { - SubmitState::OneTime { - ref already_submitted, - } => { - already_submitted.store(false, Ordering::SeqCst); - } - SubmitState::ExclusiveUse { ref in_use } => { - in_use.store(false, Ordering::SeqCst); - } - SubmitState::Concurrent => (), - }; +unsafe impl<A> VulkanObject for PrimaryAutoCommandBuffer<A> { + type Handle = ash::vk::CommandBuffer; - Err(err) + fn handle(&self) -> Self::Handle { + self.inner.as_ref().handle() } +} - #[inline] - unsafe fn unlock(&self) { - // Because of panic safety, we unlock the inner command buffer first. - self.inner.unlock(); - - 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 => (), - }; +unsafe impl<A> PrimaryCommandBufferAbstract for PrimaryAutoCommandBuffer<A> +where + A: CommandBufferAlloc, +{ + fn usage(&self) -> CommandBufferUsage { + self.usage } - #[inline] - fn check_buffer_access( - &self, - buffer: &dyn BufferAccess, - exclusive: bool, - queue: &Queue, - ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> { - self.inner.check_buffer_access(buffer, exclusive, queue) + fn state(&self) -> MutexGuard<'_, CommandBufferState> { + self.state.lock() } - #[inline] - fn check_image_access( - &self, - image: &dyn ImageAccess, - layout: ImageLayout, - exclusive: bool, - queue: &Queue, - ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> { - self.inner - .check_image_access(image, layout, exclusive, queue) + fn resources_usage(&self) -> &CommandBufferResourcesUsage { + self.inner.resources_usage() } } -pub struct SecondaryAutoCommandBuffer<P = StandardCommandPoolAlloc> { +pub struct SecondaryAutoCommandBuffer<A = StandardCommandBufferAlloc> { inner: SyncCommandBuffer, - pool_alloc: P, // Safety: must be dropped after `inner` - inheritance: CommandBufferInheritance<Box<dyn FramebufferAbstract + Send + Sync>>, + _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<P> DeviceOwned for SecondaryAutoCommandBuffer<P> { - #[inline] +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<P> SecondaryCommandBuffer for SecondaryAutoCommandBuffer<P> { - #[inline] - fn inner(&self) -> &UnsafeCommandBuffer { - self.inner.as_ref() +unsafe impl<A> SecondaryCommandBufferAbstract for SecondaryAutoCommandBuffer<A> +where + A: CommandBufferAlloc, +{ + fn usage(&self) -> CommandBufferUsage { + self.usage + } + + fn inheritance_info(&self) -> &CommandBufferInheritanceInfo { + &self.inheritance_info } - #[inline] fn lock_record(&self) -> Result<(), CommandBufferExecError> { match self.submit_state { SubmitState::OneTime { @@ -2353,7 +818,6 @@ unsafe impl<P> SecondaryCommandBuffer for SecondaryAutoCommandBuffer<P> { Ok(()) } - #[inline] unsafe fn unlock(&self) { match self.submit_state { SubmitState::OneTime { @@ -2369,51 +833,8 @@ unsafe impl<P> SecondaryCommandBuffer for SecondaryAutoCommandBuffer<P> { }; } - fn inheritance(&self) -> CommandBufferInheritance<&dyn FramebufferAbstract> { - CommandBufferInheritance { - render_pass: self.inheritance.render_pass.as_ref().map( - |CommandBufferInheritanceRenderPass { - subpass, - framebuffer, - }| { - CommandBufferInheritanceRenderPass { - subpass: subpass.clone(), - framebuffer: framebuffer.as_ref().map(|f| f.as_ref() as &_), - } - }, - ), - occlusion_query: self.inheritance.occlusion_query, - query_statistics_flags: self.inheritance.query_statistics_flags, - } - } - - #[inline] - fn num_buffers(&self) -> usize { - self.inner.num_buffers() - } - - #[inline] - fn buffer(&self, index: usize) -> Option<(&dyn BufferAccess, PipelineMemoryAccess)> { - self.inner.buffer(index) - } - - #[inline] - fn num_images(&self) -> usize { - self.inner.num_images() - } - - #[inline] - fn image( - &self, - index: usize, - ) -> Option<( - &dyn ImageAccess, - PipelineMemoryAccess, - ImageLayout, - ImageLayout, - ImageUninitializedSafe, - )> { - self.inner.image(index) + fn resources_usage(&self) -> &SecondaryCommandBufferResourcesUsage { + self.inner.secondary_resources_usage() } } @@ -2438,352 +859,96 @@ enum SubmitState { }, } -macro_rules! err_gen { - ($name:ident { $($err:ident,)+ }) => ( - #[derive(Debug, Clone)] - pub enum $name { - $( - $err($err), - )+ - } - - impl error::Error for $name { - #[inline] - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - $( - $name::$err(ref err) => Some(err), - )+ - } - } - } - - impl fmt::Display for $name { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(fmt, "{}", match *self { - $( - $name::$err(_) => { - concat!("a ", stringify!($err)) - } - )+ - }) - } - } - - $( - impl From<$err> for $name { - #[inline] - fn from(err: $err) -> $name { - $name::$err(err) - } - } - )+ - ); -} - -err_gen!(BuildError { - AutoCommandBufferBuilderContextError, - OomError, -}); - -err_gen!(BeginRenderPassError { - AutoCommandBufferBuilderContextError, - SyncCommandBufferBuilderError, -}); - -err_gen!(CopyImageError { - AutoCommandBufferBuilderContextError, - CheckCopyImageError, - SyncCommandBufferBuilderError, -}); - -err_gen!(BlitImageError { - AutoCommandBufferBuilderContextError, - CheckBlitImageError, - SyncCommandBufferBuilderError, -}); - -err_gen!(ClearColorImageError { - AutoCommandBufferBuilderContextError, - CheckClearColorImageError, - SyncCommandBufferBuilderError, -}); - -err_gen!(CopyBufferError { - AutoCommandBufferBuilderContextError, - CheckCopyBufferError, - SyncCommandBufferBuilderError, -}); - -err_gen!(CopyBufferImageError { - AutoCommandBufferBuilderContextError, - CheckCopyBufferImageError, - SyncCommandBufferBuilderError, -}); - -err_gen!(CopyQueryPoolResultsError { - AutoCommandBufferBuilderContextError, - CheckCopyQueryPoolResultsError, - SyncCommandBufferBuilderError, -}); - -err_gen!(FillBufferError { - AutoCommandBufferBuilderContextError, - CheckFillBufferError, -}); - -err_gen!(DebugMarkerError { - AutoCommandBufferBuilderContextError, - CheckColorError, -}); - -err_gen!(DispatchError { - AutoCommandBufferBuilderContextError, - CheckPushConstantsValidityError, - CheckDescriptorSetsValidityError, - CheckDispatchError, - SyncCommandBufferBuilderError, -}); - -err_gen!(DispatchIndirectError { - AutoCommandBufferBuilderContextError, - CheckPushConstantsValidityError, - CheckDescriptorSetsValidityError, - CheckIndirectBufferError, - SyncCommandBufferBuilderError, -}); - -err_gen!(DrawError { - AutoCommandBufferBuilderContextError, - CheckDynamicStateValidityError, - CheckPushConstantsValidityError, - CheckDescriptorSetsValidityError, - CheckVertexBufferError, - SyncCommandBufferBuilderError, -}); - -err_gen!(DrawIndexedError { - AutoCommandBufferBuilderContextError, - CheckDynamicStateValidityError, - CheckPushConstantsValidityError, - CheckDescriptorSetsValidityError, - CheckVertexBufferError, - CheckIndexBufferError, - SyncCommandBufferBuilderError, -}); - -err_gen!(DrawIndirectError { - AutoCommandBufferBuilderContextError, - CheckDynamicStateValidityError, - CheckPushConstantsValidityError, - CheckDescriptorSetsValidityError, - CheckVertexBufferError, - CheckIndirectBufferError, - SyncCommandBufferBuilderError, -}); - -err_gen!(DrawIndexedIndirectError { - AutoCommandBufferBuilderContextError, - CheckDynamicStateValidityError, - CheckPushConstantsValidityError, - CheckDescriptorSetsValidityError, - CheckVertexBufferError, - CheckIndexBufferError, - CheckIndirectBufferError, - SyncCommandBufferBuilderError, -}); - -err_gen!(ExecuteCommandsError { - AutoCommandBufferBuilderContextError, - SyncCommandBufferBuilderError, -}); - -err_gen!(BeginQueryError { - AutoCommandBufferBuilderContextError, - CheckBeginQueryError, -}); - -err_gen!(EndQueryError { - AutoCommandBufferBuilderContextError, - CheckEndQueryError, -}); - -err_gen!(WriteTimestampError { - AutoCommandBufferBuilderContextError, - CheckWriteTimestampError, -}); - -err_gen!(ResetQueryPoolError { - AutoCommandBufferBuilderContextError, - CheckResetQueryPoolError, -}); - -err_gen!(UpdateBufferError { - AutoCommandBufferBuilderContextError, - CheckUpdateBufferError, -}); - -#[derive(Debug, Copy, Clone)] -pub enum AutoCommandBufferBuilderContextError { - /// Operation forbidden inside of a render pass. - ForbiddenInsideRenderPass, - /// Operation forbidden outside of a render pass. - 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. - NotSupportedByQueueFamily, - /// Tried to end a render pass with subpasses remaining, or tried to go to next subpass with no - /// subpass remaining. - NumSubpassesMismatch { - /// Actual number of subpasses in the current render pass. - actual: u32, - /// Current subpass index before the failing command. - current: u32, - }, - /// A query is active that conflicts with the current operation. - QueryIsActive, - /// 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 - /// didn't match the current subpass index. - WrongSubpassIndex, - /// 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. - WrongSubpassType, -} - -impl error::Error for AutoCommandBufferBuilderContextError {} - -impl fmt::Display for AutoCommandBufferBuilderContextError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!( - fmt, - "{}", - match *self { - AutoCommandBufferBuilderContextError::ForbiddenInsideRenderPass => { - "operation forbidden inside of a render pass" - } - AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass => { - "operation forbidden outside of a render pass" - } - AutoCommandBufferBuilderContextError::IncompatibleFramebuffer => { - "tried to use a secondary command buffer with a specified framebuffer that is \ - incompatible with the current framebuffer" - } - AutoCommandBufferBuilderContextError::IncompatibleRenderPass => { - "tried to use a graphics pipeline or secondary command buffer whose 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" - } - } - ) - } -} - #[cfg(test)] mod tests { - use crate::buffer::BufferUsage; - use crate::buffer::CpuAccessibleBuffer; - use crate::command_buffer::synced::SyncCommandBufferBuilderError; - use crate::command_buffer::AutoCommandBufferBuilder; - use crate::command_buffer::CommandBufferExecError; - use crate::command_buffer::CommandBufferUsage; - use crate::command_buffer::ExecuteCommandsError; - use crate::command_buffer::PrimaryCommandBuffer; - use crate::device::physical::PhysicalDevice; - use crate::device::Device; - use crate::device::DeviceExtensions; - use crate::device::Features; - use crate::sync::GpuFuture; - use std::sync::Arc; + 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 phys = match PhysicalDevice::enumerate(&instance).next() { + let physical_device = match instance.enumerate_physical_devices().unwrap().next() { Some(p) => p, None => return, }; - let queue_family = match phys.queue_families().next() { - Some(q) => q, - None => return, - }; - let (device, mut queues) = Device::new( - phys, - &Features::none(), - &DeviceExtensions::none(), - std::iter::once((queue_family, 0.5)), + 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( - device.clone(), - BufferUsage::all(), - true, + 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 = CpuAccessibleBuffer::from_iter( - device.clone(), - BufferUsage::all(), - true, + 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( - device.clone(), - queue.family(), + &cb_allocator, + queue.queue_family_index(), CommandBufferUsage::OneTimeSubmit, ) .unwrap(); - cbb.copy_buffer_dimensions(source.clone(), 0, destination.clone(), 1, 2) - .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.clone()) + .execute(queue) .unwrap() .then_signal_fence_and_flush() .unwrap(); @@ -2798,19 +963,22 @@ mod tests { 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_compute( - device.clone(), - queue.family(), + 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( - device.clone(), - queue.family(), + &cb_allocator, + queue.queue_family_index(), CommandBufferUsage::SimultaneousUse, ) .unwrap(); @@ -2832,8 +1000,8 @@ mod tests { { let mut builder = AutoCommandBufferBuilder::primary( - device.clone(), - queue.family(), + &cb_allocator, + queue.queue_family_index(), CommandBufferUsage::SimultaneousUse, ) .unwrap(); @@ -2841,8 +1009,8 @@ mod tests { let cb1 = builder.build().unwrap(); let mut builder = AutoCommandBufferBuilder::primary( - device.clone(), - queue.family(), + &cb_allocator, + queue.queue_family_index(), CommandBufferUsage::SimultaneousUse, ) .unwrap(); @@ -2861,7 +1029,106 @@ mod tests { std::mem::drop(cb1); // Now that the first cb is dropped, we should be able to record. - builder.execute_commands(secondary.clone()).unwrap(); + 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, + }) + )); + } } |