Skip to main content

dxbc_reader/
sampling.rs

1//! Which sampler each texture read of a program uses: every sample, gather, level-of-detail query and load that names a
2//! texture register, with the texture register, the sampler register and whether the instruction compares.
3use crate::decode::{Declared, Decoded, Instruction, Operand, Register};
4
5/// One instruction of a program that reads a texture (a shader resource view, `t<n>`).
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub struct TextureRead {
8    /// The instruction's first word in the token stream.
9    pub word: usize,
10    /// The instruction's opcode (see [`crate::opcode_name`]).
11    pub opcode: u16,
12    /// The number of the `t` register, or the constant part of its index where [`TextureRead::texture_indexed`].
13    pub texture: u32,
14    /// The texture register is indexed by a register (`t[r1.x + 3]`).
15    pub texture_indexed: bool,
16    /// The number of the `s` register the instruction samples with; none for a load, which uses no sampler.
17    pub sampler: Option<u32>,
18    /// The sampler register is indexed by a register.
19    pub sampler_indexed: bool,
20    /// The instruction compares the sampled value with a reference (`sample_c`, `sample_c_lz`, `gather4_c`,
21    /// `gather4_po_c` and the feedback forms of them).
22    pub comparison: bool,
23    /// How the program declares the sampler (`dcl_sampler`): 0 default, 1 comparison, 2 mono; none where the program
24    /// declares no sampler of that number (or the instruction has no sampler).
25    pub sampler_mode: Option<u32>,
26}
27
28/// How an opcode reads its texture: whether it takes a sampler and whether it compares.
29fn kind(name: &str) -> Option<(bool, bool)> {
30    Some(match name {
31        "sample"
32        | "sample_l"
33        | "sample_d"
34        | "sample_b"
35        | "lod"
36        | "gather4"
37        | "gather4_po"
38        | "gather4_feedback"
39        | "gather4_po_feedback"
40        | "sample_l_feedback"
41        | "sample_clamp_feedback"
42        | "sample_b_clamp_feedback"
43        | "sample_d_clamp_feedback" => (true, false),
44        "sample_c"
45        | "sample_c_lz"
46        | "gather4_c"
47        | "gather4_po_c"
48        | "gather4_c_feedback"
49        | "gather4_po_c_feedback"
50        | "sample_c_lz_feedback"
51        | "sample_c_clamp_feedback" => (true, true),
52        "ld"
53        | "ld_ms"
54        | "ld_raw"
55        | "ld_structured"
56        | "ld_feedback"
57        | "ld_ms_feedback"
58        | "ld_raw_feedback"
59        | "ld_structured_feedback" => (false, false),
60        _ => return None,
61    })
62}
63
64/// The register number of an operand and whether a register adds to it; none for an operand without an index.
65fn register(operand: &Operand) -> Option<(u32, bool)> {
66    let index = operand.indices.first()?;
67    Some((index.base, index.relative.is_some()))
68}
69
70fn first(instruction: &Instruction, wanted: Register) -> Option<&Operand> {
71    instruction.operands.iter().find(|o| o.register == wanted)
72}
73
74/// Every texture read of `program` (an instruction with a `t<n>` operand that samples, gathers, queries the level of
75/// detail of or loads it), in program order. An instruction that reads a raw or structured buffer or an
76/// unordered-access view through a `u` or `g` register names no texture and is not listed.
77pub fn texture_reads(program: &Decoded) -> Vec<TextureRead> {
78    let modes: Vec<(u32, u32)> = program
79        .declarations
80        .iter()
81        .filter_map(|d| match d.kind {
82            Declared::Sampler { slot, mode } => Some((slot, mode)),
83            _ => None,
84        })
85        .collect();
86    let mut reads = Vec::new();
87    for instruction in &program.instructions {
88        let Some((sampled, comparison)) = kind(instruction.name()) else {
89            continue;
90        };
91        let Some((texture, texture_indexed)) = first(instruction, Register::Texture).and_then(register)
92        else {
93            continue;
94        };
95        let sampler = if sampled {
96            first(instruction, Register::Sampler).and_then(register)
97        } else {
98            None
99        };
100        reads.push(TextureRead {
101            word: instruction.word,
102            opcode: instruction.opcode,
103            texture,
104            texture_indexed,
105            sampler: sampler.map(|(slot, _)| slot),
106            sampler_indexed: sampler.is_some_and(|(_, indexed)| indexed),
107            comparison,
108            sampler_mode: sampler.and_then(|(slot, _)| {
109                modes
110                    .iter()
111                    .find(|(declared, _)| *declared == slot)
112                    .map(|(_, mode)| *mode)
113            }),
114        });
115    }
116    reads
117}