blob: 0fb8b58cb4698ffaf4a5dd697a84bdcf0aa5a3b4 [file] [log] [blame]
// 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
}
}