use super::acceleration_structure::{
AccelerationStructure, AccelerationStructureBuildFlags, AccelerationStructureBuildMode,
AccelerationStructureGeometry, AccelerationStructureType, BuildRange,
};
use super::descriptor::{DescriptorSet, ShaderStageFlags};
use super::device::DeviceInner;
use super::flags::{AccessFlags, AccessFlags2, PipelineStage, PipelineStage2};
use super::graphics_pipeline::GraphicsPipeline;
use super::image::{BufferImageCopy, Image, ImageBarrier, ImageLayout, ImageView, Sampler};
use super::pipeline::{ComputePipeline, PipelineLayout};
use super::query::QueryPool;
use super::ray_tracing_pipeline::{RayTracingPipeline, ShaderBindingRegion};
use super::render_pass::{Framebuffer, RenderPass};
use super::{Buffer, Device, Error, Result, check};
use crate::raw::bindings::*;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PipelineBindPoint {
Graphics,
Compute,
}
impl PipelineBindPoint {
#[inline]
fn to_raw(self) -> VkPipelineBindPoint {
match self {
Self::Graphics => VkPipelineBindPoint::PIPELINE_BIND_POINT_GRAPHICS,
Self::Compute => VkPipelineBindPoint::PIPELINE_BIND_POINT_COMPUTE,
}
}
}
#[derive(Clone, Copy)]
pub enum PushDescriptorWrite<'a> {
StorageBuffer {
binding: u32,
buffer: &'a Buffer,
offset: u64,
range: u64,
},
UniformBuffer {
binding: u32,
buffer: &'a Buffer,
offset: u64,
range: u64,
},
StorageImage {
binding: u32,
view: &'a ImageView,
layout: ImageLayout,
},
SampledImage {
binding: u32,
view: &'a ImageView,
layout: ImageLayout,
},
CombinedImageSampler {
binding: u32,
sampler: &'a Sampler,
view: &'a ImageView,
layout: ImageLayout,
},
Sampler { binding: u32, sampler: &'a Sampler },
}
#[derive(Debug, Clone, Copy)]
pub enum ClearValue {
Color([f32; 4]),
DepthStencil { depth: f32, stencil: u32 },
}
#[derive(Clone, Copy)]
pub struct RenderingAttachment<'a> {
pub view: &'a ImageView,
pub layout: ImageLayout,
pub load_op: super::AttachmentLoadOp,
pub store_op: super::AttachmentStoreOp,
pub clear_value: Option<ClearValue>,
}
#[derive(Clone, Copy)]
pub struct RenderingInfo<'a> {
pub render_area: VkRect2D,
pub layer_count: u32,
pub view_mask: u32,
pub color_attachments: &'a [RenderingAttachment<'a>],
pub depth_attachment: Option<RenderingAttachment<'a>>,
pub stencil_attachment: Option<RenderingAttachment<'a>>,
}
#[derive(Debug, Clone, Copy)]
pub struct DescriptorBufferBinding {
pub address: u64,
pub usage: u32,
}
#[derive(Debug, Clone, Copy)]
pub struct BufferCopy {
pub src_offset: u64,
pub dst_offset: u64,
pub size: u64,
}
impl BufferCopy {
pub const fn full(size: u64) -> Self {
Self {
src_offset: 0,
dst_offset: 0,
size,
}
}
}
pub struct CommandPool {
pub(crate) handle: VkCommandPool,
pub(crate) device: Arc<DeviceInner>,
}
impl CommandPool {
pub fn new(device: &Device, queue_family_index: u32) -> Result<Self> {
let create = device
.inner
.dispatch
.vkCreateCommandPool
.ok_or(Error::MissingFunction("vkCreateCommandPool"))?;
let info = VkCommandPoolCreateInfo {
sType: VkStructureType::STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
queueFamilyIndex: queue_family_index,
..Default::default()
};
let mut handle: VkCommandPool = 0;
check(unsafe { create(device.inner.handle, &info, std::ptr::null(), &mut handle) })?;
Ok(Self {
handle,
device: Arc::clone(&device.inner),
})
}
pub fn raw(&self) -> VkCommandPool {
self.handle
}
pub fn allocate_primary(&self) -> Result<CommandBuffer> {
let allocate = self
.device
.dispatch
.vkAllocateCommandBuffers
.ok_or(Error::MissingFunction("vkAllocateCommandBuffers"))?;
let info = VkCommandBufferAllocateInfo {
sType: VkStructureType::STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
commandPool: self.handle,
level: VkCommandBufferLevel::COMMAND_BUFFER_LEVEL_PRIMARY,
commandBufferCount: 1,
..Default::default()
};
let mut handle: VkCommandBuffer = std::ptr::null_mut();
check(unsafe { allocate(self.device.handle, &info, &mut handle) })?;
Ok(CommandBuffer {
handle,
device: Arc::clone(&self.device),
pool: self.handle,
})
}
}
impl Drop for CommandPool {
fn drop(&mut self) {
if let Some(destroy) = self.device.dispatch.vkDestroyCommandPool {
unsafe { destroy(self.device.handle, self.handle, std::ptr::null()) };
}
}
}
pub struct CommandBuffer {
pub(crate) handle: VkCommandBuffer,
pub(crate) device: Arc<DeviceInner>,
#[allow(dead_code)]
pool: VkCommandPool,
}
impl CommandBuffer {
pub fn raw(&self) -> VkCommandBuffer {
self.handle
}
pub fn begin(&mut self) -> Result<CommandBufferRecording<'_>> {
let begin = self
.device
.dispatch
.vkBeginCommandBuffer
.ok_or(Error::MissingFunction("vkBeginCommandBuffer"))?;
let info = VkCommandBufferBeginInfo {
sType: VkStructureType::STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
..Default::default()
};
check(unsafe { begin(self.handle, &info) })?;
Ok(CommandBufferRecording { buffer: self })
}
}
impl Drop for CommandBuffer {
fn drop(&mut self) {
if let Some(free) = self.device.dispatch.vkFreeCommandBuffers {
unsafe { free(self.device.handle, self.pool, 1, &self.handle) };
}
}
}
pub struct CommandBufferRecording<'a> {
buffer: &'a mut CommandBuffer,
}
impl<'a> CommandBufferRecording<'a> {
#[inline]
pub(crate) fn raw_cmd(&self) -> VkCommandBuffer {
self.buffer.handle
}
#[inline]
pub(crate) fn device_dispatch(&self) -> &VkDeviceDispatchTable {
&self.buffer.device.dispatch
}
pub fn fill_buffer(&mut self, buffer: &Buffer, dst_offset: u64, size: u64, data: u32) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdFillBuffer
.expect("vkCmdFillBuffer is required by Vulkan 1.0");
unsafe { cmd(self.buffer.handle, buffer.handle, dst_offset, size, data) };
}
pub fn bind_compute_pipeline(&mut self, pipeline: &ComputePipeline) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdBindPipeline
.expect("vkCmdBindPipeline is required by Vulkan 1.0");
unsafe {
cmd(
self.buffer.handle,
VkPipelineBindPoint::PIPELINE_BIND_POINT_COMPUTE,
pipeline.handle,
)
};
}
pub fn bind_compute_descriptor_sets(
&mut self,
layout: &PipelineLayout,
first_set: u32,
descriptor_sets: &[&DescriptorSet],
) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdBindDescriptorSets
.expect("vkCmdBindDescriptorSets is required by Vulkan 1.0");
let raw: Vec<VkDescriptorSet> = descriptor_sets.iter().map(|s| s.handle).collect();
unsafe {
cmd(
self.buffer.handle,
VkPipelineBindPoint::PIPELINE_BIND_POINT_COMPUTE,
layout.handle,
first_set,
raw.len() as u32,
raw.as_ptr(),
0,
std::ptr::null(),
)
};
}
pub fn push_descriptor_set(
&mut self,
bind_point: PipelineBindPoint,
layout: &PipelineLayout,
set: u32,
writes: &[PushDescriptorWrite<'_>],
) -> Result<()> {
let cmd = self
.buffer
.device
.dispatch
.vkCmdPushDescriptorSet
.ok_or(Error::MissingFunction("vkCmdPushDescriptorSetKHR"))?;
let mut buffer_infos: Vec<VkDescriptorBufferInfo> = Vec::with_capacity(writes.len());
let mut image_infos: Vec<VkDescriptorImageInfo> = Vec::with_capacity(writes.len());
let mut raw_writes: Vec<VkWriteDescriptorSet> = Vec::with_capacity(writes.len());
for w in writes {
match w {
PushDescriptorWrite::StorageBuffer {
buffer,
offset,
range,
..
}
| PushDescriptorWrite::UniformBuffer {
buffer,
offset,
range,
..
} => {
buffer_infos.push(VkDescriptorBufferInfo {
buffer: buffer.handle,
offset: *offset,
range: *range,
});
}
PushDescriptorWrite::StorageImage { view, layout, .. }
| PushDescriptorWrite::SampledImage { view, layout, .. } => {
image_infos.push(VkDescriptorImageInfo {
sampler: 0,
imageView: view.handle,
imageLayout: layout.0,
});
}
PushDescriptorWrite::CombinedImageSampler {
sampler,
view,
layout,
..
} => {
image_infos.push(VkDescriptorImageInfo {
sampler: sampler.handle,
imageView: view.handle,
imageLayout: layout.0,
});
}
PushDescriptorWrite::Sampler { sampler, .. } => {
image_infos.push(VkDescriptorImageInfo {
sampler: sampler.handle,
imageView: 0,
imageLayout: VkImageLayout::IMAGE_LAYOUT_UNDEFINED,
});
}
}
}
let mut buf_i = 0usize;
let mut img_i = 0usize;
for w in writes {
let (descriptor_type, p_buf, p_img) = match w {
PushDescriptorWrite::StorageBuffer { .. } => {
let p = &buffer_infos[buf_i] as *const _;
buf_i += 1;
(
VkDescriptorType::DESCRIPTOR_TYPE_STORAGE_BUFFER,
p,
std::ptr::null(),
)
}
PushDescriptorWrite::UniformBuffer { .. } => {
let p = &buffer_infos[buf_i] as *const _;
buf_i += 1;
(
VkDescriptorType::DESCRIPTOR_TYPE_UNIFORM_BUFFER,
p,
std::ptr::null(),
)
}
PushDescriptorWrite::StorageImage { .. } => {
let p = &image_infos[img_i] as *const _;
img_i += 1;
(
VkDescriptorType::DESCRIPTOR_TYPE_STORAGE_IMAGE,
std::ptr::null(),
p,
)
}
PushDescriptorWrite::SampledImage { .. } => {
let p = &image_infos[img_i] as *const _;
img_i += 1;
(
VkDescriptorType::DESCRIPTOR_TYPE_SAMPLED_IMAGE,
std::ptr::null(),
p,
)
}
PushDescriptorWrite::CombinedImageSampler { .. } => {
let p = &image_infos[img_i] as *const _;
img_i += 1;
(
VkDescriptorType::DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
std::ptr::null(),
p,
)
}
PushDescriptorWrite::Sampler { .. } => {
let p = &image_infos[img_i] as *const _;
img_i += 1;
(
VkDescriptorType::DESCRIPTOR_TYPE_SAMPLER,
std::ptr::null(),
p,
)
}
};
let binding = match w {
PushDescriptorWrite::StorageBuffer { binding, .. }
| PushDescriptorWrite::UniformBuffer { binding, .. }
| PushDescriptorWrite::StorageImage { binding, .. }
| PushDescriptorWrite::SampledImage { binding, .. }
| PushDescriptorWrite::CombinedImageSampler { binding, .. }
| PushDescriptorWrite::Sampler { binding, .. } => *binding,
};
raw_writes.push(VkWriteDescriptorSet {
sType: VkStructureType::STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
dstSet: 0, dstBinding: binding,
dstArrayElement: 0,
descriptorCount: 1,
descriptorType: descriptor_type,
pImageInfo: p_img,
pBufferInfo: p_buf,
..Default::default()
});
}
unsafe {
cmd(
self.buffer.handle,
bind_point.to_raw(),
layout.handle,
set,
raw_writes.len() as u32,
raw_writes.as_ptr(),
)
};
Ok(())
}
pub fn dispatch(&mut self, group_count_x: u32, group_count_y: u32, group_count_z: u32) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdDispatch
.expect("vkCmdDispatch is required by Vulkan 1.0");
unsafe {
cmd(
self.buffer.handle,
group_count_x,
group_count_y,
group_count_z,
)
};
}
pub fn dispatch_indirect(&mut self, buffer: &Buffer, offset: u64) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdDispatchIndirect
.expect("vkCmdDispatchIndirect is required by Vulkan 1.0");
unsafe { cmd(self.buffer.handle, buffer.handle, offset) };
}
pub fn copy_buffer(&mut self, src: &Buffer, dst: &Buffer, regions: &[BufferCopy]) {
debug_assert!(
!regions.is_empty(),
"vkCmdCopyBuffer requires at least one region"
);
let cmd = self
.buffer
.device
.dispatch
.vkCmdCopyBuffer
.expect("vkCmdCopyBuffer is required by Vulkan 1.0");
let raw: Vec<VkBufferCopy> = regions
.iter()
.map(|r| VkBufferCopy {
srcOffset: r.src_offset,
dstOffset: r.dst_offset,
size: r.size,
})
.collect();
unsafe {
cmd(
self.buffer.handle,
src.handle,
dst.handle,
raw.len() as u32,
raw.as_ptr(),
)
};
}
pub fn push_constants(
&mut self,
layout: &PipelineLayout,
stages: ShaderStageFlags,
offset: u32,
bytes: &[u8],
) {
debug_assert!(
offset % 4 == 0,
"push constant offset must be a multiple of 4"
);
debug_assert!(
bytes.len() % 4 == 0,
"push constant size must be a multiple of 4"
);
let cmd = self
.buffer
.device
.dispatch
.vkCmdPushConstants
.expect("vkCmdPushConstants is required by Vulkan 1.0");
unsafe {
cmd(
self.buffer.handle,
layout.handle,
stages.0,
offset,
bytes.len() as u32,
bytes.as_ptr() as *const _,
)
};
}
pub fn reset_query_pool(&mut self, pool: &QueryPool, first_query: u32, query_count: u32) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdResetQueryPool
.expect("vkCmdResetQueryPool is required by Vulkan 1.0");
unsafe { cmd(self.buffer.handle, pool.handle, first_query, query_count) };
}
pub fn write_timestamp(&mut self, pipeline_stage: PipelineStage, pool: &QueryPool, query: u32) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdWriteTimestamp
.expect("vkCmdWriteTimestamp is required by Vulkan 1.0");
unsafe { cmd(self.buffer.handle, pipeline_stage.0, pool.handle, query) };
}
pub fn image_barrier(
&mut self,
src_stage: PipelineStage,
dst_stage: PipelineStage,
barrier: ImageBarrier<'_>,
) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdPipelineBarrier
.expect("vkCmdPipelineBarrier is required by Vulkan 1.0");
let raw = VkImageMemoryBarrier {
sType: VkStructureType::STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
srcAccessMask: barrier.src_access.0,
dstAccessMask: barrier.dst_access.0,
oldLayout: barrier.old_layout.0,
newLayout: barrier.new_layout.0,
srcQueueFamilyIndex: !0u32, dstQueueFamilyIndex: !0u32,
image: barrier.image.handle,
subresourceRange: VkImageSubresourceRange {
aspectMask: barrier.aspect_mask,
baseMipLevel: 0,
levelCount: 1,
baseArrayLayer: 0,
layerCount: 1,
},
..Default::default()
};
unsafe {
cmd(
self.buffer.handle,
src_stage.0,
dst_stage.0,
0,
0,
std::ptr::null(),
0,
std::ptr::null(),
1,
&raw,
)
};
}
pub fn copy_buffer_to_image(
&mut self,
src: &Buffer,
dst: &Image,
dst_layout: super::ImageLayout,
regions: &[BufferImageCopy],
) {
debug_assert!(
!regions.is_empty(),
"vkCmdCopyBufferToImage requires at least one region"
);
let cmd = self
.buffer
.device
.dispatch
.vkCmdCopyBufferToImage
.expect("vkCmdCopyBufferToImage is required by Vulkan 1.0");
let raw: Vec<VkBufferImageCopy> = regions.iter().map(|r| r.to_raw()).collect();
unsafe {
cmd(
self.buffer.handle,
src.handle,
dst.handle,
dst_layout.0,
raw.len() as u32,
raw.as_ptr(),
)
};
}
pub fn memory_barrier2(
&mut self,
src_stage: PipelineStage2,
dst_stage: PipelineStage2,
src_access: AccessFlags2,
dst_access: AccessFlags2,
) -> Result<()> {
let cmd = self
.buffer
.device
.dispatch
.vkCmdPipelineBarrier2
.ok_or(Error::MissingFunction("vkCmdPipelineBarrier2"))?;
let mb = VkMemoryBarrier2 {
sType: VkStructureType::STRUCTURE_TYPE_MEMORY_BARRIER_2,
srcStageMask: src_stage.0,
srcAccessMask: src_access.0,
dstStageMask: dst_stage.0,
dstAccessMask: dst_access.0,
..Default::default()
};
let info = VkDependencyInfo {
sType: VkStructureType::STRUCTURE_TYPE_DEPENDENCY_INFO,
memoryBarrierCount: 1,
pMemoryBarriers: &mb,
..Default::default()
};
unsafe { cmd(self.buffer.handle, &info) };
Ok(())
}
pub fn image_barrier2(
&mut self,
src_stage: PipelineStage2,
dst_stage: PipelineStage2,
barrier: ImageBarrier<'_>,
) -> Result<()> {
let cmd = self
.buffer
.device
.dispatch
.vkCmdPipelineBarrier2
.ok_or(Error::MissingFunction("vkCmdPipelineBarrier2"))?;
let ib = VkImageMemoryBarrier2 {
sType: VkStructureType::STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
srcStageMask: src_stage.0,
srcAccessMask: barrier.src_access.0 as u64,
dstStageMask: dst_stage.0,
dstAccessMask: barrier.dst_access.0 as u64,
oldLayout: barrier.old_layout.0,
newLayout: barrier.new_layout.0,
srcQueueFamilyIndex: !0u32,
dstQueueFamilyIndex: !0u32,
image: barrier.image.handle,
subresourceRange: VkImageSubresourceRange {
aspectMask: barrier.aspect_mask,
baseMipLevel: 0,
levelCount: 1,
baseArrayLayer: 0,
layerCount: 1,
},
..Default::default()
};
let info = VkDependencyInfo {
sType: VkStructureType::STRUCTURE_TYPE_DEPENDENCY_INFO,
imageMemoryBarrierCount: 1,
pImageMemoryBarriers: &ib,
..Default::default()
};
unsafe { cmd(self.buffer.handle, &info) };
Ok(())
}
pub fn buffer_barrier2(
&mut self,
src_stage: PipelineStage2,
dst_stage: PipelineStage2,
src_access: AccessFlags2,
dst_access: AccessFlags2,
buffer: &Buffer,
) -> Result<()> {
let cmd = self
.buffer
.device
.dispatch
.vkCmdPipelineBarrier2
.ok_or(Error::MissingFunction("vkCmdPipelineBarrier2"))?;
let bb = VkBufferMemoryBarrier2 {
sType: VkStructureType::STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2,
srcStageMask: src_stage.0,
srcAccessMask: src_access.0,
dstStageMask: dst_stage.0,
dstAccessMask: dst_access.0,
srcQueueFamilyIndex: !0u32,
dstQueueFamilyIndex: !0u32,
buffer: buffer.handle,
offset: 0,
size: !0u64, ..Default::default()
};
let info = VkDependencyInfo {
sType: VkStructureType::STRUCTURE_TYPE_DEPENDENCY_INFO,
bufferMemoryBarrierCount: 1,
pBufferMemoryBarriers: &bb,
..Default::default()
};
unsafe { cmd(self.buffer.handle, &info) };
Ok(())
}
pub fn copy_image_to_buffer(
&mut self,
src: &Image,
src_layout: super::ImageLayout,
dst: &Buffer,
regions: &[BufferImageCopy],
) {
debug_assert!(
!regions.is_empty(),
"vkCmdCopyImageToBuffer requires at least one region"
);
let cmd = self
.buffer
.device
.dispatch
.vkCmdCopyImageToBuffer
.expect("vkCmdCopyImageToBuffer is required by Vulkan 1.0");
let raw: Vec<VkBufferImageCopy> = regions.iter().map(|r| r.to_raw()).collect();
unsafe {
cmd(
self.buffer.handle,
src.handle,
src_layout.0,
dst.handle,
raw.len() as u32,
raw.as_ptr(),
)
};
}
pub fn memory_barrier(
&mut self,
src_stage: PipelineStage,
dst_stage: PipelineStage,
src_access: AccessFlags,
dst_access: AccessFlags,
) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdPipelineBarrier
.expect("vkCmdPipelineBarrier is required by Vulkan 1.0");
let barrier = VkMemoryBarrier {
sType: VkStructureType::STRUCTURE_TYPE_MEMORY_BARRIER,
srcAccessMask: src_access.0,
dstAccessMask: dst_access.0,
..Default::default()
};
unsafe {
cmd(
self.buffer.handle,
src_stage.0,
dst_stage.0,
0,
1,
&barrier,
0,
std::ptr::null(),
0,
std::ptr::null(),
)
};
}
pub fn begin_render_pass(
&mut self,
render_pass: &RenderPass,
framebuffer: &Framebuffer,
clear_colors: &[[f32; 4]],
) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdBeginRenderPass
.expect("vkCmdBeginRenderPass is required by Vulkan 1.0");
let raw_clears: Vec<VkClearValue> = clear_colors
.iter()
.map(|c| VkClearValue {
color: VkClearColorValue { float32: *c },
})
.collect();
let info = VkRenderPassBeginInfo {
sType: VkStructureType::STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
renderPass: render_pass.handle,
framebuffer: framebuffer.handle,
renderArea: VkRect2D {
offset: VkOffset2D { x: 0, y: 0 },
extent: VkExtent2D {
width: framebuffer.width,
height: framebuffer.height,
},
},
clearValueCount: raw_clears.len() as u32,
pClearValues: if raw_clears.is_empty() {
std::ptr::null()
} else {
raw_clears.as_ptr()
},
..Default::default()
};
unsafe {
cmd(
self.buffer.handle,
&info,
VkSubpassContents::SUBPASS_CONTENTS_INLINE,
)
};
}
pub fn begin_render_pass_ext(
&mut self,
render_pass: &RenderPass,
framebuffer: &Framebuffer,
clear_values: &[ClearValue],
) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdBeginRenderPass
.expect("vkCmdBeginRenderPass is required by Vulkan 1.0");
let raw_clears: Vec<VkClearValue> = clear_values
.iter()
.map(|cv| match cv {
ClearValue::Color(c) => VkClearValue {
color: VkClearColorValue { float32: *c },
},
ClearValue::DepthStencil { depth, stencil } => VkClearValue {
depthStencil: VkClearDepthStencilValue {
depth: *depth,
stencil: *stencil,
},
},
})
.collect();
let info = VkRenderPassBeginInfo {
sType: VkStructureType::STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
renderPass: render_pass.handle,
framebuffer: framebuffer.handle,
renderArea: VkRect2D {
offset: VkOffset2D { x: 0, y: 0 },
extent: VkExtent2D {
width: framebuffer.width,
height: framebuffer.height,
},
},
clearValueCount: raw_clears.len() as u32,
pClearValues: if raw_clears.is_empty() {
std::ptr::null()
} else {
raw_clears.as_ptr()
},
..Default::default()
};
unsafe {
cmd(
self.buffer.handle,
&info,
VkSubpassContents::SUBPASS_CONTENTS_INLINE,
)
};
}
pub fn end_render_pass(&mut self) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdEndRenderPass
.expect("vkCmdEndRenderPass is required by Vulkan 1.0");
unsafe { cmd(self.buffer.handle) };
}
pub fn begin_rendering(&mut self, info: RenderingInfo<'_>) -> Result<()> {
let cmd = self
.buffer
.device
.dispatch
.vkCmdBeginRendering
.ok_or(Error::MissingFunction("vkCmdBeginRendering"))?;
let to_raw = |a: &RenderingAttachment<'_>| -> VkRenderingAttachmentInfo {
let clear = match a.clear_value {
Some(ClearValue::Color([r, g, b, al])) => VkClearValue {
color: VkClearColorValue {
float32: [r, g, b, al],
},
},
Some(ClearValue::DepthStencil { depth, stencil }) => VkClearValue {
depthStencil: VkClearDepthStencilValue { depth, stencil },
},
None => unsafe { std::mem::zeroed() },
};
VkRenderingAttachmentInfo {
sType: VkStructureType::STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
pNext: std::ptr::null(),
imageView: a.view.handle,
imageLayout: a.layout.0,
resolveMode: unsafe { std::mem::zeroed() },
resolveImageView: 0,
resolveImageLayout: VkImageLayout::IMAGE_LAYOUT_UNDEFINED,
loadOp: a.load_op.0,
storeOp: a.store_op.0,
clearValue: clear,
}
};
let color_raw: Vec<VkRenderingAttachmentInfo> =
info.color_attachments.iter().map(to_raw).collect();
let depth_raw = info.depth_attachment.as_ref().map(to_raw);
let stencil_raw = info.stencil_attachment.as_ref().map(to_raw);
let raw = VkRenderingInfo {
sType: VkStructureType::STRUCTURE_TYPE_RENDERING_INFO,
pNext: std::ptr::null(),
flags: 0,
renderArea: info.render_area,
layerCount: info.layer_count,
viewMask: info.view_mask,
colorAttachmentCount: color_raw.len() as u32,
pColorAttachments: if color_raw.is_empty() {
std::ptr::null()
} else {
color_raw.as_ptr()
},
pDepthAttachment: depth_raw
.as_ref()
.map_or(std::ptr::null(), |a| a as *const _),
pStencilAttachment: stencil_raw
.as_ref()
.map_or(std::ptr::null(), |a| a as *const _),
};
unsafe { cmd(self.buffer.handle, &raw) };
Ok(())
}
pub fn end_rendering(&mut self) -> Result<()> {
let cmd = self
.buffer
.device
.dispatch
.vkCmdEndRendering
.ok_or(Error::MissingFunction("vkCmdEndRendering"))?;
unsafe { cmd(self.buffer.handle) };
Ok(())
}
pub fn bind_descriptor_buffers(&mut self, bindings: &[DescriptorBufferBinding]) -> Result<()> {
let cmd = self
.buffer
.device
.dispatch
.vkCmdBindDescriptorBuffersEXT
.ok_or(Error::MissingFunction("vkCmdBindDescriptorBuffersEXT"))?;
let raw: Vec<VkDescriptorBufferBindingInfoEXT> = bindings
.iter()
.map(|b| VkDescriptorBufferBindingInfoEXT {
sType: VkStructureType::STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT,
pNext: std::ptr::null(),
address: b.address,
usage: b.usage,
})
.collect();
unsafe { cmd(self.buffer.handle, raw.len() as u32, raw.as_ptr()) };
Ok(())
}
pub fn set_descriptor_buffer_offsets(
&mut self,
bind_point: PipelineBindPoint,
layout: &PipelineLayout,
first_set: u32,
buffer_indices: &[u32],
offsets: &[u64],
) -> Result<()> {
if buffer_indices.len() != offsets.len() {
return Err(Error::InvalidArgument(
"buffer_indices and offsets must have equal length",
));
}
let cmd = self
.buffer
.device
.dispatch
.vkCmdSetDescriptorBufferOffsetsEXT
.ok_or(Error::MissingFunction("vkCmdSetDescriptorBufferOffsetsEXT"))?;
unsafe {
cmd(
self.buffer.handle,
bind_point.to_raw(),
layout.handle,
first_set,
buffer_indices.len() as u32,
buffer_indices.as_ptr(),
offsets.as_ptr(),
)
};
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub fn build_acceleration_structure(
&mut self,
type_: AccelerationStructureType,
mode: AccelerationStructureBuildMode,
flags: AccelerationStructureBuildFlags,
dst: &AccelerationStructure,
src: Option<&AccelerationStructure>,
geometries: &[AccelerationStructureGeometry],
ranges: &[BuildRange],
scratch_address: u64,
) -> Result<()> {
if geometries.len() != ranges.len() {
return Err(Error::InvalidArgument(
"geometries and ranges must have equal length",
));
}
let cmd = self
.buffer
.device
.dispatch
.vkCmdBuildAccelerationStructuresKHR
.ok_or(Error::MissingFunction(
"vkCmdBuildAccelerationStructuresKHR",
))?;
let raw_geoms: Vec<VkAccelerationStructureGeometryKHR> =
geometries.iter().map(|g| g.to_raw()).collect();
let raw_ranges: Vec<VkAccelerationStructureBuildRangeInfoKHR> =
ranges.iter().map(|r| r.to_raw()).collect();
let info = VkAccelerationStructureBuildGeometryInfoKHR {
sType: VkStructureType::STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR,
pNext: std::ptr::null(),
r#type: type_.to_raw(),
flags: flags.0,
mode: mode.to_raw(),
srcAccelerationStructure: src.map_or(0, |s| s.raw()),
dstAccelerationStructure: dst.raw(),
geometryCount: raw_geoms.len() as u32,
pGeometries: if raw_geoms.is_empty() {
std::ptr::null()
} else {
raw_geoms.as_ptr()
},
ppGeometries: std::ptr::null(),
scratchData: VkDeviceOrHostAddressKHR {
deviceAddress: scratch_address,
},
};
let ranges_ptr: *const VkAccelerationStructureBuildRangeInfoKHR = raw_ranges.as_ptr();
let range_ptrs: [*const VkAccelerationStructureBuildRangeInfoKHR; 1] = [ranges_ptr];
unsafe { cmd(self.buffer.handle, 1, &info, range_ptrs.as_ptr() as *mut _) };
Ok(())
}
pub fn bind_ray_tracing_pipeline(&mut self, pipeline: &RayTracingPipeline) -> Result<()> {
let cmd = self
.buffer
.device
.dispatch
.vkCmdBindPipeline
.ok_or(Error::MissingFunction("vkCmdBindPipeline"))?;
unsafe {
cmd(
self.buffer.handle,
VkPipelineBindPoint::PIPELINE_BIND_POINT_RAY_TRACING_KHR,
pipeline.raw(),
)
};
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub fn trace_rays(
&mut self,
raygen: ShaderBindingRegion,
miss: ShaderBindingRegion,
hit: ShaderBindingRegion,
callable: ShaderBindingRegion,
width: u32,
height: u32,
depth: u32,
) -> Result<()> {
let cmd = self
.buffer
.device
.dispatch
.vkCmdTraceRaysKHR
.ok_or(Error::MissingFunction("vkCmdTraceRaysKHR"))?;
let rgen = raygen.to_raw();
let rmiss = miss.to_raw();
let rhit = hit.to_raw();
let rcall = callable.to_raw();
unsafe {
cmd(
self.buffer.handle,
&rgen,
&rmiss,
&rhit,
&rcall,
width,
height,
depth,
)
};
Ok(())
}
pub fn bind_graphics_pipeline(&mut self, pipeline: &GraphicsPipeline) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdBindPipeline
.expect("vkCmdBindPipeline is required by Vulkan 1.0");
unsafe {
cmd(
self.buffer.handle,
VkPipelineBindPoint::PIPELINE_BIND_POINT_GRAPHICS,
pipeline.handle,
)
};
}
pub fn bind_graphics_descriptor_sets(
&mut self,
layout: &PipelineLayout,
first_set: u32,
descriptor_sets: &[&DescriptorSet],
) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdBindDescriptorSets
.expect("vkCmdBindDescriptorSets is required by Vulkan 1.0");
let raw: Vec<VkDescriptorSet> = descriptor_sets.iter().map(|s| s.handle).collect();
unsafe {
cmd(
self.buffer.handle,
VkPipelineBindPoint::PIPELINE_BIND_POINT_GRAPHICS,
layout.handle,
first_set,
raw.len() as u32,
raw.as_ptr(),
0,
std::ptr::null(),
)
};
}
pub fn bind_vertex_buffers(&mut self, first_binding: u32, buffers_offsets: &[(&Buffer, u64)]) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdBindVertexBuffers
.expect("vkCmdBindVertexBuffers is required by Vulkan 1.0");
let raw_buffers: Vec<VkBuffer> = buffers_offsets.iter().map(|(b, _)| b.handle).collect();
let raw_offsets: Vec<u64> = buffers_offsets.iter().map(|(_, o)| *o).collect();
unsafe {
cmd(
self.buffer.handle,
first_binding,
raw_buffers.len() as u32,
raw_buffers.as_ptr(),
raw_offsets.as_ptr(),
)
};
}
pub fn bind_index_buffer(&mut self, buffer: &Buffer, offset: u64, index_type: u32) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdBindIndexBuffer
.expect("vkCmdBindIndexBuffer is required by Vulkan 1.0");
let it = match index_type {
0 => VkIndexType::INDEX_TYPE_UINT16,
_ => VkIndexType::INDEX_TYPE_UINT32,
};
unsafe { cmd(self.buffer.handle, buffer.handle, offset, it) };
}
pub fn draw(
&mut self,
vertex_count: u32,
instance_count: u32,
first_vertex: u32,
first_instance: u32,
) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdDraw
.expect("vkCmdDraw is required by Vulkan 1.0");
unsafe {
cmd(
self.buffer.handle,
vertex_count,
instance_count,
first_vertex,
first_instance,
)
};
}
pub fn draw_indexed(
&mut self,
index_count: u32,
instance_count: u32,
first_index: u32,
vertex_offset: i32,
first_instance: u32,
) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdDrawIndexed
.expect("vkCmdDrawIndexed is required by Vulkan 1.0");
unsafe {
cmd(
self.buffer.handle,
index_count,
instance_count,
first_index,
vertex_offset,
first_instance,
)
};
}
pub fn set_viewport(&mut self, x: f32, y: f32, width: f32, height: f32) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdSetViewport
.expect("vkCmdSetViewport is required by Vulkan 1.0");
let viewport = VkViewport {
x,
y,
width,
height,
minDepth: 0.0,
maxDepth: 1.0,
};
unsafe { cmd(self.buffer.handle, 0, 1, &viewport) };
}
pub fn set_scissor(&mut self, x: i32, y: i32, width: u32, height: u32) {
let cmd = self
.buffer
.device
.dispatch
.vkCmdSetScissor
.expect("vkCmdSetScissor is required by Vulkan 1.0");
let scissor = VkRect2D {
offset: VkOffset2D { x, y },
extent: VkExtent2D { width, height },
};
unsafe { cmd(self.buffer.handle, 0, 1, &scissor) };
}
pub fn end(self) -> Result<()> {
let end = self
.buffer
.device
.dispatch
.vkEndCommandBuffer
.ok_or(Error::MissingFunction("vkEndCommandBuffer"))?;
let result = unsafe { end(self.buffer.handle) };
std::mem::forget(self);
check(result)
}
}
impl<'a> Drop for CommandBufferRecording<'a> {
fn drop(&mut self) {
if let Some(end) = self.buffer.device.dispatch.vkEndCommandBuffer {
unsafe { end(self.buffer.handle) };
}
}
}