// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. use 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`) /// 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 { 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>>, // 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, // If any queries are active, this hashmap contains their state. query_state: FnvHashMap, _data: PhantomData, } // The state of the current render pass, specifying the pass, subpass index and its intended contents. struct RenderPassState { subpass: (Arc, 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 { /// Starts building a primary command buffer. #[inline] pub fn primary( device: Arc, queue_family: QueueFamily, usage: CommandBufferUsage, ) -> Result< AutoCommandBufferBuilder, OomError, > { AutoCommandBufferBuilder::with_level( device, queue_family, usage, CommandBufferLevel::primary(), ) } } impl AutoCommandBufferBuilder { /// Starts building a secondary compute command buffer. #[inline] pub fn secondary_compute( device: Arc, queue_family: QueueFamily, usage: CommandBufferUsage, ) -> Result< AutoCommandBufferBuilder, 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, queue_family: QueueFamily, usage: CommandBufferUsage, occlusion_query: Option, query_statistics_flags: QueryPipelineStatisticFlags, ) -> Result< AutoCommandBufferBuilder, 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, queue_family: QueueFamily, usage: CommandBufferUsage, subpass: Subpass, ) -> Result< AutoCommandBufferBuilder, OomError, > { let level = CommandBufferLevel::Secondary(CommandBufferInheritance { render_pass: Some(CommandBufferInheritanceRenderPass { subpass, framebuffer: None::>>, }), 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, queue_family: QueueFamily, usage: CommandBufferUsage, subpass: Subpass, occlusion_query: Option, query_statistics_flags: QueryPipelineStatisticFlags, ) -> Result< AutoCommandBufferBuilder, 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::>>, }), occlusion_query, query_statistics_flags, }); Ok(AutoCommandBufferBuilder::with_level( device, queue_family, usage, level, )?) } } impl AutoCommandBufferBuilder { // Actual constructor. Private. fn with_level( device: Arc, queue_family: QueueFamily, usage: CommandBufferUsage, level: CommandBufferLevel, ) -> Result, 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 for BeginError { #[inline] fn from(err: OomError) -> Self { Self::OomError(err) } } impl

AutoCommandBufferBuilder, P> where P: CommandPoolBuilderAlloc, { /// Builds the command buffer. #[inline] pub fn build(self) -> Result, 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

AutoCommandBufferBuilder, P> where P: CommandPoolBuilderAlloc, { /// Builds the command buffer. #[inline] pub fn build(self) -> Result, 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 AutoCommandBufferBuilder { #[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( &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( &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( &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( &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( &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( &mut self, source: S, destination: D, ) -> Result<&mut Self, CopyBufferError> where S: TypedBufferAccess + Send + Sync + 'static, D: TypedBufferAccess + 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( &mut self, source: S, source_offset: DeviceSize, destination: D, destination_offset: DeviceSize, count: DeviceSize, ) -> Result<&mut Self, CopyBufferError> where S: TypedBufferAccess + Send + Sync + 'static, D: TypedBufferAccess + 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::() 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( &mut self, source: S, destination: D, ) -> Result<&mut Self, CopyBufferImageError> where S: TypedBufferAccess + 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( &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 + 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( &mut self, source: S, destination: D, ) -> Result<&mut Self, CopyBufferImageError> where S: ImageAccess + Send + Sync + 'static, D: TypedBufferAccess + 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( &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 + 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( &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( &mut self, indirect_buffer: Inb, pipeline: Cp, descriptor_sets: S, push_constants: Pc, ) -> Result<&mut Self, DispatchIndirectError> where Inb: BufferAccess + TypedBufferAccess + 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( &mut self, pipeline: Gp, dynamic: &DynamicState, vertex_buffers: V, descriptor_sets: S, push_constants: Pc, ) -> Result<&mut Self, DrawError> where Gp: GraphicsPipelineAbstract + VertexSource + 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( &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 + Send + Sync + 'static + Clone, // TODO: meh for Clone S: DescriptorSetsCollection, Inb: BufferAccess + TypedBufferAccess + 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::() 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( &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 + Send + Sync + 'static + Clone, // TODO: meh for Clone S: DescriptorSetsCollection, Ib: BufferAccess + TypedBufferAccess + 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( &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 + Send + Sync + 'static + Clone, // TODO: meh for Clone S: DescriptorSetsCollection, Ib: BufferAccess + TypedBufferAccess + Send + Sync + 'static, Inb: BufferAccess + TypedBufferAccess + 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::() 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(&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( &mut self, buffer: B, data: Dd, ) -> Result<&mut Self, UpdateBufferError> where B: TypedBufferAccess + Send + Sync + 'static, D: ?Sized, Dd: SafeDeref + 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, 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, 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, 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( &mut self, query_pool: Arc, queries: Range, destination: D, flags: QueryResultFlags, ) -> Result<&mut Self, CopyQueryPoolResultsError> where D: BufferAccess + TypedBufferAccess + 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, queries: Range, ) -> 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

AutoCommandBufferBuilder, 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( &mut self, framebuffer: F, contents: SubpassContents, clear_values: I, ) -> Result<&mut Self, BeginRenderPassError> where F: FramebufferAbstract + Clone + Send + Sync + 'static, I: IntoIterator, { 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::>().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( &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( &mut self, command_buffers: Vec, ) -> 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( &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

AutoCommandBufferBuilder, P> where P: CommandPoolBuilderAlloc { } unsafe impl DeviceOwned for AutoCommandBufferBuilder { #[inline] fn device(&self) -> &Arc { self.inner.device() } } // Shortcut function to set the push constants. unsafe fn set_push_constants( destination: &mut SyncCommandBufferBuilder, pipeline_layout: &Arc, 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::>().into_iter()); // TODO: don't collect } if let Some(ref scissors) = dynamic.scissors { destination.set_scissor(0, scissors.iter().cloned().collect::>().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>, ) -> 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, descriptor_sets: Vec, ) -> 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

{ 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

DeviceOwned for PrimaryAutoCommandBuffer

{ #[inline] fn device(&self) -> &Arc { self.inner.device() } } unsafe impl

PrimaryCommandBuffer for PrimaryAutoCommandBuffer

{ #[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, 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, AccessCheckError> { self.inner .check_image_access(image, layout, exclusive, queue) } } pub struct SecondaryAutoCommandBuffer

{ inner: SyncCommandBuffer, pool_alloc: P, // Safety: must be dropped after `inner` inheritance: CommandBufferInheritance>, // Tracks usage of the command buffer on the GPU. submit_state: SubmitState, } unsafe impl

DeviceOwned for SecondaryAutoCommandBuffer

{ #[inline] fn device(&self) -> &Arc { self.inner.device() } } unsafe impl

SecondaryCommandBuffer for SecondaryAutoCommandBuffer

{ #[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(); } } }