1use crate::decode::{Declared, Decoded, Instruction, Operand, Register};
4
5struct Named {
8 register: u32,
9 indexed: bool,
10 id: Option<u32>,
11}
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub struct TextureRead {
16 pub word: usize,
18 pub opcode: u16,
20 pub texture: u32,
23 pub texture_indexed: bool,
25 pub texture_space: Option<u32>,
28 pub sampler: Option<u32>,
30 pub sampler_indexed: bool,
32 pub sampler_space: Option<u32>,
34 pub comparison: bool,
37 pub sampler_mode: Option<u32>,
40}
41
42fn kind(name: &str) -> Option<(bool, bool)> {
44 Some(match name {
45 "sample"
46 | "sample_l"
47 | "sample_d"
48 | "sample_b"
49 | "lod"
50 | "gather4"
51 | "gather4_po"
52 | "gather4_feedback"
53 | "gather4_po_feedback"
54 | "sample_l_feedback"
55 | "sample_clamp_feedback"
56 | "sample_b_clamp_feedback"
57 | "sample_d_clamp_feedback" => (true, false),
58 "sample_c"
59 | "sample_c_lz"
60 | "gather4_c"
61 | "gather4_po_c"
62 | "gather4_c_feedback"
63 | "gather4_po_c_feedback"
64 | "sample_c_lz_feedback"
65 | "sample_c_clamp_feedback" => (true, true),
66 "ld"
67 | "ld_ms"
68 | "ld_raw"
69 | "ld_structured"
70 | "ld_feedback"
71 | "ld_ms_feedback"
72 | "ld_raw_feedback"
73 | "ld_structured_feedback" => (false, false),
74 _ => return None,
75 })
76}
77
78fn named(operand: &Operand, sm51: bool) -> Option<Named> {
81 let (id, index) = if sm51 {
82 let [id, index] = operand.indices.as_slice() else {
83 return None;
84 };
85 (Some(id.base), index)
86 } else {
87 (None, operand.indices.first()?)
88 };
89 Some(Named {
90 register: index.base,
91 indexed: index.relative.is_some(),
92 id,
93 })
94}
95
96fn first(instruction: &Instruction, wanted: Register) -> Option<&Operand> {
97 instruction.operands.iter().find(|o| o.register == wanted)
98}
99
100pub fn texture_reads(program: &Decoded) -> Vec<TextureRead> {
104 let sm51 = (program.major, program.minor) >= (5, 1);
105 let declared = |name: &Named, sampler: bool| {
107 program.declarations.iter().find(|d| {
108 let slot = match d.kind {
109 Declared::Sampler { slot, .. } if sampler => slot,
110 Declared::Resource { slot, .. }
111 | Declared::ResourceRaw { slot, .. }
112 | Declared::ResourceStructured { slot, .. }
113 if !sampler =>
114 {
115 slot
116 }
117 _ => return false,
118 };
119 slot == name.id.unwrap_or(name.register)
120 })
121 };
122 let space = |name: &Named, sampler: bool| {
123 if sm51 {
124 declared(name, sampler)
125 .and_then(|d| d.kind.registers())
126 .map(|r| r.space)
127 } else {
128 Some(0)
129 }
130 };
131 let mut reads = Vec::new();
132 for instruction in &program.instructions {
133 let Some((sampled, comparison)) = kind(instruction.name()) else {
134 continue;
135 };
136 let Some(texture) = first(instruction, Register::Texture).and_then(|o| named(o, sm51)) else {
137 continue;
138 };
139 let sampler = if sampled {
140 first(instruction, Register::Sampler).and_then(|o| named(o, sm51))
141 } else {
142 None
143 };
144 reads.push(TextureRead {
145 word: instruction.word,
146 opcode: instruction.opcode,
147 texture: texture.register,
148 texture_indexed: texture.indexed,
149 texture_space: space(&texture, false),
150 sampler: sampler.as_ref().map(|s| s.register),
151 sampler_indexed: sampler.as_ref().is_some_and(|s| s.indexed),
152 sampler_space: sampler.as_ref().and_then(|s| space(s, true)),
153 comparison,
154 sampler_mode: sampler.as_ref().and_then(|s| {
155 declared(s, true).and_then(|d| match d.kind {
156 Declared::Sampler { mode, .. } => Some(mode),
157 _ => None,
158 })
159 }),
160 });
161 }
162 reads
163}