aboutsummaryrefslogtreecommitdiff
path: root/src/command_buffer/sys.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/command_buffer/sys.rs')
-rw-r--r--src/command_buffer/sys.rs1972
1 files changed, 1972 insertions, 0 deletions
diff --git a/src/command_buffer/sys.rs b/src/command_buffer/sys.rs
new file mode 100644
index 0000000..0fb8b58
--- /dev/null
+++ b/src/command_buffer/sys.rs
@@ -0,0 +1,1972 @@
+// 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::BufferInner;
+use crate::buffer::TypedBufferAccess;
+use crate::check_errors;
+use crate::command_buffer::pool::UnsafeCommandPoolAlloc;
+use crate::command_buffer::CommandBufferInheritance;
+use crate::command_buffer::CommandBufferLevel;
+use crate::command_buffer::CommandBufferUsage;
+use crate::command_buffer::SecondaryCommandBuffer;
+use crate::command_buffer::SubpassContents;
+use crate::descriptor_set::sys::UnsafeDescriptorSet;
+use crate::device::Device;
+use crate::device::DeviceOwned;
+use crate::format::ClearValue;
+use crate::format::FormatTy;
+use crate::image::ImageAccess;
+use crate::image::ImageAspect;
+use crate::image::ImageAspects;
+use crate::image::ImageLayout;
+use crate::image::SampleCount;
+use crate::pipeline::depth_stencil::StencilFaces;
+use crate::pipeline::input_assembly::IndexType;
+use crate::pipeline::layout::PipelineLayout;
+use crate::pipeline::shader::ShaderStages;
+use crate::pipeline::viewport::Scissor;
+use crate::pipeline::viewport::Viewport;
+use crate::pipeline::ComputePipelineAbstract;
+use crate::pipeline::GraphicsPipelineAbstract;
+use crate::pipeline::PipelineBindPoint;
+use crate::query::QueriesRange;
+use crate::query::Query;
+use crate::query::QueryControlFlags;
+use crate::query::QueryResultElement;
+use crate::query::QueryResultFlags;
+use crate::render_pass::FramebufferAbstract;
+use crate::sampler::Filter;
+use crate::sync::AccessFlags;
+use crate::sync::Event;
+use crate::sync::PipelineStage;
+use crate::sync::PipelineStages;
+use crate::DeviceSize;
+use crate::OomError;
+use crate::VulkanObject;
+use ash::vk::Handle;
+use smallvec::SmallVec;
+use std::ffi::CStr;
+use std::fmt;
+use std::mem;
+use std::ops::Range;
+use std::sync::Arc;
+
+/// Command buffer being built.
+///
+/// You can add commands to an `UnsafeCommandBufferBuilder` by using the `AddCommand` trait.
+/// The `AddCommand<&Cmd>` trait is implemented on the `UnsafeCommandBufferBuilder` for any `Cmd`
+/// that is a raw Vulkan command.
+///
+/// When you are finished adding commands, you can use the `CommandBufferBuild` trait to turn this
+/// builder into an `UnsafeCommandBuffer`.
+pub struct UnsafeCommandBufferBuilder {
+ command_buffer: ash::vk::CommandBuffer,
+ device: Arc<Device>,
+ usage: CommandBufferUsage,
+}
+
+impl fmt::Debug for UnsafeCommandBufferBuilder {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "<Vulkan command buffer builder #{}>",
+ self.command_buffer.as_raw()
+ )
+ }
+}
+
+impl UnsafeCommandBufferBuilder {
+ /// Creates a new builder, for recording commands.
+ ///
+ /// # Safety
+ ///
+ /// - `pool_alloc` must outlive the returned builder and its created command buffer.
+ /// - `kind` must match how `pool_alloc` was created.
+ /// - All submitted commands must be valid and follow the requirements of the Vulkan specification.
+ /// - Any resources used by submitted commands must outlive the returned builder and its created command buffer. They must be protected against data races through manual synchronization.
+ ///
+ /// > **Note**: Some checks are still made with `debug_assert!`. Do not expect to be able to
+ /// > submit invalid commands.
+ pub unsafe fn new<F>(
+ pool_alloc: &UnsafeCommandPoolAlloc,
+ level: CommandBufferLevel<F>,
+ usage: CommandBufferUsage,
+ ) -> Result<UnsafeCommandBufferBuilder, OomError>
+ where
+ F: FramebufferAbstract,
+ {
+ let secondary = match level {
+ CommandBufferLevel::Primary => false,
+ CommandBufferLevel::Secondary(..) => true,
+ };
+
+ let device = pool_alloc.device().clone();
+ let fns = device.fns();
+
+ let vk_flags = {
+ let a = ash::vk::CommandBufferUsageFlags::from(usage);
+ let b = match level {
+ CommandBufferLevel::Secondary(ref inheritance)
+ if inheritance.render_pass.is_some() =>
+ {
+ ash::vk::CommandBufferUsageFlags::RENDER_PASS_CONTINUE
+ }
+ _ => ash::vk::CommandBufferUsageFlags::empty(),
+ };
+
+ a | b
+ };
+
+ let (rp, sp, fb) = match level {
+ CommandBufferLevel::Secondary(CommandBufferInheritance {
+ render_pass: Some(ref render_pass),
+ ..
+ }) => {
+ let rp = render_pass.subpass.render_pass().inner().internal_object();
+ let sp = render_pass.subpass.index();
+ let fb = match render_pass.framebuffer {
+ Some(ref fb) => {
+ // TODO: debug assert that the framebuffer is compatible with
+ // the render pass?
+ FramebufferAbstract::inner(fb).internal_object()
+ }
+ None => ash::vk::Framebuffer::null(),
+ };
+ (rp, sp, fb)
+ }
+ _ => (ash::vk::RenderPass::null(), 0, ash::vk::Framebuffer::null()),
+ };
+
+ let (oqe, qf, ps) = match level {
+ CommandBufferLevel::Secondary(CommandBufferInheritance {
+ occlusion_query,
+ query_statistics_flags,
+ ..
+ }) => {
+ let ps: ash::vk::QueryPipelineStatisticFlags = query_statistics_flags.into();
+ let (oqe, qf) = match occlusion_query {
+ Some(flags) => {
+ let qf = if flags.precise {
+ ash::vk::QueryControlFlags::PRECISE
+ } else {
+ ash::vk::QueryControlFlags::empty()
+ };
+ (ash::vk::TRUE, qf)
+ }
+ None => (0, ash::vk::QueryControlFlags::empty()),
+ };
+
+ (oqe, qf, ps)
+ }
+ _ => (
+ 0,
+ ash::vk::QueryControlFlags::empty(),
+ ash::vk::QueryPipelineStatisticFlags::empty(),
+ ),
+ };
+
+ let inheritance = ash::vk::CommandBufferInheritanceInfo {
+ render_pass: rp,
+ subpass: sp,
+ framebuffer: fb,
+ occlusion_query_enable: oqe,
+ query_flags: qf,
+ pipeline_statistics: ps,
+ ..Default::default()
+ };
+
+ let infos = ash::vk::CommandBufferBeginInfo {
+ flags: vk_flags,
+ p_inheritance_info: &inheritance,
+ ..Default::default()
+ };
+
+ check_errors(
+ fns.v1_0
+ .begin_command_buffer(pool_alloc.internal_object(), &infos),
+ )?;
+
+ Ok(UnsafeCommandBufferBuilder {
+ command_buffer: pool_alloc.internal_object(),
+ device: device.clone(),
+ usage,
+ })
+ }
+
+ /// Turns the builder into an actual command buffer.
+ #[inline]
+ pub fn build(self) -> Result<UnsafeCommandBuffer, OomError> {
+ unsafe {
+ let fns = self.device.fns();
+ check_errors(fns.v1_0.end_command_buffer(self.command_buffer))?;
+
+ Ok(UnsafeCommandBuffer {
+ command_buffer: self.command_buffer,
+ device: self.device.clone(),
+ usage: self.usage,
+ })
+ }
+ }
+
+ /// Calls `vkCmdBeginQuery` on the builder.
+ #[inline]
+ pub unsafe fn begin_query(&mut self, query: Query, flags: QueryControlFlags) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ let flags = if flags.precise {
+ ash::vk::QueryControlFlags::PRECISE
+ } else {
+ ash::vk::QueryControlFlags::empty()
+ };
+ fns.v1_0
+ .cmd_begin_query(cmd, query.pool().internal_object(), query.index(), flags);
+ }
+
+ /// Calls `vkCmdBeginRenderPass` on the builder.
+ #[inline]
+ pub unsafe fn begin_render_pass<F, I>(
+ &mut self,
+ framebuffer: &F,
+ subpass_contents: SubpassContents,
+ clear_values: I,
+ ) where
+ F: ?Sized + FramebufferAbstract,
+ I: IntoIterator<Item = ClearValue>,
+ {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+
+ // TODO: allow passing a different render pass
+ let raw_render_pass = framebuffer.render_pass().inner().internal_object();
+ let raw_framebuffer = framebuffer.inner().internal_object();
+
+ let raw_clear_values: SmallVec<[_; 12]> = clear_values
+ .into_iter()
+ .map(|clear_value| match clear_value {
+ ClearValue::None => ash::vk::ClearValue {
+ color: ash::vk::ClearColorValue { float32: [0.0; 4] },
+ },
+ ClearValue::Float(val) => ash::vk::ClearValue {
+ color: ash::vk::ClearColorValue { float32: val },
+ },
+ ClearValue::Int(val) => ash::vk::ClearValue {
+ color: ash::vk::ClearColorValue { int32: val },
+ },
+ ClearValue::Uint(val) => ash::vk::ClearValue {
+ color: ash::vk::ClearColorValue { uint32: val },
+ },
+ ClearValue::Depth(val) => ash::vk::ClearValue {
+ depth_stencil: ash::vk::ClearDepthStencilValue {
+ depth: val,
+ stencil: 0,
+ },
+ },
+ ClearValue::Stencil(val) => ash::vk::ClearValue {
+ depth_stencil: ash::vk::ClearDepthStencilValue {
+ depth: 0.0,
+ stencil: val,
+ },
+ },
+ ClearValue::DepthStencil((depth, stencil)) => ash::vk::ClearValue {
+ depth_stencil: ash::vk::ClearDepthStencilValue { depth, stencil },
+ },
+ })
+ .collect();
+
+ // TODO: allow customizing
+ let rect = [
+ 0..framebuffer.dimensions()[0],
+ 0..framebuffer.dimensions()[1],
+ ];
+
+ let begin = ash::vk::RenderPassBeginInfo {
+ render_pass: raw_render_pass,
+ framebuffer: raw_framebuffer,
+ render_area: ash::vk::Rect2D {
+ offset: ash::vk::Offset2D {
+ x: rect[0].start as i32,
+ y: rect[1].start as i32,
+ },
+ extent: ash::vk::Extent2D {
+ width: rect[0].end - rect[0].start,
+ height: rect[1].end - rect[1].start,
+ },
+ },
+ clear_value_count: raw_clear_values.len() as u32,
+ p_clear_values: raw_clear_values.as_ptr(),
+ ..Default::default()
+ };
+
+ fns.v1_0
+ .cmd_begin_render_pass(cmd, &begin, subpass_contents.into());
+ }
+
+ /// Calls `vkCmdBindDescriptorSets` on the builder.
+ ///
+ /// Does nothing if the list of descriptor sets is empty, as it would be a no-op and isn't a
+ /// valid usage of the command anyway.
+ #[inline]
+ pub unsafe fn bind_descriptor_sets<'s, S, I>(
+ &mut self,
+ pipeline_bind_point: PipelineBindPoint,
+ pipeline_layout: &PipelineLayout,
+ first_binding: u32,
+ sets: S,
+ dynamic_offsets: I,
+ ) where
+ S: IntoIterator<Item = &'s UnsafeDescriptorSet>,
+ I: IntoIterator<Item = u32>,
+ {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+
+ let sets: SmallVec<[_; 12]> = sets.into_iter().map(|s| s.internal_object()).collect();
+ if sets.is_empty() {
+ return;
+ }
+ let dynamic_offsets: SmallVec<[u32; 32]> = dynamic_offsets.into_iter().collect();
+
+ let num_bindings = sets.len() as u32;
+ debug_assert!(
+ first_binding + num_bindings <= pipeline_layout.descriptor_set_layouts().len() as u32
+ );
+
+ fns.v1_0.cmd_bind_descriptor_sets(
+ cmd,
+ pipeline_bind_point.into(),
+ pipeline_layout.internal_object(),
+ first_binding,
+ num_bindings,
+ sets.as_ptr(),
+ dynamic_offsets.len() as u32,
+ dynamic_offsets.as_ptr(),
+ );
+ }
+
+ /// Calls `vkCmdBindIndexBuffer` on the builder.
+ #[inline]
+ pub unsafe fn bind_index_buffer<B>(&mut self, buffer: &B, index_ty: IndexType)
+ where
+ B: ?Sized + BufferAccess,
+ {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+
+ let inner = buffer.inner();
+ debug_assert!(inner.offset < inner.buffer.size());
+ debug_assert!(inner.buffer.usage().index_buffer);
+
+ fns.v1_0.cmd_bind_index_buffer(
+ cmd,
+ inner.buffer.internal_object(),
+ inner.offset,
+ index_ty.into(),
+ );
+ }
+
+ /// Calls `vkCmdBindPipeline` on the builder with a compute pipeline.
+ #[inline]
+ pub unsafe fn bind_pipeline_compute<Cp>(&mut self, pipeline: &Cp)
+ where
+ Cp: ?Sized + ComputePipelineAbstract,
+ {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_bind_pipeline(
+ cmd,
+ ash::vk::PipelineBindPoint::COMPUTE,
+ pipeline.inner().internal_object(),
+ );
+ }
+
+ /// Calls `vkCmdBindPipeline` on the builder with a graphics pipeline.
+ #[inline]
+ pub unsafe fn bind_pipeline_graphics<Gp>(&mut self, pipeline: &Gp)
+ where
+ Gp: ?Sized + GraphicsPipelineAbstract,
+ {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ let inner = GraphicsPipelineAbstract::inner(pipeline).internal_object();
+ fns.v1_0
+ .cmd_bind_pipeline(cmd, ash::vk::PipelineBindPoint::GRAPHICS, inner);
+ }
+
+ /// Calls `vkCmdBindVertexBuffers` on the builder.
+ ///
+ /// Does nothing if the list of buffers is empty, as it would be a no-op and isn't a valid
+ /// usage of the command anyway.
+ #[inline]
+ pub unsafe fn bind_vertex_buffers(
+ &mut self,
+ first_binding: u32,
+ params: UnsafeCommandBufferBuilderBindVertexBuffer,
+ ) {
+ debug_assert_eq!(params.raw_buffers.len(), params.offsets.len());
+
+ if params.raw_buffers.is_empty() {
+ return;
+ }
+
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+
+ let num_bindings = params.raw_buffers.len() as u32;
+
+ debug_assert!({
+ let max_bindings = self
+ .device()
+ .physical_device()
+ .properties()
+ .max_vertex_input_bindings;
+ first_binding + num_bindings <= max_bindings
+ });
+
+ fns.v1_0.cmd_bind_vertex_buffers(
+ cmd,
+ first_binding,
+ num_bindings,
+ params.raw_buffers.as_ptr(),
+ params.offsets.as_ptr(),
+ );
+ }
+
+ /// Calls `vkCmdCopyImage` on the builder.
+ ///
+ /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid
+ /// usage of the command anyway.
+ #[inline]
+ pub unsafe fn copy_image<S, D, R>(
+ &mut self,
+ source: &S,
+ source_layout: ImageLayout,
+ destination: &D,
+ destination_layout: ImageLayout,
+ regions: R,
+ ) where
+ S: ?Sized + ImageAccess,
+ D: ?Sized + ImageAccess,
+ R: IntoIterator<Item = UnsafeCommandBufferBuilderImageCopy>,
+ {
+ // TODO: The correct check here is that the uncompressed element size of the source is
+ // equal to the compressed element size of the destination.
+ debug_assert!(
+ source.format().ty() == FormatTy::Compressed
+ || destination.format().ty() == FormatTy::Compressed
+ || source.format().size() == destination.format().size()
+ );
+
+ // Depth/Stencil formats are required to match exactly.
+ debug_assert!(
+ !matches!(
+ source.format().ty(),
+ FormatTy::Depth | FormatTy::Stencil | FormatTy::DepthStencil
+ ) || source.format() == destination.format()
+ );
+
+ debug_assert_eq!(source.samples(), destination.samples());
+ let source = source.inner();
+ debug_assert!(source.image.usage().transfer_source);
+ debug_assert!(
+ source_layout == ImageLayout::General
+ || source_layout == ImageLayout::TransferSrcOptimal
+ );
+
+ let destination = destination.inner();
+ debug_assert!(destination.image.usage().transfer_destination);
+ debug_assert!(
+ destination_layout == ImageLayout::General
+ || destination_layout == ImageLayout::TransferDstOptimal
+ );
+
+ let regions: SmallVec<[_; 8]> = regions
+ .into_iter()
+ .filter_map(|copy| {
+ // TODO: not everything is checked here
+ debug_assert!(
+ copy.source_base_array_layer + copy.layer_count <= source.num_layers as u32
+ );
+ debug_assert!(
+ copy.destination_base_array_layer + copy.layer_count
+ <= destination.num_layers as u32
+ );
+ debug_assert!(copy.source_mip_level < destination.num_mipmap_levels as u32);
+ debug_assert!(copy.destination_mip_level < destination.num_mipmap_levels as u32);
+
+ if copy.layer_count == 0 {
+ return None;
+ }
+
+ Some(ash::vk::ImageCopy {
+ src_subresource: ash::vk::ImageSubresourceLayers {
+ aspect_mask: copy.aspects.into(),
+ mip_level: copy.source_mip_level,
+ base_array_layer: copy.source_base_array_layer + source.first_layer as u32,
+ layer_count: copy.layer_count,
+ },
+ src_offset: ash::vk::Offset3D {
+ x: copy.source_offset[0],
+ y: copy.source_offset[1],
+ z: copy.source_offset[2],
+ },
+ dst_subresource: ash::vk::ImageSubresourceLayers {
+ aspect_mask: copy.aspects.into(),
+ mip_level: copy.destination_mip_level,
+ base_array_layer: copy.destination_base_array_layer
+ + destination.first_layer as u32,
+ layer_count: copy.layer_count,
+ },
+ dst_offset: ash::vk::Offset3D {
+ x: copy.destination_offset[0],
+ y: copy.destination_offset[1],
+ z: copy.destination_offset[2],
+ },
+ extent: ash::vk::Extent3D {
+ width: copy.extent[0],
+ height: copy.extent[1],
+ depth: copy.extent[2],
+ },
+ })
+ })
+ .collect();
+
+ if regions.is_empty() {
+ return;
+ }
+
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_copy_image(
+ cmd,
+ source.image.internal_object(),
+ source_layout.into(),
+ destination.image.internal_object(),
+ destination_layout.into(),
+ regions.len() as u32,
+ regions.as_ptr(),
+ );
+ }
+
+ /// Calls `vkCmdBlitImage` on the builder.
+ ///
+ /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid
+ /// usage of the command anyway.
+ #[inline]
+ pub unsafe fn blit_image<S, D, R>(
+ &mut self,
+ source: &S,
+ source_layout: ImageLayout,
+ destination: &D,
+ destination_layout: ImageLayout,
+ regions: R,
+ filter: Filter,
+ ) where
+ S: ?Sized + ImageAccess,
+ D: ?Sized + ImageAccess,
+ R: IntoIterator<Item = UnsafeCommandBufferBuilderImageBlit>,
+ {
+ debug_assert!(
+ filter == Filter::Nearest
+ || !matches!(
+ source.format().ty(),
+ FormatTy::Depth | FormatTy::Stencil | FormatTy::DepthStencil
+ )
+ );
+ debug_assert!(
+ (source.format().ty() == FormatTy::Uint)
+ == (destination.format().ty() == FormatTy::Uint)
+ );
+ debug_assert!(
+ (source.format().ty() == FormatTy::Sint)
+ == (destination.format().ty() == FormatTy::Sint)
+ );
+ debug_assert!(
+ source.format() == destination.format()
+ || !matches!(
+ source.format().ty(),
+ FormatTy::Depth | FormatTy::Stencil | FormatTy::DepthStencil
+ )
+ );
+
+ debug_assert_eq!(source.samples(), SampleCount::Sample1);
+ let source = source.inner();
+ debug_assert!(source.image.format_features().blit_src);
+ debug_assert!(source.image.usage().transfer_source);
+ debug_assert!(
+ source_layout == ImageLayout::General
+ || source_layout == ImageLayout::TransferSrcOptimal
+ );
+
+ debug_assert_eq!(destination.samples(), SampleCount::Sample1);
+ let destination = destination.inner();
+ debug_assert!(destination.image.format_features().blit_dst);
+ debug_assert!(destination.image.usage().transfer_destination);
+ debug_assert!(
+ destination_layout == ImageLayout::General
+ || destination_layout == ImageLayout::TransferDstOptimal
+ );
+
+ let regions: SmallVec<[_; 8]> = regions
+ .into_iter()
+ .filter_map(|blit| {
+ // TODO: not everything is checked here
+ debug_assert!(
+ blit.source_base_array_layer + blit.layer_count <= source.num_layers as u32
+ );
+ debug_assert!(
+ blit.destination_base_array_layer + blit.layer_count
+ <= destination.num_layers as u32
+ );
+ debug_assert!(blit.source_mip_level < destination.num_mipmap_levels as u32);
+ debug_assert!(blit.destination_mip_level < destination.num_mipmap_levels as u32);
+
+ if blit.layer_count == 0 {
+ return None;
+ }
+
+ Some(ash::vk::ImageBlit {
+ src_subresource: ash::vk::ImageSubresourceLayers {
+ aspect_mask: blit.aspects.into(),
+ mip_level: blit.source_mip_level,
+ base_array_layer: blit.source_base_array_layer + source.first_layer as u32,
+ layer_count: blit.layer_count,
+ },
+ src_offsets: [
+ ash::vk::Offset3D {
+ x: blit.source_top_left[0],
+ y: blit.source_top_left[1],
+ z: blit.source_top_left[2],
+ },
+ ash::vk::Offset3D {
+ x: blit.source_bottom_right[0],
+ y: blit.source_bottom_right[1],
+ z: blit.source_bottom_right[2],
+ },
+ ],
+ dst_subresource: ash::vk::ImageSubresourceLayers {
+ aspect_mask: blit.aspects.into(),
+ mip_level: blit.destination_mip_level,
+ base_array_layer: blit.destination_base_array_layer
+ + destination.first_layer as u32,
+ layer_count: blit.layer_count,
+ },
+ dst_offsets: [
+ ash::vk::Offset3D {
+ x: blit.destination_top_left[0],
+ y: blit.destination_top_left[1],
+ z: blit.destination_top_left[2],
+ },
+ ash::vk::Offset3D {
+ x: blit.destination_bottom_right[0],
+ y: blit.destination_bottom_right[1],
+ z: blit.destination_bottom_right[2],
+ },
+ ],
+ })
+ })
+ .collect();
+
+ if regions.is_empty() {
+ return;
+ }
+
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_blit_image(
+ cmd,
+ source.image.internal_object(),
+ source_layout.into(),
+ destination.image.internal_object(),
+ destination_layout.into(),
+ regions.len() as u32,
+ regions.as_ptr(),
+ filter.into(),
+ );
+ }
+
+ // TODO: missing structs
+ /*/// Calls `vkCmdClearAttachments` on the builder.
+ ///
+ /// Does nothing if the list of attachments or the list of rects is empty, as it would be a
+ /// no-op and isn't a valid usage of the command anyway.
+ #[inline]
+ pub unsafe fn clear_attachments<A, R>(&mut self, attachments: A, rects: R)
+ where A: IntoIterator<Item = >,
+ R: IntoIterator<Item = >
+ {
+ let attachments: SmallVec<[_; 16]> = attachments.map().collect();
+ let rects: SmallVec<[_; 4]> = rects.map().collect();
+
+ if attachments.is_empty() || rects.is_empty() {
+ return;
+ }
+
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.CmdClearAttachments(cmd, attachments.len() as u32, attachments.as_ptr(),
+ rects.len() as u32, rects.as_ptr());
+ }*/
+
+ /// Calls `vkCmdClearColorImage` on the builder.
+ ///
+ /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid
+ /// usage of the command anyway.
+ // TODO: ClearValue could be more precise
+ pub unsafe fn clear_color_image<I, R>(
+ &mut self,
+ image: &I,
+ layout: ImageLayout,
+ color: ClearValue,
+ regions: R,
+ ) where
+ I: ?Sized + ImageAccess,
+ R: IntoIterator<Item = UnsafeCommandBufferBuilderColorImageClear>,
+ {
+ debug_assert!(
+ image.format().ty() == FormatTy::Float
+ || image.format().ty() == FormatTy::Uint
+ || image.format().ty() == FormatTy::Sint
+ );
+
+ let image = image.inner();
+ debug_assert!(image.image.usage().transfer_destination);
+ debug_assert!(layout == ImageLayout::General || layout == ImageLayout::TransferDstOptimal);
+
+ let color = match color {
+ ClearValue::Float(val) => ash::vk::ClearColorValue { float32: val },
+ ClearValue::Int(val) => ash::vk::ClearColorValue { int32: val },
+ ClearValue::Uint(val) => ash::vk::ClearColorValue { uint32: val },
+ _ => ash::vk::ClearColorValue { float32: [0.0; 4] },
+ };
+
+ let regions: SmallVec<[_; 8]> = regions
+ .into_iter()
+ .filter_map(|region| {
+ debug_assert!(
+ region.layer_count + region.base_array_layer <= image.num_layers as u32
+ );
+ debug_assert!(
+ region.level_count + region.base_mip_level <= image.num_mipmap_levels as u32
+ );
+
+ if region.layer_count == 0 || region.level_count == 0 {
+ return None;
+ }
+
+ Some(ash::vk::ImageSubresourceRange {
+ aspect_mask: ash::vk::ImageAspectFlags::COLOR,
+ base_mip_level: region.base_mip_level + image.first_mipmap_level as u32,
+ level_count: region.level_count,
+ base_array_layer: region.base_array_layer + image.first_layer as u32,
+ layer_count: region.layer_count,
+ })
+ })
+ .collect();
+
+ if regions.is_empty() {
+ return;
+ }
+
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_clear_color_image(
+ cmd,
+ image.image.internal_object(),
+ layout.into(),
+ &color,
+ regions.len() as u32,
+ regions.as_ptr(),
+ );
+ }
+
+ /// Calls `vkCmdCopyBuffer` on the builder.
+ ///
+ /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid
+ /// usage of the command anyway.
+ #[inline]
+ pub unsafe fn copy_buffer<S, D, R>(&mut self, source: &S, destination: &D, regions: R)
+ where
+ S: ?Sized + BufferAccess,
+ D: ?Sized + BufferAccess,
+ R: IntoIterator<Item = (DeviceSize, DeviceSize, DeviceSize)>,
+ {
+ // TODO: debug assert that there's no overlap in the destinations?
+
+ let source = source.inner();
+ debug_assert!(source.offset < source.buffer.size());
+ debug_assert!(source.buffer.usage().transfer_source);
+
+ let destination = destination.inner();
+ debug_assert!(destination.offset < destination.buffer.size());
+ debug_assert!(destination.buffer.usage().transfer_destination);
+
+ let regions: SmallVec<[_; 8]> = regions
+ .into_iter()
+ .map(|(sr, de, sz)| ash::vk::BufferCopy {
+ src_offset: sr + source.offset,
+ dst_offset: de + destination.offset,
+ size: sz,
+ })
+ .collect();
+
+ if regions.is_empty() {
+ return;
+ }
+
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_copy_buffer(
+ cmd,
+ source.buffer.internal_object(),
+ destination.buffer.internal_object(),
+ regions.len() as u32,
+ regions.as_ptr(),
+ );
+ }
+
+ /// Calls `vkCmdCopyBufferToImage` on the builder.
+ ///
+ /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid
+ /// usage of the command anyway.
+ #[inline]
+ pub unsafe fn copy_buffer_to_image<S, D, R>(
+ &mut self,
+ source: &S,
+ destination: &D,
+ destination_layout: ImageLayout,
+ regions: R,
+ ) where
+ S: ?Sized + BufferAccess,
+ D: ?Sized + ImageAccess,
+ R: IntoIterator<Item = UnsafeCommandBufferBuilderBufferImageCopy>,
+ {
+ let source = source.inner();
+ debug_assert!(source.offset < source.buffer.size());
+ debug_assert!(source.buffer.usage().transfer_source);
+
+ debug_assert_eq!(destination.samples(), SampleCount::Sample1);
+ let destination = destination.inner();
+ debug_assert!(destination.image.usage().transfer_destination);
+ debug_assert!(
+ destination_layout == ImageLayout::General
+ || destination_layout == ImageLayout::TransferDstOptimal
+ );
+
+ let regions: SmallVec<[_; 8]> = regions
+ .into_iter()
+ .map(|copy| {
+ debug_assert!(copy.image_layer_count <= destination.num_layers as u32);
+ debug_assert!(copy.image_mip_level < destination.num_mipmap_levels as u32);
+
+ ash::vk::BufferImageCopy {
+ buffer_offset: source.offset + copy.buffer_offset,
+ buffer_row_length: copy.buffer_row_length,
+ buffer_image_height: copy.buffer_image_height,
+ image_subresource: ash::vk::ImageSubresourceLayers {
+ aspect_mask: copy.image_aspect.into(),
+ mip_level: copy.image_mip_level + destination.first_mipmap_level as u32,
+ base_array_layer: copy.image_base_array_layer
+ + destination.first_layer as u32,
+ layer_count: copy.image_layer_count,
+ },
+ image_offset: ash::vk::Offset3D {
+ x: copy.image_offset[0],
+ y: copy.image_offset[1],
+ z: copy.image_offset[2],
+ },
+ image_extent: ash::vk::Extent3D {
+ width: copy.image_extent[0],
+ height: copy.image_extent[1],
+ depth: copy.image_extent[2],
+ },
+ }
+ })
+ .collect();
+
+ if regions.is_empty() {
+ return;
+ }
+
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_copy_buffer_to_image(
+ cmd,
+ source.buffer.internal_object(),
+ destination.image.internal_object(),
+ destination_layout.into(),
+ regions.len() as u32,
+ regions.as_ptr(),
+ );
+ }
+
+ /// Calls `vkCmdCopyImageToBuffer` on the builder.
+ ///
+ /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid
+ /// usage of the command anyway.
+ #[inline]
+ pub unsafe fn copy_image_to_buffer<S, D, R>(
+ &mut self,
+ source: &S,
+ source_layout: ImageLayout,
+ destination: &D,
+ regions: R,
+ ) where
+ S: ?Sized + ImageAccess,
+ D: ?Sized + BufferAccess,
+ R: IntoIterator<Item = UnsafeCommandBufferBuilderBufferImageCopy>,
+ {
+ debug_assert_eq!(source.samples(), SampleCount::Sample1);
+ let source = source.inner();
+ debug_assert!(source.image.usage().transfer_source);
+ debug_assert!(
+ source_layout == ImageLayout::General
+ || source_layout == ImageLayout::TransferSrcOptimal
+ );
+
+ let destination = destination.inner();
+ debug_assert!(destination.offset < destination.buffer.size());
+ debug_assert!(destination.buffer.usage().transfer_destination);
+
+ let regions: SmallVec<[_; 8]> = regions
+ .into_iter()
+ .map(|copy| {
+ debug_assert!(copy.image_layer_count <= source.num_layers as u32);
+ debug_assert!(copy.image_mip_level < source.num_mipmap_levels as u32);
+
+ ash::vk::BufferImageCopy {
+ buffer_offset: destination.offset + copy.buffer_offset,
+ buffer_row_length: copy.buffer_row_length,
+ buffer_image_height: copy.buffer_image_height,
+ image_subresource: ash::vk::ImageSubresourceLayers {
+ aspect_mask: copy.image_aspect.into(),
+ mip_level: copy.image_mip_level + source.first_mipmap_level as u32,
+ base_array_layer: copy.image_base_array_layer + source.first_layer as u32,
+ layer_count: copy.image_layer_count,
+ },
+ image_offset: ash::vk::Offset3D {
+ x: copy.image_offset[0],
+ y: copy.image_offset[1],
+ z: copy.image_offset[2],
+ },
+ image_extent: ash::vk::Extent3D {
+ width: copy.image_extent[0],
+ height: copy.image_extent[1],
+ depth: copy.image_extent[2],
+ },
+ }
+ })
+ .collect();
+
+ if regions.is_empty() {
+ return;
+ }
+
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_copy_image_to_buffer(
+ cmd,
+ source.image.internal_object(),
+ source_layout.into(),
+ destination.buffer.internal_object(),
+ regions.len() as u32,
+ regions.as_ptr(),
+ );
+ }
+
+ /// Calls `vkCmdCopyQueryPoolResults` on the builder.
+ #[inline]
+ pub unsafe fn copy_query_pool_results<D, T>(
+ &mut self,
+ queries: QueriesRange,
+ destination: D,
+ stride: DeviceSize,
+ flags: QueryResultFlags,
+ ) where
+ D: BufferAccess + TypedBufferAccess<Content = [T]>,
+ T: QueryResultElement,
+ {
+ let destination = destination.inner();
+ let range = queries.range();
+ debug_assert!(destination.offset < destination.buffer.size());
+ debug_assert!(destination.buffer.usage().transfer_destination);
+ debug_assert!(destination.offset % std::mem::size_of::<T>() as DeviceSize == 0);
+ debug_assert!(stride % std::mem::size_of::<T>() as DeviceSize == 0);
+
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_copy_query_pool_results(
+ cmd,
+ queries.pool().internal_object(),
+ range.start,
+ range.end - range.start,
+ destination.buffer.internal_object(),
+ destination.offset,
+ stride,
+ ash::vk::QueryResultFlags::from(flags) | T::FLAG,
+ );
+ }
+
+ /// Calls `vkCmdDispatch` on the builder.
+ #[inline]
+ pub unsafe fn dispatch(&mut self, group_counts: [u32; 3]) {
+ debug_assert!({
+ let max_group_counts = self
+ .device()
+ .physical_device()
+ .properties()
+ .max_compute_work_group_count;
+ group_counts[0] <= max_group_counts[0]
+ && group_counts[1] <= max_group_counts[1]
+ && group_counts[2] <= max_group_counts[2]
+ });
+
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0
+ .cmd_dispatch(cmd, group_counts[0], group_counts[1], group_counts[2]);
+ }
+
+ /// Calls `vkCmdDispatchIndirect` on the builder.
+ #[inline]
+ pub unsafe fn dispatch_indirect<B>(&mut self, buffer: &B)
+ where
+ B: ?Sized + BufferAccess,
+ {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+
+ let inner = buffer.inner();
+ debug_assert!(inner.offset < inner.buffer.size());
+ debug_assert!(inner.buffer.usage().indirect_buffer);
+ debug_assert_eq!(inner.offset % 4, 0);
+
+ fns.v1_0
+ .cmd_dispatch_indirect(cmd, inner.buffer.internal_object(), inner.offset);
+ }
+
+ /// Calls `vkCmdDraw` on the builder.
+ #[inline]
+ pub unsafe fn draw(
+ &mut self,
+ vertex_count: u32,
+ instance_count: u32,
+ first_vertex: u32,
+ first_instance: u32,
+ ) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_draw(
+ cmd,
+ vertex_count,
+ instance_count,
+ first_vertex,
+ first_instance,
+ );
+ }
+
+ /// Calls `vkCmdDrawIndexed` on the builder.
+ #[inline]
+ pub unsafe fn draw_indexed(
+ &mut self,
+ index_count: u32,
+ instance_count: u32,
+ first_index: u32,
+ vertex_offset: i32,
+ first_instance: u32,
+ ) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_draw_indexed(
+ cmd,
+ index_count,
+ instance_count,
+ first_index,
+ vertex_offset,
+ first_instance,
+ );
+ }
+
+ /// Calls `vkCmdDrawIndirect` on the builder.
+ #[inline]
+ pub unsafe fn draw_indirect<B>(&mut self, buffer: &B, draw_count: u32, stride: u32)
+ where
+ B: ?Sized + BufferAccess,
+ {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+
+ debug_assert!(
+ draw_count == 0
+ || ((stride % 4) == 0)
+ && stride as usize >= mem::size_of::<ash::vk::DrawIndirectCommand>()
+ );
+
+ let inner = buffer.inner();
+ debug_assert!(inner.offset < inner.buffer.size());
+ debug_assert!(inner.buffer.usage().indirect_buffer);
+
+ fns.v1_0.cmd_draw_indirect(
+ cmd,
+ inner.buffer.internal_object(),
+ inner.offset,
+ draw_count,
+ stride,
+ );
+ }
+
+ /// Calls `vkCmdDrawIndexedIndirect` on the builder.
+ #[inline]
+ pub unsafe fn draw_indexed_indirect<B>(&mut self, buffer: &B, draw_count: u32, stride: u32)
+ where
+ B: ?Sized + BufferAccess,
+ {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+
+ let inner = buffer.inner();
+ debug_assert!(inner.offset < inner.buffer.size());
+ debug_assert!(inner.buffer.usage().indirect_buffer);
+
+ fns.v1_0.cmd_draw_indexed_indirect(
+ cmd,
+ inner.buffer.internal_object(),
+ inner.offset,
+ draw_count,
+ stride,
+ );
+ }
+
+ /// Calls `vkCmdEndQuery` on the builder.
+ #[inline]
+ pub unsafe fn end_query(&mut self, query: Query) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0
+ .cmd_end_query(cmd, query.pool().internal_object(), query.index());
+ }
+
+ /// Calls `vkCmdEndRenderPass` on the builder.
+ #[inline]
+ pub unsafe fn end_render_pass(&mut self) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_end_render_pass(cmd);
+ }
+
+ /// Calls `vkCmdExecuteCommands` on the builder.
+ ///
+ /// Does nothing if the list of command buffers is empty, as it would be a no-op and isn't a
+ /// valid usage of the command anyway.
+ #[inline]
+ pub unsafe fn execute_commands(&mut self, cbs: UnsafeCommandBufferBuilderExecuteCommands) {
+ if cbs.raw_cbs.is_empty() {
+ return;
+ }
+
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0
+ .cmd_execute_commands(cmd, cbs.raw_cbs.len() as u32, cbs.raw_cbs.as_ptr());
+ }
+
+ /// Calls `vkCmdFillBuffer` on the builder.
+ #[inline]
+ pub unsafe fn fill_buffer<B>(&mut self, buffer: &B, data: u32)
+ where
+ B: ?Sized + BufferAccess,
+ {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+
+ let size = buffer.size();
+
+ let (buffer_handle, offset) = {
+ let BufferInner {
+ buffer: buffer_inner,
+ offset,
+ } = buffer.inner();
+ debug_assert!(buffer_inner.usage().transfer_destination);
+ debug_assert_eq!(offset % 4, 0);
+ (buffer_inner.internal_object(), offset)
+ };
+
+ fns.v1_0
+ .cmd_fill_buffer(cmd, buffer_handle, offset, size, data);
+ }
+
+ /// Calls `vkCmdNextSubpass` on the builder.
+ #[inline]
+ pub unsafe fn next_subpass(&mut self, subpass_contents: SubpassContents) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_next_subpass(cmd, subpass_contents.into());
+ }
+
+ #[inline]
+ pub unsafe fn pipeline_barrier(&mut self, command: &UnsafeCommandBufferBuilderPipelineBarrier) {
+ // If barrier is empty, don't do anything.
+ if command.src_stage_mask.is_empty() || command.dst_stage_mask.is_empty() {
+ debug_assert!(command.src_stage_mask.is_empty() && command.dst_stage_mask.is_empty());
+ debug_assert!(command.memory_barriers.is_empty());
+ debug_assert!(command.buffer_barriers.is_empty());
+ debug_assert!(command.image_barriers.is_empty());
+ return;
+ }
+
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+
+ debug_assert!(!command.src_stage_mask.is_empty());
+ debug_assert!(!command.dst_stage_mask.is_empty());
+
+ fns.v1_0.cmd_pipeline_barrier(
+ cmd,
+ command.src_stage_mask,
+ command.dst_stage_mask,
+ command.dependency_flags,
+ command.memory_barriers.len() as u32,
+ command.memory_barriers.as_ptr(),
+ command.buffer_barriers.len() as u32,
+ command.buffer_barriers.as_ptr(),
+ command.image_barriers.len() as u32,
+ command.image_barriers.as_ptr(),
+ );
+ }
+
+ /// Calls `vkCmdPushConstants` on the builder.
+ #[inline]
+ pub unsafe fn push_constants<D>(
+ &mut self,
+ pipeline_layout: &PipelineLayout,
+ stages: ShaderStages,
+ offset: u32,
+ size: u32,
+ data: &D,
+ ) where
+ D: ?Sized,
+ {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+
+ debug_assert!(stages != ShaderStages::none());
+ debug_assert!(size > 0);
+ debug_assert_eq!(size % 4, 0);
+ debug_assert_eq!(offset % 4, 0);
+ debug_assert!(mem::size_of_val(data) >= size as usize);
+
+ fns.v1_0.cmd_push_constants(
+ cmd,
+ pipeline_layout.internal_object(),
+ stages.into(),
+ offset as u32,
+ size as u32,
+ data as *const D as *const _,
+ );
+ }
+
+ /// Calls `vkCmdResetEvent` on the builder.
+ #[inline]
+ pub unsafe fn reset_event(&mut self, event: &Event, stages: PipelineStages) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+
+ debug_assert!(!stages.host);
+ debug_assert_ne!(stages, PipelineStages::none());
+
+ fns.v1_0
+ .cmd_reset_event(cmd, event.internal_object(), stages.into());
+ }
+
+ /// Calls `vkCmdResetQueryPool` on the builder.
+ #[inline]
+ pub unsafe fn reset_query_pool(&mut self, queries: QueriesRange) {
+ let range = queries.range();
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_reset_query_pool(
+ cmd,
+ queries.pool().internal_object(),
+ range.start,
+ range.end - range.start,
+ );
+ }
+
+ /// Calls `vkCmdSetBlendConstants` on the builder.
+ #[inline]
+ pub unsafe fn set_blend_constants(&mut self, constants: [f32; 4]) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_set_blend_constants(cmd, &constants);
+ }
+
+ /// Calls `vkCmdSetDepthBias` on the builder.
+ #[inline]
+ pub unsafe fn set_depth_bias(&mut self, constant_factor: f32, clamp: f32, slope_factor: f32) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ debug_assert!(clamp == 0.0 || self.device().enabled_features().depth_bias_clamp);
+ fns.v1_0
+ .cmd_set_depth_bias(cmd, constant_factor, clamp, slope_factor);
+ }
+
+ /// Calls `vkCmdSetDepthBounds` on the builder.
+ #[inline]
+ pub unsafe fn set_depth_bounds(&mut self, min: f32, max: f32) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ debug_assert!(min >= 0.0 && min <= 1.0);
+ debug_assert!(max >= 0.0 && max <= 1.0);
+ fns.v1_0.cmd_set_depth_bounds(cmd, min, max);
+ }
+
+ /// Calls `vkCmdSetEvent` on the builder.
+ #[inline]
+ pub unsafe fn set_event(&mut self, event: &Event, stages: PipelineStages) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+
+ debug_assert!(!stages.host);
+ debug_assert_ne!(stages, PipelineStages::none());
+
+ fns.v1_0
+ .cmd_set_event(cmd, event.internal_object(), stages.into());
+ }
+
+ /// Calls `vkCmdSetLineWidth` on the builder.
+ #[inline]
+ pub unsafe fn set_line_width(&mut self, line_width: f32) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ debug_assert!(line_width == 1.0 || self.device().enabled_features().wide_lines);
+ fns.v1_0.cmd_set_line_width(cmd, line_width);
+ }
+
+ /// Calls `vkCmdSetStencilCompareMask` on the builder.
+ #[inline]
+ pub unsafe fn set_stencil_compare_mask(&mut self, face_mask: StencilFaces, compare_mask: u32) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0
+ .cmd_set_stencil_compare_mask(cmd, face_mask.into(), compare_mask);
+ }
+
+ /// Calls `vkCmdSetStencilReference` on the builder.
+ #[inline]
+ pub unsafe fn set_stencil_reference(&mut self, face_mask: StencilFaces, reference: u32) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0
+ .cmd_set_stencil_reference(cmd, face_mask.into(), reference);
+ }
+
+ /// Calls `vkCmdSetStencilWriteMask` on the builder.
+ #[inline]
+ pub unsafe fn set_stencil_write_mask(&mut self, face_mask: StencilFaces, write_mask: u32) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0
+ .cmd_set_stencil_write_mask(cmd, face_mask.into(), write_mask);
+ }
+
+ /// Calls `vkCmdSetScissor` on the builder.
+ ///
+ /// If the list is empty then the command is automatically ignored.
+ #[inline]
+ pub unsafe fn set_scissor<I>(&mut self, first_scissor: u32, scissors: I)
+ where
+ I: IntoIterator<Item = Scissor>,
+ {
+ let scissors = scissors
+ .into_iter()
+ .map(|v| ash::vk::Rect2D::from(v.clone()))
+ .collect::<SmallVec<[_; 16]>>();
+ if scissors.is_empty() {
+ return;
+ }
+
+ debug_assert!(scissors.iter().all(|s| s.offset.x >= 0 && s.offset.y >= 0));
+ debug_assert!(scissors.iter().all(|s| {
+ s.extent.width < i32::MAX as u32
+ && s.extent.height < i32::MAX as u32
+ && s.offset.x.checked_add(s.extent.width as i32).is_some()
+ && s.offset.y.checked_add(s.extent.height as i32).is_some()
+ }));
+ debug_assert!(
+ (first_scissor == 0 && scissors.len() == 1)
+ || self.device().enabled_features().multi_viewport
+ );
+ debug_assert!({
+ let max = self
+ .device()
+ .physical_device()
+ .properties()
+ .max_viewports;
+ first_scissor + scissors.len() as u32 <= max
+ });
+
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0
+ .cmd_set_scissor(cmd, first_scissor, scissors.len() as u32, scissors.as_ptr());
+ }
+
+ /// Calls `vkCmdSetViewport` on the builder.
+ ///
+ /// If the list is empty then the command is automatically ignored.
+ #[inline]
+ pub unsafe fn set_viewport<I>(&mut self, first_viewport: u32, viewports: I)
+ where
+ I: IntoIterator<Item = Viewport>,
+ {
+ let viewports = viewports
+ .into_iter()
+ .map(|v| v.clone().into())
+ .collect::<SmallVec<[_; 16]>>();
+ if viewports.is_empty() {
+ return;
+ }
+
+ debug_assert!(
+ (first_viewport == 0 && viewports.len() == 1)
+ || self.device().enabled_features().multi_viewport
+ );
+ debug_assert!({
+ let max = self
+ .device()
+ .physical_device()
+ .properties()
+ .max_viewports;
+ first_viewport + viewports.len() as u32 <= max
+ });
+
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_set_viewport(
+ cmd,
+ first_viewport,
+ viewports.len() as u32,
+ viewports.as_ptr(),
+ );
+ }
+
+ /// Calls `vkCmdUpdateBuffer` on the builder.
+ #[inline]
+ pub unsafe fn update_buffer<B, D>(&mut self, buffer: &B, data: &D)
+ where
+ B: ?Sized + BufferAccess,
+ D: ?Sized,
+ {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+
+ let size = buffer.size();
+ debug_assert_eq!(size % 4, 0);
+ debug_assert!(size <= 65536);
+ debug_assert!(size <= mem::size_of_val(data) as DeviceSize);
+
+ let (buffer_handle, offset) = {
+ let BufferInner {
+ buffer: buffer_inner,
+ offset,
+ } = buffer.inner();
+ debug_assert!(buffer_inner.usage().transfer_destination);
+ debug_assert_eq!(offset % 4, 0);
+ (buffer_inner.internal_object(), offset)
+ };
+
+ fns.v1_0.cmd_update_buffer(
+ cmd,
+ buffer_handle,
+ offset,
+ size,
+ data as *const D as *const _,
+ );
+ }
+
+ /// Calls `vkCmdWriteTimestamp` on the builder.
+ #[inline]
+ pub unsafe fn write_timestamp(&mut self, query: Query, stage: PipelineStage) {
+ let fns = self.device().fns();
+ let cmd = self.internal_object();
+ fns.v1_0.cmd_write_timestamp(
+ cmd,
+ stage.into(),
+ query.pool().internal_object(),
+ query.index(),
+ );
+ }
+
+ /// Calls `vkCmdBeginDebugUtilsLabelEXT` on the builder.
+ ///
+ /// # Safety
+ /// The command pool that this command buffer was allocated from must support graphics or
+ /// compute operations
+ #[inline]
+ pub unsafe fn debug_marker_begin(&mut self, name: &CStr, color: [f32; 4]) {
+ let fns = self.device().instance().fns();
+ let cmd = self.internal_object();
+ let info = ash::vk::DebugUtilsLabelEXT {
+ p_label_name: name.as_ptr(),
+ color,
+ ..Default::default()
+ };
+ fns.ext_debug_utils
+ .cmd_begin_debug_utils_label_ext(cmd, &info);
+ }
+
+ /// Calls `vkCmdEndDebugUtilsLabelEXT` on the builder.
+ ///
+ /// # Safety
+ /// There must be an outstanding `vkCmdBeginDebugUtilsLabelEXT` command prior to the
+ /// `vkQueueEndDebugUtilsLabelEXT` on the queue tha `CommandBuffer` is submitted to.
+ #[inline]
+ pub unsafe fn debug_marker_end(&mut self) {
+ let fns = self.device().instance().fns();
+ let cmd = self.internal_object();
+ fns.ext_debug_utils.cmd_end_debug_utils_label_ext(cmd);
+ }
+
+ /// Calls `vkCmdInsertDebugUtilsLabelEXT` on the builder.
+ ///
+ /// # Safety
+ /// The command pool that this command buffer was allocated from must support graphics or
+ /// compute operations
+ #[inline]
+ pub unsafe fn debug_marker_insert(&mut self, name: &CStr, color: [f32; 4]) {
+ let fns = self.device().instance().fns();
+ let cmd = self.internal_object();
+ let info = ash::vk::DebugUtilsLabelEXT {
+ p_label_name: name.as_ptr(),
+ color,
+ ..Default::default()
+ };
+ fns.ext_debug_utils
+ .cmd_insert_debug_utils_label_ext(cmd, &info);
+ }
+}
+
+unsafe impl DeviceOwned for UnsafeCommandBufferBuilder {
+ #[inline]
+ fn device(&self) -> &Arc<Device> {
+ &self.device
+ }
+}
+
+unsafe impl VulkanObject for UnsafeCommandBufferBuilder {
+ type Object = ash::vk::CommandBuffer;
+
+ #[inline]
+ fn internal_object(&self) -> ash::vk::CommandBuffer {
+ self.command_buffer
+ }
+}
+
+/// Prototype for a `vkCmdBindVertexBuffers`.
+pub struct UnsafeCommandBufferBuilderBindVertexBuffer {
+ // Raw handles of the buffers to bind.
+ raw_buffers: SmallVec<[ash::vk::Buffer; 4]>,
+ // Raw offsets of the buffers to bind.
+ offsets: SmallVec<[DeviceSize; 4]>,
+}
+
+impl UnsafeCommandBufferBuilderBindVertexBuffer {
+ /// Builds a new empty list.
+ #[inline]
+ pub fn new() -> UnsafeCommandBufferBuilderBindVertexBuffer {
+ UnsafeCommandBufferBuilderBindVertexBuffer {
+ raw_buffers: SmallVec::new(),
+ offsets: SmallVec::new(),
+ }
+ }
+
+ /// Adds a buffer to the list.
+ #[inline]
+ pub fn add<B>(&mut self, buffer: &B)
+ where
+ B: ?Sized + BufferAccess,
+ {
+ let inner = buffer.inner();
+ debug_assert!(inner.buffer.usage().vertex_buffer);
+ self.raw_buffers.push(inner.buffer.internal_object());
+ self.offsets.push(inner.offset);
+ }
+}
+
+/// Prototype for a `vkCmdExecuteCommands`.
+pub struct UnsafeCommandBufferBuilderExecuteCommands {
+ // Raw handles of the command buffers to execute.
+ raw_cbs: SmallVec<[ash::vk::CommandBuffer; 4]>,
+}
+
+impl UnsafeCommandBufferBuilderExecuteCommands {
+ /// Builds a new empty list.
+ #[inline]
+ pub fn new() -> UnsafeCommandBufferBuilderExecuteCommands {
+ UnsafeCommandBufferBuilderExecuteCommands {
+ raw_cbs: SmallVec::new(),
+ }
+ }
+
+ /// Adds a command buffer to the list.
+ #[inline]
+ pub fn add<C>(&mut self, cb: &C)
+ where
+ C: ?Sized + SecondaryCommandBuffer,
+ {
+ // TODO: debug assert that it is a secondary command buffer?
+ self.raw_cbs.push(cb.inner().internal_object());
+ }
+
+ /// Adds a command buffer to the list.
+ #[inline]
+ pub unsafe fn add_raw(&mut self, cb: ash::vk::CommandBuffer) {
+ self.raw_cbs.push(cb);
+ }
+}
+
+// TODO: move somewhere else?
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct UnsafeCommandBufferBuilderColorImageClear {
+ pub base_mip_level: u32,
+ pub level_count: u32,
+ pub base_array_layer: u32,
+ pub layer_count: u32,
+}
+
+// TODO: move somewhere else?
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct UnsafeCommandBufferBuilderBufferImageCopy {
+ pub buffer_offset: DeviceSize,
+ pub buffer_row_length: u32,
+ pub buffer_image_height: u32,
+ pub image_aspect: ImageAspect,
+ pub image_mip_level: u32,
+ pub image_base_array_layer: u32,
+ pub image_layer_count: u32,
+ pub image_offset: [i32; 3],
+ pub image_extent: [u32; 3],
+}
+
+// TODO: move somewhere else?
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct UnsafeCommandBufferBuilderImageCopy {
+ pub aspects: ImageAspects,
+ pub source_mip_level: u32,
+ pub destination_mip_level: u32,
+ pub source_base_array_layer: u32,
+ pub destination_base_array_layer: u32,
+ pub layer_count: u32,
+ pub source_offset: [i32; 3],
+ pub destination_offset: [i32; 3],
+ pub extent: [u32; 3],
+}
+
+// TODO: move somewhere else?
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct UnsafeCommandBufferBuilderImageBlit {
+ pub aspects: ImageAspects,
+ pub source_mip_level: u32,
+ pub destination_mip_level: u32,
+ pub source_base_array_layer: u32,
+ pub destination_base_array_layer: u32,
+ pub layer_count: u32,
+ pub source_top_left: [i32; 3],
+ pub source_bottom_right: [i32; 3],
+ pub destination_top_left: [i32; 3],
+ pub destination_bottom_right: [i32; 3],
+}
+
+/// Command that adds a pipeline barrier to a command buffer builder.
+///
+/// A pipeline barrier is a low-level system-ish command that is often necessary for safety. By
+/// default all commands that you add to a command buffer can potentially run simultaneously.
+/// Adding a pipeline barrier separates commands before the barrier from commands after the barrier
+/// and prevents them from running simultaneously.
+///
+/// Please take a look at the Vulkan specifications for more information. Pipeline barriers are a
+/// complex topic and explaining them in this documentation would be redundant.
+///
+/// > **Note**: We use a builder-like API here so that users can pass multiple buffers or images of
+/// > multiple different types. Doing so with a single function would be very tedious in terms of
+/// > API.
+pub struct UnsafeCommandBufferBuilderPipelineBarrier {
+ src_stage_mask: ash::vk::PipelineStageFlags,
+ dst_stage_mask: ash::vk::PipelineStageFlags,
+ dependency_flags: ash::vk::DependencyFlags,
+ memory_barriers: SmallVec<[ash::vk::MemoryBarrier; 2]>,
+ buffer_barriers: SmallVec<[ash::vk::BufferMemoryBarrier; 8]>,
+ image_barriers: SmallVec<[ash::vk::ImageMemoryBarrier; 8]>,
+}
+
+impl UnsafeCommandBufferBuilderPipelineBarrier {
+ /// Creates a new empty pipeline barrier command.
+ #[inline]
+ pub fn new() -> UnsafeCommandBufferBuilderPipelineBarrier {
+ UnsafeCommandBufferBuilderPipelineBarrier {
+ src_stage_mask: ash::vk::PipelineStageFlags::empty(),
+ dst_stage_mask: ash::vk::PipelineStageFlags::empty(),
+ dependency_flags: ash::vk::DependencyFlags::BY_REGION,
+ memory_barriers: SmallVec::new(),
+ buffer_barriers: SmallVec::new(),
+ image_barriers: SmallVec::new(),
+ }
+ }
+
+ /// Returns true if no barrier or execution dependency has been added yet.
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.src_stage_mask.is_empty() || self.dst_stage_mask.is_empty()
+ }
+
+ /// Merges another pipeline builder into this one.
+ #[inline]
+ pub fn merge(&mut self, other: UnsafeCommandBufferBuilderPipelineBarrier) {
+ self.src_stage_mask |= other.src_stage_mask;
+ self.dst_stage_mask |= other.dst_stage_mask;
+ self.dependency_flags &= other.dependency_flags;
+
+ self.memory_barriers
+ .extend(other.memory_barriers.into_iter());
+ self.buffer_barriers
+ .extend(other.buffer_barriers.into_iter());
+ self.image_barriers.extend(other.image_barriers.into_iter());
+ }
+
+ /// Adds an execution dependency. This means that all the stages in `source` of the previous
+ /// commands must finish before any of the stages in `destination` of the following commands can start.
+ ///
+ /// # Safety
+ ///
+ /// - If the pipeline stages include geometry or tessellation stages, then the corresponding
+ /// features must have been enabled in the device.
+ /// - There are certain rules regarding the pipeline barriers inside render passes.
+ ///
+ #[inline]
+ pub unsafe fn add_execution_dependency(
+ &mut self,
+ source: PipelineStages,
+ destination: PipelineStages,
+ by_region: bool,
+ ) {
+ if !by_region {
+ self.dependency_flags = ash::vk::DependencyFlags::empty();
+ }
+
+ debug_assert_ne!(source, PipelineStages::none());
+ debug_assert_ne!(destination, PipelineStages::none());
+
+ self.src_stage_mask |= ash::vk::PipelineStageFlags::from(source);
+ self.dst_stage_mask |= ash::vk::PipelineStageFlags::from(destination);
+ }
+
+ /// Adds a memory barrier. This means that all the memory writes by the given source stages
+ /// for the given source accesses must be visible by the given destination stages for the given
+ /// destination accesses.
+ ///
+ /// Also adds an execution dependency similar to `add_execution_dependency`.
+ ///
+ /// # Safety
+ ///
+ /// - Same as `add_execution_dependency`.
+ ///
+ pub unsafe fn add_memory_barrier(
+ &mut self,
+ source_stage: PipelineStages,
+ source_access: AccessFlags,
+ destination_stage: PipelineStages,
+ destination_access: AccessFlags,
+ by_region: bool,
+ ) {
+ debug_assert!(source_access.is_compatible_with(&source_stage));
+ debug_assert!(destination_access.is_compatible_with(&destination_stage));
+
+ self.add_execution_dependency(source_stage, destination_stage, by_region);
+
+ self.memory_barriers.push(ash::vk::MemoryBarrier {
+ src_access_mask: source_access.into(),
+ dst_access_mask: destination_access.into(),
+ ..Default::default()
+ });
+ }
+
+ /// Adds a buffer memory barrier. This means that all the memory writes to the given buffer by
+ /// the given source stages for the given source accesses must be visible by the given dest
+ /// stages for the given destination accesses.
+ ///
+ /// Also adds an execution dependency similar to `add_execution_dependency`.
+ ///
+ /// Also allows transferring buffer ownership between queues.
+ ///
+ /// # Safety
+ ///
+ /// - Same as `add_execution_dependency`.
+ /// - The buffer must be alive for at least as long as the command buffer to which this barrier
+ /// is added.
+ /// - Queue ownership transfers must be correct.
+ ///
+ pub unsafe fn add_buffer_memory_barrier<B>(
+ &mut self,
+ buffer: &B,
+ source_stage: PipelineStages,
+ source_access: AccessFlags,
+ destination_stage: PipelineStages,
+ destination_access: AccessFlags,
+ by_region: bool,
+ queue_transfer: Option<(u32, u32)>,
+ offset: DeviceSize,
+ size: DeviceSize,
+ ) where
+ B: ?Sized + BufferAccess,
+ {
+ debug_assert!(source_access.is_compatible_with(&source_stage));
+ debug_assert!(destination_access.is_compatible_with(&destination_stage));
+
+ self.add_execution_dependency(source_stage, destination_stage, by_region);
+
+ debug_assert!(size <= buffer.size());
+ let BufferInner {
+ buffer,
+ offset: org_offset,
+ } = buffer.inner();
+ let offset = offset + org_offset;
+
+ let (src_queue, dest_queue) = if let Some((src_queue, dest_queue)) = queue_transfer {
+ (src_queue, dest_queue)
+ } else {
+ (ash::vk::QUEUE_FAMILY_IGNORED, ash::vk::QUEUE_FAMILY_IGNORED)
+ };
+
+ self.buffer_barriers.push(ash::vk::BufferMemoryBarrier {
+ src_access_mask: source_access.into(),
+ dst_access_mask: destination_access.into(),
+ src_queue_family_index: src_queue,
+ dst_queue_family_index: dest_queue,
+ buffer: buffer.internal_object(),
+ offset,
+ size,
+ ..Default::default()
+ });
+ }
+
+ /// Adds an image memory barrier. This is the equivalent of `add_buffer_memory_barrier` but
+ /// for images.
+ ///
+ /// In addition to transferring image ownership between queues, it also allows changing the
+ /// layout of images.
+ ///
+ /// Also adds an execution dependency similar to `add_execution_dependency`.
+ ///
+ /// # Safety
+ ///
+ /// - Same as `add_execution_dependency`.
+ /// - The buffer must be alive for at least as long as the command buffer to which this barrier
+ /// is added.
+ /// - Queue ownership transfers must be correct.
+ /// - Image layouts transfers must be correct.
+ /// - Access flags must be compatible with the image usage flags passed at image creation.
+ ///
+ pub unsafe fn add_image_memory_barrier<I>(
+ &mut self,
+ image: &I,
+ mipmaps: Range<u32>,
+ layers: Range<u32>,
+ source_stage: PipelineStages,
+ source_access: AccessFlags,
+ destination_stage: PipelineStages,
+ destination_access: AccessFlags,
+ by_region: bool,
+ queue_transfer: Option<(u32, u32)>,
+ current_layout: ImageLayout,
+ new_layout: ImageLayout,
+ ) where
+ I: ?Sized + ImageAccess,
+ {
+ debug_assert!(source_access.is_compatible_with(&source_stage));
+ debug_assert!(destination_access.is_compatible_with(&destination_stage));
+
+ self.add_execution_dependency(source_stage, destination_stage, by_region);
+
+ debug_assert_ne!(new_layout, ImageLayout::Undefined);
+ debug_assert_ne!(new_layout, ImageLayout::Preinitialized);
+
+ debug_assert!(mipmaps.start < mipmaps.end);
+ debug_assert!(mipmaps.end <= image.mipmap_levels());
+ debug_assert!(layers.start < layers.end);
+ debug_assert!(layers.end <= image.dimensions().array_layers());
+
+ let (src_queue, dest_queue) = if let Some((src_queue, dest_queue)) = queue_transfer {
+ (src_queue, dest_queue)
+ } else {
+ (ash::vk::QUEUE_FAMILY_IGNORED, ash::vk::QUEUE_FAMILY_IGNORED)
+ };
+
+ if image.format().ty() == FormatTy::Ycbcr {
+ unimplemented!();
+ }
+
+ // TODO: Let user choose
+ let aspects = image.format().aspects();
+ let image = image.inner();
+
+ self.image_barriers.push(ash::vk::ImageMemoryBarrier {
+ src_access_mask: source_access.into(),
+ dst_access_mask: destination_access.into(),
+ old_layout: current_layout.into(),
+ new_layout: new_layout.into(),
+ src_queue_family_index: src_queue,
+ dst_queue_family_index: dest_queue,
+ image: image.image.internal_object(),
+ subresource_range: ash::vk::ImageSubresourceRange {
+ aspect_mask: aspects.into(),
+ base_mip_level: mipmaps.start + image.first_mipmap_level as u32,
+ level_count: mipmaps.end - mipmaps.start,
+ base_array_layer: layers.start + image.first_layer as u32,
+ layer_count: layers.end - layers.start,
+ },
+ ..Default::default()
+ });
+ }
+}
+
+/// Command buffer that has been built.
+///
+/// # Safety
+///
+/// The command buffer must not outlive the command pool that it was created from,
+/// nor the resources used by the recorded commands.
+pub struct UnsafeCommandBuffer {
+ command_buffer: ash::vk::CommandBuffer,
+ device: Arc<Device>,
+ usage: CommandBufferUsage,
+}
+
+impl UnsafeCommandBuffer {
+ #[inline]
+ pub fn usage(&self) -> CommandBufferUsage {
+ self.usage
+ }
+}
+
+unsafe impl DeviceOwned for UnsafeCommandBuffer {
+ #[inline]
+ fn device(&self) -> &Arc<Device> {
+ &self.device
+ }
+}
+
+unsafe impl VulkanObject for UnsafeCommandBuffer {
+ type Object = ash::vk::CommandBuffer;
+
+ #[inline]
+ fn internal_object(&self) -> ash::vk::CommandBuffer {
+ self.command_buffer
+ }
+}