spirq_core/
annotation.rs

1//! Annotation instruction (decorations and names) utilities.
2use fnv::FnvHashMap as HashMap;
3
4use crate::{
5    error::{anyhow, Result},
6    ty::{AccessType, Type},
7    var::{DescriptorBinding, InterfaceLocation},
8};
9
10pub use spirv::Decoration;
11
12type VariableId = u32;
13type InstrId = u32;
14
15#[derive(Clone, Copy, PartialEq, Eq, Hash)]
16struct DecorationKey {
17    pub id: InstrId,
18    pub member_idx: Option<u32>,
19    pub deco: Decoration,
20}
21impl DecorationKey {
22    pub fn new(id: InstrId, deco: Decoration) -> Self {
23        Self {
24            id,
25            member_idx: None,
26            deco,
27        }
28    }
29    pub fn new_member(id: InstrId, member_idx: u32, deco: Decoration) -> Self {
30        Self {
31            id,
32            member_idx: Some(member_idx),
33            deco,
34        }
35    }
36}
37
38#[derive(Default)]
39pub struct DecorationRegistry<'a> {
40    deco_map: HashMap<DecorationKey, &'a [u32]>,
41}
42impl<'a> DecorationRegistry<'a> {
43    fn set_impl(&mut self, key: DecorationKey, operands: &'a [u32]) -> Result<()> {
44        use std::collections::hash_map::Entry;
45        match self.deco_map.entry(key) {
46            Entry::Vacant(entry) => {
47                entry.insert(operands);
48                Ok(())
49            }
50            Entry::Occupied(_) => Err(anyhow!("duplicate decoration at id {}", key.id)),
51        }
52    }
53    fn get_impl(&self, key: DecorationKey) -> Result<&'a [u32]> {
54        self.deco_map
55            .get(&key)
56            .copied()
57            .ok_or(anyhow!("missing decoration at id {}", key.id))
58    }
59
60    pub fn set(&mut self, id: InstrId, deco: Decoration, operands: &'a [u32]) -> Result<()> {
61        self.set_impl(DecorationKey::new(id, deco), operands)
62    }
63    pub fn set_member(
64        &mut self,
65        id: InstrId,
66        member_idx: u32,
67        deco: Decoration,
68        operands: &'a [u32],
69    ) -> Result<()> {
70        self.set_impl(DecorationKey::new_member(id, member_idx, deco), operands)
71    }
72    pub fn get(&self, id: InstrId, deco: Decoration) -> Result<&'a [u32]> {
73        self.get_impl(DecorationKey::new(id, deco))
74    }
75    pub fn get_member(&self, id: InstrId, member_idx: u32, deco: Decoration) -> Result<&'a [u32]> {
76        self.get_impl(DecorationKey::new_member(id, member_idx, deco))
77    }
78
79    pub fn get_u32(&self, id: InstrId, deco: Decoration) -> Result<u32> {
80        self.get(id, deco)
81            .and_then(|x| {
82                x.get(0).ok_or(anyhow!(
83                    "expected a single operand for decoration {:?} at id {}",
84                    deco,
85                    id
86                ))
87            })
88            .copied()
89    }
90    pub fn get_member_u32(&self, id: InstrId, member_idx: u32, deco: Decoration) -> Result<u32> {
91        self.get_member(id, member_idx, deco)
92            .and_then(|x| {
93                x.get(0).ok_or(anyhow!(
94                    "expected a single operand for member decoration {:?} at id {} for member {}",
95                    deco,
96                    id,
97                    member_idx
98                ))
99            })
100            .copied()
101    }
102
103    pub fn contains(&self, id: InstrId, deco: Decoration) -> bool {
104        self.deco_map.contains_key(&DecorationKey::new(id, deco))
105    }
106    pub fn contains_member(&self, id: InstrId, member_idx: u32, deco: Decoration) -> bool {
107        self.deco_map
108            .contains_key(&DecorationKey::new_member(id, member_idx, deco))
109    }
110
111    pub fn get_all(&self, deco: Decoration) -> impl Iterator<Item = (InstrId, &[u32])> {
112        self.deco_map
113            .iter()
114            .filter(move |(key, _)| key.deco == deco)
115            .map(|(key, value)| (key.id, *value))
116    }
117
118    /// Get the location-component pair of an interface variable.
119    pub fn get_var_location(&self, var_id: VariableId) -> Result<InterfaceLocation> {
120        let comp = self.get_u32(var_id, Decoration::Component).unwrap_or(0);
121        self.get_u32(var_id, Decoration::Location)
122            .map(|loc| InterfaceLocation::new(loc, comp))
123    }
124    /// Get the set-binding pair of a descriptor resource.
125    pub fn get_var_desc_bind(&self, var_id: VariableId) -> Result<DescriptorBinding> {
126        let desc_set = self.get_u32(var_id, Decoration::DescriptorSet).unwrap_or(0);
127        self.get_u32(var_id, Decoration::Binding)
128            .map(|bind_point| DescriptorBinding::new(desc_set, bind_point))
129    }
130    /// Get the set-binding pair of a descriptor resource, but the binding point
131    /// is forced to 0 if it's not specified in SPIR-V source.
132    pub fn get_var_desc_bind_or_default(&self, var_id: VariableId) -> DescriptorBinding {
133        self.get_var_desc_bind(var_id)
134            .unwrap_or(DescriptorBinding::new(0, 0))
135    }
136    /// Get the access type of an memory object.
137    pub fn get_desc_access_ty(&self, id: InstrId, ty: &Type) -> Option<AccessType> {
138        self.get_access_ty_from_deco(id).and_then(|x| {
139            // Use the stricter one.
140            if x == AccessType::ReadWrite {
141                match ty.access_ty() {
142                    Some(x) => Some(x),
143                    None => Some(AccessType::ReadWrite),
144                }
145            } else {
146                Some(x)
147            }
148        })
149    }
150    pub fn get_access_ty_from_deco(&self, id: InstrId) -> Option<AccessType> {
151        let write_only = self.contains(id, Decoration::NonReadable);
152        let read_only = self.contains(id, Decoration::NonWritable);
153        match (write_only, read_only) {
154            (true, true) => None,
155            (true, false) => Some(AccessType::WriteOnly),
156            (false, true) => Some(AccessType::ReadOnly),
157            (false, false) => Some(AccessType::ReadWrite),
158        }
159    }
160    pub fn get_member_access_ty_from_deco(
161        &self,
162        id: InstrId,
163        member_idx: u32,
164    ) -> Option<AccessType> {
165        let write_only = self.contains_member(id, member_idx, Decoration::NonReadable);
166        let read_only = self.contains_member(id, member_idx, Decoration::NonWritable);
167        match (write_only, read_only) {
168            (true, true) => None,
169            (true, false) => Some(AccessType::WriteOnly),
170            (false, true) => Some(AccessType::ReadOnly),
171            (false, false) => Some(AccessType::ReadWrite),
172        }
173    }
174
175    /// Get the input attachment index of the variable.
176    pub fn get_var_input_attm_idx(&self, var_id: VariableId) -> Result<u32> {
177        self.get_u32(var_id, Decoration::InputAttachmentIndex)
178    }
179}
180
181#[derive(Clone, PartialEq, Eq, Hash)]
182pub struct NameKey {
183    pub id: InstrId,
184    pub member_idx: Option<u32>,
185}
186#[derive(Default)]
187pub struct NameRegistry<'a> {
188    name_map: HashMap<NameKey, &'a str>,
189}
190impl<'a> NameRegistry<'a> {
191    // Names are debuf information. Not important so ID collisions are ignored.
192    pub fn set(&mut self, id: InstrId, name: &'a str) {
193        use std::collections::hash_map::Entry;
194        let key = NameKey {
195            id,
196            member_idx: None,
197        };
198        match self.name_map.entry(key) {
199            Entry::Vacant(entry) => {
200                entry.insert(name);
201            }
202            _ => {}
203        }
204    }
205    pub fn set_member(&mut self, id: InstrId, member_idx: u32, name: &'a str) {
206        use std::collections::hash_map::Entry;
207        let key = NameKey {
208            id,
209            member_idx: Some(member_idx),
210        };
211        match self.name_map.entry(key) {
212            Entry::Vacant(entry) => {
213                entry.insert(name);
214            }
215            _ => {}
216        }
217    }
218
219    pub fn get(&self, id: InstrId) -> Option<&'a str> {
220        self.name_map
221            .get(&NameKey {
222                id,
223                member_idx: None,
224            })
225            .copied()
226    }
227    pub fn get_member(&self, id: InstrId, member_idx: u32) -> Option<&'a str> {
228        self.name_map
229            .get(&NameKey {
230                id,
231                member_idx: Some(member_idx),
232            })
233            .copied()
234    }
235
236    pub fn iter(&self) -> impl Iterator<Item = (&NameKey, &&'a str)> {
237        self.name_map.iter()
238    }
239}