use alloc::vec::Vec;
use crate::interface::ResourceId;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize)]
pub struct ValueId(pub u32);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize)]
pub struct VarId(pub u32);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize)]
pub struct LabelId(pub u32);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum MinMaxNan {
MinNum,
Propagate,
Any,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum SaturateNan {
Zero,
Any,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum MulZero {
Ieee,
ZeroWins,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum OnZero {
AllOnes,
Dividend,
Undef,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum FloatToInt {
Saturate,
Undef,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Rounding {
Floor,
Ceil,
Trunc,
NearestEven,
NearestAway,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Axis {
X,
Y,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Fineness {
Coarse,
Fine,
Any,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Cmp {
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Unary {
FNeg,
FAbs,
FFract,
FRound(Rounding),
FSqrt,
FInverseSqrt,
FExp2,
FLog2,
FSin,
FCos,
Saturate(SaturateNan),
ClampRange {
lo: u32,
hi: u32,
nan: SaturateNan,
},
Quantize {
frac_bits: u8,
rounding: Rounding,
},
Derivative(Axis, Fineness),
Not,
INeg,
Any,
All,
IsNan,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Binary {
FAdd,
FSub,
FMul(MulZero),
FDiv,
FMin(MinMaxNan),
FMax(MinMaxNan),
FPow,
FCmp(Cmp),
SCmp(Cmp),
UCmp(Cmp),
IAdd,
ISub,
IMul,
UDiv(OnZero),
URem(OnZero),
SMin,
SMax,
And,
Or,
Xor,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Level {
Auto,
Bias(ValueId),
Exact(ValueId),
Zero,
Grad(ValueId, ValueId),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum OutOfBounds {
Zero,
Undef,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Op {
Literal([u64; 4]),
Undef,
Param,
Unary(Unary, ValueId),
Binary(Binary, ValueId, ValueId),
Select {
cond: ValueId,
accept: ValueId,
reject: ValueId,
},
Compose(Vec<ValueId>),
Swizzle(ValueId, [u8; 4]),
Splat(ValueId),
Dot(ValueId, ValueId),
Convert(ValueId, FloatToInt),
Bitcast(ValueId),
LoadInput(u32, u8),
LoadVar(VarId),
LoadState(ResourceId),
LoadBuffer {
resource: ResourceId,
index: Option<ValueId>,
stride: u32,
offset: u32,
oob: OutOfBounds,
},
Sample {
texture: ResourceId,
sampler: ResourceId,
coord: ValueId,
level: Level,
},
}
impl Op {
pub fn operands(&self) -> Vec<ValueId> {
let mut v = Vec::new();
match self {
Op::Literal(_)
| Op::Undef
| Op::Param
| Op::LoadInput(..)
| Op::LoadVar(_)
| Op::LoadState(_) => {}
Op::Unary(_, a) | Op::Swizzle(a, _) | Op::Splat(a) | Op::Convert(a, _) | Op::Bitcast(a) => {
v.push(*a)
}
Op::Binary(_, a, b) | Op::Dot(a, b) => {
v.push(*a);
v.push(*b);
}
Op::Select { cond, accept, reject } => v.extend([*cond, *accept, *reject]),
Op::Compose(parts) => v.extend(parts.iter().copied()),
Op::LoadBuffer { index, .. } => v.extend(index.iter().copied()),
Op::Sample { coord, level, .. } => {
v.push(*coord);
match level {
Level::Bias(x) | Level::Exact(x) => v.push(*x),
Level::Grad(x, y) => v.extend([*x, *y]),
Level::Auto | Level::Zero => {}
}
}
}
v
}
pub fn resources(&self) -> Vec<ResourceId> {
match self {
Op::LoadState(r) | Op::LoadBuffer { resource: r, .. } => alloc::vec![*r],
Op::Sample { texture, sampler, .. } => alloc::vec![*texture, *sampler],
_ => Vec::new(),
}
}
pub fn reads_state(&self) -> bool {
matches!(
self,
Op::LoadInput(..)
| Op::LoadVar(_)
| Op::LoadState(_)
| Op::LoadBuffer { .. }
| Op::Sample { .. }
| Op::Unary(Unary::Derivative(..), _)
)
}
pub fn map_operands(&mut self, mut f: impl FnMut(ValueId) -> ValueId) {
match self {
Op::Literal(_)
| Op::Undef
| Op::Param
| Op::LoadInput(..)
| Op::LoadVar(_)
| Op::LoadState(_) => {}
Op::Unary(_, a) | Op::Swizzle(a, _) | Op::Splat(a) | Op::Convert(a, _) | Op::Bitcast(a) => {
*a = f(*a)
}
Op::Binary(_, a, b) | Op::Dot(a, b) => {
*a = f(*a);
*b = f(*b);
}
Op::Select { cond, accept, reject } => {
*cond = f(*cond);
*accept = f(*accept);
*reject = f(*reject);
}
Op::Compose(parts) => parts.iter_mut().for_each(|p| *p = f(*p)),
Op::LoadBuffer { index, .. } => {
if let Some(i) = index {
*i = f(*i);
}
}
Op::Sample { coord, level, .. } => {
*coord = f(*coord);
match level {
Level::Bias(x) | Level::Exact(x) => *x = f(*x),
Level::Grad(x, y) => {
*x = f(*x);
*y = f(*y);
}
Level::Auto | Level::Zero => {}
}
}
}
}
}