use super::passes::PassId;
use super::types::{
BufferDesc, BufferHandle, PassKind, ResourceId, ResourceOrigin, TextureDesc, TextureHandle,
};
use alloc::vec::Vec;
#[derive(Debug, Clone)]
pub(super) enum ResourceDecl {
Texture {
label: &'static str,
desc: TextureDesc,
origin: ResourceOrigin,
version: u32,
},
Buffer {
label: &'static str,
desc: BufferDesc,
origin: ResourceOrigin,
version: u32,
},
}
impl ResourceDecl {
pub(super) fn label(&self) -> &'static str {
match self {
ResourceDecl::Texture { label, .. } | ResourceDecl::Buffer { label, .. } => label,
}
}
pub(super) fn origin(&self) -> ResourceOrigin {
match self {
ResourceDecl::Texture { origin, .. } | ResourceDecl::Buffer { origin, .. } => *origin,
}
}
pub(super) fn bump_version(&mut self) -> u32 {
match self {
ResourceDecl::Texture { version, .. } | ResourceDecl::Buffer { version, .. } => {
*version += 1;
*version
}
}
}
pub(super) fn texture_desc(&self) -> Option<TextureDesc> {
match self {
ResourceDecl::Texture { desc, .. } => Some(*desc),
ResourceDecl::Buffer { .. } => None,
}
}
pub(super) fn buffer_desc(&self) -> Option<BufferDesc> {
match self {
ResourceDecl::Buffer { desc, .. } => Some(*desc),
ResourceDecl::Texture { .. } => None,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ResourceVersion {
pub(super) resource: ResourceId,
pub(super) version: u32,
}
impl ResourceVersion {
pub fn resource_index(self) -> usize {
self.resource.index()
}
pub fn version(self) -> u32 {
self.version
}
}
#[derive(Debug, Clone)]
pub(super) struct PassDecl {
pub(super) id: PassId,
pub(super) kind: PassKind,
pub(super) reads: Vec<ResourceVersion>,
pub(super) writes: Vec<ResourceVersion>,
pub(super) presents: bool,
}
pub(crate) struct GraphBuilder {
pub(super) resources: Vec<ResourceDecl>,
pub(super) passes: Vec<PassDecl>,
}
impl Default for GraphBuilder {
fn default() -> Self {
Self::new()
}
}
impl GraphBuilder {
pub(crate) fn new() -> Self {
Self {
resources: Vec::with_capacity(32),
passes: Vec::with_capacity(super::passes::PASS_COUNT),
}
}
pub(crate) fn import_texture(
&mut self,
label: &'static str,
desc: TextureDesc,
) -> TextureHandle {
self.push_texture(label, desc, ResourceOrigin::Imported)
}
pub(crate) fn import_buffer(&mut self, label: &'static str, desc: BufferDesc) -> BufferHandle {
self.push_buffer(label, desc, ResourceOrigin::Imported)
}
pub(crate) fn create_texture(
&mut self,
label: &'static str,
desc: TextureDesc,
) -> TextureHandle {
self.push_texture(label, desc, ResourceOrigin::Transient)
}
#[cfg(test)]
pub(crate) fn create_buffer(&mut self, label: &'static str, desc: BufferDesc) -> BufferHandle {
self.push_buffer(label, desc, ResourceOrigin::Transient)
}
pub(crate) fn add_pass(&mut self, id: PassId, kind: PassKind) -> PassBuilder<'_> {
let pass_idx = self.passes.len();
self.passes.push(PassDecl {
id,
kind,
reads: Vec::with_capacity(4),
writes: Vec::with_capacity(2),
presents: false,
});
PassBuilder {
builder: self,
pass_idx,
}
}
fn push_texture(
&mut self,
label: &'static str,
desc: TextureDesc,
origin: ResourceOrigin,
) -> TextureHandle {
let resource = ResourceId(self.resources.len() as u32);
self.resources.push(ResourceDecl::Texture {
label,
desc,
origin,
version: 0,
});
TextureHandle {
resource,
version: 0,
}
}
fn push_buffer(
&mut self,
label: &'static str,
desc: BufferDesc,
origin: ResourceOrigin,
) -> BufferHandle {
let resource = ResourceId(self.resources.len() as u32);
self.resources.push(ResourceDecl::Buffer {
label,
desc,
origin,
version: 0,
});
BufferHandle {
resource,
version: 0,
}
}
}
pub(crate) struct PassBuilder<'g> {
builder: &'g mut GraphBuilder,
pass_idx: usize,
}
impl PassBuilder<'_> {
pub(crate) fn read_texture(&mut self, h: TextureHandle) -> &mut Self {
if h.is_valid() {
self.builder.passes[self.pass_idx]
.reads
.push(ResourceVersion {
resource: h.resource,
version: h.version,
});
}
self
}
pub(crate) fn read_buffer(&mut self, h: BufferHandle) -> &mut Self {
if h.is_valid() {
self.builder.passes[self.pass_idx]
.reads
.push(ResourceVersion {
resource: h.resource,
version: h.version,
});
}
self
}
pub(crate) fn write_texture(&mut self, h: TextureHandle) -> TextureHandle {
if !h.is_valid() {
return TextureHandle::INVALID;
}
let new_version = self.builder.resources[h.resource.index()].bump_version();
self.builder.passes[self.pass_idx]
.writes
.push(ResourceVersion {
resource: h.resource,
version: new_version,
});
TextureHandle {
resource: h.resource,
version: new_version,
}
}
pub(crate) fn write_buffer(&mut self, h: BufferHandle) -> BufferHandle {
if !h.is_valid() {
return BufferHandle::INVALID;
}
let new_version = self.builder.resources[h.resource.index()].bump_version();
self.builder.passes[self.pass_idx]
.writes
.push(ResourceVersion {
resource: h.resource,
version: new_version,
});
BufferHandle {
resource: h.resource,
version: new_version,
}
}
pub(crate) fn presents(&mut self) -> &mut Self {
self.builder.passes[self.pass_idx].presents = true;
self
}
}
#[cfg(test)]
mod tests {
use super::super::types::{BufferUsage, PixelFormat, TextureSize, TextureUsage};
use super::*;
fn dummy_tex_desc() -> TextureDesc {
TextureDesc::texture_2d(
TextureSize::Drawable,
TextureSize::Drawable,
PixelFormat::Rgba16Float,
TextureUsage::SHADER_READ | TextureUsage::RENDER_TARGET,
)
}
fn dummy_buf_desc() -> BufferDesc {
BufferDesc {
size_bytes: None,
usage: BufferUsage::STORAGE,
}
}
#[test]
fn create_texture_assigns_dense_ids() {
let mut b = GraphBuilder::new();
let a = b.import_texture("a", dummy_tex_desc());
let c = b.create_texture("c", dummy_tex_desc());
assert_eq!(a.resource.index(), 0);
assert_eq!(c.resource.index(), 1);
assert_eq!(a.version, 0);
assert_eq!(c.version, 0);
}
#[test]
fn write_bumps_version_and_returns_new_handle() {
let mut b = GraphBuilder::new();
let h0 = b.create_texture("t", dummy_tex_desc());
let h1 = {
let mut p = b.add_pass(PassId::Main, PassKind::Render);
p.write_texture(h0)
};
assert_eq!(h0.version, 0);
assert_eq!(h1.version, 1);
assert_eq!(h0.resource, h1.resource);
}
#[test]
fn read_write_record_declarations() {
let mut b = GraphBuilder::new();
let t = b.create_texture("t", dummy_tex_desc());
let buf = b.create_buffer("b", dummy_buf_desc());
{
let mut p = b.add_pass(PassId::Main, PassKind::Render);
p.read_texture(t).read_buffer(buf);
let _ = p.write_texture(t);
}
let pass = &b.passes[0];
assert_eq!(pass.reads.len(), 2);
assert_eq!(pass.writes.len(), 1);
assert_eq!(pass.reads[0].resource, t.resource);
assert_eq!(pass.reads[1].resource, buf.resource);
assert_eq!(pass.writes[0].version, 1);
}
#[test]
fn invalid_handle_skips_declaration() {
let mut b = GraphBuilder::new();
let out = {
let mut p = b.add_pass(PassId::Composite, PassKind::Render);
p.read_texture(TextureHandle::INVALID);
p.write_texture(TextureHandle::INVALID)
};
assert!(!out.is_valid());
let pass = &b.passes[0];
assert!(pass.reads.is_empty());
assert!(pass.writes.is_empty());
}
#[test]
fn presents_marks_the_pass() {
let mut b = GraphBuilder::new();
b.add_pass(PassId::Composite, PassKind::Render).presents();
assert!(b.passes[0].presents);
}
}