iridia_ir/interface/binding.rs
1//! What a program's own code shows of how it uses what it is bound: how a constant buffer's lines hold a matrix, what a
2//! discard compares a constant with, and what an output's lanes are tested or blended by. These are observations the
3//! analyses of `iridia` make (`iridia::matrix`, `iridia::output_uses`), held as plain data so that a consumer without
4//! naga can keep them; what an observation makes a value stand for is the consumer's to decide.
5#[allow(unused_imports)]
6use alloc::{
7 boxed::Box,
8 format,
9 string::{String, ToString},
10 vec,
11 vec::Vec,
12};
13
14use serde::Serialize;
15
16use super::{Label, ResourceId};
17
18/// What a discard compares against a constant word (an alpha reference), proved from the program's data flow
19/// (`iridia::output_uses::discard_compares`).
20#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
21#[serde(rename_all = "snake_case")]
22pub enum AlphaComparison {
23 /// The output alpha itself (including unchanged branch arms).
24 Output,
25 /// A scalar that reaches output alpha through arithmetic. Cutting the output is an approximation.
26 Transformed,
27 /// A pre-transform alpha plus `(dither.a - 0.5) * scale`. `scale_word` is a word offset in the reference's own
28 /// buffer. A hard cutoff on output alpha approximates the program's stochastic, pre-transform test.
29 Dithered {
30 /// Word offset of the dither scale in the reference's own buffer.
31 scale_word: u32,
32 },
33 /// The discard compares a value made of texture samples and constants that reaches no target's alpha (a G-buffer
34 /// program writes none: `t1.w * t0.w + scale * (noise - 0.5) < reference`). The test is the program's own
35 /// expression, read exactly from its graph (`iridia::output_uses::alpha_tests`); a cutoff on any output alpha is not
36 /// it, and a consumer that cuts evaluates that expression, or does not cut.
37 Unwritten,
38}
39/// How a matrix constant's 16-byte lines hold the matrix the program applies to column vectors (a view-projection's
40/// clip position is `M * p`). The layout's packing (`row_major`, `column_major`) does not decide it, since a program may
41/// multiply on either side (`mul(M, v)`, `mul(v, M)`): `iridia::matrix::lines_at` reads it from what the program does.
42#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
43#[serde(rename_all = "snake_case")]
44pub enum MatrixLines {
45 /// Each line is one of the matrix's rows.
46 Rows,
47 /// Each line is one of its columns.
48 Columns,
49 /// The program does not show which, and why ([`Unshown`]). A consumer that writes a matrix unequal to its
50 /// transpose (a camera's) cannot bind it; one that writes the identity can.
51 Unshown(Unshown),
52}
53/// Why a program does not show a matrix's lines ([`MatrixLines::Unshown`]): the kind a consumer branches on, its text
54/// ([`core::fmt::Display`]) the reason.
55#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
56#[serde(rename_all = "snake_case")]
57pub enum Unshown {
58 /// Its results are made of none of the matrix's words.
59 NotRead,
60 /// It reads them but sums no product of them with other values: it does not apply the matrix.
61 NotApplied,
62 /// It applies the matrix both ways: `along` sums of one line's words, `across` of one lane's.
63 BothWays {
64 /// How many sums of the words of one line there are.
65 along: usize,
66 /// How many sums of the words of one lane across lines there are.
67 across: usize,
68 },
69 /// The program could not be examined for it, and `why`: its value graph could not be built, or the matrix's buffer
70 /// could not be found in it.
71 Unexamined {
72 /// Why the program could not be examined.
73 why: String,
74 },
75}
76impl core::fmt::Display for Unshown {
77 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
78 match self {
79 Unshown::NotRead => f.write_str("the program's results are made of none of the matrix's words"),
80 Unshown::NotApplied => f.write_str(
81 "the program sums no product of the matrix's words with other values: it does not show how it \
82 applies the matrix",
83 ),
84 Unshown::BothWays { along, across } => write!(
85 f,
86 "the program applies the matrix both ways: {along} sums of one line's words and {across} of one lane's"
87 ),
88 Unshown::Unexamined { why } => f.write_str(why),
89 }
90 }
91}
92/// What the source states of how it binds a constant buffer of the program, beyond what its constants are.
93///
94/// A program declares a constant buffer at the size it indexes it, and a source can bind more: D3D12 bounds a constant
95/// buffer read by the size of the view the draw binds (the CBV's `SizeInBytes`), not by the shader's declaration, so a
96/// skinning palette a program declares as two matrices is bound as the hundreds the draw uploads, and a joint index past
97/// the declaration reads the palette, not zero. A lifter takes the rows of a buffer's uniform array from the bound size
98/// when it has one (`iridia::BoundSizes`).
99#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
100pub struct ConstantBufferBinding {
101 /// A constant buffer resource of the shader.
102 pub resource: ResourceId,
103 /// The bytes the source binds the buffer with, more than the program declares and a whole number of 16-byte rows;
104 /// `None` is the declared size. Where the engine binds different sizes for different draws, the largest it binds:
105 /// a consumer that fills fewer rows leaves the rest zero.
106 pub bound_size: Option<u32>,
107}
108/// What a pixel program's code and its target's stated blend show of the lanes of one output that is a render target
109/// (`iridia::output_uses::output_uses`): observations, not roles. Each is a lane mask, bit k lane k.
110#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
111pub struct OutputUse {
112 /// The output.
113 pub label: Label,
114 /// The render target it is written to: its system value `SV_Target<n>`, or its label's index where it has none.
115 pub target: u32,
116 /// Lane 3 where the target's stated blend weighs the colour by the source alpha (the colour equation's source or
117 /// destination factor is `SrcAlpha` or `OneMinusSrcAlpha`), else none.
118 pub blend_weight: u8,
119 /// The lanes whose written value is among what a discard's conditions are made of. A literal is no evidence (equal
120 /// literals are one node of the value graph); none where the program's value graph was not given.
121 pub discard_tested: u8,
122}