1use super::*;
2
3mod layout;
4mod type_registry;
5
6use layout::*;
7use type_registry::*;
8
9pub fn immediatespatch(in_spv: &[u32], corrections: &mut CorrectionMap) -> Result<Vec<u32>, ()> {
11 let spv = in_spv.to_owned();
12
13 let instruction_bound = spv[SPV_HEADER_INSTRUCTION_BOUND_OFFSET];
14 let magic_number = spv[SPV_HEADER_MAGIC_NUM_OFFSET];
15
16 let spv_header = spv[0..SPV_HEADER_LENGTH].to_owned();
17
18 assert_eq!(magic_number, SPV_HEADER_MAGIC);
19
20 let mut instruction_inserts = vec![];
21 let word_inserts = vec![];
22
23 let spv = spv.into_iter().skip(SPV_HEADER_LENGTH).collect::<Vec<_>>();
24 let mut new_spv = spv.clone();
25
26 let mut op_variable_idxs = vec![];
28 let mut op_type_pointer_idxs = vec![];
29 let mut op_type_struct_idxs = vec![];
30 let mut op_type_array_idxs = vec![];
31 let mut op_type_matrix_idxs = vec![];
32 let mut op_type_vector_idxs = vec![];
33 let mut op_type_float_idxs = vec![];
34 let mut op_type_int_idxs = vec![];
35 let mut op_constant_idxs = vec![];
36 let mut op_decorate_idxs = vec![];
37 let mut op_member_decorate_idxs = vec![];
38
39 let mut spv_idx = 0;
40 while spv_idx < spv.len() {
41 let op = spv[spv_idx];
42 let word_count = hiword(op);
43 let instruction = loword(op);
44
45 match instruction {
46 SPV_INSTRUCTION_OP_VARIABLE => op_variable_idxs.push(spv_idx),
47 SPV_INSTRUCTION_OP_TYPE_POINTER => op_type_pointer_idxs.push(spv_idx),
48 SPV_INSTRUCTION_OP_TYPE_STRUCT => op_type_struct_idxs.push(spv_idx),
49 SPV_INSTRUCTION_OP_TYPE_ARRAY => op_type_array_idxs.push(spv_idx),
50 SPV_INSTRUCTION_OP_TYPE_MATRIX => op_type_matrix_idxs.push(spv_idx),
51 SPV_INSTRUCTION_OP_TYPE_VECTOR => op_type_vector_idxs.push(spv_idx),
52 SPV_INSTRUCTION_OP_TYPE_FLOAT => op_type_float_idxs.push(spv_idx),
53 SPV_INSTRUCTION_OP_TYPE_INT => op_type_int_idxs.push(spv_idx),
54 SPV_INSTRUCTION_OP_CONSTANT => op_constant_idxs.push(spv_idx),
55 SPV_INSTRUCTION_OP_DECORATE => op_decorate_idxs.push(spv_idx),
56 SPV_INSTRUCTION_OP_MEMBER_DECORATE => op_member_decorate_idxs.push(spv_idx),
57 _ => {}
58 }
59
60 spv_idx += word_count as usize;
61 }
62
63 let pc_variables = op_variable_idxs
65 .iter()
66 .filter_map(|&v_idx| {
67 let result_type_id = spv[v_idx + 1];
68 let result_id = spv[v_idx + 2];
69 let storage_class = spv[v_idx + 3];
70 (storage_class == SPV_STORAGE_CLASS_PUSH_CONSTANT).then_some((
71 v_idx,
72 result_type_id,
73 result_id,
74 ))
75 })
76 .collect::<Vec<_>>();
77
78 if pc_variables.is_empty() {
79 return Ok(in_spv.to_vec());
80 }
81
82 let block_struct_ids = pc_variables
84 .iter()
85 .map(|&(_, ptr_id, _)| {
86 op_type_pointer_idxs
87 .iter()
88 .find_map(|&tp_idx| {
89 let result_id = spv[tp_idx + 1];
90 let underlying_type_id = spv[tp_idx + 3];
91 (result_id == ptr_id).then_some(underlying_type_id)
92 })
93 .expect("OpVariable PushConstant referenced an undefined OpTypePointer")
94 })
95 .collect::<Vec<_>>();
96
97 let type_registry = build_type_registry(BuildTypeRegistryIn {
99 spv: &spv,
100 op_type_float_idxs: &op_type_float_idxs,
101 op_type_int_idxs: &op_type_int_idxs,
102 op_type_vector_idxs: &op_type_vector_idxs,
103 op_type_matrix_idxs: &op_type_matrix_idxs,
104 op_type_array_idxs: &op_type_array_idxs,
105 op_type_struct_idxs: &op_type_struct_idxs,
106 op_constant_idxs: &op_constant_idxs,
107 });
108
109 for &block_struct_id in &block_struct_ids {
111 relayout_type_recursive(
112 &spv,
113 &mut new_spv,
114 block_struct_id,
115 &type_registry,
116 &op_decorate_idxs,
117 &op_member_decorate_idxs,
118 );
119 }
120
121 for &tp_idx in &op_type_pointer_idxs {
124 let storage_class = spv[tp_idx + 2];
125 if storage_class == SPV_STORAGE_CLASS_PUSH_CONSTANT {
126 new_spv[tp_idx + 2] = SPV_STORAGE_CLASS_UNIFORM;
127 }
128 }
129 for &(v_idx, _, _) in &pc_variables {
130 new_spv[v_idx + 3] = SPV_STORAGE_CLASS_UNIFORM;
131 }
132
133 let first_op_decorate_idx = op_decorate_idxs.first().copied();
135 let get_max_set = || {
137 op_decorate_idxs
138 .iter()
139 .filter_map(|&d_idx| {
140 let decoration_id = spv[d_idx + 2];
141 let decoration_value = spv[d_idx + 3];
142 (decoration_id == SPV_DECORATION_DESCRIPTOR_SET).then_some(decoration_value)
143 })
144 .max()
145 };
146 let target_set = match (
147 corrections.immediates_set,
148 corrections.immediates_set_mode.unwrap_or_default(),
149 ) {
150 (Some(set), ImmediatesSetMode::MaxUpTo) => {
151 let max_set = get_max_set().unwrap_or(0);
152 if max_set < set { max_set } else { set }
153 }
154 (Some(set), ImmediatesSetMode::MaxPlusOneUpTo) => {
155 let max_set = get_max_set();
156 if max_set < Some(set) {
157 max_set.map(|x| x + 1).unwrap_or(0)
158 } else {
159 set
160 }
161 }
162 (Some(set), ImmediatesSetMode::Absolute) => set,
163 (None, _) => get_max_set().map(|x| x + 1).unwrap_or(0),
164 };
165
166 let set_bindings =
167 decorate_map_set_bindings(&spv, &op_decorate_idxs, &HashSet::from([target_set]));
168 let target_binding = match (
169 corrections.immediates_binding,
170 corrections.immediates_binding_mode,
171 ) {
172 (Some(binding_idx), Some(ImmediatesBindingMode::Absolute)) => binding_idx,
173 _ => set_bindings
174 .get(&target_set)
175 .unwrap()
176 .last()
177 .map(|&(_, b)| b + 1)
178 .unwrap_or(0u32),
179 };
180
181 for (binding_idx, &(_, _, var_id)) in pc_variables.iter().enumerate() {
182 instruction_inserts.push(InstructionInsert {
183 previous_spv_idx: first_op_decorate_idx
184 .expect("Push constant block has no OpDecorate (missing Block decoration?)"),
185 instruction: vec![
186 encode_word(4, SPV_INSTRUCTION_OP_DECORATE),
187 var_id,
188 SPV_DECORATION_DESCRIPTOR_SET,
189 target_set,
190 encode_word(4, SPV_INSTRUCTION_OP_DECORATE),
191 var_id,
192 SPV_DECORATION_BINDING,
193 target_binding + binding_idx as u32,
194 ],
195 });
196 }
197
198 corrections.immediates_set = Some(target_set);
199 corrections.immediates_binding = Some(target_binding);
200
201 insert_new_instructions(&spv, &mut new_spv, &word_inserts, &instruction_inserts);
203
204 prune_noops(&mut new_spv);
206
207 Ok(fuse_final(spv_header, new_spv, instruction_bound))
209}
210
211fn relayout_type_recursive(
213 spv: &[u32],
214 new_spv: &mut [u32],
215 type_id: u32,
216 registry: &TypeRegistry,
217 op_decorate_idxs: &[usize],
218 op_member_decorate_idxs: &[usize],
219) {
220 let ty = match registry.get(&type_id) {
221 Some(t) => t,
222 None => return,
223 };
224
225 match &ty.kind {
226 TypeKind::Struct { members } => {
227 let layout = layout_struct(members, LayoutRule::Std140);
228
229 for (i, new_offset) in layout.member_offsets.iter().enumerate() {
230 patch_member_decoration_literal(
231 spv,
232 new_spv,
233 op_member_decorate_idxs,
234 type_id,
235 i as u32,
236 SPV_DECORATION_OFFSET,
237 *new_offset,
238 );
239 }
240
241 for (i, member) in members.iter().enumerate() {
242 let matrix_kind = match &member.kind {
243 TypeKind::Matrix { .. } => Some(&member.kind),
244 TypeKind::Array { element, .. } => match &element.kind {
245 TypeKind::Matrix { .. } => Some(&element.kind),
246 _ => None,
247 },
248 _ => None,
249 };
250 if let Some(TypeKind::Matrix { column, .. }) = matrix_kind {
251 let col_count = column_vec_count(column);
252 let scalar_w = column_scalar_width(column);
253 let new_stride = matrix_stride(col_count, scalar_w, LayoutRule::Std140);
254 patch_member_decoration_literal(
255 spv,
256 new_spv,
257 op_member_decorate_idxs,
258 type_id,
259 i as u32,
260 SPV_DECORATION_MATRIX_STRIDE,
261 new_stride,
262 );
263 }
264 relayout_type_recursive(
265 spv,
266 new_spv,
267 member.id,
268 registry,
269 op_decorate_idxs,
270 op_member_decorate_idxs,
271 );
272 }
273 }
274
275 TypeKind::Array { element, .. } => {
276 let new_stride = array_stride(&element.kind, LayoutRule::Std140);
277 for &d_idx in op_decorate_idxs {
278 let target_id = spv[d_idx + 1];
279 let decoration_id = spv[d_idx + 2];
280 if target_id == type_id && decoration_id == SPV_DECORATION_ARRAY_STRIDE {
281 new_spv[d_idx + 3] = new_stride;
282 }
283 }
284 relayout_type_recursive(
286 spv,
287 new_spv,
288 element.id,
289 registry,
290 op_decorate_idxs,
291 op_member_decorate_idxs,
292 );
293 }
294 _ => {}
296 }
297}
298
299fn patch_member_decoration_literal(
300 spv: &[u32],
301 new_spv: &mut [u32],
302 op_member_decorate_idxs: &[usize],
303 target_id: u32,
304 member: u32,
305 decoration: u32,
306 new_value: u32,
307) {
308 for &md_idx in op_member_decorate_idxs {
309 let md_target_id = spv[md_idx + 1];
310 let md_member = spv[md_idx + 2];
311 let md_decoration = spv[md_idx + 3];
312 if md_target_id == target_id && md_member == member && md_decoration == decoration {
313 new_spv[md_idx + 4] = new_value;
314 }
315 }
316}