diff options
Diffstat (limited to 'src/command_buffer/auto.rs')
-rw-r--r-- | src/command_buffer/auto.rs | 2867 |
1 files changed, 0 insertions, 2867 deletions
diff --git a/src/command_buffer/auto.rs b/src/command_buffer/auto.rs deleted file mode 100644 index 64d0d2d..0000000 --- a/src/command_buffer/auto.rs +++ /dev/null @@ -1,2867 +0,0 @@ -// 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(); - } - } -} |