diff options
Diffstat (limited to 'src/command_buffer/auto.rs')
-rw-r--r-- | src/command_buffer/auto.rs | 2867 |
1 files changed, 2867 insertions, 0 deletions
diff --git a/src/command_buffer/auto.rs b/src/command_buffer/auto.rs new file mode 100644 index 0000000..64d0d2d --- /dev/null +++ b/src/command_buffer/auto.rs @@ -0,0 +1,2867 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use crate::buffer::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, + + // The inheritance for secondary command buffers. + inheritance: Option<CommandBufferInheritance<Box<dyn FramebufferAbstract + Send + Sync>>>, + + // Usage flags passed when creating the command buffer. + usage: CommandBufferUsage, + + // If we're inside a render pass, contains the render pass state. + render_pass_state: Option<RenderPassState>, + + // If any queries are active, this hashmap contains their state. + query_state: FnvHashMap<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 an active query. +struct QueryState { + query_pool: ash::vk::QueryPool, + query: u32, + ty: QueryType, + flags: QueryControlFlags, + in_subpass: bool, +} + +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<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, + }) + } + } +} + +#[derive(Clone, Copy, Debug)] +pub enum BeginError { + /// Occlusion query inheritance was requested, but the `inherited_queries` feature was not enabled. + InheritedQueriesFeatureNotEnabled, + /// Not enough memory. + OomError(OomError), + /// Pipeline statistics query inheritance was requested, but the `pipeline_statistics_query` feature was not enabled. + PipelineStatisticsQueryFeatureNotEnabled, +} + +impl error::Error for BeginError { + #[inline] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + Self::OomError(ref err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for BeginError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + fmt, + "{}", + match *self { + Self::InheritedQueriesFeatureNotEnabled => { + "occlusion query inheritance was requested but the corresponding feature \ + wasn't enabled" + } + Self::OomError(_) => "not enough memory available", + Self::PipelineStatisticsQueryFeatureNotEnabled => { + "pipeline statistics query inheritance was requested but the corresponding \ + feature wasn't enabled" + } + } + ) + } +} + +impl From<OomError> for BeginError { + #[inline] + fn from(err: OomError) -> Self { + Self::OomError(err) + } +} + +impl<P> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P> +where + P: CommandPoolBuilderAlloc, +{ + /// Builds the 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()); + } + + 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> +where + P: CommandPoolBuilderAlloc, +{ + /// 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(()) + } + + #[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, + { + 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() + }, + 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()); + } + + 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() + } + } 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()); + } + + 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) + } + + /// 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); + } + + Ok(self) + } + + /// 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) + } + + /// 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()); + } + + check_debug_marker_color(color)?; + + unsafe { + self.inner.debug_marker_insert(name.into(), color); + } + + Ok(self) + } + + /// 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(); + + unsafe { + if !self.queue_family().supports_compute() { + return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); + } + + 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 StateCacherOutcome::NeedChange = + self.state_cacher.bind_compute_pipeline(&pipeline) + { + self.inner.bind_pipeline_compute(pipeline.clone()); + } + + 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) + } + } + + /// 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(); + + unsafe { + if !self.queue_family().supports_compute() { + return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); + } + + 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 StateCacherOutcome::NeedChange = + self.state_cacher.bind_compute_pipeline(&pipeline) + { + self.inner.bind_pipeline_compute(pipeline.clone()); + } + + 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 + + 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) + { + 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( + vb_infos.vertex_count as u32, + vb_infos.instance_count as u32, + 0, + 0, + ); + Ok(self) + } + } + + /// 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) + } + } + + /// 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) + } + } + + /// 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(); + + 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(), + ); + } + + 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, + )?; + + debug_assert!(self.queue_family().supports_graphics()); + + self.inner.draw_indexed_indirect( + indirect_buffer, + requested, + mem::size_of::<DrawIndexedIndirectCommand>() as u32, + )?; + Ok(self) + } + } + + /// 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) + } + } + + /// 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); + } + + Ok(self) + } + } + + /// Adds a command that begins a query. + /// + /// The query will be active until [`end_query`](Self::end_query) is called for the same query. + /// + /// # Safety + /// The query must be unavailable, ensured by calling [`reset_query_pool`](Self::reset_query_pool). + pub unsafe fn begin_query( + &mut self, + query_pool: Arc<QueryPool>, + query: u32, + flags: QueryControlFlags, + ) -> Result<&mut Self, BeginQueryError> { + check_begin_query(self.device(), &query_pool, query, flags)?; + + match query_pool.ty() { + QueryType::Occlusion => { + if !self.queue_family().supports_graphics() { + return Err( + AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into(), + ); + } + } + QueryType::PipelineStatistics(flags) => { + if flags.is_compute() && !self.queue_family().supports_compute() + || flags.is_graphics() && !self.queue_family().supports_graphics() + { + return Err( + AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into(), + ); + } + } + QueryType::Timestamp => unreachable!(), + } + + let ty = query_pool.ty(); + let raw_ty = ty.into(); + let raw_query_pool = query_pool.internal_object(); + if self.query_state.contains_key(&raw_ty) { + return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into()); + } + + // TODO: validity checks + self.inner.begin_query(query_pool, query, flags); + self.query_state.insert( + raw_ty, + QueryState { + query_pool: raw_query_pool, + query, + ty, + flags, + in_subpass: self.render_pass_state.is_some(), + }, + ); + + Ok(self) + } + + /// Adds a command that ends an active query. + pub fn end_query( + &mut self, + query_pool: Arc<QueryPool>, + query: u32, + ) -> Result<&mut Self, EndQueryError> { + unsafe { + check_end_query(self.device(), &query_pool, query)?; + + let raw_ty = query_pool.ty().into(); + let raw_query_pool = query_pool.internal_object(); + if !self.query_state.get(&raw_ty).map_or(false, |state| { + state.query_pool == raw_query_pool && state.query == query + }) { + return Err(AutoCommandBufferBuilderContextError::QueryNotActive.into()); + } + + self.inner.end_query(query_pool, query); + self.query_state.remove(&raw_ty); + } + + Ok(self) + } + + /// Adds a command that writes a timestamp to a timestamp query. + /// + /// # Safety + /// The query must be unavailable, ensured by calling [`reset_query_pool`](Self::reset_query_pool). + pub unsafe fn write_timestamp( + &mut self, + query_pool: Arc<QueryPool>, + query: u32, + stage: PipelineStage, + ) -> Result<&mut Self, WriteTimestampError> { + check_write_timestamp( + self.device(), + self.queue_family(), + &query_pool, + query, + stage, + )?; + + if !(self.queue_family().supports_graphics() + || self.queue_family().supports_compute() + || self.queue_family().explicitly_supports_transfers()) + { + return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into()); + } + + // TODO: validity checks + self.inner.write_timestamp(query_pool, query, stage); + + Ok(self) + } + + /// Adds a command that copies the results of a range of queries to a buffer on the GPU. + /// + /// [`query_pool.ty().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) + } + + /// Adds a command to reset a range of queries on a query pool. + /// + /// The affected queries will be marked as "unavailable" after this command runs, and will no + /// longer return any results. They will be ready to have new results recorded for them. + /// + /// # Safety + /// The queries in the specified range must not be active in another command buffer. + pub unsafe fn reset_query_pool( + &mut self, + query_pool: Arc<QueryPool>, + queries: Range<u32>, + ) -> Result<&mut Self, ResetQueryPoolError> { + self.ensure_outside_render_pass()?; + check_reset_query_pool(self.device(), &query_pool, queries.clone())?; + + let raw_query_pool = query_pool.internal_object(); + if self + .query_state + .values() + .any(|state| state.query_pool == raw_query_pool && queries.contains(&state.query)) + { + return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into()); + } + + // TODO: validity checks + // Do other command buffers actually matter here? Not sure on the Vulkan spec. + self.inner.reset_query_pool(query_pool, queries); + + Ok(self) + } +} + +/// Commands that can only be executed on primary command buffers +impl<P> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P> +where + P: CommandPoolBuilderAlloc, +{ + /// 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)?; + } + + 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()?; + } + + // 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) + } + + // 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()?; + } + + 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); + } + } + _ => (), + } + } + + Ok(()) + } + + #[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); + } + + // Subpasses must be the same. + if render_pass.subpass.index() != render_pass_state.subpass.1 { + return Err(AutoCommandBufferBuilderContextError::WrongSubpassIndex); + } + + // 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); + } + + // 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); + } + } + + 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); + } + + debug_assert!(self.queue_family().supports_graphics()); + + self.inner.next_subpass(contents); + Ok(self) + } + } +} + +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, + ); + } +} + +// 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); + } + + if let Some(reference) = dynamic.reference { + destination.set_stencil_reference(StencilFaces::Front, reference.front); + destination.set_stencil_reference(StencilFaces::Back, reference.back); + } +} + +// 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); + } + sets_binder.submit(pipeline_bind_point, pipeline_layout.clone(), first_binding)?; + Ok(()) +} + +pub struct PrimaryAutoCommandBuffer<P = StandardCommandPoolAlloc> { + inner: SyncCommandBuffer, + pool_alloc: P, // Safety: must be dropped after `inner` + + // Tracks usage of the command buffer on the GPU. + submit_state: SubmitState, +} + +unsafe impl<P> DeviceOwned for PrimaryAutoCommandBuffer<P> { + #[inline] + 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 => (), + }; + + Err(err) + } + + #[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 => (), + }; + } + + #[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) + } + + #[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) + } +} + +pub struct SecondaryAutoCommandBuffer<P = StandardCommandPoolAlloc> { + inner: SyncCommandBuffer, + pool_alloc: P, // Safety: must be dropped after `inner` + inheritance: CommandBufferInheritance<Box<dyn FramebufferAbstract + Send + Sync>>, + + // Tracks usage of the command buffer on the GPU. + submit_state: SubmitState, +} + +unsafe impl<P> DeviceOwned for SecondaryAutoCommandBuffer<P> { + #[inline] + fn device(&self) -> &Arc<Device> { + self.inner.device() + } +} + +unsafe impl<P> SecondaryCommandBuffer for SecondaryAutoCommandBuffer<P> { + #[inline] + fn inner(&self) -> &UnsafeCommandBuffer { + self.inner.as_ref() + } + + #[inline] + fn lock_record(&self) -> Result<(), CommandBufferExecError> { + match self.submit_state { + SubmitState::OneTime { + ref already_submitted, + } => { + let was_already_submitted = already_submitted.swap(true, Ordering::SeqCst); + if was_already_submitted { + return Err(CommandBufferExecError::OneTimeSubmitAlreadySubmitted); + } + } + SubmitState::ExclusiveUse { ref in_use } => { + let already_in_use = in_use.swap(true, Ordering::SeqCst); + if already_in_use { + return Err(CommandBufferExecError::ExclusiveAlreadyInUse); + } + } + SubmitState::Concurrent => (), + }; + + Ok(()) + } + + #[inline] + unsafe fn unlock(&self) { + match self.submit_state { + SubmitState::OneTime { + ref already_submitted, + } => { + debug_assert!(already_submitted.load(Ordering::SeqCst)); + } + SubmitState::ExclusiveUse { ref in_use } => { + let old_val = in_use.swap(false, Ordering::SeqCst); + debug_assert!(old_val); + } + SubmitState::Concurrent => (), + }; + } + + fn 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) + } +} + +// Whether the command buffer can be submitted. +#[derive(Debug)] +enum SubmitState { + // The command buffer was created with the "SimultaneousUse" flag. Can always be submitted at + // any time. + Concurrent, + + // The command buffer can only be submitted once simultaneously. + ExclusiveUse { + // True if the command buffer is current in use by the GPU. + in_use: AtomicBool, + }, + + // The command buffer can only ever be submitted once. + OneTime { + // True if the command buffer has already been submitted once and can be no longer be + // submitted. + already_submitted: AtomicBool, + }, +} + +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; + + #[test] + fn copy_buffer_dimensions() { + let instance = instance!(); + + let phys = match PhysicalDevice::enumerate(&instance).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)), + ) + .unwrap(); + + let queue = queues.next().unwrap(); + + let source = CpuAccessibleBuffer::from_iter( + device.clone(), + BufferUsage::all(), + true, + [1_u32, 2].iter().copied(), + ) + .unwrap(); + + let destination = CpuAccessibleBuffer::from_iter( + device.clone(), + BufferUsage::all(), + true, + [0_u32, 10, 20, 3, 4].iter().copied(), + ) + .unwrap(); + + let mut cbb = AutoCommandBufferBuilder::primary( + device.clone(), + queue.family(), + CommandBufferUsage::OneTimeSubmit, + ) + .unwrap(); + + cbb.copy_buffer_dimensions(source.clone(), 0, destination.clone(), 1, 2) + .unwrap(); + + let cb = cbb.build().unwrap(); + + let future = cb + .execute(queue.clone()) + .unwrap() + .then_signal_fence_and_flush() + .unwrap(); + future.wait(None).unwrap(); + + let result = destination.read().unwrap(); + + assert_eq!(*result, [0_u32, 1, 2, 3, 4]); + } + + #[test] + fn secondary_nonconcurrent_conflict() { + let (device, queue) = gfx_dev_and_queue!(); + + // Make a secondary CB that doesn't support simultaneous use. + let builder = AutoCommandBufferBuilder::secondary_compute( + device.clone(), + queue.family(), + CommandBufferUsage::MultipleSubmit, + ) + .unwrap(); + let secondary = Arc::new(builder.build().unwrap()); + + { + let mut builder = AutoCommandBufferBuilder::primary( + device.clone(), + queue.family(), + CommandBufferUsage::SimultaneousUse, + ) + .unwrap(); + + // Add the secondary a first time + builder.execute_commands(secondary.clone()).unwrap(); + + // Recording the same non-concurrent secondary command buffer twice into the same + // primary is an error. + assert!(matches!( + builder.execute_commands(secondary.clone()), + Err(ExecuteCommandsError::SyncCommandBufferBuilderError( + SyncCommandBufferBuilderError::ExecError( + CommandBufferExecError::ExclusiveAlreadyInUse + ) + )) + )); + } + + { + let mut builder = AutoCommandBufferBuilder::primary( + device.clone(), + queue.family(), + CommandBufferUsage::SimultaneousUse, + ) + .unwrap(); + builder.execute_commands(secondary.clone()).unwrap(); + let cb1 = builder.build().unwrap(); + + let mut builder = AutoCommandBufferBuilder::primary( + device.clone(), + queue.family(), + CommandBufferUsage::SimultaneousUse, + ) + .unwrap(); + + // Recording the same non-concurrent secondary command buffer into multiple + // primaries is an error. + assert!(matches!( + builder.execute_commands(secondary.clone()), + Err(ExecuteCommandsError::SyncCommandBufferBuilderError( + SyncCommandBufferBuilderError::ExecError( + CommandBufferExecError::ExclusiveAlreadyInUse + ) + )) + )); + + std::mem::drop(cb1); + + // Now that the first cb is dropped, we should be able to record. + builder.execute_commands(secondary.clone()).unwrap(); + } + } +} |