use crate::decode::{Declared, Decoded, Instruction, Operand, Register};
struct Named {
register: u32,
indexed: bool,
id: Option<u32>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TextureRead {
pub word: usize,
pub opcode: u16,
pub texture: u32,
pub texture_indexed: bool,
pub texture_space: Option<u32>,
pub sampler: Option<u32>,
pub sampler_indexed: bool,
pub sampler_space: Option<u32>,
pub comparison: bool,
pub sampler_mode: Option<u32>,
}
fn kind(name: &str) -> Option<(bool, bool)> {
Some(match name {
"sample"
| "sample_l"
| "sample_d"
| "sample_b"
| "lod"
| "gather4"
| "gather4_po"
| "gather4_feedback"
| "gather4_po_feedback"
| "sample_l_feedback"
| "sample_clamp_feedback"
| "sample_b_clamp_feedback"
| "sample_d_clamp_feedback" => (true, false),
"sample_c"
| "sample_c_lz"
| "gather4_c"
| "gather4_po_c"
| "gather4_c_feedback"
| "gather4_po_c_feedback"
| "sample_c_lz_feedback"
| "sample_c_clamp_feedback" => (true, true),
"ld"
| "ld_ms"
| "ld_raw"
| "ld_structured"
| "ld_feedback"
| "ld_ms_feedback"
| "ld_raw_feedback"
| "ld_structured_feedback" => (false, false),
_ => return None,
})
}
fn named(operand: &Operand, sm51: bool) -> Option<Named> {
let (id, index) = if sm51 {
let [id, index] = operand.indices.as_slice() else {
return None;
};
(Some(id.base), index)
} else {
(None, operand.indices.first()?)
};
Some(Named {
register: index.base,
indexed: index.relative.is_some(),
id,
})
}
fn first(instruction: &Instruction, wanted: Register) -> Option<&Operand> {
instruction.operands.iter().find(|o| o.register == wanted)
}
pub fn texture_reads(program: &Decoded) -> Vec<TextureRead> {
let sm51 = (program.major, program.minor) >= (5, 1);
let declared = |name: &Named, sampler: bool| {
program.declarations.iter().find(|d| {
let slot = match d.kind {
Declared::Sampler { slot, .. } if sampler => slot,
Declared::Resource { slot, .. }
| Declared::ResourceRaw { slot, .. }
| Declared::ResourceStructured { slot, .. }
if !sampler =>
{
slot
}
_ => return false,
};
slot == name.id.unwrap_or(name.register)
})
};
let space = |name: &Named, sampler: bool| {
if sm51 {
declared(name, sampler)
.and_then(|d| d.kind.registers())
.map(|r| r.space)
} else {
Some(0)
}
};
let mut reads = Vec::new();
for instruction in &program.instructions {
let Some((sampled, comparison)) = kind(instruction.name()) else {
continue;
};
let Some(texture) = first(instruction, Register::Texture).and_then(|o| named(o, sm51)) else {
continue;
};
let sampler = if sampled {
first(instruction, Register::Sampler).and_then(|o| named(o, sm51))
} else {
None
};
reads.push(TextureRead {
word: instruction.word,
opcode: instruction.opcode,
texture: texture.register,
texture_indexed: texture.indexed,
texture_space: space(&texture, false),
sampler: sampler.as_ref().map(|s| s.register),
sampler_indexed: sampler.as_ref().is_some_and(|s| s.indexed),
sampler_space: sampler.as_ref().and_then(|s| space(s, true)),
comparison,
sampler_mode: sampler.as_ref().and_then(|s| {
declared(s, true).and_then(|d| match d.kind {
Declared::Sampler { mode, .. } => Some(mode),
_ => None,
})
}),
});
}
reads
}