use super::types::{struct_member_starts_at, tokenize, Tok};
use super::{arg_type_name, role_strings, texture_shape_from_name};
use crate::passes::ImageComp;
use spirv::Dim;
use std::collections::HashMap;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EmbeddedTexture {
pub buffer_index: u32,
pub field_offset: u32,
pub dim: Dim,
pub comp: ImageComp,
pub synthetic_texture_index: u32,
}
pub fn embedded_synthetic_texture_index(texture_locations: &[u32]) -> u32 {
texture_locations.iter().copied().max().map_or(0, |m| m + 1)
}
pub(super) fn body_uses_read_texture(ll: &str) -> bool {
ll.contains("@air.read_texture")
}
pub(super) fn detect_embedded_textures(
nodes: &HashMap<u32, String>,
indirect_buffer_struct_refs: &[(u32, u32)],
top_level_texture_locations: &[u32],
) -> Vec<EmbeddedTexture> {
let mut out = vec![];
let mut next_k = embedded_synthetic_texture_index(top_level_texture_locations);
for &(buffer_index, sref) in indirect_buffer_struct_refs {
for (field_offset, tex_node_ref) in embedded_texture_members(nodes, sref) {
let Some(tex_node) = nodes.get(&tex_node_ref) else {
continue;
};
let strs = role_strings(tex_node);
if !strs.iter().any(|s| s == "texture") || !strs.iter().any(|s| s == "sample") {
continue;
}
let name = arg_type_name(tex_node).unwrap_or_default();
let shape = texture_shape_from_name(&name);
let dim = shape.dimension.to_spirv_dim();
if dim != Dim::Dim2D || shape.arrayed {
continue;
}
out.push(EmbeddedTexture {
buffer_index,
field_offset,
dim,
comp: shape.component.to_image_comp(),
synthetic_texture_index: next_k,
});
next_k += 1;
}
}
out
}
fn embedded_texture_members(nodes: &HashMap<u32, String>, sref: u32) -> Vec<(u32, u32)> {
let Some(body) = nodes.get(&sref) else {
return vec![];
};
let toks = tokenize(body);
let mut out = vec![];
let mut i = 0;
while i < toks.len() {
if let (Some(Tok::Str(s)), Some(Tok::Ref(_))) = (toks.get(i), toks.get(i + 1)) {
if s == "air.struct_type_info" {
i += 2;
}
}
let offset = match (
toks.get(i),
toks.get(i + 1),
toks.get(i + 2),
toks.get(i + 3),
) {
(Some(Tok::Int(off)), Some(Tok::Int(_)), Some(Tok::Int(_)), Some(Tok::Str(_))) => *off,
_ => break,
};
i += 5; while i < toks.len() && !struct_member_starts_at(&toks, i) {
if let (Some(Tok::Str(s)), Some(Tok::Ref(x))) = (toks.get(i), toks.get(i + 1)) {
if s == "air.indirect_argument" {
out.push((offset, *x));
}
}
i += 1;
}
}
out
}