Skip to main content

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}