aboutsummaryrefslogtreecommitdiff
path: root/src/command_buffer/auto.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/command_buffer/auto.rs')
-rw-r--r--src/command_buffer/auto.rs2867
1 files changed, 2867 insertions, 0 deletions
diff --git a/src/command_buffer/auto.rs b/src/command_buffer/auto.rs
new file mode 100644
index 0000000..64d0d2d
--- /dev/null
+++ b/src/command_buffer/auto.rs
@@ -0,0 +1,2867 @@
+// Copyright (c) 2016 The vulkano developers
+// Licensed under the Apache License, Version 2.0
+// <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
+// at your option. All files in the project carrying such
+// notice may not be copied, modified, or distributed except
+// according to those terms.
+
+use crate::buffer::BufferAccess;
+use crate::buffer::TypedBufferAccess;
+use crate::command_buffer::pool::standard::StandardCommandPoolAlloc;
+use crate::command_buffer::pool::standard::StandardCommandPoolBuilder;
+use crate::command_buffer::pool::CommandPool;
+use crate::command_buffer::pool::CommandPoolBuilderAlloc;
+use crate::command_buffer::synced::SyncCommandBuffer;
+use crate::command_buffer::synced::SyncCommandBufferBuilder;
+use crate::command_buffer::synced::SyncCommandBufferBuilderError;
+use crate::command_buffer::sys::UnsafeCommandBuffer;
+use crate::command_buffer::sys::UnsafeCommandBufferBuilderBufferImageCopy;
+use crate::command_buffer::sys::UnsafeCommandBufferBuilderColorImageClear;
+use crate::command_buffer::sys::UnsafeCommandBufferBuilderImageBlit;
+use crate::command_buffer::sys::UnsafeCommandBufferBuilderImageCopy;
+use crate::command_buffer::validity::*;
+use crate::command_buffer::CommandBufferExecError;
+use crate::command_buffer::CommandBufferInheritance;
+use crate::command_buffer::CommandBufferInheritanceRenderPass;
+use crate::command_buffer::CommandBufferLevel;
+use crate::command_buffer::CommandBufferUsage;
+use crate::command_buffer::DispatchIndirectCommand;
+use crate::command_buffer::DrawIndexedIndirectCommand;
+use crate::command_buffer::DrawIndirectCommand;
+use crate::command_buffer::DynamicState;
+use crate::command_buffer::ImageUninitializedSafe;
+use crate::command_buffer::PrimaryCommandBuffer;
+use crate::command_buffer::SecondaryCommandBuffer;
+use crate::command_buffer::StateCacher;
+use crate::command_buffer::StateCacherOutcome;
+use crate::command_buffer::SubpassContents;
+use crate::descriptor_set::DescriptorSetWithOffsets;
+use crate::descriptor_set::DescriptorSetsCollection;
+use crate::device::physical::QueueFamily;
+use crate::device::Device;
+use crate::device::DeviceOwned;
+use crate::device::Queue;
+use crate::format::ClearValue;
+use crate::format::FormatTy;
+use crate::format::Pixel;
+use crate::image::ImageAccess;
+use crate::image::ImageAspect;
+use crate::image::ImageAspects;
+use crate::image::ImageLayout;
+use crate::pipeline::depth_stencil::StencilFaces;
+use crate::pipeline::input_assembly::Index;
+use crate::pipeline::layout::PipelineLayout;
+use crate::pipeline::vertex::VertexSource;
+use crate::pipeline::ComputePipelineAbstract;
+use crate::pipeline::GraphicsPipelineAbstract;
+use crate::pipeline::PipelineBindPoint;
+use crate::query::QueryControlFlags;
+use crate::query::QueryPipelineStatisticFlags;
+use crate::query::QueryPool;
+use crate::query::QueryResultElement;
+use crate::query::QueryResultFlags;
+use crate::query::QueryType;
+use crate::render_pass::Framebuffer;
+use crate::render_pass::FramebufferAbstract;
+use crate::render_pass::LoadOp;
+use crate::render_pass::RenderPass;
+use crate::render_pass::Subpass;
+use crate::sampler::Filter;
+use crate::sync::AccessCheckError;
+use crate::sync::AccessFlags;
+use crate::sync::GpuFuture;
+use crate::sync::PipelineMemoryAccess;
+use crate::sync::PipelineStage;
+use crate::sync::PipelineStages;
+use crate::DeviceSize;
+use crate::VulkanObject;
+use crate::{OomError, SafeDeref};
+use fnv::FnvHashMap;
+use std::error;
+use std::ffi::CStr;
+use std::fmt;
+use std::iter;
+use std::marker::PhantomData;
+use std::mem;
+use std::ops::Range;
+use std::slice;
+use std::sync::atomic::AtomicBool;
+use std::sync::atomic::Ordering;
+use std::sync::Arc;
+
+/// Note that command buffers allocated from the default command pool (`Arc<StandardCommandPool>`)
+/// don't implement the `Send` and `Sync` traits. If you use this pool, then the
+/// `AutoCommandBufferBuilder` will not implement `Send` and `Sync` either. Once a command buffer
+/// is built, however, it *does* implement `Send` and `Sync`.
+pub struct AutoCommandBufferBuilder<L, P = StandardCommandPoolBuilder> {
+ inner: SyncCommandBufferBuilder,
+ pool_builder_alloc: P, // Safety: must be dropped after `inner`
+ state_cacher: StateCacher,
+
+ // The queue family that this command buffer is being created for.
+ queue_family_id: u32,
+
+ // The inheritance for secondary command buffers.
+ inheritance: Option<CommandBufferInheritance<Box<dyn FramebufferAbstract + Send + Sync>>>,
+
+ // Usage flags passed when creating the command buffer.
+ usage: CommandBufferUsage,
+
+ // If we're inside a render pass, contains the render pass state.
+ render_pass_state: Option<RenderPassState>,
+
+ // If any queries are active, this hashmap contains their state.
+ query_state: FnvHashMap<ash::vk::QueryType, QueryState>,
+
+ _data: PhantomData<L>,
+}
+
+// The state of the current render pass, specifying the pass, subpass index and its intended contents.
+struct RenderPassState {
+ subpass: (Arc<RenderPass>, u32),
+ contents: SubpassContents,
+ framebuffer: ash::vk::Framebuffer, // Always null for secondary command buffers
+}
+
+// The state of an active query.
+struct QueryState {
+ query_pool: ash::vk::QueryPool,
+ query: u32,
+ ty: QueryType,
+ flags: QueryControlFlags,
+ in_subpass: bool,
+}
+
+impl AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, StandardCommandPoolBuilder> {
+ /// Starts building a primary command buffer.
+ #[inline]
+ pub fn primary(
+ device: Arc<Device>,
+ queue_family: QueueFamily,
+ usage: CommandBufferUsage,
+ ) -> Result<
+ AutoCommandBufferBuilder<PrimaryAutoCommandBuffer, StandardCommandPoolBuilder>,
+ OomError,
+ > {
+ AutoCommandBufferBuilder::with_level(
+ device,
+ queue_family,
+ usage,
+ CommandBufferLevel::primary(),
+ )
+ }
+}
+
+impl AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder> {
+ /// Starts building a secondary compute command buffer.
+ #[inline]
+ pub fn secondary_compute(
+ device: Arc<Device>,
+ queue_family: QueueFamily,
+ usage: CommandBufferUsage,
+ ) -> Result<
+ AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
+ OomError,
+ > {
+ let level = CommandBufferLevel::secondary(None, QueryPipelineStatisticFlags::none());
+ AutoCommandBufferBuilder::with_level(device, queue_family, usage, level)
+ }
+
+ /// Same as `secondary_compute`, but allows specifying how queries are being inherited.
+ #[inline]
+ pub fn secondary_compute_inherit_queries(
+ device: Arc<Device>,
+ queue_family: QueueFamily,
+ usage: CommandBufferUsage,
+ occlusion_query: Option<QueryControlFlags>,
+ query_statistics_flags: QueryPipelineStatisticFlags,
+ ) -> Result<
+ AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
+ BeginError,
+ > {
+ if occlusion_query.is_some() && !device.enabled_features().inherited_queries {
+ return Err(BeginError::InheritedQueriesFeatureNotEnabled);
+ }
+
+ if query_statistics_flags.count() > 0
+ && !device.enabled_features().pipeline_statistics_query
+ {
+ return Err(BeginError::PipelineStatisticsQueryFeatureNotEnabled);
+ }
+
+ let level = CommandBufferLevel::secondary(occlusion_query, query_statistics_flags);
+ Ok(AutoCommandBufferBuilder::with_level(
+ device,
+ queue_family,
+ usage,
+ level,
+ )?)
+ }
+
+ /// Starts building a secondary graphics command buffer.
+ #[inline]
+ pub fn secondary_graphics(
+ device: Arc<Device>,
+ queue_family: QueueFamily,
+ usage: CommandBufferUsage,
+ subpass: Subpass,
+ ) -> Result<
+ AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
+ OomError,
+ > {
+ let level = CommandBufferLevel::Secondary(CommandBufferInheritance {
+ render_pass: Some(CommandBufferInheritanceRenderPass {
+ subpass,
+ framebuffer: None::<Arc<Framebuffer<()>>>,
+ }),
+ occlusion_query: None,
+ query_statistics_flags: QueryPipelineStatisticFlags::none(),
+ });
+
+ AutoCommandBufferBuilder::with_level(device, queue_family, usage, level)
+ }
+
+ /// Same as `secondary_graphics`, but allows specifying how queries are being inherited.
+ #[inline]
+ pub fn secondary_graphics_inherit_queries(
+ device: Arc<Device>,
+ queue_family: QueueFamily,
+ usage: CommandBufferUsage,
+ subpass: Subpass,
+ occlusion_query: Option<QueryControlFlags>,
+ query_statistics_flags: QueryPipelineStatisticFlags,
+ ) -> Result<
+ AutoCommandBufferBuilder<SecondaryAutoCommandBuffer, StandardCommandPoolBuilder>,
+ BeginError,
+ > {
+ if occlusion_query.is_some() && !device.enabled_features().inherited_queries {
+ return Err(BeginError::InheritedQueriesFeatureNotEnabled);
+ }
+
+ if query_statistics_flags.count() > 0
+ && !device.enabled_features().pipeline_statistics_query
+ {
+ return Err(BeginError::PipelineStatisticsQueryFeatureNotEnabled);
+ }
+
+ let level = CommandBufferLevel::Secondary(CommandBufferInheritance {
+ render_pass: Some(CommandBufferInheritanceRenderPass {
+ subpass,
+ framebuffer: None::<Arc<Framebuffer<()>>>,
+ }),
+ occlusion_query,
+ query_statistics_flags,
+ });
+
+ Ok(AutoCommandBufferBuilder::with_level(
+ device,
+ queue_family,
+ usage,
+ level,
+ )?)
+ }
+}
+
+impl<L> AutoCommandBufferBuilder<L, StandardCommandPoolBuilder> {
+ // Actual constructor. Private.
+ fn with_level<F>(
+ device: Arc<Device>,
+ queue_family: QueueFamily,
+ usage: CommandBufferUsage,
+ level: CommandBufferLevel<F>,
+ ) -> Result<AutoCommandBufferBuilder<L, StandardCommandPoolBuilder>, OomError>
+ where
+ F: FramebufferAbstract + Clone + Send + Sync + 'static,
+ {
+ let (inheritance, render_pass_state) = match &level {
+ CommandBufferLevel::Primary => (None, None),
+ CommandBufferLevel::Secondary(inheritance) => {
+ let (render_pass, render_pass_state) = match inheritance.render_pass.as_ref() {
+ Some(CommandBufferInheritanceRenderPass {
+ subpass,
+ framebuffer,
+ }) => {
+ let render_pass = CommandBufferInheritanceRenderPass {
+ subpass: Subpass::from(subpass.render_pass().clone(), subpass.index())
+ .unwrap(),
+ framebuffer: framebuffer
+ .as_ref()
+ .map(|f| Box::new(f.clone()) as Box<_>),
+ };
+ let render_pass_state = RenderPassState {
+ subpass: (subpass.render_pass().clone(), subpass.index()),
+ contents: SubpassContents::Inline,
+ framebuffer: ash::vk::Framebuffer::null(), // Only needed for primary command buffers
+ };
+ (Some(render_pass), Some(render_pass_state))
+ }
+ None => (None, None),
+ };
+
+ (
+ Some(CommandBufferInheritance {
+ render_pass,
+ occlusion_query: inheritance.occlusion_query,
+ query_statistics_flags: inheritance.query_statistics_flags,
+ }),
+ render_pass_state,
+ )
+ }
+ };
+
+ unsafe {
+ let pool = Device::standard_command_pool(&device, queue_family);
+ let pool_builder_alloc = pool
+ .alloc(!matches!(level, CommandBufferLevel::Primary), 1)?
+ .next()
+ .expect("Requested one command buffer from the command pool, but got zero.");
+ let inner = SyncCommandBufferBuilder::new(pool_builder_alloc.inner(), level, usage)?;
+
+ Ok(AutoCommandBufferBuilder {
+ inner,
+ pool_builder_alloc,
+ state_cacher: StateCacher::new(),
+ queue_family_id: queue_family.id(),
+ render_pass_state,
+ query_state: FnvHashMap::default(),
+ inheritance,
+ usage,
+ _data: PhantomData,
+ })
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum BeginError {
+ /// Occlusion query inheritance was requested, but the `inherited_queries` feature was not enabled.
+ InheritedQueriesFeatureNotEnabled,
+ /// Not enough memory.
+ OomError(OomError),
+ /// Pipeline statistics query inheritance was requested, but the `pipeline_statistics_query` feature was not enabled.
+ PipelineStatisticsQueryFeatureNotEnabled,
+}
+
+impl error::Error for BeginError {
+ #[inline]
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match *self {
+ Self::OomError(ref err) => Some(err),
+ _ => None,
+ }
+ }
+}
+
+impl fmt::Display for BeginError {
+ #[inline]
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ write!(
+ fmt,
+ "{}",
+ match *self {
+ Self::InheritedQueriesFeatureNotEnabled => {
+ "occlusion query inheritance was requested but the corresponding feature \
+ wasn't enabled"
+ }
+ Self::OomError(_) => "not enough memory available",
+ Self::PipelineStatisticsQueryFeatureNotEnabled => {
+ "pipeline statistics query inheritance was requested but the corresponding \
+ feature wasn't enabled"
+ }
+ }
+ )
+ }
+}
+
+impl From<OomError> for BeginError {
+ #[inline]
+ fn from(err: OomError) -> Self {
+ Self::OomError(err)
+ }
+}
+
+impl<P> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P>
+where
+ P: CommandPoolBuilderAlloc,
+{
+ /// Builds the command buffer.
+ #[inline]
+ pub fn build(self) -> Result<PrimaryAutoCommandBuffer<P::Alloc>, BuildError> {
+ if self.render_pass_state.is_some() {
+ return Err(AutoCommandBufferBuilderContextError::ForbiddenInsideRenderPass.into());
+ }
+
+ if !self.query_state.is_empty() {
+ return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into());
+ }
+
+ let submit_state = match self.usage {
+ CommandBufferUsage::MultipleSubmit => SubmitState::ExclusiveUse {
+ in_use: AtomicBool::new(false),
+ },
+ CommandBufferUsage::SimultaneousUse => SubmitState::Concurrent,
+ CommandBufferUsage::OneTimeSubmit => SubmitState::OneTime {
+ already_submitted: AtomicBool::new(false),
+ },
+ };
+
+ Ok(PrimaryAutoCommandBuffer {
+ inner: self.inner.build()?,
+ pool_alloc: self.pool_builder_alloc.into_alloc(),
+ submit_state,
+ })
+ }
+}
+
+impl<P> AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<P::Alloc>, P>
+where
+ P: CommandPoolBuilderAlloc,
+{
+ /// Builds the command buffer.
+ #[inline]
+ pub fn build(self) -> Result<SecondaryAutoCommandBuffer<P::Alloc>, BuildError> {
+ if !self.query_state.is_empty() {
+ return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into());
+ }
+
+ let submit_state = match self.usage {
+ CommandBufferUsage::MultipleSubmit => SubmitState::ExclusiveUse {
+ in_use: AtomicBool::new(false),
+ },
+ CommandBufferUsage::SimultaneousUse => SubmitState::Concurrent,
+ CommandBufferUsage::OneTimeSubmit => SubmitState::OneTime {
+ already_submitted: AtomicBool::new(false),
+ },
+ };
+
+ Ok(SecondaryAutoCommandBuffer {
+ inner: self.inner.build()?,
+ pool_alloc: self.pool_builder_alloc.into_alloc(),
+ inheritance: self.inheritance.unwrap(),
+ submit_state,
+ })
+ }
+}
+
+impl<L, P> AutoCommandBufferBuilder<L, P> {
+ #[inline]
+ fn ensure_outside_render_pass(&self) -> Result<(), AutoCommandBufferBuilderContextError> {
+ if self.render_pass_state.is_some() {
+ return Err(AutoCommandBufferBuilderContextError::ForbiddenInsideRenderPass);
+ }
+
+ Ok(())
+ }
+
+ #[inline]
+ fn ensure_inside_render_pass_inline<Gp>(
+ &self,
+ pipeline: &Gp,
+ ) -> Result<(), AutoCommandBufferBuilderContextError>
+ where
+ Gp: ?Sized + GraphicsPipelineAbstract,
+ {
+ let render_pass_state = self
+ .render_pass_state
+ .as_ref()
+ .ok_or(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass)?;
+
+ // Subpass must be for inline commands
+ if render_pass_state.contents != SubpassContents::Inline {
+ return Err(AutoCommandBufferBuilderContextError::WrongSubpassType);
+ }
+
+ // Subpasses must be the same.
+ if pipeline.subpass().index() != render_pass_state.subpass.1 {
+ return Err(AutoCommandBufferBuilderContextError::WrongSubpassIndex);
+ }
+
+ // Render passes must be compatible.
+ if !pipeline
+ .subpass()
+ .render_pass()
+ .desc()
+ .is_compatible_with_desc(&render_pass_state.subpass.0.desc())
+ {
+ return Err(AutoCommandBufferBuilderContextError::IncompatibleRenderPass);
+ }
+
+ Ok(())
+ }
+
+ #[inline]
+ fn queue_family(&self) -> QueueFamily {
+ self.device()
+ .physical_device()
+ .queue_family_by_id(self.queue_family_id)
+ .unwrap()
+ }
+
+ /// Adds a command that copies an image to another.
+ ///
+ /// Copy operations have several restrictions:
+ ///
+ /// - Copy operations are only allowed on queue families that support transfer, graphics, or
+ /// compute operations.
+ /// - The number of samples in the source and destination images must be equal.
+ /// - The size of the uncompressed element format of the source image must be equal to the
+ /// compressed element format of the destination.
+ /// - If you copy between depth, stencil or depth-stencil images, the format of both images
+ /// must match exactly.
+ /// - For two-dimensional images, the Z coordinate must be 0 for the image offsets and 1 for
+ /// the extent. Same for the Y coordinate for one-dimensional images.
+ /// - For non-array images, the base array layer must be 0 and the number of layers must be 1.
+ ///
+ /// If `layer_count` is greater than 1, the copy will happen between each individual layer as
+ /// if they were separate images.
+ ///
+ /// # Panic
+ ///
+ /// - Panics if the source or the destination was not created with `device`.
+ ///
+ pub fn copy_image<S, D>(
+ &mut self,
+ source: S,
+ source_offset: [i32; 3],
+ source_base_array_layer: u32,
+ source_mip_level: u32,
+ destination: D,
+ destination_offset: [i32; 3],
+ destination_base_array_layer: u32,
+ destination_mip_level: u32,
+ extent: [u32; 3],
+ layer_count: u32,
+ ) -> Result<&mut Self, CopyImageError>
+ where
+ S: ImageAccess + Send + Sync + 'static,
+ D: ImageAccess + Send + Sync + 'static,
+ {
+ unsafe {
+ self.ensure_outside_render_pass()?;
+
+ check_copy_image(
+ self.device(),
+ &source,
+ source_offset,
+ source_base_array_layer,
+ source_mip_level,
+ &destination,
+ destination_offset,
+ destination_base_array_layer,
+ destination_mip_level,
+ extent,
+ layer_count,
+ )?;
+
+ let copy = UnsafeCommandBufferBuilderImageCopy {
+ // TODO: Allowing choosing a subset of the image aspects, but note that if color
+ // is included, neither depth nor stencil may.
+ aspects: ImageAspects {
+ color: source.has_color(),
+ depth: !source.has_color() && source.has_depth() && destination.has_depth(),
+ stencil: !source.has_color()
+ && source.has_stencil()
+ && destination.has_stencil(),
+ ..ImageAspects::none()
+ },
+ source_mip_level,
+ destination_mip_level,
+ source_base_array_layer,
+ destination_base_array_layer,
+ layer_count,
+ source_offset,
+ destination_offset,
+ extent,
+ };
+
+ // TODO: Allow choosing layouts, but note that only Transfer*Optimal and General are
+ // valid.
+ self.inner.copy_image(
+ source,
+ ImageLayout::TransferSrcOptimal,
+ destination,
+ ImageLayout::TransferDstOptimal,
+ iter::once(copy),
+ )?;
+ Ok(self)
+ }
+ }
+
+ /// Adds a command that blits an image to another.
+ ///
+ /// A *blit* is similar to an image copy operation, except that the portion of the image that
+ /// is transferred can be resized. You choose an area of the source and an area of the
+ /// destination, and the implementation will resize the area of the source so that it matches
+ /// the size of the area of the destination before writing it.
+ ///
+ /// Blit operations have several restrictions:
+ ///
+ /// - Blit operations are only allowed on queue families that support graphics operations.
+ /// - The format of the source and destination images must support blit operations, which
+ /// depends on the Vulkan implementation. Vulkan guarantees that some specific formats must
+ /// always be supported. See tables 52 to 61 of the specifications.
+ /// - Only single-sampled images are allowed.
+ /// - You can only blit between two images whose formats belong to the same type. The types
+ /// are: floating-point, signed integers, unsigned integers, depth-stencil.
+ /// - If you blit between depth, stencil or depth-stencil images, the format of both images
+ /// must match exactly.
+ /// - If you blit between depth, stencil or depth-stencil images, only the `Nearest` filter is
+ /// allowed.
+ /// - For two-dimensional images, the Z coordinate must be 0 for the top-left offset and 1 for
+ /// the bottom-right offset. Same for the Y coordinate for one-dimensional images.
+ /// - For non-array images, the base array layer must be 0 and the number of layers must be 1.
+ ///
+ /// If `layer_count` is greater than 1, the blit will happen between each individual layer as
+ /// if they were separate images.
+ ///
+ /// # Panic
+ ///
+ /// - Panics if the source or the destination was not created with `device`.
+ ///
+ pub fn blit_image<S, D>(
+ &mut self,
+ source: S,
+ source_top_left: [i32; 3],
+ source_bottom_right: [i32; 3],
+ source_base_array_layer: u32,
+ source_mip_level: u32,
+ destination: D,
+ destination_top_left: [i32; 3],
+ destination_bottom_right: [i32; 3],
+ destination_base_array_layer: u32,
+ destination_mip_level: u32,
+ layer_count: u32,
+ filter: Filter,
+ ) -> Result<&mut Self, BlitImageError>
+ where
+ S: ImageAccess + Send + Sync + 'static,
+ D: ImageAccess + Send + Sync + 'static,
+ {
+ unsafe {
+ if !self.queue_family().supports_graphics() {
+ return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
+ }
+
+ self.ensure_outside_render_pass()?;
+
+ check_blit_image(
+ self.device(),
+ &source,
+ source_top_left,
+ source_bottom_right,
+ source_base_array_layer,
+ source_mip_level,
+ &destination,
+ destination_top_left,
+ destination_bottom_right,
+ destination_base_array_layer,
+ destination_mip_level,
+ layer_count,
+ filter,
+ )?;
+
+ let blit = UnsafeCommandBufferBuilderImageBlit {
+ // TODO:
+ aspects: if source.has_color() {
+ ImageAspects {
+ color: true,
+ ..ImageAspects::none()
+ }
+ } else {
+ unimplemented!()
+ },
+ source_mip_level,
+ destination_mip_level,
+ source_base_array_layer,
+ destination_base_array_layer,
+ layer_count,
+ source_top_left,
+ source_bottom_right,
+ destination_top_left,
+ destination_bottom_right,
+ };
+
+ self.inner.blit_image(
+ source,
+ ImageLayout::TransferSrcOptimal,
+ destination, // TODO: let choose layout
+ ImageLayout::TransferDstOptimal,
+ iter::once(blit),
+ filter,
+ )?;
+ Ok(self)
+ }
+ }
+
+ /// Adds a command that clears all the layers and mipmap levels of a color image with a
+ /// specific value.
+ ///
+ /// # Panic
+ ///
+ /// Panics if `color` is not a color value.
+ ///
+ pub fn clear_color_image<I>(
+ &mut self,
+ image: I,
+ color: ClearValue,
+ ) -> Result<&mut Self, ClearColorImageError>
+ where
+ I: ImageAccess + Send + Sync + 'static,
+ {
+ let layers = image.dimensions().array_layers();
+ let levels = image.mipmap_levels();
+
+ self.clear_color_image_dimensions(image, 0, layers, 0, levels, color)
+ }
+
+ /// Adds a command that clears a color image with a specific value.
+ ///
+ /// # Panic
+ ///
+ /// - Panics if `color` is not a color value.
+ ///
+ pub fn clear_color_image_dimensions<I>(
+ &mut self,
+ image: I,
+ first_layer: u32,
+ num_layers: u32,
+ first_mipmap: u32,
+ num_mipmaps: u32,
+ color: ClearValue,
+ ) -> Result<&mut Self, ClearColorImageError>
+ where
+ I: ImageAccess + Send + Sync + 'static,
+ {
+ unsafe {
+ if !self.queue_family().supports_graphics() && !self.queue_family().supports_compute() {
+ return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
+ }
+
+ self.ensure_outside_render_pass()?;
+ check_clear_color_image(
+ self.device(),
+ &image,
+ first_layer,
+ num_layers,
+ first_mipmap,
+ num_mipmaps,
+ )?;
+
+ match color {
+ ClearValue::Float(_) | ClearValue::Int(_) | ClearValue::Uint(_) => {}
+ _ => panic!("The clear color is not a color value"),
+ };
+
+ let region = UnsafeCommandBufferBuilderColorImageClear {
+ base_mip_level: first_mipmap,
+ level_count: num_mipmaps,
+ base_array_layer: first_layer,
+ layer_count: num_layers,
+ };
+
+ // TODO: let choose layout
+ self.inner.clear_color_image(
+ image,
+ ImageLayout::TransferDstOptimal,
+ color,
+ iter::once(region),
+ )?;
+ Ok(self)
+ }
+ }
+
+ /// Adds a command that copies from a buffer to another.
+ ///
+ /// This command will copy from the source to the destination. If their size is not equal, then
+ /// the amount of data copied is equal to the smallest of the two.
+ #[inline]
+ pub fn copy_buffer<S, D, T>(
+ &mut self,
+ source: S,
+ destination: D,
+ ) -> Result<&mut Self, CopyBufferError>
+ where
+ S: TypedBufferAccess<Content = T> + Send + Sync + 'static,
+ D: TypedBufferAccess<Content = T> + Send + Sync + 'static,
+ T: ?Sized,
+ {
+ unsafe {
+ self.ensure_outside_render_pass()?;
+ let infos = check_copy_buffer(self.device(), &source, &destination)?;
+ self.inner
+ .copy_buffer(source, destination, iter::once((0, 0, infos.copy_size)))?;
+ Ok(self)
+ }
+ }
+
+ /// Adds a command that copies a range from the source to the destination buffer.
+ /// Panics if out of bounds.
+ #[inline]
+ pub fn copy_buffer_dimensions<S, D, T>(
+ &mut self,
+ source: S,
+ source_offset: DeviceSize,
+ destination: D,
+ destination_offset: DeviceSize,
+ count: DeviceSize,
+ ) -> Result<&mut Self, CopyBufferError>
+ where
+ S: TypedBufferAccess<Content = [T]> + Send + Sync + 'static,
+ D: TypedBufferAccess<Content = [T]> + Send + Sync + 'static,
+ {
+ self.ensure_outside_render_pass()?;
+
+ let _infos = check_copy_buffer(self.device(), &source, &destination)?;
+ debug_assert!(source_offset + count <= source.len());
+ debug_assert!(destination_offset + count <= destination.len());
+
+ let size = std::mem::size_of::<T>() as DeviceSize;
+ unsafe {
+ self.inner.copy_buffer(
+ source,
+ destination,
+ iter::once((
+ source_offset * size,
+ destination_offset * size,
+ count * size,
+ )),
+ )?;
+ }
+ Ok(self)
+ }
+
+ /// Adds a command that copies from a buffer to an image.
+ pub fn copy_buffer_to_image<S, D, Px>(
+ &mut self,
+ source: S,
+ destination: D,
+ ) -> Result<&mut Self, CopyBufferImageError>
+ where
+ S: TypedBufferAccess<Content = [Px]> + Send + Sync + 'static,
+ D: ImageAccess + Send + Sync + 'static,
+ Px: Pixel,
+ {
+ self.ensure_outside_render_pass()?;
+
+ let dims = destination.dimensions().width_height_depth();
+ self.copy_buffer_to_image_dimensions(source, destination, [0, 0, 0], dims, 0, 1, 0)
+ }
+
+ /// Adds a command that copies from a buffer to an image.
+ pub fn copy_buffer_to_image_dimensions<S, D, Px>(
+ &mut self,
+ source: S,
+ destination: D,
+ offset: [u32; 3],
+ size: [u32; 3],
+ first_layer: u32,
+ num_layers: u32,
+ mipmap: u32,
+ ) -> Result<&mut Self, CopyBufferImageError>
+ where
+ S: TypedBufferAccess<Content = [Px]> + Send + Sync + 'static,
+ D: ImageAccess + Send + Sync + 'static,
+ Px: Pixel,
+ {
+ unsafe {
+ self.ensure_outside_render_pass()?;
+
+ check_copy_buffer_image(
+ self.device(),
+ &source,
+ &destination,
+ CheckCopyBufferImageTy::BufferToImage,
+ offset,
+ size,
+ first_layer,
+ num_layers,
+ mipmap,
+ )?;
+
+ let copy = UnsafeCommandBufferBuilderBufferImageCopy {
+ buffer_offset: 0,
+ buffer_row_length: 0,
+ buffer_image_height: 0,
+ image_aspect: if destination.has_color() {
+ ImageAspect::Color
+ } else {
+ unimplemented!()
+ },
+ image_mip_level: mipmap,
+ image_base_array_layer: first_layer,
+ image_layer_count: num_layers,
+ image_offset: [offset[0] as i32, offset[1] as i32, offset[2] as i32],
+ image_extent: size,
+ };
+
+ self.inner.copy_buffer_to_image(
+ source,
+ destination,
+ ImageLayout::TransferDstOptimal, // TODO: let choose layout
+ iter::once(copy),
+ )?;
+ Ok(self)
+ }
+ }
+
+ /// Adds a command that copies from an image to a buffer.
+ // The data layout of the image on the gpu is opaque, as in, it is non of our business how the gpu stores the image.
+ // This does not matter since the act of copying the image into a buffer converts it to linear form.
+ pub fn copy_image_to_buffer<S, D, Px>(
+ &mut self,
+ source: S,
+ destination: D,
+ ) -> Result<&mut Self, CopyBufferImageError>
+ where
+ S: ImageAccess + Send + Sync + 'static,
+ D: TypedBufferAccess<Content = [Px]> + Send + Sync + 'static,
+ Px: Pixel,
+ {
+ self.ensure_outside_render_pass()?;
+
+ let dims = source.dimensions().width_height_depth();
+ self.copy_image_to_buffer_dimensions(source, destination, [0, 0, 0], dims, 0, 1, 0)
+ }
+
+ /// Adds a command that copies from an image to a buffer.
+ pub fn copy_image_to_buffer_dimensions<S, D, Px>(
+ &mut self,
+ source: S,
+ destination: D,
+ offset: [u32; 3],
+ size: [u32; 3],
+ first_layer: u32,
+ num_layers: u32,
+ mipmap: u32,
+ ) -> Result<&mut Self, CopyBufferImageError>
+ where
+ S: ImageAccess + Send + Sync + 'static,
+ D: TypedBufferAccess<Content = [Px]> + Send + Sync + 'static,
+ Px: Pixel,
+ {
+ unsafe {
+ self.ensure_outside_render_pass()?;
+
+ check_copy_buffer_image(
+ self.device(),
+ &destination,
+ &source,
+ CheckCopyBufferImageTy::ImageToBuffer,
+ offset,
+ size,
+ first_layer,
+ num_layers,
+ mipmap,
+ )?;
+
+ let copy = UnsafeCommandBufferBuilderBufferImageCopy {
+ buffer_offset: 0,
+ buffer_row_length: 0,
+ buffer_image_height: 0,
+ // TODO: Allow the user to choose aspect
+ image_aspect: if source.has_color() {
+ ImageAspect::Color
+ } else if source.has_depth() {
+ ImageAspect::Depth
+ } else if source.has_stencil() {
+ ImageAspect::Stencil
+ } else {
+ unimplemented!()
+ },
+ image_mip_level: mipmap,
+ image_base_array_layer: first_layer,
+ image_layer_count: num_layers,
+ image_offset: [offset[0] as i32, offset[1] as i32, offset[2] as i32],
+ image_extent: size,
+ };
+
+ self.inner.copy_image_to_buffer(
+ source,
+ ImageLayout::TransferSrcOptimal,
+ destination, // TODO: let choose layout
+ iter::once(copy),
+ )?;
+ Ok(self)
+ }
+ }
+
+ /// Open a command buffer debug label region.
+ ///
+ /// Note: you need to enable `VK_EXT_debug_utils` extension when creating an instance.
+ #[inline]
+ pub fn debug_marker_begin(
+ &mut self,
+ name: &'static CStr,
+ color: [f32; 4],
+ ) -> Result<&mut Self, DebugMarkerError> {
+ if !self.queue_family().supports_graphics() && self.queue_family().supports_compute() {
+ return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
+ }
+
+ check_debug_marker_color(color)?;
+
+ unsafe {
+ self.inner.debug_marker_begin(name.into(), color);
+ }
+
+ Ok(self)
+ }
+
+ /// Close a command buffer label region.
+ ///
+ /// Note: you need to open a command buffer label region first with `debug_marker_begin`.
+ /// Note: you need to enable `VK_EXT_debug_utils` extension when creating an instance.
+ #[inline]
+ pub fn debug_marker_end(&mut self) -> Result<&mut Self, DebugMarkerError> {
+ if !self.queue_family().supports_graphics() && self.queue_family().supports_compute() {
+ return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
+ }
+
+ // TODO: validate that debug_marker_begin with same name was sent earlier
+
+ unsafe {
+ self.inner.debug_marker_end();
+ }
+
+ Ok(self)
+ }
+
+ /// Insert a label into a command buffer.
+ ///
+ /// Note: you need to enable `VK_EXT_debug_utils` extension when creating an instance.
+ #[inline]
+ pub fn debug_marker_insert(
+ &mut self,
+ name: &'static CStr,
+ color: [f32; 4],
+ ) -> Result<&mut Self, DebugMarkerError> {
+ if !self.queue_family().supports_graphics() && self.queue_family().supports_compute() {
+ return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
+ }
+
+ check_debug_marker_color(color)?;
+
+ unsafe {
+ self.inner.debug_marker_insert(name.into(), color);
+ }
+
+ Ok(self)
+ }
+
+ /// Perform a single compute operation using a compute pipeline.
+ #[inline]
+ pub fn dispatch<Cp, S, Pc>(
+ &mut self,
+ group_counts: [u32; 3],
+ pipeline: Cp,
+ descriptor_sets: S,
+ push_constants: Pc,
+ ) -> Result<&mut Self, DispatchError>
+ where
+ Cp: ComputePipelineAbstract + Send + Sync + 'static + Clone, // TODO: meh for Clone
+ S: DescriptorSetsCollection,
+ {
+ let descriptor_sets = descriptor_sets.into_vec();
+
+ unsafe {
+ if !self.queue_family().supports_compute() {
+ return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
+ }
+
+ self.ensure_outside_render_pass()?;
+ check_push_constants_validity(pipeline.layout(), &push_constants)?;
+ check_descriptor_sets_validity(pipeline.layout(), &descriptor_sets)?;
+ check_dispatch(pipeline.device(), group_counts)?;
+
+ if let StateCacherOutcome::NeedChange =
+ self.state_cacher.bind_compute_pipeline(&pipeline)
+ {
+ self.inner.bind_pipeline_compute(pipeline.clone());
+ }
+
+ set_push_constants(&mut self.inner, pipeline.layout(), push_constants);
+ bind_descriptor_sets(
+ &mut self.inner,
+ &mut self.state_cacher,
+ PipelineBindPoint::Compute,
+ pipeline.layout(),
+ descriptor_sets,
+ )?;
+
+ self.inner.dispatch(group_counts);
+ Ok(self)
+ }
+ }
+
+ /// Perform multiple compute operations using a compute pipeline. One dispatch is performed for
+ /// each `vulkano::command_buffer::DispatchIndirectCommand` struct in `indirect_buffer`.
+ #[inline]
+ pub fn dispatch_indirect<Inb, Cp, S, Pc>(
+ &mut self,
+ indirect_buffer: Inb,
+ pipeline: Cp,
+ descriptor_sets: S,
+ push_constants: Pc,
+ ) -> Result<&mut Self, DispatchIndirectError>
+ where
+ Inb: BufferAccess
+ + TypedBufferAccess<Content = [DispatchIndirectCommand]>
+ + Send
+ + Sync
+ + 'static,
+ Cp: ComputePipelineAbstract + Send + Sync + 'static + Clone, // TODO: meh for Clone
+ S: DescriptorSetsCollection,
+ {
+ let descriptor_sets = descriptor_sets.into_vec();
+
+ unsafe {
+ if !self.queue_family().supports_compute() {
+ return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
+ }
+
+ self.ensure_outside_render_pass()?;
+ check_indirect_buffer(self.device(), &indirect_buffer)?;
+ check_push_constants_validity(pipeline.layout(), &push_constants)?;
+ check_descriptor_sets_validity(pipeline.layout(), &descriptor_sets)?;
+
+ if let StateCacherOutcome::NeedChange =
+ self.state_cacher.bind_compute_pipeline(&pipeline)
+ {
+ self.inner.bind_pipeline_compute(pipeline.clone());
+ }
+
+ set_push_constants(&mut self.inner, pipeline.layout(), push_constants);
+ bind_descriptor_sets(
+ &mut self.inner,
+ &mut self.state_cacher,
+ PipelineBindPoint::Compute,
+ pipeline.layout(),
+ descriptor_sets,
+ )?;
+
+ self.inner.dispatch_indirect(indirect_buffer)?;
+ Ok(self)
+ }
+ }
+
+ /// Perform a single draw operation using a graphics pipeline.
+ ///
+ /// `vertex_buffer` is a set of vertex and/or instance buffers used to provide input.
+ ///
+ /// All data in `vertex_buffer` is used for the draw operation. To use only some data in the
+ /// buffer, wrap it in a `vulkano::buffer::BufferSlice`.
+ #[inline]
+ pub fn draw<V, Gp, S, Pc>(
+ &mut self,
+ pipeline: Gp,
+ dynamic: &DynamicState,
+ vertex_buffers: V,
+ descriptor_sets: S,
+ push_constants: Pc,
+ ) -> Result<&mut Self, DrawError>
+ where
+ Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone
+ S: DescriptorSetsCollection,
+ {
+ let descriptor_sets = descriptor_sets.into_vec();
+
+ unsafe {
+ // TODO: must check that pipeline is compatible with render pass
+
+ self.ensure_inside_render_pass_inline(&pipeline)?;
+ check_dynamic_state_validity(&pipeline, dynamic)?;
+ check_push_constants_validity(pipeline.layout(), &push_constants)?;
+ check_descriptor_sets_validity(pipeline.layout(), &descriptor_sets)?;
+ let vb_infos = check_vertex_buffers(&pipeline, vertex_buffers)?;
+
+ if let StateCacherOutcome::NeedChange =
+ self.state_cacher.bind_graphics_pipeline(&pipeline)
+ {
+ self.inner.bind_pipeline_graphics(pipeline.clone());
+ }
+
+ let dynamic = self.state_cacher.dynamic_state(dynamic);
+
+ set_push_constants(&mut self.inner, pipeline.layout(), push_constants);
+ set_state(&mut self.inner, &dynamic);
+ bind_descriptor_sets(
+ &mut self.inner,
+ &mut self.state_cacher,
+ PipelineBindPoint::Graphics,
+ pipeline.layout(),
+ descriptor_sets,
+ )?;
+ bind_vertex_buffers(
+ &mut self.inner,
+ &mut self.state_cacher,
+ vb_infos.vertex_buffers,
+ )?;
+
+ debug_assert!(self.queue_family().supports_graphics());
+
+ self.inner.draw(
+ vb_infos.vertex_count as u32,
+ vb_infos.instance_count as u32,
+ 0,
+ 0,
+ );
+ Ok(self)
+ }
+ }
+
+ /// Perform multiple draw operations using a graphics pipeline.
+ ///
+ /// One draw is performed for each [`DrawIndirectCommand`] struct in `indirect_buffer`.
+ /// The maximum number of draw commands in the buffer is limited by the
+ /// [`max_draw_indirect_count`](crate::device::Properties::max_draw_indirect_count) limit.
+ /// This limit is 1 unless the
+ /// [`multi_draw_indirect`](crate::device::Features::multi_draw_indirect) feature has been
+ /// enabled.
+ ///
+ /// `vertex_buffer` is a set of vertex and/or instance buffers used to provide input. It is
+ /// used for every draw operation.
+ ///
+ /// All data in `vertex_buffer` is used for every draw operation. To use only some data in the
+ /// buffer, wrap it in a `vulkano::buffer::BufferSlice`.
+ #[inline]
+ pub fn draw_indirect<V, Gp, S, Pc, Inb>(
+ &mut self,
+ pipeline: Gp,
+ dynamic: &DynamicState,
+ vertex_buffers: V,
+ indirect_buffer: Inb,
+ descriptor_sets: S,
+ push_constants: Pc,
+ ) -> Result<&mut Self, DrawIndirectError>
+ where
+ Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone
+ S: DescriptorSetsCollection,
+ Inb: BufferAccess
+ + TypedBufferAccess<Content = [DrawIndirectCommand]>
+ + Send
+ + Sync
+ + 'static,
+ {
+ let descriptor_sets = descriptor_sets.into_vec();
+
+ unsafe {
+ // TODO: must check that pipeline is compatible with render pass
+
+ self.ensure_inside_render_pass_inline(&pipeline)?;
+ check_indirect_buffer(self.device(), &indirect_buffer)?;
+ check_dynamic_state_validity(&pipeline, dynamic)?;
+ check_push_constants_validity(pipeline.layout(), &push_constants)?;
+ check_descriptor_sets_validity(pipeline.layout(), &descriptor_sets)?;
+ let vb_infos = check_vertex_buffers(&pipeline, vertex_buffers)?;
+
+ let requested = indirect_buffer.len() as u32;
+ let limit = self
+ .device()
+ .physical_device()
+ .properties()
+ .max_draw_indirect_count;
+
+ if requested > limit {
+ return Err(
+ CheckIndirectBufferError::MaxDrawIndirectCountLimitExceeded {
+ limit,
+ requested,
+ }
+ .into(),
+ );
+ }
+
+ if let StateCacherOutcome::NeedChange =
+ self.state_cacher.bind_graphics_pipeline(&pipeline)
+ {
+ self.inner.bind_pipeline_graphics(pipeline.clone());
+ }
+
+ let dynamic = self.state_cacher.dynamic_state(dynamic);
+
+ set_push_constants(&mut self.inner, pipeline.layout(), push_constants);
+ set_state(&mut self.inner, &dynamic);
+ bind_descriptor_sets(
+ &mut self.inner,
+ &mut self.state_cacher,
+ PipelineBindPoint::Graphics,
+ pipeline.layout(),
+ descriptor_sets,
+ )?;
+ bind_vertex_buffers(
+ &mut self.inner,
+ &mut self.state_cacher,
+ vb_infos.vertex_buffers,
+ )?;
+
+ debug_assert!(self.queue_family().supports_graphics());
+
+ self.inner.draw_indirect(
+ indirect_buffer,
+ requested,
+ mem::size_of::<DrawIndirectCommand>() as u32,
+ )?;
+ Ok(self)
+ }
+ }
+
+ /// Perform a single draw operation using a graphics pipeline, using an index buffer.
+ ///
+ /// `vertex_buffer` is a set of vertex and/or instance buffers used to provide input.
+ /// `index_buffer` is a buffer containing indices into the vertex buffer that should be
+ /// processed in order.
+ ///
+ /// All data in `vertex_buffer` and `index_buffer` is used for the draw operation. To use
+ /// only some data in the buffer, wrap it in a `vulkano::buffer::BufferSlice`.
+ #[inline]
+ pub fn draw_indexed<V, Gp, S, Pc, Ib, I>(
+ &mut self,
+ pipeline: Gp,
+ dynamic: &DynamicState,
+ vertex_buffers: V,
+ index_buffer: Ib,
+ descriptor_sets: S,
+ push_constants: Pc,
+ ) -> Result<&mut Self, DrawIndexedError>
+ where
+ Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone
+ S: DescriptorSetsCollection,
+ Ib: BufferAccess + TypedBufferAccess<Content = [I]> + Send + Sync + 'static,
+ I: Index + 'static,
+ {
+ let descriptor_sets = descriptor_sets.into_vec();
+
+ unsafe {
+ // TODO: must check that pipeline is compatible with render pass
+
+ self.ensure_inside_render_pass_inline(&pipeline)?;
+ let ib_infos = check_index_buffer(self.device(), &index_buffer)?;
+ check_dynamic_state_validity(&pipeline, dynamic)?;
+ check_push_constants_validity(pipeline.layout(), &push_constants)?;
+ check_descriptor_sets_validity(pipeline.layout(), &descriptor_sets)?;
+ let vb_infos = check_vertex_buffers(&pipeline, vertex_buffers)?;
+
+ if let StateCacherOutcome::NeedChange =
+ self.state_cacher.bind_graphics_pipeline(&pipeline)
+ {
+ self.inner.bind_pipeline_graphics(pipeline.clone());
+ }
+
+ if let StateCacherOutcome::NeedChange =
+ self.state_cacher.bind_index_buffer(&index_buffer, I::ty())
+ {
+ self.inner.bind_index_buffer(index_buffer, I::ty())?;
+ }
+
+ let dynamic = self.state_cacher.dynamic_state(dynamic);
+
+ set_push_constants(&mut self.inner, pipeline.layout(), push_constants);
+ set_state(&mut self.inner, &dynamic);
+ bind_descriptor_sets(
+ &mut self.inner,
+ &mut self.state_cacher,
+ PipelineBindPoint::Graphics,
+ pipeline.layout(),
+ descriptor_sets,
+ )?;
+ bind_vertex_buffers(
+ &mut self.inner,
+ &mut self.state_cacher,
+ vb_infos.vertex_buffers,
+ )?;
+ // TODO: how to handle an index out of range of the vertex buffers?
+
+ debug_assert!(self.queue_family().supports_graphics());
+
+ self.inner.draw_indexed(
+ ib_infos.num_indices as u32,
+ vb_infos.instance_count as u32,
+ 0,
+ 0,
+ 0,
+ );
+ Ok(self)
+ }
+ }
+
+ /// Perform multiple draw operations using a graphics pipeline, using an index buffer.
+ ///
+ /// One draw is performed for each [`DrawIndirectCommand`] struct in `indirect_buffer`.
+ /// The maximum number of draw commands in the buffer is limited by the
+ /// [`max_draw_indirect_count`](crate::device::Properties::max_draw_indirect_count) limit.
+ /// This limit is 1 unless the
+ /// [`multi_draw_indirect`](crate::device::Features::multi_draw_indirect) feature has been
+ /// enabled.
+ ///
+ /// `vertex_buffer` is a set of vertex and/or instance buffers used to provide input.
+ /// `index_buffer` is a buffer containing indices into the vertex buffer that should be
+ /// processed in order.
+ ///
+ /// All data in `vertex_buffer` and `index_buffer` is used for every draw operation. To use
+ /// only some data in the buffer, wrap it in a `vulkano::buffer::BufferSlice`.
+ #[inline]
+ pub fn draw_indexed_indirect<V, Gp, S, Pc, Ib, Inb, I>(
+ &mut self,
+ pipeline: Gp,
+ dynamic: &DynamicState,
+ vertex_buffers: V,
+ index_buffer: Ib,
+ indirect_buffer: Inb,
+ descriptor_sets: S,
+ push_constants: Pc,
+ ) -> Result<&mut Self, DrawIndexedIndirectError>
+ where
+ Gp: GraphicsPipelineAbstract + VertexSource<V> + Send + Sync + 'static + Clone, // TODO: meh for Clone
+ S: DescriptorSetsCollection,
+ Ib: BufferAccess + TypedBufferAccess<Content = [I]> + Send + Sync + 'static,
+ Inb: BufferAccess
+ + TypedBufferAccess<Content = [DrawIndexedIndirectCommand]>
+ + Send
+ + Sync
+ + 'static,
+ I: Index + 'static,
+ {
+ let descriptor_sets = descriptor_sets.into_vec();
+
+ unsafe {
+ // TODO: must check that pipeline is compatible with render pass
+
+ self.ensure_inside_render_pass_inline(&pipeline)?;
+ let ib_infos = check_index_buffer(self.device(), &index_buffer)?;
+ check_indirect_buffer(self.device(), &indirect_buffer)?;
+ check_dynamic_state_validity(&pipeline, dynamic)?;
+ check_push_constants_validity(pipeline.layout(), &push_constants)?;
+ check_descriptor_sets_validity(pipeline.layout(), &descriptor_sets)?;
+ let vb_infos = check_vertex_buffers(&pipeline, vertex_buffers)?;
+
+ let requested = indirect_buffer.len() as u32;
+ let limit = self
+ .device()
+ .physical_device()
+ .properties()
+ .max_draw_indirect_count;
+
+ if requested > limit {
+ return Err(
+ CheckIndirectBufferError::MaxDrawIndirectCountLimitExceeded {
+ limit,
+ requested,
+ }
+ .into(),
+ );
+ }
+
+ if let StateCacherOutcome::NeedChange =
+ self.state_cacher.bind_graphics_pipeline(&pipeline)
+ {
+ self.inner.bind_pipeline_graphics(pipeline.clone());
+ }
+
+ if let StateCacherOutcome::NeedChange =
+ self.state_cacher.bind_index_buffer(&index_buffer, I::ty())
+ {
+ self.inner.bind_index_buffer(index_buffer, I::ty())?;
+ }
+
+ let dynamic = self.state_cacher.dynamic_state(dynamic);
+
+ set_push_constants(&mut self.inner, pipeline.layout(), push_constants);
+ set_state(&mut self.inner, &dynamic);
+ bind_descriptor_sets(
+ &mut self.inner,
+ &mut self.state_cacher,
+ PipelineBindPoint::Graphics,
+ pipeline.layout(),
+ descriptor_sets,
+ )?;
+ bind_vertex_buffers(
+ &mut self.inner,
+ &mut self.state_cacher,
+ vb_infos.vertex_buffers,
+ )?;
+
+ debug_assert!(self.queue_family().supports_graphics());
+
+ self.inner.draw_indexed_indirect(
+ indirect_buffer,
+ requested,
+ mem::size_of::<DrawIndexedIndirectCommand>() as u32,
+ )?;
+ Ok(self)
+ }
+ }
+
+ /// Adds a command that writes the content of a buffer.
+ ///
+ /// This function is similar to the `memset` function in C. The `data` parameter is a number
+ /// that will be repeatedly written through the entire buffer.
+ ///
+ /// > **Note**: This function is technically safe because buffers can only contain integers or
+ /// > floating point numbers, which are always valid whatever their memory representation is.
+ /// > But unless your buffer actually contains only 32-bits integers, you are encouraged to use
+ /// > this function only for zeroing the content of a buffer by passing `0` for the data.
+ // TODO: not safe because of signalling NaNs
+ #[inline]
+ pub fn fill_buffer<B>(&mut self, buffer: B, data: u32) -> Result<&mut Self, FillBufferError>
+ where
+ B: BufferAccess + Send + Sync + 'static,
+ {
+ unsafe {
+ self.ensure_outside_render_pass()?;
+ check_fill_buffer(self.device(), &buffer)?;
+ self.inner.fill_buffer(buffer, data);
+ Ok(self)
+ }
+ }
+
+ /// Adds a command that writes data to a buffer.
+ ///
+ /// If `data` is larger than the buffer, only the part of `data` that fits is written. If the
+ /// buffer is larger than `data`, only the start of the buffer is written.
+ #[inline]
+ pub fn update_buffer<B, D, Dd>(
+ &mut self,
+ buffer: B,
+ data: Dd,
+ ) -> Result<&mut Self, UpdateBufferError>
+ where
+ B: TypedBufferAccess<Content = D> + Send + Sync + 'static,
+ D: ?Sized,
+ Dd: SafeDeref<Target = D> + Send + Sync + 'static,
+ {
+ unsafe {
+ self.ensure_outside_render_pass()?;
+ check_update_buffer(self.device(), &buffer, data.deref())?;
+
+ let size_of_data = mem::size_of_val(data.deref()) as DeviceSize;
+ if buffer.size() >= size_of_data {
+ self.inner.update_buffer(buffer, data);
+ } else {
+ unimplemented!() // TODO:
+ //self.inner.update_buffer(buffer.slice(0 .. size_of_data), data);
+ }
+
+ Ok(self)
+ }
+ }
+
+ /// Adds a command that begins a query.
+ ///
+ /// The query will be active until [`end_query`](Self::end_query) is called for the same query.
+ ///
+ /// # Safety
+ /// The query must be unavailable, ensured by calling [`reset_query_pool`](Self::reset_query_pool).
+ pub unsafe fn begin_query(
+ &mut self,
+ query_pool: Arc<QueryPool>,
+ query: u32,
+ flags: QueryControlFlags,
+ ) -> Result<&mut Self, BeginQueryError> {
+ check_begin_query(self.device(), &query_pool, query, flags)?;
+
+ match query_pool.ty() {
+ QueryType::Occlusion => {
+ if !self.queue_family().supports_graphics() {
+ return Err(
+ AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into(),
+ );
+ }
+ }
+ QueryType::PipelineStatistics(flags) => {
+ if flags.is_compute() && !self.queue_family().supports_compute()
+ || flags.is_graphics() && !self.queue_family().supports_graphics()
+ {
+ return Err(
+ AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into(),
+ );
+ }
+ }
+ QueryType::Timestamp => unreachable!(),
+ }
+
+ let ty = query_pool.ty();
+ let raw_ty = ty.into();
+ let raw_query_pool = query_pool.internal_object();
+ if self.query_state.contains_key(&raw_ty) {
+ return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into());
+ }
+
+ // TODO: validity checks
+ self.inner.begin_query(query_pool, query, flags);
+ self.query_state.insert(
+ raw_ty,
+ QueryState {
+ query_pool: raw_query_pool,
+ query,
+ ty,
+ flags,
+ in_subpass: self.render_pass_state.is_some(),
+ },
+ );
+
+ Ok(self)
+ }
+
+ /// Adds a command that ends an active query.
+ pub fn end_query(
+ &mut self,
+ query_pool: Arc<QueryPool>,
+ query: u32,
+ ) -> Result<&mut Self, EndQueryError> {
+ unsafe {
+ check_end_query(self.device(), &query_pool, query)?;
+
+ let raw_ty = query_pool.ty().into();
+ let raw_query_pool = query_pool.internal_object();
+ if !self.query_state.get(&raw_ty).map_or(false, |state| {
+ state.query_pool == raw_query_pool && state.query == query
+ }) {
+ return Err(AutoCommandBufferBuilderContextError::QueryNotActive.into());
+ }
+
+ self.inner.end_query(query_pool, query);
+ self.query_state.remove(&raw_ty);
+ }
+
+ Ok(self)
+ }
+
+ /// Adds a command that writes a timestamp to a timestamp query.
+ ///
+ /// # Safety
+ /// The query must be unavailable, ensured by calling [`reset_query_pool`](Self::reset_query_pool).
+ pub unsafe fn write_timestamp(
+ &mut self,
+ query_pool: Arc<QueryPool>,
+ query: u32,
+ stage: PipelineStage,
+ ) -> Result<&mut Self, WriteTimestampError> {
+ check_write_timestamp(
+ self.device(),
+ self.queue_family(),
+ &query_pool,
+ query,
+ stage,
+ )?;
+
+ if !(self.queue_family().supports_graphics()
+ || self.queue_family().supports_compute()
+ || self.queue_family().explicitly_supports_transfers())
+ {
+ return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
+ }
+
+ // TODO: validity checks
+ self.inner.write_timestamp(query_pool, query, stage);
+
+ Ok(self)
+ }
+
+ /// Adds a command that copies the results of a range of queries to a buffer on the GPU.
+ ///
+ /// [`query_pool.ty().result_size()`](crate::query::QueryType::result_size) elements
+ /// will be written for each query in the range, plus 1 extra element per query if
+ /// [`QueryResultFlags::with_availability`] is enabled.
+ /// The provided buffer must be large enough to hold the data.
+ ///
+ /// See also [`get_results`](crate::query::QueriesRange::get_results).
+ pub fn copy_query_pool_results<D, T>(
+ &mut self,
+ query_pool: Arc<QueryPool>,
+ queries: Range<u32>,
+ destination: D,
+ flags: QueryResultFlags,
+ ) -> Result<&mut Self, CopyQueryPoolResultsError>
+ where
+ D: BufferAccess + TypedBufferAccess<Content = [T]> + Send + Sync + 'static,
+ T: QueryResultElement,
+ {
+ unsafe {
+ self.ensure_outside_render_pass()?;
+ let stride = check_copy_query_pool_results(
+ self.device(),
+ &query_pool,
+ queries.clone(),
+ &destination,
+ flags,
+ )?;
+ self.inner
+ .copy_query_pool_results(query_pool, queries, destination, stride, flags)?;
+ }
+
+ Ok(self)
+ }
+
+ /// Adds a command to reset a range of queries on a query pool.
+ ///
+ /// The affected queries will be marked as "unavailable" after this command runs, and will no
+ /// longer return any results. They will be ready to have new results recorded for them.
+ ///
+ /// # Safety
+ /// The queries in the specified range must not be active in another command buffer.
+ pub unsafe fn reset_query_pool(
+ &mut self,
+ query_pool: Arc<QueryPool>,
+ queries: Range<u32>,
+ ) -> Result<&mut Self, ResetQueryPoolError> {
+ self.ensure_outside_render_pass()?;
+ check_reset_query_pool(self.device(), &query_pool, queries.clone())?;
+
+ let raw_query_pool = query_pool.internal_object();
+ if self
+ .query_state
+ .values()
+ .any(|state| state.query_pool == raw_query_pool && queries.contains(&state.query))
+ {
+ return Err(AutoCommandBufferBuilderContextError::QueryIsActive.into());
+ }
+
+ // TODO: validity checks
+ // Do other command buffers actually matter here? Not sure on the Vulkan spec.
+ self.inner.reset_query_pool(query_pool, queries);
+
+ Ok(self)
+ }
+}
+
+/// Commands that can only be executed on primary command buffers
+impl<P> AutoCommandBufferBuilder<PrimaryAutoCommandBuffer<P::Alloc>, P>
+where
+ P: CommandPoolBuilderAlloc,
+{
+ /// Adds a command that enters a render pass.
+ ///
+ /// If `contents` is `SubpassContents::SecondaryCommandBuffers`, then you will only be able to
+ /// add secondary command buffers while you're inside the first subpass of the render pass.
+ /// If it is `SubpassContents::Inline`, you will only be able to add inline draw commands and
+ /// not secondary command buffers.
+ ///
+ /// C must contain exactly one clear value for each attachment in the framebuffer.
+ ///
+ /// You must call this before you can add draw commands.
+ #[inline]
+ pub fn begin_render_pass<F, I>(
+ &mut self,
+ framebuffer: F,
+ contents: SubpassContents,
+ clear_values: I,
+ ) -> Result<&mut Self, BeginRenderPassError>
+ where
+ F: FramebufferAbstract + Clone + Send + Sync + 'static,
+ I: IntoIterator<Item = ClearValue>,
+ {
+ unsafe {
+ if !self.queue_family().supports_graphics() {
+ return Err(AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily.into());
+ }
+
+ self.ensure_outside_render_pass()?;
+
+ let clear_values = framebuffer
+ .render_pass()
+ .desc()
+ .convert_clear_values(clear_values);
+ let clear_values = clear_values.collect::<Vec<_>>().into_iter(); // TODO: necessary for Send + Sync ; needs an API rework of convert_clear_values
+ let mut clear_values_copy = clear_values.clone().enumerate(); // TODO: Proper errors for clear value errors instead of panics
+
+ for (atch_i, atch_desc) in framebuffer
+ .render_pass()
+ .desc()
+ .attachments()
+ .into_iter()
+ .enumerate()
+ {
+ match clear_values_copy.next() {
+ Some((clear_i, clear_value)) => {
+ if atch_desc.load == LoadOp::Clear {
+ match clear_value {
+ ClearValue::None => panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: None",
+ clear_i, atch_i, atch_desc.format.ty()),
+ ClearValue::Float(_) => if atch_desc.format.ty() != FormatTy::Float {
+ panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Float",
+ clear_i, atch_i, atch_desc.format.ty());
+ }
+ ClearValue::Int(_) => if atch_desc.format.ty() != FormatTy::Sint {
+ panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Int",
+ clear_i, atch_i, atch_desc.format.ty());
+ }
+ ClearValue::Uint(_) => if atch_desc.format.ty() != FormatTy::Uint {
+ panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Uint",
+ clear_i, atch_i, atch_desc.format.ty());
+ }
+ ClearValue::Depth(_) => if atch_desc.format.ty() != FormatTy::Depth {
+ panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Depth",
+ clear_i, atch_i, atch_desc.format.ty());
+ }
+ ClearValue::Stencil(_) => if atch_desc.format.ty() != FormatTy::Stencil {
+ panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: Stencil",
+ clear_i, atch_i, atch_desc.format.ty());
+ }
+ ClearValue::DepthStencil(_) => if atch_desc.format.ty() != FormatTy::DepthStencil {
+ panic!("Bad ClearValue! index: {}, attachment index: {}, expected: {:?}, got: DepthStencil",
+ clear_i, atch_i, atch_desc.format.ty());
+ }
+ }
+ } else {
+ if clear_value != ClearValue::None {
+ panic!("Bad ClearValue! index: {}, attachment index: {}, expected: None, got: {:?}",
+ clear_i, atch_i, clear_value);
+ }
+ }
+ }
+ None => panic!("Not enough clear values"),
+ }
+ }
+
+ if clear_values_copy.count() != 0 {
+ panic!("Too many clear values")
+ }
+
+ if let Some(multiview_desc) = framebuffer.render_pass().desc().multiview() {
+ // When multiview is enabled, at the beginning of each subpass all non-render pass state is undefined
+ self.state_cacher.invalidate();
+
+ // ensure that the framebuffer is compatible with the render pass multiview configuration
+ if multiview_desc
+ .view_masks
+ .iter()
+ .chain(multiview_desc.correlation_masks.iter())
+ .map(|&mask| 32 - mask.leading_zeros()) // calculates the highest used layer index of the mask
+ .any(|highest_used_layer| highest_used_layer > framebuffer.layers())
+ {
+ panic!("A multiview mask references more layers than exist in the framebuffer");
+ }
+ }
+
+ let framebuffer_object = FramebufferAbstract::inner(&framebuffer).internal_object();
+ self.inner
+ .begin_render_pass(framebuffer.clone(), contents, clear_values)?;
+ self.render_pass_state = Some(RenderPassState {
+ subpass: (framebuffer.render_pass().clone(), 0),
+ contents,
+ framebuffer: framebuffer_object,
+ });
+ Ok(self)
+ }
+ }
+
+ /// Adds a command that ends the current render pass.
+ ///
+ /// This must be called after you went through all the subpasses and before you can build
+ /// the command buffer or add further commands.
+ #[inline]
+ pub fn end_render_pass(&mut self) -> Result<&mut Self, AutoCommandBufferBuilderContextError> {
+ unsafe {
+ if let Some(render_pass_state) = self.render_pass_state.as_ref() {
+ let (ref rp, index) = render_pass_state.subpass;
+
+ if rp.desc().subpasses().len() as u32 != index + 1 {
+ return Err(AutoCommandBufferBuilderContextError::NumSubpassesMismatch {
+ actual: rp.desc().subpasses().len() as u32,
+ current: index,
+ });
+ }
+ } else {
+ return Err(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass);
+ }
+
+ if self.query_state.values().any(|state| state.in_subpass) {
+ return Err(AutoCommandBufferBuilderContextError::QueryIsActive);
+ }
+
+ debug_assert!(self.queue_family().supports_graphics());
+
+ self.inner.end_render_pass();
+ self.render_pass_state = None;
+ Ok(self)
+ }
+ }
+
+ /// Adds a command that executes a secondary command buffer.
+ ///
+ /// If the `flags` that `command_buffer` was created with are more restrictive than those of
+ /// `self`, then `self` will be restricted to match. E.g. executing a secondary command buffer
+ /// with `Flags::OneTimeSubmit` will set `self`'s flags to `Flags::OneTimeSubmit` also.
+ pub fn execute_commands<C>(
+ &mut self,
+ command_buffer: C,
+ ) -> Result<&mut Self, ExecuteCommandsError>
+ where
+ C: SecondaryCommandBuffer + Send + Sync + 'static,
+ {
+ self.check_command_buffer(&command_buffer)?;
+ let secondary_usage = command_buffer.inner().usage();
+
+ unsafe {
+ let mut builder = self.inner.execute_commands();
+ builder.add(command_buffer);
+ builder.submit()?;
+ }
+
+ // Secondary command buffer could leave the primary in any state.
+ self.state_cacher.invalidate();
+
+ // If the secondary is non-concurrent or one-time use, that restricts the primary as well.
+ self.usage = std::cmp::min(self.usage, secondary_usage);
+
+ Ok(self)
+ }
+
+ /// Adds a command that multiple secondary command buffers in a vector.
+ ///
+ /// This requires that the secondary command buffers do not have resource conflicts; an error
+ /// will be returned if there are any. Use `execute_commands` if you want to ensure that
+ /// resource conflicts are automatically resolved.
+ // TODO ^ would be nice if this just worked without errors
+ pub fn execute_commands_from_vec<C>(
+ &mut self,
+ command_buffers: Vec<C>,
+ ) -> Result<&mut Self, ExecuteCommandsError>
+ where
+ C: SecondaryCommandBuffer + Send + Sync + 'static,
+ {
+ for command_buffer in &command_buffers {
+ self.check_command_buffer(command_buffer)?;
+ }
+
+ let mut secondary_usage = CommandBufferUsage::SimultaneousUse; // Most permissive usage
+ unsafe {
+ let mut builder = self.inner.execute_commands();
+ for command_buffer in command_buffers {
+ secondary_usage = std::cmp::min(secondary_usage, command_buffer.inner().usage());
+ builder.add(command_buffer);
+ }
+ builder.submit()?;
+ }
+
+ // Secondary command buffer could leave the primary in any state.
+ self.state_cacher.invalidate();
+
+ // If the secondary is non-concurrent or one-time use, that restricts the primary as well.
+ self.usage = std::cmp::min(self.usage, secondary_usage);
+
+ Ok(self)
+ }
+
+ // Helper function for execute_commands
+ fn check_command_buffer<C>(
+ &self,
+ command_buffer: &C,
+ ) -> Result<(), AutoCommandBufferBuilderContextError>
+ where
+ C: SecondaryCommandBuffer + Send + Sync + 'static,
+ {
+ if let Some(render_pass) = command_buffer.inheritance().render_pass {
+ self.ensure_inside_render_pass_secondary(&render_pass)?;
+ } else {
+ self.ensure_outside_render_pass()?;
+ }
+
+ for state in self.query_state.values() {
+ match state.ty {
+ QueryType::Occlusion => match command_buffer.inheritance().occlusion_query {
+ Some(inherited_flags) => {
+ let inherited_flags = ash::vk::QueryControlFlags::from(inherited_flags);
+ let state_flags = ash::vk::QueryControlFlags::from(state.flags);
+
+ if inherited_flags & state_flags != state_flags {
+ return Err(AutoCommandBufferBuilderContextError::QueryNotInherited);
+ }
+ }
+ None => return Err(AutoCommandBufferBuilderContextError::QueryNotInherited),
+ },
+ QueryType::PipelineStatistics(state_flags) => {
+ let inherited_flags = command_buffer.inheritance().query_statistics_flags;
+ let inherited_flags =
+ ash::vk::QueryPipelineStatisticFlags::from(inherited_flags);
+ let state_flags = ash::vk::QueryPipelineStatisticFlags::from(state_flags);
+
+ if inherited_flags & state_flags != state_flags {
+ return Err(AutoCommandBufferBuilderContextError::QueryNotInherited);
+ }
+ }
+ _ => (),
+ }
+ }
+
+ Ok(())
+ }
+
+ #[inline]
+ fn ensure_inside_render_pass_secondary(
+ &self,
+ render_pass: &CommandBufferInheritanceRenderPass<&dyn FramebufferAbstract>,
+ ) -> Result<(), AutoCommandBufferBuilderContextError> {
+ let render_pass_state = self
+ .render_pass_state
+ .as_ref()
+ .ok_or(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass)?;
+
+ if render_pass_state.contents != SubpassContents::SecondaryCommandBuffers {
+ return Err(AutoCommandBufferBuilderContextError::WrongSubpassType);
+ }
+
+ // Subpasses must be the same.
+ if render_pass.subpass.index() != render_pass_state.subpass.1 {
+ return Err(AutoCommandBufferBuilderContextError::WrongSubpassIndex);
+ }
+
+ // Render passes must be compatible.
+ if !render_pass
+ .subpass
+ .render_pass()
+ .desc()
+ .is_compatible_with_desc(render_pass_state.subpass.0.desc())
+ {
+ return Err(AutoCommandBufferBuilderContextError::IncompatibleRenderPass);
+ }
+
+ // Framebuffer, if present on the secondary command buffer, must be the
+ // same as the one in the current render pass.
+ if let Some(framebuffer) = render_pass.framebuffer {
+ if FramebufferAbstract::inner(framebuffer).internal_object()
+ != render_pass_state.framebuffer
+ {
+ return Err(AutoCommandBufferBuilderContextError::IncompatibleFramebuffer);
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Adds a command that jumps to the next subpass of the current render pass.
+ #[inline]
+ pub fn next_subpass(
+ &mut self,
+ contents: SubpassContents,
+ ) -> Result<&mut Self, AutoCommandBufferBuilderContextError> {
+ unsafe {
+ if let Some(render_pass_state) = self.render_pass_state.as_mut() {
+ let (ref rp, ref mut index) = render_pass_state.subpass;
+
+ if *index + 1 >= rp.desc().subpasses().len() as u32 {
+ return Err(AutoCommandBufferBuilderContextError::NumSubpassesMismatch {
+ actual: rp.desc().subpasses().len() as u32,
+ current: *index,
+ });
+ } else {
+ *index += 1;
+ render_pass_state.contents = contents;
+ }
+
+ if let Some(multiview) = rp.desc().multiview() {
+ // When multiview is enabled, at the beginning of each subpass all non-render pass state is undefined
+ self.state_cacher.invalidate();
+ }
+ } else {
+ return Err(AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass);
+ }
+
+ if self.query_state.values().any(|state| state.in_subpass) {
+ return Err(AutoCommandBufferBuilderContextError::QueryIsActive);
+ }
+
+ debug_assert!(self.queue_family().supports_graphics());
+
+ self.inner.next_subpass(contents);
+ Ok(self)
+ }
+ }
+}
+
+impl<P> AutoCommandBufferBuilder<SecondaryAutoCommandBuffer<P::Alloc>, P> where
+ P: CommandPoolBuilderAlloc
+{
+}
+
+unsafe impl<L, P> DeviceOwned for AutoCommandBufferBuilder<L, P> {
+ #[inline]
+ fn device(&self) -> &Arc<Device> {
+ self.inner.device()
+ }
+}
+
+// Shortcut function to set the push constants.
+unsafe fn set_push_constants<Pc>(
+ destination: &mut SyncCommandBufferBuilder,
+ pipeline_layout: &Arc<PipelineLayout>,
+ push_constants: Pc,
+) {
+ for range in pipeline_layout.push_constant_ranges() {
+ debug_assert_eq!(range.offset % 4, 0);
+ debug_assert_eq!(range.size % 4, 0);
+
+ let data = slice::from_raw_parts(
+ (&push_constants as *const Pc as *const u8).offset(range.offset as isize),
+ range.size as usize,
+ );
+
+ destination.push_constants::<[u8]>(
+ pipeline_layout.clone(),
+ range.stages,
+ range.offset as u32,
+ range.size as u32,
+ data,
+ );
+ }
+}
+
+// Shortcut function to change the state of the pipeline.
+unsafe fn set_state(destination: &mut SyncCommandBufferBuilder, dynamic: &DynamicState) {
+ if let Some(line_width) = dynamic.line_width {
+ destination.set_line_width(line_width);
+ }
+
+ if let Some(ref viewports) = dynamic.viewports {
+ destination.set_viewport(0, viewports.iter().cloned().collect::<Vec<_>>().into_iter());
+ // TODO: don't collect
+ }
+
+ if let Some(ref scissors) = dynamic.scissors {
+ destination.set_scissor(0, scissors.iter().cloned().collect::<Vec<_>>().into_iter());
+ // TODO: don't collect
+ }
+
+ if let Some(compare_mask) = dynamic.compare_mask {
+ destination.set_stencil_compare_mask(StencilFaces::Front, compare_mask.front);
+ destination.set_stencil_compare_mask(StencilFaces::Back, compare_mask.back);
+ }
+
+ if let Some(write_mask) = dynamic.write_mask {
+ destination.set_stencil_write_mask(StencilFaces::Front, write_mask.front);
+ destination.set_stencil_write_mask(StencilFaces::Back, write_mask.back);
+ }
+
+ if let Some(reference) = dynamic.reference {
+ destination.set_stencil_reference(StencilFaces::Front, reference.front);
+ destination.set_stencil_reference(StencilFaces::Back, reference.back);
+ }
+}
+
+// Shortcut function to bind vertex buffers.
+unsafe fn bind_vertex_buffers(
+ destination: &mut SyncCommandBufferBuilder,
+ state_cacher: &mut StateCacher,
+ vertex_buffers: Vec<Box<dyn BufferAccess + Send + Sync>>,
+) -> Result<(), SyncCommandBufferBuilderError> {
+ let binding_range = {
+ let mut compare = state_cacher.bind_vertex_buffers();
+ for vb in vertex_buffers.iter() {
+ compare.add(vb);
+ }
+ match compare.compare() {
+ Some(r) => r,
+ None => return Ok(()),
+ }
+ };
+
+ let first_binding = binding_range.start;
+ let num_bindings = binding_range.end - binding_range.start;
+
+ let mut binder = destination.bind_vertex_buffers();
+ for vb in vertex_buffers
+ .into_iter()
+ .skip(first_binding as usize)
+ .take(num_bindings as usize)
+ {
+ binder.add(vb);
+ }
+ binder.submit(first_binding)?;
+ Ok(())
+}
+
+unsafe fn bind_descriptor_sets(
+ destination: &mut SyncCommandBufferBuilder,
+ state_cacher: &mut StateCacher,
+ pipeline_bind_point: PipelineBindPoint,
+ pipeline_layout: &Arc<PipelineLayout>,
+ descriptor_sets: Vec<DescriptorSetWithOffsets>,
+) -> Result<(), SyncCommandBufferBuilderError> {
+ let first_binding = {
+ let mut compare = state_cacher.bind_descriptor_sets(pipeline_bind_point);
+ for descriptor_set in descriptor_sets.iter() {
+ compare.add(descriptor_set);
+ }
+ compare.compare()
+ };
+
+ let first_binding = match first_binding {
+ None => return Ok(()),
+ Some(fb) => fb,
+ };
+
+ let mut sets_binder = destination.bind_descriptor_sets();
+ for set in descriptor_sets.into_iter().skip(first_binding as usize) {
+ sets_binder.add(set);
+ }
+ sets_binder.submit(pipeline_bind_point, pipeline_layout.clone(), first_binding)?;
+ Ok(())
+}
+
+pub struct PrimaryAutoCommandBuffer<P = StandardCommandPoolAlloc> {
+ inner: SyncCommandBuffer,
+ pool_alloc: P, // Safety: must be dropped after `inner`
+
+ // Tracks usage of the command buffer on the GPU.
+ submit_state: SubmitState,
+}
+
+unsafe impl<P> DeviceOwned for PrimaryAutoCommandBuffer<P> {
+ #[inline]
+ fn device(&self) -> &Arc<Device> {
+ self.inner.device()
+ }
+}
+
+unsafe impl<P> PrimaryCommandBuffer for PrimaryAutoCommandBuffer<P> {
+ #[inline]
+ fn inner(&self) -> &UnsafeCommandBuffer {
+ self.inner.as_ref()
+ }
+
+ #[inline]
+ fn lock_submit(
+ &self,
+ future: &dyn GpuFuture,
+ queue: &Queue,
+ ) -> Result<(), CommandBufferExecError> {
+ match self.submit_state {
+ SubmitState::OneTime {
+ ref already_submitted,
+ } => {
+ let was_already_submitted = already_submitted.swap(true, Ordering::SeqCst);
+ if was_already_submitted {
+ return Err(CommandBufferExecError::OneTimeSubmitAlreadySubmitted);
+ }
+ }
+ SubmitState::ExclusiveUse { ref in_use } => {
+ let already_in_use = in_use.swap(true, Ordering::SeqCst);
+ if already_in_use {
+ return Err(CommandBufferExecError::ExclusiveAlreadyInUse);
+ }
+ }
+ SubmitState::Concurrent => (),
+ };
+
+ let err = match self.inner.lock_submit(future, queue) {
+ Ok(()) => return Ok(()),
+ Err(err) => err,
+ };
+
+ // If `self.inner.lock_submit()` failed, we revert action.
+ match self.submit_state {
+ SubmitState::OneTime {
+ ref already_submitted,
+ } => {
+ already_submitted.store(false, Ordering::SeqCst);
+ }
+ SubmitState::ExclusiveUse { ref in_use } => {
+ in_use.store(false, Ordering::SeqCst);
+ }
+ SubmitState::Concurrent => (),
+ };
+
+ Err(err)
+ }
+
+ #[inline]
+ unsafe fn unlock(&self) {
+ // Because of panic safety, we unlock the inner command buffer first.
+ self.inner.unlock();
+
+ match self.submit_state {
+ SubmitState::OneTime {
+ ref already_submitted,
+ } => {
+ debug_assert!(already_submitted.load(Ordering::SeqCst));
+ }
+ SubmitState::ExclusiveUse { ref in_use } => {
+ let old_val = in_use.swap(false, Ordering::SeqCst);
+ debug_assert!(old_val);
+ }
+ SubmitState::Concurrent => (),
+ };
+ }
+
+ #[inline]
+ fn check_buffer_access(
+ &self,
+ buffer: &dyn BufferAccess,
+ exclusive: bool,
+ queue: &Queue,
+ ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
+ self.inner.check_buffer_access(buffer, exclusive, queue)
+ }
+
+ #[inline]
+ fn check_image_access(
+ &self,
+ image: &dyn ImageAccess,
+ layout: ImageLayout,
+ exclusive: bool,
+ queue: &Queue,
+ ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
+ self.inner
+ .check_image_access(image, layout, exclusive, queue)
+ }
+}
+
+pub struct SecondaryAutoCommandBuffer<P = StandardCommandPoolAlloc> {
+ inner: SyncCommandBuffer,
+ pool_alloc: P, // Safety: must be dropped after `inner`
+ inheritance: CommandBufferInheritance<Box<dyn FramebufferAbstract + Send + Sync>>,
+
+ // Tracks usage of the command buffer on the GPU.
+ submit_state: SubmitState,
+}
+
+unsafe impl<P> DeviceOwned for SecondaryAutoCommandBuffer<P> {
+ #[inline]
+ fn device(&self) -> &Arc<Device> {
+ self.inner.device()
+ }
+}
+
+unsafe impl<P> SecondaryCommandBuffer for SecondaryAutoCommandBuffer<P> {
+ #[inline]
+ fn inner(&self) -> &UnsafeCommandBuffer {
+ self.inner.as_ref()
+ }
+
+ #[inline]
+ fn lock_record(&self) -> Result<(), CommandBufferExecError> {
+ match self.submit_state {
+ SubmitState::OneTime {
+ ref already_submitted,
+ } => {
+ let was_already_submitted = already_submitted.swap(true, Ordering::SeqCst);
+ if was_already_submitted {
+ return Err(CommandBufferExecError::OneTimeSubmitAlreadySubmitted);
+ }
+ }
+ SubmitState::ExclusiveUse { ref in_use } => {
+ let already_in_use = in_use.swap(true, Ordering::SeqCst);
+ if already_in_use {
+ return Err(CommandBufferExecError::ExclusiveAlreadyInUse);
+ }
+ }
+ SubmitState::Concurrent => (),
+ };
+
+ Ok(())
+ }
+
+ #[inline]
+ unsafe fn unlock(&self) {
+ match self.submit_state {
+ SubmitState::OneTime {
+ ref already_submitted,
+ } => {
+ debug_assert!(already_submitted.load(Ordering::SeqCst));
+ }
+ SubmitState::ExclusiveUse { ref in_use } => {
+ let old_val = in_use.swap(false, Ordering::SeqCst);
+ debug_assert!(old_val);
+ }
+ SubmitState::Concurrent => (),
+ };
+ }
+
+ fn inheritance(&self) -> CommandBufferInheritance<&dyn FramebufferAbstract> {
+ CommandBufferInheritance {
+ render_pass: self.inheritance.render_pass.as_ref().map(
+ |CommandBufferInheritanceRenderPass {
+ subpass,
+ framebuffer,
+ }| {
+ CommandBufferInheritanceRenderPass {
+ subpass: subpass.clone(),
+ framebuffer: framebuffer.as_ref().map(|f| f.as_ref() as &_),
+ }
+ },
+ ),
+ occlusion_query: self.inheritance.occlusion_query,
+ query_statistics_flags: self.inheritance.query_statistics_flags,
+ }
+ }
+
+ #[inline]
+ fn num_buffers(&self) -> usize {
+ self.inner.num_buffers()
+ }
+
+ #[inline]
+ fn buffer(&self, index: usize) -> Option<(&dyn BufferAccess, PipelineMemoryAccess)> {
+ self.inner.buffer(index)
+ }
+
+ #[inline]
+ fn num_images(&self) -> usize {
+ self.inner.num_images()
+ }
+
+ #[inline]
+ fn image(
+ &self,
+ index: usize,
+ ) -> Option<(
+ &dyn ImageAccess,
+ PipelineMemoryAccess,
+ ImageLayout,
+ ImageLayout,
+ ImageUninitializedSafe,
+ )> {
+ self.inner.image(index)
+ }
+}
+
+// Whether the command buffer can be submitted.
+#[derive(Debug)]
+enum SubmitState {
+ // The command buffer was created with the "SimultaneousUse" flag. Can always be submitted at
+ // any time.
+ Concurrent,
+
+ // The command buffer can only be submitted once simultaneously.
+ ExclusiveUse {
+ // True if the command buffer is current in use by the GPU.
+ in_use: AtomicBool,
+ },
+
+ // The command buffer can only ever be submitted once.
+ OneTime {
+ // True if the command buffer has already been submitted once and can be no longer be
+ // submitted.
+ already_submitted: AtomicBool,
+ },
+}
+
+macro_rules! err_gen {
+ ($name:ident { $($err:ident,)+ }) => (
+ #[derive(Debug, Clone)]
+ pub enum $name {
+ $(
+ $err($err),
+ )+
+ }
+
+ impl error::Error for $name {
+ #[inline]
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match *self {
+ $(
+ $name::$err(ref err) => Some(err),
+ )+
+ }
+ }
+ }
+
+ impl fmt::Display for $name {
+ #[inline]
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ write!(fmt, "{}", match *self {
+ $(
+ $name::$err(_) => {
+ concat!("a ", stringify!($err))
+ }
+ )+
+ })
+ }
+ }
+
+ $(
+ impl From<$err> for $name {
+ #[inline]
+ fn from(err: $err) -> $name {
+ $name::$err(err)
+ }
+ }
+ )+
+ );
+}
+
+err_gen!(BuildError {
+ AutoCommandBufferBuilderContextError,
+ OomError,
+});
+
+err_gen!(BeginRenderPassError {
+ AutoCommandBufferBuilderContextError,
+ SyncCommandBufferBuilderError,
+});
+
+err_gen!(CopyImageError {
+ AutoCommandBufferBuilderContextError,
+ CheckCopyImageError,
+ SyncCommandBufferBuilderError,
+});
+
+err_gen!(BlitImageError {
+ AutoCommandBufferBuilderContextError,
+ CheckBlitImageError,
+ SyncCommandBufferBuilderError,
+});
+
+err_gen!(ClearColorImageError {
+ AutoCommandBufferBuilderContextError,
+ CheckClearColorImageError,
+ SyncCommandBufferBuilderError,
+});
+
+err_gen!(CopyBufferError {
+ AutoCommandBufferBuilderContextError,
+ CheckCopyBufferError,
+ SyncCommandBufferBuilderError,
+});
+
+err_gen!(CopyBufferImageError {
+ AutoCommandBufferBuilderContextError,
+ CheckCopyBufferImageError,
+ SyncCommandBufferBuilderError,
+});
+
+err_gen!(CopyQueryPoolResultsError {
+ AutoCommandBufferBuilderContextError,
+ CheckCopyQueryPoolResultsError,
+ SyncCommandBufferBuilderError,
+});
+
+err_gen!(FillBufferError {
+ AutoCommandBufferBuilderContextError,
+ CheckFillBufferError,
+});
+
+err_gen!(DebugMarkerError {
+ AutoCommandBufferBuilderContextError,
+ CheckColorError,
+});
+
+err_gen!(DispatchError {
+ AutoCommandBufferBuilderContextError,
+ CheckPushConstantsValidityError,
+ CheckDescriptorSetsValidityError,
+ CheckDispatchError,
+ SyncCommandBufferBuilderError,
+});
+
+err_gen!(DispatchIndirectError {
+ AutoCommandBufferBuilderContextError,
+ CheckPushConstantsValidityError,
+ CheckDescriptorSetsValidityError,
+ CheckIndirectBufferError,
+ SyncCommandBufferBuilderError,
+});
+
+err_gen!(DrawError {
+ AutoCommandBufferBuilderContextError,
+ CheckDynamicStateValidityError,
+ CheckPushConstantsValidityError,
+ CheckDescriptorSetsValidityError,
+ CheckVertexBufferError,
+ SyncCommandBufferBuilderError,
+});
+
+err_gen!(DrawIndexedError {
+ AutoCommandBufferBuilderContextError,
+ CheckDynamicStateValidityError,
+ CheckPushConstantsValidityError,
+ CheckDescriptorSetsValidityError,
+ CheckVertexBufferError,
+ CheckIndexBufferError,
+ SyncCommandBufferBuilderError,
+});
+
+err_gen!(DrawIndirectError {
+ AutoCommandBufferBuilderContextError,
+ CheckDynamicStateValidityError,
+ CheckPushConstantsValidityError,
+ CheckDescriptorSetsValidityError,
+ CheckVertexBufferError,
+ CheckIndirectBufferError,
+ SyncCommandBufferBuilderError,
+});
+
+err_gen!(DrawIndexedIndirectError {
+ AutoCommandBufferBuilderContextError,
+ CheckDynamicStateValidityError,
+ CheckPushConstantsValidityError,
+ CheckDescriptorSetsValidityError,
+ CheckVertexBufferError,
+ CheckIndexBufferError,
+ CheckIndirectBufferError,
+ SyncCommandBufferBuilderError,
+});
+
+err_gen!(ExecuteCommandsError {
+ AutoCommandBufferBuilderContextError,
+ SyncCommandBufferBuilderError,
+});
+
+err_gen!(BeginQueryError {
+ AutoCommandBufferBuilderContextError,
+ CheckBeginQueryError,
+});
+
+err_gen!(EndQueryError {
+ AutoCommandBufferBuilderContextError,
+ CheckEndQueryError,
+});
+
+err_gen!(WriteTimestampError {
+ AutoCommandBufferBuilderContextError,
+ CheckWriteTimestampError,
+});
+
+err_gen!(ResetQueryPoolError {
+ AutoCommandBufferBuilderContextError,
+ CheckResetQueryPoolError,
+});
+
+err_gen!(UpdateBufferError {
+ AutoCommandBufferBuilderContextError,
+ CheckUpdateBufferError,
+});
+
+#[derive(Debug, Copy, Clone)]
+pub enum AutoCommandBufferBuilderContextError {
+ /// Operation forbidden inside of a render pass.
+ ForbiddenInsideRenderPass,
+ /// Operation forbidden outside of a render pass.
+ ForbiddenOutsideRenderPass,
+ /// Tried to use a secondary command buffer with a specified framebuffer that is
+ /// incompatible with the current framebuffer.
+ IncompatibleFramebuffer,
+ /// Tried to use a graphics pipeline or secondary command buffer whose render pass
+ /// is incompatible with the current render pass.
+ IncompatibleRenderPass,
+ /// The queue family doesn't allow this operation.
+ NotSupportedByQueueFamily,
+ /// Tried to end a render pass with subpasses remaining, or tried to go to next subpass with no
+ /// subpass remaining.
+ NumSubpassesMismatch {
+ /// Actual number of subpasses in the current render pass.
+ actual: u32,
+ /// Current subpass index before the failing command.
+ current: u32,
+ },
+ /// A query is active that conflicts with the current operation.
+ QueryIsActive,
+ /// This query was not active.
+ QueryNotActive,
+ /// A query is active that is not included in the `inheritance` of the secondary command buffer.
+ QueryNotInherited,
+ /// Tried to use a graphics pipeline or secondary command buffer whose subpass index
+ /// didn't match the current subpass index.
+ WrongSubpassIndex,
+ /// Tried to execute a secondary command buffer inside a subpass that only allows inline
+ /// commands, or a draw command in a subpass that only allows secondary command buffers.
+ WrongSubpassType,
+}
+
+impl error::Error for AutoCommandBufferBuilderContextError {}
+
+impl fmt::Display for AutoCommandBufferBuilderContextError {
+ #[inline]
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ write!(
+ fmt,
+ "{}",
+ match *self {
+ AutoCommandBufferBuilderContextError::ForbiddenInsideRenderPass => {
+ "operation forbidden inside of a render pass"
+ }
+ AutoCommandBufferBuilderContextError::ForbiddenOutsideRenderPass => {
+ "operation forbidden outside of a render pass"
+ }
+ AutoCommandBufferBuilderContextError::IncompatibleFramebuffer => {
+ "tried to use a secondary command buffer with a specified framebuffer that is \
+ incompatible with the current framebuffer"
+ }
+ AutoCommandBufferBuilderContextError::IncompatibleRenderPass => {
+ "tried to use a graphics pipeline or secondary command buffer whose render pass \
+ is incompatible with the current render pass"
+ }
+ AutoCommandBufferBuilderContextError::NotSupportedByQueueFamily => {
+ "the queue family doesn't allow this operation"
+ }
+ AutoCommandBufferBuilderContextError::NumSubpassesMismatch { .. } => {
+ "tried to end a render pass with subpasses remaining, or tried to go to next \
+ subpass with no subpass remaining"
+ }
+ AutoCommandBufferBuilderContextError::QueryIsActive => {
+ "a query is active that conflicts with the current operation"
+ }
+ AutoCommandBufferBuilderContextError::QueryNotActive => {
+ "this query was not active"
+ }
+ AutoCommandBufferBuilderContextError::QueryNotInherited => {
+ "a query is active that is not included in the inheritance of the secondary command buffer"
+ }
+ AutoCommandBufferBuilderContextError::WrongSubpassIndex => {
+ "tried to use a graphics pipeline whose subpass index didn't match the current \
+ subpass index"
+ }
+ AutoCommandBufferBuilderContextError::WrongSubpassType => {
+ "tried to execute a secondary command buffer inside a subpass that only allows \
+ inline commands, or a draw command in a subpass that only allows secondary \
+ command buffers"
+ }
+ }
+ )
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::buffer::BufferUsage;
+ use crate::buffer::CpuAccessibleBuffer;
+ use crate::command_buffer::synced::SyncCommandBufferBuilderError;
+ use crate::command_buffer::AutoCommandBufferBuilder;
+ use crate::command_buffer::CommandBufferExecError;
+ use crate::command_buffer::CommandBufferUsage;
+ use crate::command_buffer::ExecuteCommandsError;
+ use crate::command_buffer::PrimaryCommandBuffer;
+ use crate::device::physical::PhysicalDevice;
+ use crate::device::Device;
+ use crate::device::DeviceExtensions;
+ use crate::device::Features;
+ use crate::sync::GpuFuture;
+ use std::sync::Arc;
+
+ #[test]
+ fn copy_buffer_dimensions() {
+ let instance = instance!();
+
+ let phys = match PhysicalDevice::enumerate(&instance).next() {
+ Some(p) => p,
+ None => return,
+ };
+
+ let queue_family = match phys.queue_families().next() {
+ Some(q) => q,
+ None => return,
+ };
+
+ let (device, mut queues) = Device::new(
+ phys,
+ &Features::none(),
+ &DeviceExtensions::none(),
+ std::iter::once((queue_family, 0.5)),
+ )
+ .unwrap();
+
+ let queue = queues.next().unwrap();
+
+ let source = CpuAccessibleBuffer::from_iter(
+ device.clone(),
+ BufferUsage::all(),
+ true,
+ [1_u32, 2].iter().copied(),
+ )
+ .unwrap();
+
+ let destination = CpuAccessibleBuffer::from_iter(
+ device.clone(),
+ BufferUsage::all(),
+ true,
+ [0_u32, 10, 20, 3, 4].iter().copied(),
+ )
+ .unwrap();
+
+ let mut cbb = AutoCommandBufferBuilder::primary(
+ device.clone(),
+ queue.family(),
+ CommandBufferUsage::OneTimeSubmit,
+ )
+ .unwrap();
+
+ cbb.copy_buffer_dimensions(source.clone(), 0, destination.clone(), 1, 2)
+ .unwrap();
+
+ let cb = cbb.build().unwrap();
+
+ let future = cb
+ .execute(queue.clone())
+ .unwrap()
+ .then_signal_fence_and_flush()
+ .unwrap();
+ future.wait(None).unwrap();
+
+ let result = destination.read().unwrap();
+
+ assert_eq!(*result, [0_u32, 1, 2, 3, 4]);
+ }
+
+ #[test]
+ fn secondary_nonconcurrent_conflict() {
+ let (device, queue) = gfx_dev_and_queue!();
+
+ // Make a secondary CB that doesn't support simultaneous use.
+ let builder = AutoCommandBufferBuilder::secondary_compute(
+ device.clone(),
+ queue.family(),
+ CommandBufferUsage::MultipleSubmit,
+ )
+ .unwrap();
+ let secondary = Arc::new(builder.build().unwrap());
+
+ {
+ let mut builder = AutoCommandBufferBuilder::primary(
+ device.clone(),
+ queue.family(),
+ CommandBufferUsage::SimultaneousUse,
+ )
+ .unwrap();
+
+ // Add the secondary a first time
+ builder.execute_commands(secondary.clone()).unwrap();
+
+ // Recording the same non-concurrent secondary command buffer twice into the same
+ // primary is an error.
+ assert!(matches!(
+ builder.execute_commands(secondary.clone()),
+ Err(ExecuteCommandsError::SyncCommandBufferBuilderError(
+ SyncCommandBufferBuilderError::ExecError(
+ CommandBufferExecError::ExclusiveAlreadyInUse
+ )
+ ))
+ ));
+ }
+
+ {
+ let mut builder = AutoCommandBufferBuilder::primary(
+ device.clone(),
+ queue.family(),
+ CommandBufferUsage::SimultaneousUse,
+ )
+ .unwrap();
+ builder.execute_commands(secondary.clone()).unwrap();
+ let cb1 = builder.build().unwrap();
+
+ let mut builder = AutoCommandBufferBuilder::primary(
+ device.clone(),
+ queue.family(),
+ CommandBufferUsage::SimultaneousUse,
+ )
+ .unwrap();
+
+ // Recording the same non-concurrent secondary command buffer into multiple
+ // primaries is an error.
+ assert!(matches!(
+ builder.execute_commands(secondary.clone()),
+ Err(ExecuteCommandsError::SyncCommandBufferBuilderError(
+ SyncCommandBufferBuilderError::ExecError(
+ CommandBufferExecError::ExclusiveAlreadyInUse
+ )
+ ))
+ ));
+
+ std::mem::drop(cb1);
+
+ // Now that the first cb is dropped, we should be able to record.
+ builder.execute_commands(secondary.clone()).unwrap();
+ }
+ }
+}