command trait
This commit is contained in:
parent
3438dfde84
commit
15afc00e51
|
|
@ -1,4 +1,19 @@
|
|||
#![allow(dead_code)]
|
||||
//! 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 crate::render_graph::recorder::{CommandMeta, CommandRecorder, SideEffectMap};
|
||||
|
||||
use super::resources::*;
|
||||
|
||||
|
|
@ -12,11 +27,6 @@ pub struct CopyTextures {
|
|||
pub dst: Write<TextureRegion>,
|
||||
}
|
||||
|
||||
pub struct ClearTexture {
|
||||
pub dst: Write<TextureRegion>,
|
||||
pub clear_value: [f32; 4],
|
||||
}
|
||||
|
||||
pub struct CopyBufferToTexture {
|
||||
pub src: Read<BufferSlice>,
|
||||
pub dst: Write<TextureRegion>,
|
||||
|
|
@ -31,20 +41,108 @@ pub struct Copy<T: Resource, U: Resource> {
|
|||
pub src: Read<T>,
|
||||
pub dst: Write<U>,
|
||||
}
|
||||
impl<T: Resource, U: Resource> Copy<T, U> {
|
||||
fn side_effects_inner(&self, mut map: SideEffectMap) {
|
||||
map.insert(
|
||||
self.src.id(),
|
||||
ResourceAccess {
|
||||
range: self.src.into_access(),
|
||||
access: vk::AccessFlags2::TRANSFER_READ,
|
||||
},
|
||||
);
|
||||
map.insert(
|
||||
self.dst.id(),
|
||||
ResourceAccess {
|
||||
range: self.dst.into_access(),
|
||||
access: vk::AccessFlags2::TRANSFER_WRITE,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl Command for Copy<BufferSlice, TextureRegion> {
|
||||
fn side_effects(&self, map: SideEffectMap) {
|
||||
self.side_effects_inner(map)
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {
|
||||
// CopyBufferToTexture {src, dst}.apply(recorder)
|
||||
}
|
||||
}
|
||||
impl Command for Copy<TextureRegion, BufferSlice> {
|
||||
fn side_effects(&self, map: SideEffectMap) {
|
||||
self.side_effects_inner(map)
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {
|
||||
// CopyTextureToBuffer {src, dst}.apply(recorder)
|
||||
}
|
||||
|
||||
fn get_meta(&self) -> CommandMeta {
|
||||
CommandMeta {
|
||||
apply_command: |recorder, command_ptr, cursor| {
|
||||
let command = unsafe { command_ptr.cast::<Self>().read() };
|
||||
*cursor += std::mem::size_of::<Self>();
|
||||
command.apply(recorder);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Command for Copy<TextureRegion, TextureRegion> {
|
||||
fn side_effects(&self, map: SideEffectMap) {
|
||||
self.side_effects_inner(map)
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {
|
||||
// CopyTextures {src, dst}.apply(recorder)
|
||||
}
|
||||
}
|
||||
impl Command for Copy<BufferSlice, BufferSlice> {
|
||||
fn side_effects(&self, map: SideEffectMap) {
|
||||
self.side_effects_inner(map)
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {
|
||||
// CopyBuffers {src, dst}.apply(recorder)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClearTexture {
|
||||
pub dst: Write<TextureRegion>,
|
||||
pub clear_value: [f32; 4],
|
||||
}
|
||||
|
||||
impl Command for ClearTexture {
|
||||
fn side_effects(&self, mut map: SideEffectMap) {
|
||||
map.insert(
|
||||
self.dst.id(),
|
||||
ResourceAccess {
|
||||
range: self.dst.into_access(),
|
||||
access: vk::AccessFlags2::TRANSFER_WRITE,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||
}
|
||||
|
||||
pub struct UpdateBuffer {
|
||||
pub dst: Write<BufferSlice>,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct UpdateTexture {
|
||||
pub dst: Write<TextureRegion>,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
impl Command for UpdateBuffer {
|
||||
fn side_effects(&self, mut map: SideEffectMap) {
|
||||
map.insert(
|
||||
self.dst.id(),
|
||||
ResourceAccess {
|
||||
range: self.dst.into_access(),
|
||||
access: vk::AccessFlags2::TRANSFER_WRITE,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub struct Update<T: Resource> {
|
||||
pub dst: Write<T>,
|
||||
pub data: Vec<u8>,
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||
}
|
||||
|
||||
pub struct RenderPass {
|
||||
|
|
@ -55,15 +153,98 @@ pub struct RenderPass {
|
|||
pub layers: u32,
|
||||
}
|
||||
|
||||
impl Command for RenderPass {
|
||||
fn side_effects(&self, mut map: SideEffectMap) {
|
||||
for attachment in &self.color_attachments {
|
||||
map.insert(
|
||||
attachment.id(),
|
||||
ResourceAccess {
|
||||
range: attachment.into_access(),
|
||||
access: vk::AccessFlags2::COLOR_ATTACHMENT_WRITE,
|
||||
},
|
||||
);
|
||||
}
|
||||
if let Some(depth) = &self.depth_attachment {
|
||||
map.insert(
|
||||
depth.id(),
|
||||
ResourceAccess {
|
||||
range: depth.into_access(),
|
||||
access: vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_WRITE,
|
||||
},
|
||||
);
|
||||
}
|
||||
if let Some(stencil) = &self.stencil_attachment {
|
||||
map.insert(
|
||||
stencil.id(),
|
||||
ResourceAccess {
|
||||
range: stencil.into_access(),
|
||||
access: vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_WRITE,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||
}
|
||||
|
||||
pub struct BindPipeline(pub Pipeline);
|
||||
|
||||
impl Command for BindPipeline {
|
||||
fn side_effects(&self, _map: SideEffectMap) {
|
||||
// No resource access, but affects the pipeline state
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||
}
|
||||
|
||||
pub struct BindVertexBuffers {
|
||||
pub buffers: Vec<Read<BufferSlice>>,
|
||||
}
|
||||
|
||||
impl Command for BindVertexBuffers {
|
||||
fn side_effects(&self, mut map: SideEffectMap) {
|
||||
for buffer in &self.buffers {
|
||||
map.insert(
|
||||
buffer.id(),
|
||||
ResourceAccess {
|
||||
range: buffer.into_access(),
|
||||
access: vk::AccessFlags2::VERTEX_ATTRIBUTE_READ,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||
}
|
||||
|
||||
pub struct BindIndexBuffer(pub Read<BufferSlice>, pub IndexFormat);
|
||||
|
||||
impl Command for BindIndexBuffer {
|
||||
fn side_effects(&self, mut map: SideEffectMap) {
|
||||
map.insert(
|
||||
self.0.id(),
|
||||
ResourceAccess {
|
||||
range: self.0.into_access(),
|
||||
access: vk::AccessFlags2::INDEX_READ,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||
}
|
||||
|
||||
pub struct BindDescriptorSets {
|
||||
pub sets: Vec<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) {}
|
||||
}
|
||||
|
||||
pub struct SetViewport {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
|
|
@ -73,6 +254,14 @@ pub struct SetViewport {
|
|||
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,
|
||||
|
|
@ -80,10 +269,26 @@ pub struct SetScissor {
|
|||
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 data: Box<[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) {}
|
||||
}
|
||||
|
||||
pub struct DrawData {
|
||||
pub vertex_count: u32,
|
||||
pub instance_count: u32,
|
||||
|
|
@ -121,19 +326,94 @@ pub struct Draw<T: IsDrawData> {
|
|||
pub count_buffer: Option<Read<BufferSlice>>,
|
||||
}
|
||||
|
||||
pub struct Dispatch;
|
||||
impl<T: IsDrawData> Command for Draw<T> {
|
||||
fn side_effects(&self, mut map: SideEffectMap) {
|
||||
self.data.side_effects(map.reborrow());
|
||||
for vertex_buffer in &self.vertex_buffers {
|
||||
map.insert(
|
||||
vertex_buffer.id(),
|
||||
ResourceAccess {
|
||||
range: vertex_buffer.into_access(),
|
||||
access: vk::AccessFlags2::VERTEX_ATTRIBUTE_READ,
|
||||
},
|
||||
);
|
||||
}
|
||||
if let Some((index_buffer, _)) = &self.index_buffer {
|
||||
map.insert(
|
||||
index_buffer.id(),
|
||||
ResourceAccess {
|
||||
range: index_buffer.into_access(),
|
||||
access: vk::AccessFlags2::INDEX_READ,
|
||||
},
|
||||
);
|
||||
}
|
||||
if let Some(count_buffer) = &self.count_buffer {
|
||||
map.insert(
|
||||
count_buffer.id(),
|
||||
ResourceAccess {
|
||||
range: count_buffer.into_access(),
|
||||
access: vk::AccessFlags2::INDIRECT_COMMAND_READ,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn apply(self, _recorder: &mut CommandRecorder) {}
|
||||
}
|
||||
|
||||
pub trait Command: Sized {
|
||||
fn side_effects(&self, map: SideEffectMap);
|
||||
fn apply(self, recorder: &mut CommandRecorder);
|
||||
fn get_meta(&self) -> CommandMeta {
|
||||
CommandMeta {
|
||||
apply_command: |recorder, command_ptr, cursor| {
|
||||
let command = unsafe { command_ptr.cast::<Self>().read() };
|
||||
*cursor += std::mem::size_of::<Self>();
|
||||
command.apply(recorder);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait IsDrawData {}
|
||||
pub trait IsDrawData {
|
||||
fn side_effects(&self, _map: super::SideEffectMap) {}
|
||||
}
|
||||
pub trait InsideRenderPass {}
|
||||
pub trait OutsideRenderPass {}
|
||||
}
|
||||
use ash::vk;
|
||||
use sealed::*;
|
||||
|
||||
impl IsDrawData for DrawData {}
|
||||
impl IsDrawData for DrawIndexedData {}
|
||||
impl IsDrawData for DrawIndirectData {}
|
||||
impl IsDrawData for DrawIndexedIndirectData {}
|
||||
impl IsDrawData for DrawIndirectData {
|
||||
fn side_effects(&self, mut map: SideEffectMap) {
|
||||
map.insert(
|
||||
self.indirect_buffer.id(),
|
||||
ResourceAccess {
|
||||
range: self.indirect_buffer.into_access(),
|
||||
access: vk::AccessFlags2::INDIRECT_COMMAND_READ,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
impl IsDrawData for DrawIndexedIndirectData {
|
||||
fn side_effects(&self, mut map: SideEffectMap) {
|
||||
map.insert(
|
||||
self.indirect_buffer.id(),
|
||||
ResourceAccess {
|
||||
range: self.indirect_buffer.into_access(),
|
||||
access: vk::AccessFlags2::INDIRECT_COMMAND_READ,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl OutsideRenderPass for RenderPass {}
|
||||
impl<T: Resource, U: Resource> OutsideRenderPass for Copy<T, U> {}
|
||||
impl OutsideRenderPass for ClearTexture {}
|
||||
impl OutsideRenderPass for UpdateBuffer {}
|
||||
|
||||
impl InsideRenderPass for BindPipeline {}
|
||||
impl OutsideRenderPass for BindPipeline {}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,71 @@
|
|||
struct CommandRecorder;
|
||||
struct RenderPassRecorder;
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
mem::{MaybeUninit, size_of},
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
||||
use crate::render_graph::{
|
||||
commands::Command,
|
||||
resources::{ResourceAccess, ResourceId},
|
||||
};
|
||||
|
||||
pub struct CommandRecorder;
|
||||
pub struct RenderPassRecorder;
|
||||
|
||||
pub struct CommandMeta {
|
||||
pub(crate) apply_command: unsafe fn(&mut CommandRecorder, NonNull<()>, cursor: &mut usize),
|
||||
}
|
||||
|
||||
pub struct SideEffectMap<'a>(&'a mut BTreeMap<(ResourceId, u32), ResourceAccess>, u32);
|
||||
|
||||
impl<'a> SideEffectMap<'a> {
|
||||
pub fn insert(&mut self, id: ResourceId, access: ResourceAccess) {
|
||||
self.0.insert((id, self.1), access);
|
||||
}
|
||||
pub fn reborrow(&mut self) -> SideEffectMap<'_> {
|
||||
SideEffectMap(self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommandList {
|
||||
command_bytes: Vec<MaybeUninit<u8>>,
|
||||
cursor: usize,
|
||||
// each command that accesses a resource adds an entry to this map (id, command_index) -> access
|
||||
// during command recording, we fold accesses to the same resource as commands are recorded, and when a command reads a resource, we check for any previous writes to be made available/visible
|
||||
side_effects: BTreeMap<(ResourceId, u32), ResourceAccess>,
|
||||
}
|
||||
|
||||
impl CommandList {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
command_bytes: Vec::new(),
|
||||
cursor: 0,
|
||||
side_effects: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push<C: Command>(&mut self, command: C) {
|
||||
struct Packed<C> {
|
||||
meta: CommandMeta,
|
||||
command: C,
|
||||
}
|
||||
|
||||
let packed = Packed {
|
||||
meta: CommandMeta {
|
||||
apply_command: |recorder, command_ptr, cursor| {
|
||||
*cursor += size_of::<C>();
|
||||
let command = unsafe { command_ptr.cast::<C>().read_unaligned() };
|
||||
C::apply(command, recorder);
|
||||
},
|
||||
},
|
||||
command,
|
||||
};
|
||||
|
||||
let len = self.command_bytes.len();
|
||||
self.command_bytes.reserve(size_of::<Packed<C>>());
|
||||
unsafe {
|
||||
let bytes = self.command_bytes.as_mut_ptr().add(len);
|
||||
bytes.cast::<Packed<C>>().write_unaligned(packed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,139 @@
|
|||
use ash::vk;
|
||||
|
||||
use crate::images::MipRange;
|
||||
|
||||
pub trait Resource: Sized {}
|
||||
pub trait Resource: Sized + AsResourceRange {
|
||||
fn id(&self) -> ResourceId;
|
||||
}
|
||||
|
||||
pub trait AsResourceRange {
|
||||
fn into_access(&self) -> ResourceRange;
|
||||
}
|
||||
|
||||
mod impls {
|
||||
use super::*;
|
||||
|
||||
impl Resource for Buffer {}
|
||||
impl Resource for Texture {}
|
||||
impl Resource for BufferSlice {}
|
||||
impl Resource for TextureRegion {}
|
||||
impl Resource for Buffer {
|
||||
fn id(&self) -> ResourceId {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
impl Resource for Texture {
|
||||
fn id(&self) -> ResourceId {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
impl Resource for BufferSlice {
|
||||
fn id(&self) -> ResourceId {
|
||||
self.buffer.id()
|
||||
}
|
||||
}
|
||||
impl Resource for TextureRegion {
|
||||
fn id(&self) -> ResourceId {
|
||||
self.texture.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsResourceRange for BufferSlice {
|
||||
fn into_access(&self) -> ResourceRange {
|
||||
ResourceRange::Buffer {
|
||||
offset: self.offset,
|
||||
size: self.size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsResourceRange for TextureRegion {
|
||||
fn into_access(&self) -> ResourceRange {
|
||||
ResourceRange::Texture {
|
||||
aspect: self.aspect,
|
||||
mip_level: self.mip_level,
|
||||
array_layers: self.array_layers,
|
||||
origin: self.origin,
|
||||
extent: self.extent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsResourceRange for Texture {
|
||||
fn into_access(&self) -> ResourceRange {
|
||||
ResourceRange::Texture {
|
||||
aspect: vk::ImageAspectFlags::NONE,
|
||||
mip_level: 0,
|
||||
array_layers: MipRange::default(),
|
||||
origin: (0, 0, 0),
|
||||
extent: (u32::MAX, u32::MAX, u32::MAX),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsResourceRange for Buffer {
|
||||
fn into_access(&self) -> ResourceRange {
|
||||
ResourceRange::Buffer {
|
||||
offset: 0,
|
||||
size: u64::MAX,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: AsResourceRange> AsResourceRange for Read<T> {
|
||||
fn into_access(&self) -> ResourceRange {
|
||||
self.0.into_access()
|
||||
}
|
||||
}
|
||||
impl<T: AsResourceRange> AsResourceRange for Write<T> {
|
||||
fn into_access(&self) -> ResourceRange {
|
||||
self.0.into_access()
|
||||
}
|
||||
}
|
||||
impl<T: AsResourceRange> AsResourceRange for ReadWrite<T> {
|
||||
fn into_access(&self) -> ResourceRange {
|
||||
self.0.into_access()
|
||||
}
|
||||
}
|
||||
impl<T: Resource> Resource for Read<T> {
|
||||
fn id(&self) -> ResourceId {
|
||||
self.0.id()
|
||||
}
|
||||
}
|
||||
impl<T: Resource> Resource for Write<T> {
|
||||
fn id(&self) -> ResourceId {
|
||||
self.0.id()
|
||||
}
|
||||
}
|
||||
impl<T: Resource> Resource for ReadWrite<T> {
|
||||
fn id(&self) -> ResourceId {
|
||||
self.0.id()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Pipeline;
|
||||
pub struct DescriptorSet;
|
||||
|
||||
pub struct Buffer;
|
||||
pub struct Texture;
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct ResourceId(u32);
|
||||
|
||||
pub struct ResourceAccess {
|
||||
pub range: ResourceRange,
|
||||
pub access: vk::AccessFlags2,
|
||||
}
|
||||
|
||||
pub enum ResourceRange {
|
||||
Buffer {
|
||||
offset: u64,
|
||||
size: u64,
|
||||
},
|
||||
Texture {
|
||||
aspect: vk::ImageAspectFlags,
|
||||
mip_level: u32,
|
||||
array_layers: MipRange,
|
||||
origin: (u32, u32, u32),
|
||||
extent: (u32, u32, u32),
|
||||
},
|
||||
}
|
||||
|
||||
pub struct Buffer(ResourceId);
|
||||
pub struct Texture(ResourceId);
|
||||
pub struct BufferSlice {
|
||||
pub buffer: Buffer,
|
||||
pub offset: u64,
|
||||
|
|
@ -23,7 +141,8 @@ pub struct BufferSlice {
|
|||
}
|
||||
pub struct TextureRegion {
|
||||
pub texture: Texture,
|
||||
pub mip_levels: MipRange,
|
||||
pub aspect: vk::ImageAspectFlags,
|
||||
pub mip_level: u32,
|
||||
pub array_layers: MipRange,
|
||||
pub origin: (u32, u32, u32),
|
||||
pub extent: (u32, u32, u32),
|
||||
|
|
|
|||
Loading…
Reference in a new issue