828 lines
24 KiB
Rust
828 lines
24 KiB
Rust
//! This module defines the commands that can be recorded in the render
|
|
//! graph.
|
|
//! Commands can be allocated, and then recorded into a command
|
|
//! buffer.
|
|
//! the lifecycle of a command is thus as follows:
|
|
//! 1. A command is constructed, with its parameters and resource dependencies.
|
|
//! 2. The command is inserted into a command list. At this stage, the command's side effects are recorded and barriers are automatically inserted as required.
|
|
//! 3. When the command list is recorded into a command buffer, the command's `apply` method is called, which translates the command into actual GPU commands using the provided `CommandRecorder`.
|
|
//!
|
|
//! Each command declares its side effects, such as what resources it
|
|
//! reads from or write to, and which pipeline stages it affects. Commands are
|
|
//! recorded sequentially, in the order they were inserted into a command
|
|
//! buffer. During command insertion
|
|
|
|
use std::marker::PhantomData;
|
|
|
|
use crate::{
|
|
device::DeviceOwned,
|
|
render_graph::recorder::{CommandRecorder, SideEffectMap},
|
|
};
|
|
|
|
use super::resources::*;
|
|
|
|
pub struct ImportResource<T: Resource> {
|
|
pub resource: T,
|
|
pub range: Option<Subresource>,
|
|
pub access: vk::AccessFlags2,
|
|
pub stage: vk::PipelineStageFlags2,
|
|
pub layout: Option<vk::ImageLayout>,
|
|
}
|
|
|
|
impl<T: Resource> Command for ImportResource<T> {
|
|
fn side_effects(&self, mut map: SideEffectMap) {
|
|
let range = self.range.unwrap_or_else(|| self.resource.full_range());
|
|
map.with(self.stage, self.access, self.layout)
|
|
.insert_range(self.resource.id(), range);
|
|
}
|
|
|
|
fn apply(self, _recorder: &mut CommandRecorder) {
|
|
// No actual GPU commands needed for import, as it's just a logical operation
|
|
}
|
|
}
|
|
|
|
pub trait BufferImageBufferCopyRegion {
|
|
fn regions(&self) -> &[BufferRows];
|
|
}
|
|
impl BufferImageBufferCopyRegion for BufferRows {
|
|
fn regions(&self) -> &[BufferRows] {
|
|
core::slice::from_ref(self)
|
|
}
|
|
}
|
|
impl<T: AsRef<[BufferRows]>> BufferImageBufferCopyRegion for T {
|
|
fn regions(&self) -> &[BufferRows] {
|
|
self.as_ref()
|
|
}
|
|
}
|
|
|
|
pub trait ImageCopyRegion<T: IntoImageSubresourceRange> {
|
|
fn regions(&self) -> &[TextureRegion<T>];
|
|
}
|
|
impl<T: IntoImageSubresourceRange> ImageCopyRegion<T> for TextureRegion<T> {
|
|
fn regions(&self) -> &[TextureRegion<T>] {
|
|
core::slice::from_ref(self)
|
|
}
|
|
}
|
|
impl<U: IntoImageSubresourceRange, T: AsRef<[TextureRegion<U>]>> ImageCopyRegion<U> for T {
|
|
fn regions(&self) -> &[TextureRegion<U>] {
|
|
self.as_ref()
|
|
}
|
|
}
|
|
|
|
pub trait BufferCopyRegion {
|
|
fn regions(&self) -> &[BufferRange];
|
|
}
|
|
impl BufferCopyRegion for BufferRange {
|
|
fn regions(&self) -> &[BufferRange] {
|
|
core::slice::from_ref(self)
|
|
}
|
|
}
|
|
impl<T: AsRef<[BufferRange]>> BufferCopyRegion for T {
|
|
fn regions(&self) -> &[BufferRange] {
|
|
self.as_ref()
|
|
}
|
|
}
|
|
|
|
pub struct CopyBuffers<T: BufferCopyRegion> {
|
|
pub src: Buffer,
|
|
pub src_region: T,
|
|
pub dst: Buffer,
|
|
pub dst_region: T,
|
|
}
|
|
|
|
pub struct CopyTextures<T: ImageCopyRegion<R>, R: IntoImageSubresourceRange> {
|
|
pub src: Texture,
|
|
pub src_region: T,
|
|
pub dst: Texture,
|
|
pub dst_region: T,
|
|
_pd: PhantomData<R>,
|
|
}
|
|
|
|
pub struct CopyBufferToTexture<
|
|
T: BufferImageBufferCopyRegion,
|
|
U: ImageCopyRegion<R>,
|
|
R: IntoImageSubresourceLayers,
|
|
> {
|
|
pub src: Buffer,
|
|
pub src_region: T,
|
|
pub dst: Texture,
|
|
pub dst_region: U,
|
|
_pd: PhantomData<R>,
|
|
}
|
|
|
|
pub struct CopyTextureToBuffer<
|
|
T: ImageCopyRegion<R>,
|
|
U: BufferImageBufferCopyRegion,
|
|
R: IntoImageSubresourceLayers,
|
|
> {
|
|
pub src: Texture,
|
|
pub src_region: T,
|
|
pub dst: Buffer,
|
|
pub dst_region: U,
|
|
_pd: PhantomData<R>,
|
|
}
|
|
|
|
impl<T, U, R> Command for CopyBufferToTexture<T, U, R>
|
|
where
|
|
T: BufferImageBufferCopyRegion,
|
|
U: ImageCopyRegion<R>,
|
|
R: IntoImageSubresourceLayers,
|
|
{
|
|
fn side_effects(&self, mut map: SideEffectMap) {
|
|
let block_size = self.dst.format().block_size() as u64;
|
|
let texels_per_block = self.dst.format().texels_per_block() as u64;
|
|
|
|
let src_id = self.src.id();
|
|
let dst_id = self.dst.id();
|
|
|
|
for (src_region, dst_region) in self
|
|
.src_region
|
|
.regions()
|
|
.iter()
|
|
.zip(self.dst_region.regions())
|
|
{
|
|
let texel_count = if src_region.row_size != 0 {
|
|
let texel_count = src_region.row_size as u64 * src_region.row_count as u64;
|
|
debug_assert!(
|
|
texel_count % texels_per_block == 0,
|
|
"Buffer copy region must be a multiple of the block size"
|
|
);
|
|
texel_count
|
|
} else {
|
|
dst_region.extent.width as u64
|
|
* dst_region.extent.height as u64
|
|
* dst_region.extent.depth as u64
|
|
};
|
|
|
|
let byte_size = texel_count.div_ceil(texels_per_block) * block_size;
|
|
|
|
map.with(
|
|
vk::PipelineStageFlags2::COPY,
|
|
vk::AccessFlags2::TRANSFER_READ,
|
|
Some(vk::ImageLayout::TRANSFER_SRC_OPTIMAL),
|
|
)
|
|
.insert_range(
|
|
src_id,
|
|
BufferRange {
|
|
offset: src_region.offset,
|
|
size: byte_size,
|
|
},
|
|
);
|
|
|
|
map.with(
|
|
vk::PipelineStageFlags2::COPY,
|
|
vk::AccessFlags2::TRANSFER_WRITE,
|
|
Some(vk::ImageLayout::TRANSFER_DST_OPTIMAL),
|
|
)
|
|
.insert_range(dst_id, dst_region.range.into_range());
|
|
}
|
|
}
|
|
|
|
fn apply(self, recorder: &mut CommandRecorder) {
|
|
let cmd = recorder.cmd_buffer();
|
|
let dev = &cmd.device().raw;
|
|
|
|
let regions = self
|
|
.src_region
|
|
.regions()
|
|
.iter()
|
|
.zip(self.dst_region.regions())
|
|
.map(|(src_region, dst_region)| {
|
|
vk::BufferImageCopy2::default()
|
|
.buffer_offset(src_region.offset)
|
|
.buffer_row_length(src_region.row_size)
|
|
.buffer_image_height(src_region.row_count)
|
|
.image_offset(dst_region.offset)
|
|
.image_extent(dst_region.extent)
|
|
.image_subresource(dst_region.range.into_layers())
|
|
})
|
|
.collect::<Box<[_]>>();
|
|
|
|
let info = vk::CopyBufferToImageInfo2::default()
|
|
.src_buffer(recorder.get_buffer_handle(self.src.id()))
|
|
.dst_image(recorder.get_image_handle(self.dst.id()))
|
|
.dst_image_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
|
|
.regions(®ions);
|
|
|
|
unsafe {
|
|
dev.cmd_copy_buffer_to_image2(cmd.raw(), &info);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, U, R> Command for CopyTextureToBuffer<T, U, R>
|
|
where
|
|
T: ImageCopyRegion<R>,
|
|
U: BufferImageBufferCopyRegion,
|
|
R: IntoImageSubresourceLayers,
|
|
{
|
|
fn side_effects(&self, mut map: SideEffectMap) {
|
|
let block_size = self.src.format().block_size() as u64;
|
|
let texels_per_block = self.src.format().texels_per_block() as u64;
|
|
|
|
let src_id = self.src.id();
|
|
let dst_id = self.dst.id();
|
|
|
|
for (src_region, dst_region) in self
|
|
.src_region
|
|
.regions()
|
|
.iter()
|
|
.zip(self.dst_region.regions())
|
|
{
|
|
let texel_count = if dst_region.row_size != 0 {
|
|
let texel_count = dst_region.row_size as u64 * dst_region.row_count as u64;
|
|
debug_assert!(
|
|
texel_count % texels_per_block == 0,
|
|
"Buffer copy region must be a multiple of the block size"
|
|
);
|
|
texel_count
|
|
} else {
|
|
src_region.extent.width as u64
|
|
* src_region.extent.height as u64
|
|
* src_region.extent.depth as u64
|
|
};
|
|
|
|
let byte_size = texel_count.div_ceil(texels_per_block) * block_size;
|
|
|
|
map.with(
|
|
vk::PipelineStageFlags2::COPY,
|
|
vk::AccessFlags2::TRANSFER_READ,
|
|
Some(vk::ImageLayout::TRANSFER_SRC_OPTIMAL),
|
|
)
|
|
.insert_range(dst_id, src_region.range.into_range());
|
|
|
|
map.with(
|
|
vk::PipelineStageFlags2::COPY,
|
|
vk::AccessFlags2::TRANSFER_WRITE,
|
|
Some(vk::ImageLayout::TRANSFER_DST_OPTIMAL),
|
|
)
|
|
.insert_range(
|
|
src_id,
|
|
BufferRange {
|
|
offset: dst_region.offset,
|
|
size: byte_size,
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
fn apply(self, recorder: &mut CommandRecorder) {
|
|
let cmd = recorder.cmd_buffer();
|
|
let dev = &cmd.device().raw;
|
|
|
|
let regions = self
|
|
.src_region
|
|
.regions()
|
|
.iter()
|
|
.zip(self.dst_region.regions())
|
|
.map(|(src_region, dst_region)| {
|
|
vk::BufferImageCopy2::default()
|
|
.buffer_offset(dst_region.offset)
|
|
.buffer_row_length(dst_region.row_size)
|
|
.buffer_image_height(dst_region.row_count)
|
|
.image_offset(src_region.offset)
|
|
.image_extent(src_region.extent)
|
|
.image_subresource(src_region.range.into_layers())
|
|
})
|
|
.collect::<Box<[_]>>();
|
|
|
|
let info = vk::CopyImageToBufferInfo2::default()
|
|
.dst_buffer(recorder.get_buffer_handle(self.src.id()))
|
|
.src_image(recorder.get_image_handle(self.dst.id()))
|
|
.src_image_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL)
|
|
.regions(®ions);
|
|
|
|
unsafe {
|
|
dev.cmd_copy_image_to_buffer2(cmd.raw(), &info);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> Command for CopyBuffers<T>
|
|
where
|
|
T: BufferCopyRegion,
|
|
{
|
|
fn side_effects(&self, mut map: SideEffectMap) {
|
|
for region in self.src_region.regions() {
|
|
map.with(
|
|
vk::PipelineStageFlags2::COPY,
|
|
vk::AccessFlags2::TRANSFER_READ,
|
|
None,
|
|
)
|
|
.insert_range(self.src.id(), *region);
|
|
}
|
|
|
|
for region in self.dst_region.regions() {
|
|
map.with(
|
|
vk::PipelineStageFlags2::COPY,
|
|
vk::AccessFlags2::TRANSFER_WRITE,
|
|
None,
|
|
)
|
|
.insert_range(self.dst.id(), *region);
|
|
}
|
|
}
|
|
|
|
fn apply(self, recorder: &mut CommandRecorder) {
|
|
let cmd = recorder.cmd_buffer();
|
|
let dev = &cmd.device().raw;
|
|
|
|
let regions = self
|
|
.src_region
|
|
.regions()
|
|
.iter()
|
|
.zip(self.dst_region.regions())
|
|
.map(|(src_region, dst_region)| {
|
|
vk::BufferCopy2::default()
|
|
.src_offset(src_region.offset)
|
|
.dst_offset(dst_region.offset)
|
|
.size(src_region.size)
|
|
})
|
|
.collect::<Box<[_]>>();
|
|
|
|
let info = vk::CopyBufferInfo2::default()
|
|
.dst_buffer(recorder.get_buffer_handle(self.dst.id()))
|
|
.src_buffer(recorder.get_buffer_handle(self.src.id()))
|
|
.regions(®ions);
|
|
|
|
unsafe {
|
|
dev.cmd_copy_buffer2(cmd.raw(), &info);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, R> Command for CopyTextures<T, R>
|
|
where
|
|
T: ImageCopyRegion<R>,
|
|
R: IntoImageSubresourceLayers,
|
|
{
|
|
fn side_effects(&self, mut map: SideEffectMap) {
|
|
for region in self.src_region.regions() {
|
|
map.with(
|
|
vk::PipelineStageFlags2::COPY,
|
|
vk::AccessFlags2::TRANSFER_READ,
|
|
None,
|
|
)
|
|
.insert_range(self.src.id(), region.range.into_range());
|
|
}
|
|
|
|
for region in self.dst_region.regions() {
|
|
map.with(
|
|
vk::PipelineStageFlags2::COPY,
|
|
vk::AccessFlags2::TRANSFER_WRITE,
|
|
None,
|
|
)
|
|
.insert_range(self.dst.id(), region.range.into_range());
|
|
}
|
|
}
|
|
|
|
fn apply(self, recorder: &mut CommandRecorder) {
|
|
let cmd = recorder.cmd_buffer();
|
|
let dev = &cmd.device().raw;
|
|
|
|
let regions = self
|
|
.src_region
|
|
.regions()
|
|
.iter()
|
|
.zip(self.dst_region.regions())
|
|
.map(|(src_region, dst_region)| {
|
|
vk::ImageCopy2::default()
|
|
.src_offset(src_region.offset)
|
|
.src_subresource(src_region.range.into_layers())
|
|
.dst_offset(dst_region.offset)
|
|
.dst_subresource(dst_region.range.into_layers())
|
|
.extent(src_region.extent)
|
|
})
|
|
.collect::<Box<[_]>>();
|
|
|
|
let info = vk::CopyImageInfo2::default()
|
|
.src_image(recorder.get_image_handle(self.src.id()))
|
|
.src_image_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL)
|
|
.dst_image(recorder.get_image_handle(self.dst.id()))
|
|
.dst_image_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL)
|
|
.regions(®ions);
|
|
|
|
unsafe {
|
|
dev.cmd_copy_image2(cmd.raw(), &info);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct ClearTexture<R: IntoImageSubresourceRange> {
|
|
pub dst: Texture,
|
|
pub dst_region: TextureRegion<R>,
|
|
pub clear_value: [f32; 4],
|
|
}
|
|
|
|
impl<R: IntoImageSubresourceRange> Command for ClearTexture<R> {
|
|
fn side_effects(&self, mut map: SideEffectMap) {
|
|
map.with(
|
|
vk::PipelineStageFlags2::CLEAR,
|
|
vk::AccessFlags2::TRANSFER_WRITE,
|
|
Some(vk::ImageLayout::TRANSFER_DST_OPTIMAL),
|
|
)
|
|
.insert_range(self.dst.id(), self.dst_region.range.into_range());
|
|
}
|
|
|
|
fn apply(self, recorder: &mut CommandRecorder) {
|
|
let cmd = recorder.cmd_buffer();
|
|
let dev = &cmd.device().raw;
|
|
|
|
let values = vk::ClearColorValue {
|
|
float32: self.clear_value,
|
|
};
|
|
|
|
unsafe {
|
|
dev.cmd_clear_color_image(
|
|
cmd.raw(),
|
|
recorder.get_image_handle(self.dst.id()),
|
|
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
|
&values,
|
|
&[self.dst_region.range.into_range()],
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct UpdateBuffer {
|
|
pub dst: Buffer,
|
|
pub dst_region: BufferRange,
|
|
pub data: Vec<u8>,
|
|
}
|
|
|
|
impl Command for UpdateBuffer {
|
|
fn side_effects(&self, mut map: SideEffectMap) {
|
|
map.with(
|
|
vk::PipelineStageFlags2::TRANSFER,
|
|
vk::AccessFlags2::TRANSFER_WRITE,
|
|
None,
|
|
)
|
|
.insert_range(self.dst.id(), self.dst_region);
|
|
}
|
|
|
|
fn apply(self, _recorder: &mut CommandRecorder) {}
|
|
}
|
|
|
|
pub struct RenderAttachment {
|
|
pub texture: Texture,
|
|
pub region: vk::ImageSubresourceRange,
|
|
pub layout: vk::ImageLayout,
|
|
pub load_op: vk::AttachmentLoadOp,
|
|
pub store_op: vk::AttachmentStoreOp,
|
|
}
|
|
|
|
pub struct BeginRendering {
|
|
pub color_attachments: Vec<RenderAttachment>,
|
|
pub depth_attachment: Option<RenderAttachment>,
|
|
pub stencil_attachment: Option<RenderAttachment>,
|
|
pub area: (u32, u32),
|
|
pub layers: u32,
|
|
}
|
|
|
|
impl Command for BeginRendering {
|
|
fn side_effects(&self, mut map: SideEffectMap) {
|
|
for attachment in &self.color_attachments {
|
|
// The dependencies declared here need to be barrier'd before the
|
|
// render pass: the spec says pipelineBarrier inside of a render
|
|
// pass instance referencing framebuffer-local stages must contain
|
|
// only framebuffer-local stages.
|
|
|
|
map.with(
|
|
vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT,
|
|
if attachment.load_op == vk::AttachmentLoadOp::LOAD {
|
|
vk::AccessFlags2::COLOR_ATTACHMENT_READ
|
|
} else {
|
|
vk::AccessFlags2::empty()
|
|
},
|
|
Some(attachment.layout),
|
|
)
|
|
.insert_range(attachment.texture.id(), attachment.region);
|
|
}
|
|
if let Some(depth) = &self.depth_attachment {
|
|
map.with(
|
|
vk::PipelineStageFlags2::EARLY_FRAGMENT_TESTS,
|
|
if depth.load_op == vk::AttachmentLoadOp::LOAD {
|
|
vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_READ
|
|
} else {
|
|
vk::AccessFlags2::empty()
|
|
},
|
|
Some(depth.layout),
|
|
)
|
|
.insert_range(depth.texture.id(), depth.region);
|
|
}
|
|
if let Some(stencil) = &self.stencil_attachment {
|
|
map.with(
|
|
vk::PipelineStageFlags2::EARLY_FRAGMENT_TESTS,
|
|
if stencil.load_op == vk::AttachmentLoadOp::LOAD {
|
|
vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_READ
|
|
} else {
|
|
vk::AccessFlags2::empty()
|
|
},
|
|
Some(stencil.layout),
|
|
)
|
|
.insert_range(stencil.texture.id(), stencil.region);
|
|
}
|
|
}
|
|
|
|
fn apply(self, _recorder: &mut CommandRecorder) {}
|
|
}
|
|
|
|
pub struct EndRendering;
|
|
|
|
impl Command for EndRendering {
|
|
fn side_effects(&self, _map: SideEffectMap) {
|
|
// TODO: need the beginrendering struct to declare the store-op dependencies.
|
|
}
|
|
|
|
fn apply(self, _recorder: &mut CommandRecorder) {}
|
|
}
|
|
|
|
pub struct BindPipeline(pub vk::Pipeline, pub vk::PipelineBindPoint);
|
|
|
|
impl Command for BindPipeline {
|
|
fn side_effects(&self, _map: SideEffectMap) {
|
|
// No resource access, but affects the pipeline state
|
|
}
|
|
|
|
fn apply(self, recorder: &mut CommandRecorder) {
|
|
let cmd = recorder.cmd_buffer();
|
|
let dev = &cmd.device().raw;
|
|
|
|
unsafe {
|
|
dev.cmd_bind_pipeline(cmd.raw(), self.1, self.0);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct BindVertexBuffers {
|
|
pub buffers: Vec<BufferSlice>,
|
|
pub first_binding: u32,
|
|
}
|
|
|
|
impl Command for BindVertexBuffers {
|
|
fn side_effects(&self, mut map: SideEffectMap) {
|
|
for buffer in &self.buffers {
|
|
map.with(
|
|
vk::PipelineStageFlags2::VERTEX_ATTRIBUTE_INPUT,
|
|
vk::AccessFlags2::VERTEX_ATTRIBUTE_READ,
|
|
None,
|
|
)
|
|
.insert_range(buffer.id(), buffer.range);
|
|
}
|
|
}
|
|
|
|
fn apply(self, recorder: &mut CommandRecorder) {
|
|
let cmd = recorder.cmd_buffer();
|
|
let dev = &cmd.device().raw;
|
|
|
|
let buffers = self
|
|
.buffers
|
|
.iter()
|
|
.map(|buffer| recorder.get_buffer_handle(buffer.id()))
|
|
.collect::<Vec<_>>();
|
|
|
|
let offsets = self
|
|
.buffers
|
|
.iter()
|
|
.map(|buffer| buffer.range.offset)
|
|
.collect::<Vec<_>>();
|
|
|
|
unsafe {
|
|
dev.cmd_bind_vertex_buffers(cmd.raw(), self.first_binding, &buffers, &offsets);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct BindIndexBuffer(pub BufferSlice, pub IndexFormat);
|
|
|
|
impl Command for BindIndexBuffer {
|
|
fn side_effects(&self, mut map: SideEffectMap) {
|
|
map.with(
|
|
vk::PipelineStageFlags2::INDEX_INPUT,
|
|
vk::AccessFlags2::INDEX_READ,
|
|
None,
|
|
)
|
|
.insert_range(self.0.id(), self.0.range);
|
|
}
|
|
|
|
fn apply(self, recorder: &mut CommandRecorder) {
|
|
let cmd = recorder.cmd_buffer();
|
|
let dev = &cmd.device().raw;
|
|
|
|
let buffer = recorder.get_buffer_handle(self.0.id());
|
|
let index_type = match self.1 {
|
|
IndexFormat::Uint8 => vk::IndexType::UINT8_KHR,
|
|
IndexFormat::Uint16 => vk::IndexType::UINT16,
|
|
IndexFormat::Uint32 => vk::IndexType::UINT32,
|
|
};
|
|
|
|
unsafe {
|
|
dev.cmd_bind_index_buffer(cmd.raw(), buffer, self.0.range.offset, index_type);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct BindDescriptorSets {
|
|
pub first_set: u32,
|
|
pub pipeline_layout: vk::PipelineLayout,
|
|
pub bind_point: vk::PipelineBindPoint,
|
|
pub sets: Vec<vk::DescriptorSet>,
|
|
}
|
|
|
|
impl Command for BindDescriptorSets {
|
|
fn side_effects(&self, _map: SideEffectMap) {
|
|
// No resource access, but affects the descriptor set state
|
|
}
|
|
|
|
fn apply(self, recorder: &mut CommandRecorder) {
|
|
let cmd = recorder.cmd_buffer();
|
|
let dev = &cmd.device().raw;
|
|
|
|
unsafe {
|
|
dev.cmd_bind_descriptor_sets(
|
|
cmd.raw(),
|
|
self.bind_point,
|
|
self.pipeline_layout,
|
|
self.first_set,
|
|
&self.sets,
|
|
&[],
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct SetViewport {
|
|
pub x: f32,
|
|
pub y: f32,
|
|
pub width: f32,
|
|
pub height: f32,
|
|
pub min_depth: f32,
|
|
pub max_depth: f32,
|
|
}
|
|
|
|
impl Command for SetViewport {
|
|
fn side_effects(&self, _map: SideEffectMap) {
|
|
// No resource access, but affects the viewport state
|
|
}
|
|
|
|
fn apply(self, _recorder: &mut CommandRecorder) {}
|
|
}
|
|
|
|
pub struct SetScissor {
|
|
pub x: u32,
|
|
pub y: u32,
|
|
pub width: u32,
|
|
pub height: u32,
|
|
}
|
|
|
|
impl Command for SetScissor {
|
|
fn side_effects(&self, _map: SideEffectMap) {
|
|
// No resource access, but affects the scissor state
|
|
}
|
|
|
|
fn apply(self, _recorder: &mut CommandRecorder) {}
|
|
}
|
|
|
|
pub struct PushConstants {
|
|
pub pipeline_layout: vk::PipelineLayout,
|
|
pub stage_flags: vk::ShaderStageFlags,
|
|
pub offset: u32,
|
|
pub data: Vec<u8>,
|
|
}
|
|
|
|
impl Command for PushConstants {
|
|
fn side_effects(&self, _map: SideEffectMap) {
|
|
// No resource access, but affects the push constant state
|
|
}
|
|
|
|
fn apply(self, recorder: &mut CommandRecorder) {
|
|
let cmd = recorder.cmd_buffer();
|
|
let dev = &cmd.device().raw;
|
|
|
|
unsafe {
|
|
dev.cmd_push_constants(
|
|
cmd.raw(),
|
|
self.pipeline_layout,
|
|
self.stage_flags,
|
|
self.offset,
|
|
&self.data,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct DrawData {
|
|
pub vertex_count: u32,
|
|
pub instance_count: u32,
|
|
pub first_vertex: u32,
|
|
pub first_instance: u32,
|
|
}
|
|
|
|
pub struct DrawIndexedData {
|
|
pub index_count: u32,
|
|
pub instance_count: u32,
|
|
pub first_index: u32,
|
|
pub vertex_offset: i32,
|
|
pub first_instance: u32,
|
|
}
|
|
|
|
pub struct DrawIndirectData {
|
|
pub indirect_buffer: BufferSlice,
|
|
pub count: u32,
|
|
pub stride: u32,
|
|
}
|
|
|
|
pub struct DrawIndexedIndirectData {
|
|
pub indirect_buffer: BufferSlice,
|
|
pub count: u32,
|
|
pub stride: u32,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub enum IndexFormat {
|
|
Uint8,
|
|
Uint16,
|
|
Uint32,
|
|
}
|
|
|
|
pub struct Draw<T: IsDrawData> {
|
|
pub data: T,
|
|
}
|
|
|
|
impl<T: IsDrawData> Command for Draw<T> {
|
|
fn side_effects(&self, mut map: SideEffectMap) {
|
|
self.data.side_effects(map.reborrow());
|
|
}
|
|
|
|
fn apply(self, _recorder: &mut CommandRecorder) {}
|
|
}
|
|
|
|
pub struct PipelineBarrier;
|
|
|
|
impl Command for PipelineBarrier {
|
|
fn side_effects(&self, _map: SideEffectMap) {}
|
|
|
|
fn apply(self, _recorder: &mut CommandRecorder) {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
pub trait Command: Sized {
|
|
fn side_effects(&self, map: SideEffectMap);
|
|
fn apply(self, recorder: &mut CommandRecorder);
|
|
}
|
|
|
|
mod sealed {
|
|
pub trait IsDrawData {
|
|
fn side_effects(&self, _map: super::SideEffectMap) {}
|
|
}
|
|
pub trait InsideRenderPass {}
|
|
pub trait OutsideRenderPass {}
|
|
}
|
|
use ash::vk;
|
|
use ash_formats::FormatExt;
|
|
pub(super) use sealed::*;
|
|
|
|
impl IsDrawData for DrawData {}
|
|
impl IsDrawData for DrawIndexedData {}
|
|
impl IsDrawData for DrawIndirectData {
|
|
fn side_effects(&self, mut map: SideEffectMap) {
|
|
map.with(
|
|
vk::PipelineStageFlags2::DRAW_INDIRECT,
|
|
vk::AccessFlags2::INDIRECT_COMMAND_READ,
|
|
None,
|
|
)
|
|
.insert_range(self.indirect_buffer.id(), self.indirect_buffer.range);
|
|
}
|
|
}
|
|
impl IsDrawData for DrawIndexedIndirectData {
|
|
fn side_effects(&self, mut map: SideEffectMap) {
|
|
map.with(
|
|
vk::PipelineStageFlags2::DRAW_INDIRECT,
|
|
vk::AccessFlags2::INDIRECT_COMMAND_READ,
|
|
None,
|
|
)
|
|
.insert_range(self.indirect_buffer.id(), self.indirect_buffer.range);
|
|
}
|
|
}
|
|
|
|
impl OutsideRenderPass for BeginRendering {}
|
|
impl InsideRenderPass for EndRendering {}
|
|
impl<T: IntoImageSubresourceRange> OutsideRenderPass for ClearTexture<T> {}
|
|
impl OutsideRenderPass for UpdateBuffer {}
|
|
|
|
impl InsideRenderPass for BindPipeline {}
|
|
impl OutsideRenderPass for BindPipeline {}
|
|
impl InsideRenderPass for BindVertexBuffers {}
|
|
impl OutsideRenderPass for BindVertexBuffers {}
|
|
impl InsideRenderPass for BindIndexBuffer {}
|
|
impl OutsideRenderPass for BindIndexBuffer {}
|
|
impl InsideRenderPass for BindDescriptorSets {}
|
|
impl OutsideRenderPass for BindDescriptorSets {}
|
|
impl OutsideRenderPass for PushConstants {}
|
|
|
|
impl InsideRenderPass for SetViewport {}
|
|
impl InsideRenderPass for SetScissor {}
|
|
impl InsideRenderPass for PushConstants {}
|
|
impl<T: IsDrawData> InsideRenderPass for Draw<T> {}
|
|
impl<T: IsDrawData> InsideRenderPass for T {}
|