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,
ByOne,
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,
FTan,
FAsin,
FAcos,
FAtan,
FSinh,
FCosh,
FTanh,
FSign,
Saturate(SaturateNan),
ClampRange {
lo: u32,
hi: u32,
nan: SaturateNan,
},
Quantize {
frac_bits: u8,
rounding: Rounding,
},
Derivative(Axis, Fineness),
Not,
INeg,
Any,
All,
IsNan,
IsInf,
CountOnes,
BitReverse,
FirstBitHigh,
FirstBitHighSigned,
FirstBitLow,
F32ToF16,
F16ToF32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Binary {
FAdd,
FSub,
FMul(MulZero),
FDiv,
FRem,
FMin(MinMaxNan),
FMax(MinMaxNan),
FPow,
FCmp(Cmp),
SCmp(Cmp),
UCmp(Cmp),
IAdd,
ISub,
IMul,
UMulHigh,
SMulHigh,
UAddCarry,
USubBorrow,
UDiv(OnZero),
URem(OnZero),
SDiv(OnZero),
SRem(OnZero),
SMin,
SMax,
UMin,
UMax,
And,
Or,
Xor,
Shl,
ShrU,
ShrS,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Ternary {
Fma,
BitExtract {
signed: bool,
},
}
#[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, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize)]
pub struct Res {
pub id: ResourceId,
pub index: Option<ValueId>,
pub nonuniform: bool,
}
impl Res {
pub const fn of(id: ResourceId) -> Res {
Res {
id,
index: None,
nonuniform: false,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub struct Address {
pub index: Option<ValueId>,
pub stride: u32,
pub offset: u32,
}
impl Address {
pub const fn fixed(offset: u32) -> Address {
Address {
index: None,
stride: 0,
offset,
}
}
pub const fn bytes(index: ValueId) -> Address {
Address {
index: Some(index),
stride: 1,
offset: 0,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Memory {
Buffer(Res, Address),
Image(Res, ValueId),
Var(VarId, Option<ValueId>),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum AtomicOp {
Add,
Sub,
And,
Or,
Xor,
SMin,
SMax,
UMin,
UMax,
Exchange,
CompareExchange,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum CounterOp {
Increment,
Decrement,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum GatherOffset {
Fixed([i8; 3]),
Dynamic(ValueId),
}
const STANDARD_POSITIONS: [(i8, i8); 31] = [
(0, 0),
(4, 4),
(-4, -4),
(-2, -6),
(6, -2),
(-6, 2),
(2, 6),
(1, -3),
(-1, 3),
(5, 1),
(-3, -5),
(-5, 5),
(-7, -1),
(3, 7),
(7, -7),
(1, 1),
(-1, -3),
(-3, 2),
(4, -1),
(-5, -2),
(2, 5),
(5, 3),
(3, -5),
(-2, 6),
(0, -7),
(-4, -6),
(-6, 4),
(-8, 0),
(7, -4),
(6, 7),
(-7, -8),
];
pub fn standard_sample_position(count: u32, index: u32) -> Option<[f32; 2]> {
if !matches!(count, 1 | 2 | 4 | 8 | 16) || index >= count {
return None;
}
let row = usize::try_from(count.checked_sub(1)?.checked_add(index)?).ok()?;
let (x, y) = *STANDARD_POSITIONS.get(row)?;
Some([f32::from(x) / 16.0, f32::from(y) / 16.0])
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Query {
Size(Option<ValueId>),
Layers,
Levels,
Samples,
SamplePosition(ValueId),
Elements,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Wave {
Ballot(ValueId),
First(ValueId),
Lane(ValueId, ValueId),
QuadLane(ValueId, ValueId),
QuadSwap(ValueId, QuadSwap),
Collective {
op: WaveOp,
scan: WaveScan,
value: ValueId,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum QuadSwap {
X,
Y,
Diagonal,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum WaveOp {
Add,
Mul,
Min,
Max,
And,
Or,
Xor,
All,
Any,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum WaveScan {
Reduce,
Inclusive,
Exclusive,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Builtin {
OutputControlPoint,
PrimitiveId,
GsInstance,
DomainLocation,
PhaseInstance,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, serde::Serialize)]
pub struct Barrier {
pub execution: bool,
pub workgroup: bool,
pub storage: Option<Scope>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Scope {
Workgroup,
Device,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize)]
pub enum Op {
Literal([u64; 4]),
Undef,
Param,
Unary(Unary, ValueId),
Binary(Binary, ValueId, ValueId),
Ternary(Ternary, ValueId, ValueId, ValueId),
BitInsert {
base: ValueId,
insert: ValueId,
offset: ValueId,
count: 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),
LoadInputAt(u32, u8, ValueId),
LoadOutputAt(u32, u8, ValueId),
LoadPatch(u32, u8),
Builtin(Builtin),
LoadVar(VarId),
LoadElement(VarId, ValueId, OutOfBounds),
LoadState(ResourceId),
LoadBuffer {
resource: Res,
address: Address,
oob: OutOfBounds,
},
Sample {
texture: Res,
sampler: Res,
coord: ValueId,
level: Level,
compare: Option<ValueId>,
offset: [i8; 3],
clamp: Option<ValueId>,
},
Gather {
texture: Res,
sampler: Res,
coord: ValueId,
component: u8,
compare: Option<ValueId>,
offset: GatherOffset,
},
LoadTexel {
texture: Res,
coord: ValueId,
level: Option<ValueId>,
sample: Option<ValueId>,
offset: [i8; 3],
},
Query(Res, Query),
Atomic {
memory: Memory,
op: AtomicOp,
value: ValueId,
compare: Option<ValueId>,
},
Counter(Res, CounterOp),
Wave(Wave),
}
impl Res {
fn operands_mut<'a>(&'a mut self, out: &mut Vec<&'a mut ValueId>) {
out.extend(self.index.as_mut());
}
}
impl Op {
pub fn operands_mut(&mut self) -> Vec<&mut ValueId> {
let mut v: Vec<&mut ValueId> = Vec::new();
match self {
Op::Literal(_)
| Op::Undef
| Op::Param
| Op::LoadInput(..)
| Op::LoadPatch(..)
| Op::Builtin(_)
| Op::LoadVar(_)
| Op::LoadState(_) => {}
Op::Unary(_, a)
| Op::Swizzle(a, _)
| Op::Splat(a)
| Op::Convert(a, _)
| Op::Bitcast(a)
| Op::LoadInputAt(_, _, a)
| Op::LoadOutputAt(_, _, a)
| Op::LoadElement(_, a, _) => v.push(a),
Op::Binary(_, a, b) | Op::Dot(a, b) => {
v.push(a);
v.push(b);
}
Op::Ternary(_, a, b, c) => v.extend([a, b, c]),
Op::BitInsert {
base,
insert,
offset,
count,
} => v.extend([base, insert, offset, count]),
Op::Select { cond, accept, reject } => v.extend([cond, accept, reject]),
Op::Compose(parts) => v.extend(parts.iter_mut()),
Op::LoadBuffer {
resource, address, ..
} => {
resource.operands_mut(&mut v);
v.extend(address.index.as_mut());
}
Op::Sample {
texture,
sampler,
coord,
level,
compare,
clamp,
..
} => {
texture.operands_mut(&mut v);
sampler.operands_mut(&mut v);
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.extend(compare.as_mut());
v.extend(clamp.as_mut());
}
Op::Gather {
texture,
sampler,
coord,
compare,
offset,
..
} => {
texture.operands_mut(&mut v);
sampler.operands_mut(&mut v);
v.push(coord);
v.extend(compare.as_mut());
if let GatherOffset::Dynamic(o) = offset {
v.push(o);
}
}
Op::LoadTexel {
texture,
coord,
level,
sample,
..
} => {
texture.operands_mut(&mut v);
v.push(coord);
v.extend(level.as_mut());
v.extend(sample.as_mut());
}
Op::Query(r, q) => {
r.operands_mut(&mut v);
match q {
Query::Size(Some(l)) | Query::SamplePosition(l) => v.push(l),
_ => {}
}
}
Op::Atomic {
memory,
value,
compare,
..
} => {
memory.operands_mut(&mut v);
v.push(value);
v.extend(compare.as_mut());
}
Op::Counter(r, _) => r.operands_mut(&mut v),
Op::Wave(w) => match w {
Wave::Ballot(a) | Wave::First(a) | Wave::QuadSwap(a, _) => v.push(a),
Wave::Lane(a, l) | Wave::QuadLane(a, l) => v.extend([a, l]),
Wave::Collective { value, .. } => v.push(value),
},
}
v
}
pub fn operands(&self) -> Vec<ValueId> {
self.clone().operands_mut().into_iter().map(|v| *v).collect()
}
pub fn map_operands(&mut self, mut f: impl FnMut(ValueId) -> ValueId) {
for v in self.operands_mut() {
*v = f(*v);
}
}
pub fn var(&self) -> Option<VarId> {
self.clone().var_mut().copied()
}
pub fn var_mut(&mut self) -> Option<&mut VarId> {
match self {
Op::LoadVar(var)
| Op::LoadElement(var, ..)
| Op::Atomic {
memory: Memory::Var(var, _),
..
} => Some(var),
_ => None,
}
}
pub fn resources(&self) -> Vec<ResourceId> {
match self {
Op::LoadState(r) => alloc::vec![*r],
Op::LoadBuffer { resource: r, .. }
| Op::LoadTexel { texture: r, .. }
| Op::Query(r, _)
| Op::Counter(r, _)
| Op::Atomic {
memory: Memory::Buffer(r, _) | Memory::Image(r, _),
..
} => alloc::vec![r.id],
Op::Sample { texture, sampler, .. } | Op::Gather { texture, sampler, .. } => {
alloc::vec![texture.id, sampler.id]
}
_ => Vec::new(),
}
}
pub fn reads_state(&self) -> bool {
!matches!(
self,
Op::Literal(_)
| Op::Undef
| Op::Param
| Op::Unary(..)
| Op::Binary(..)
| Op::Ternary(..)
| Op::BitInsert { .. }
| Op::Select { .. }
| Op::Compose(_)
| Op::Swizzle(..)
| Op::Splat(_)
| Op::Dot(..)
| Op::Convert(..)
| Op::Bitcast(_)
) || matches!(self, Op::Unary(Unary::Derivative(..), _))
}
pub fn has_effect(&self) -> bool {
matches!(self, Op::Atomic { .. } | Op::Counter(..))
}
}
impl Memory {
fn operands_mut<'a>(&'a mut self, out: &mut Vec<&'a mut ValueId>) {
match self {
Memory::Buffer(r, a) => {
r.operands_mut(out);
out.extend(a.index.as_mut());
}
Memory::Image(r, c) => {
r.operands_mut(out);
out.push(c);
}
Memory::Var(_, i) => out.extend(i.as_mut()),
}
}
}