use alloc::{string::String, vec::Vec};
use crate::{Address, Barrier, LabelId, Op, OutOfBounds, Res, Type, Unit, ValueId, VarId};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub struct Origin {
pub unit: Unit,
pub offset: u32,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Precision {
#[default]
Full,
AtLeast16,
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
pub struct ValueDef {
pub ty: Type,
pub op: Op,
pub origin: Option<Origin>,
pub precision: Precision,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Space {
#[default]
Private,
Workgroup,
Payload,
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
pub struct Var {
pub name: String,
pub ty: Type,
pub len: Option<u32>,
pub space: Space,
pub init: Vec<[u64; 4]>,
}
impl Var {
pub fn new(name: &str, ty: Type) -> Var {
Var {
name: name.into(),
ty,
len: None,
space: Space::Private,
init: Vec::new(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
pub enum Node {
Value(ValueId),
StoreVar(VarId, ValueId),
StoreOutput(u32, u8, ValueId),
Demote,
StoreElement(VarId, ValueId, ValueId, OutOfBounds),
StoreBuffer {
resource: Res,
address: Address,
value: ValueId,
},
StoreTexel {
image: Res,
coord: ValueId,
value: ValueId,
},
StorePatch(u32, u8, ValueId),
Barrier(Barrier),
Emit(u8),
Cut(u8),
SetMeshCounts {
vertices: ValueId,
primitives: ValueId,
},
StoreVertex {
vertex: ValueId,
slot: u32,
mask: u8,
value: ValueId,
},
StoreIndices {
primitive: ValueId,
indices: ValueId,
},
DispatchMesh {
groups: ValueId,
payload: VarId,
},
Switch {
label: LabelId,
selector: ValueId,
cases: Vec<Case>,
default: Region,
results: Vec<ValueId>,
},
If {
cond: ValueId,
accept: Region,
reject: Region,
results: Vec<ValueId>,
},
Block {
label: LabelId,
body: Region,
results: Vec<ValueId>,
},
Loop {
label: LabelId,
params: Vec<ValueId>,
inits: Vec<ValueId>,
body: Region,
results: Vec<ValueId>,
},
}
impl Node {
pub fn values(&self) -> Vec<ValueId> {
match self {
Node::Value(_)
| Node::Demote
| Node::Barrier(_)
| Node::Emit(_)
| Node::Cut(_)
| Node::Block { .. } => Vec::new(),
Node::StoreVar(_, v) | Node::StoreOutput(_, _, v) | Node::StorePatch(_, _, v) => alloc::vec![*v],
Node::StoreElement(_, i, v, _) => alloc::vec![*i, *v],
Node::SetMeshCounts { vertices, primitives } => alloc::vec![*vertices, *primitives],
Node::StoreVertex { vertex, value, .. } => alloc::vec![*vertex, *value],
Node::StoreIndices { primitive, indices } => alloc::vec![*primitive, *indices],
Node::DispatchMesh { groups, .. } => alloc::vec![*groups],
Node::StoreBuffer {
resource,
address,
value,
} => resource
.index
.into_iter()
.chain(address.index)
.chain(core::iter::once(*value))
.collect(),
Node::StoreTexel { image, coord, value } => {
image.index.into_iter().chain([*coord, *value]).collect()
}
Node::If { cond, .. } => alloc::vec![*cond],
Node::Switch { selector, .. } => alloc::vec![*selector],
Node::Loop { inits, .. } => inits.clone(),
}
}
pub fn values_mut(&mut self) -> Vec<&mut ValueId> {
match self {
Node::Value(_)
| Node::Demote
| Node::Barrier(_)
| Node::Emit(_)
| Node::Cut(_)
| Node::Block { .. } => Vec::new(),
Node::StoreVar(_, v) | Node::StoreOutput(_, _, v) | Node::StorePatch(_, _, v) => alloc::vec![v],
Node::StoreElement(_, i, v, _) => alloc::vec![i, v],
Node::SetMeshCounts { vertices, primitives } => alloc::vec![vertices, primitives],
Node::StoreVertex { vertex, value, .. } => alloc::vec![vertex, value],
Node::StoreIndices { primitive, indices } => alloc::vec![primitive, indices],
Node::DispatchMesh { groups, .. } => alloc::vec![groups],
Node::StoreBuffer {
resource,
address,
value,
} => resource
.index
.as_mut()
.into_iter()
.chain(address.index.as_mut())
.chain(core::iter::once(value))
.collect(),
Node::StoreTexel { image, coord, value } => {
image.index.as_mut().into_iter().chain([coord, value]).collect()
}
Node::If { cond, .. } => alloc::vec![cond],
Node::Switch { selector, .. } => alloc::vec![selector],
Node::Loop { inits, .. } => inits.iter_mut().collect(),
}
}
pub fn vars_mut(&mut self) -> Vec<&mut VarId> {
match self {
Node::StoreVar(var, _)
| Node::StoreElement(var, ..)
| Node::DispatchMesh { payload: var, .. } => {
alloc::vec![var]
}
_ => Vec::new(),
}
}
pub fn has_effect(&self) -> bool {
matches!(
self,
Node::StoreBuffer { .. }
| Node::StoreTexel { .. }
| Node::Barrier(_)
| Node::Emit(_)
| Node::Cut(_)
| Node::SetMeshCounts { .. }
| Node::StoreVertex { .. }
| Node::StoreIndices { .. }
| Node::DispatchMesh { .. }
)
}
pub fn regions(&self) -> Vec<&Region> {
match self {
Node::If { accept, reject, .. } => alloc::vec![accept, reject],
Node::Block { body, .. } | Node::Loop { body, .. } => alloc::vec![body],
Node::Switch { cases, default, .. } => cases
.iter()
.map(|c| &c.body)
.chain(core::iter::once(default))
.collect(),
_ => Vec::new(),
}
}
pub fn regions_mut(&mut self) -> Vec<&mut Region> {
match self {
Node::If { accept, reject, .. } => alloc::vec![accept, reject],
Node::Block { body, .. } | Node::Loop { body, .. } => alloc::vec![body],
Node::Switch { cases, default, .. } => cases
.iter_mut()
.map(|c| &mut c.body)
.chain(core::iter::once(default))
.collect(),
_ => Vec::new(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
pub struct Case {
pub values: Vec<u32>,
pub body: Region,
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
pub enum End {
Yield(Vec<ValueId>),
Br(LabelId, Vec<ValueId>),
Continue(LabelId, Vec<ValueId>),
Return,
Kill,
Unreachable,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize)]
pub struct Region {
pub nodes: Vec<Node>,
pub end: Option<End>,
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
pub struct Target {
pub block: u32,
pub args: Vec<ValueId>,
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
pub enum Terminator {
Jump(Target),
Branch {
cond: ValueId,
accept: Target,
reject: Target,
},
Switch {
selector: ValueId,
cases: Vec<(u32, Target)>,
default: Target,
},
Return,
Kill,
Unreachable,
}
impl Terminator {
pub fn targets(&self) -> Vec<&Target> {
match self {
Terminator::Jump(t) => alloc::vec![t],
Terminator::Branch { accept, reject, .. } => alloc::vec![accept, reject],
Terminator::Switch { cases, default, .. } => cases
.iter()
.map(|(_, t)| t)
.chain(core::iter::once(default))
.collect(),
Terminator::Return | Terminator::Kill | Terminator::Unreachable => Vec::new(),
}
}
pub fn targets_mut(&mut self) -> Vec<&mut Target> {
match self {
Terminator::Jump(t) => alloc::vec![t],
Terminator::Branch { accept, reject, .. } => alloc::vec![accept, reject],
Terminator::Switch { cases, default, .. } => cases
.iter_mut()
.map(|(_, t)| t)
.chain(core::iter::once(default))
.collect(),
Terminator::Return | Terminator::Kill | Terminator::Unreachable => Vec::new(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
pub struct Block {
pub params: Vec<ValueId>,
pub nodes: Vec<Node>,
pub term: Terminator,
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
pub enum Body {
Regions(Region),
Cfg(Vec<Block>),
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
pub struct Function {
pub name: String,
pub values: Vec<ValueDef>,
pub vars: Vec<Var>,
pub body: Body,
}
impl Function {
pub fn value(&self, v: ValueId) -> Option<&ValueDef> {
self.values.get(v.0 as usize)
}
pub fn ty(&self, v: ValueId) -> Option<Type> {
self.value(v).map(|d| d.ty)
}
}