1use rspirv::binary::Parser;
10use rspirv::dr::{Instruction, Loader, Module, Operand};
11use std::collections::BTreeMap;
12use std::convert::TryInto;
13use std::num::TryFromIntError;
14use thiserror::Error;
15
16pub use rspirv;
17pub use rspirv::spirv;
18
19pub struct Reflection(pub Module);
20
21#[derive(Error, Debug)]
22pub enum ReflectError {
23 #[error("{0:?} missing binding decoration")]
27 MissingBindingDecoration(Instruction),
28 #[error("{0:?} missing set decoration")]
29 MissingSetDecoration(Instruction),
30 #[error("Expecting operand {1} in position {2} for instruction {0:?}")]
31 OperandError(Instruction, &'static str, usize),
32 #[error("Expecting operand {1} in position {2} but instruction {0:?} has only {3} operands")]
33 OperandIndexError(Instruction, &'static str, usize, usize),
34 #[error("OpVariable {0:?} lacks a return type")]
35 VariableWithoutReturnType(Instruction),
36 #[error("Unknown storage class {0:?}")]
37 UnknownStorageClass(spirv::StorageClass),
38 #[error("Unknown struct (missing Block or BufferBlock annotation): {0:?}")]
39 UnknownStruct(Instruction),
40 #[error("Unknown value {1} for `sampled` field: {0:?}")]
41 ImageSampledFieldUnknown(Instruction, u32),
42 #[error("Unhandled OpType instruction {0:?}")]
43 UnhandledTypeInstruction(Instruction),
44 #[error("{0:?} does not generate a result")]
45 MissingResultId(Instruction),
46 #[error("No instruction assigns to {0:?}")]
47 UnassignedResultId(u32),
48 #[error("rspirv reflect lacks module header")]
49 MissingHeader,
50 #[error("rspirv reflect lacks `OpMemoryModel`")]
51 MissingMemoryModel,
52 #[error("Accidentally binding global parameter buffer. Global variables are currently not supported in HLSL")]
53 BindingGlobalParameterBuffer,
54 #[error("Only one push constant block can be defined per shader entry")]
55 TooManyPushConstants,
56 #[error("SPIR-V parse error")]
57 ParseError(#[from] rspirv::binary::ParseState),
58 #[error("OpTypeInt cannot have width {0}")]
59 UnexpectedIntWidth(u32),
60 #[error(
61 "Invalid or unimplemented combination of AddressingModel {0:?} and StorageClass {1:?}"
62 )]
63 InvalidAddressingModelAndStorageClass(spirv::AddressingModel, spirv::StorageClass),
64 #[error(transparent)]
65 TryFromIntError(#[from] TryFromIntError),
66}
67
68type Result<V, E = ReflectError> = ::std::result::Result<V, E>;
69
70#[derive(Copy, Clone, Eq, PartialEq)]
73#[repr(transparent)]
74pub struct DescriptorType(pub u32);
75
76impl DescriptorType {
78 pub const SAMPLER: Self = Self(0);
79 pub const COMBINED_IMAGE_SAMPLER: Self = Self(1);
80 pub const SAMPLED_IMAGE: Self = Self(2);
81 pub const STORAGE_IMAGE: Self = Self(3);
82 pub const UNIFORM_TEXEL_BUFFER: Self = Self(4);
83 pub const STORAGE_TEXEL_BUFFER: Self = Self(5);
84 pub const UNIFORM_BUFFER: Self = Self(6);
85 pub const STORAGE_BUFFER: Self = Self(7);
86 pub const UNIFORM_BUFFER_DYNAMIC: Self = Self(8);
87 pub const STORAGE_BUFFER_DYNAMIC: Self = Self(9);
88 pub const INPUT_ATTACHMENT: Self = Self(10);
89
90 pub const INLINE_UNIFORM_BLOCK_EXT: Self = Self(1_000_138_000);
91 pub const ACCELERATION_STRUCTURE_KHR: Self = Self(1_000_150_000);
92 pub const ACCELERATION_STRUCTURE_NV: Self = Self(1_000_165_000);
93}
94
95impl std::fmt::Debug for DescriptorType {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 f.write_str(match *self {
98 Self::SAMPLER => "SAMPLER",
99 Self::COMBINED_IMAGE_SAMPLER => "COMBINED_IMAGE_SAMPLER",
100 Self::SAMPLED_IMAGE => "SAMPLED_IMAGE",
101 Self::STORAGE_IMAGE => "STORAGE_IMAGE",
102 Self::UNIFORM_TEXEL_BUFFER => "UNIFORM_TEXEL_BUFFER",
103 Self::STORAGE_TEXEL_BUFFER => "STORAGE_TEXEL_BUFFER",
104 Self::UNIFORM_BUFFER => "UNIFORM_BUFFER",
105 Self::STORAGE_BUFFER => "STORAGE_BUFFER",
106 Self::UNIFORM_BUFFER_DYNAMIC => "UNIFORM_BUFFER_DYNAMIC",
107 Self::STORAGE_BUFFER_DYNAMIC => "STORAGE_BUFFER_DYNAMIC",
108 Self::INPUT_ATTACHMENT => "INPUT_ATTACHMENT",
109 Self::INLINE_UNIFORM_BLOCK_EXT => "INLINE_UNIFORM_BLOCK_EXT",
110 Self::ACCELERATION_STRUCTURE_KHR => "ACCELERATION_STRUCTURE_KHR",
111 Self::ACCELERATION_STRUCTURE_NV => "ACCELERATION_STRUCTURE_NV",
112 _ => "(UNDEFINED)",
113 })
114 }
115}
116
117#[derive(Debug, Clone, PartialEq, Eq)]
118pub enum BindingCount {
119 One,
126 StaticSized(usize),
133 Unbounded,
142}
143
144#[derive(Debug, Clone, PartialEq, Eq)]
145pub struct DescriptorInfo {
146 pub ty: DescriptorType,
147 pub binding_count: BindingCount,
148 pub name: String,
149}
150
151#[derive(Clone, Copy, Debug, PartialEq, Eq)]
152pub struct PushConstantInfo {
153 pub offset: u32,
154 pub size: u32,
155}
156
157macro_rules! get_ref_operand_at {
158 ($instr:expr, $op:path, $idx:expr) => {
160 if $idx >= $instr.operands.len() {
161 Err(ReflectError::OperandIndexError(
162 $instr.clone(),
163 stringify!($op),
164 $idx,
165 $instr.operands.len(),
166 ))
167 } else if let $op(val) = &$instr.operands[$idx] {
168 Ok(val)
169 } else {
170 Err(ReflectError::OperandError(
171 $instr.clone(),
172 stringify!($op),
173 $idx,
174 ))
175 }
176 };
177}
178
179macro_rules! get_operand_at {
180 ($ops:expr, $op:path, $idx:expr) => {
181 get_ref_operand_at!($ops, $op, $idx).map(|v| *v)
182 };
183}
184
185impl Reflection {
186 pub fn new(module: Module) -> Self {
187 Self(module)
188 }
189
190 pub fn new_from_spirv(code: &[u8]) -> Result<Self> {
191 Ok(Self::new({
192 let mut loader = Loader::new();
193 let p = Parser::new(code, &mut loader);
194 p.parse()?;
195 loader.module()
196 }))
197 }
198
199 pub fn find_annotations_for_id(
201 annotations: &[Instruction],
202 id: u32,
203 ) -> Result<Vec<&Instruction>> {
204 annotations
205 .iter()
206 .filter_map(|a| {
207 let op = get_operand_at!(a, Operand::IdRef, 0);
208 match op {
209 Ok(idref) if idref == id => Some(Ok(a)),
210 Err(e) => Some(Err(e)),
211 _ => None,
212 }
213 })
214 .collect::<Result<Vec<_>>>()
215 }
216
217 pub fn find_assignment_for(instructions: &[Instruction], id: u32) -> Result<&Instruction> {
219 instructions
221 .iter()
222 .find(|instr| instr.result_id == Some(id))
223 .ok_or(ReflectError::UnassignedResultId(id))
224 }
225
226 pub fn get_compute_group_size(&self) -> Option<(u32, u32, u32)> {
227 for inst in self.0.global_inst_iter() {
228 if inst.class.opcode == spirv::Op::ExecutionMode {
229 use rspirv::dr::Operand::{ExecutionMode, LiteralBit32};
230 if let [ExecutionMode(
231 spirv::ExecutionMode::LocalSize | spirv::ExecutionMode::LocalSizeHint,
232 ), LiteralBit32(x), LiteralBit32(y), LiteralBit32(z)] = inst.operands[1..]
233 {
234 return Some((x, y, z));
235 } else {
236 }
238 }
239 }
240 None
241 }
242
243 fn get_descriptor_type_for_var(
245 &self,
246 type_id: u32,
247 storage_class: spirv::StorageClass,
248 ) -> Result<DescriptorInfo> {
249 let type_instruction = Self::find_assignment_for(&self.0.types_global_values, type_id)?;
250 self.get_descriptor_type(type_instruction, storage_class)
251 }
252
253 fn get_descriptor_type(
255 &self,
256 type_instruction: &Instruction,
257 storage_class: spirv::StorageClass,
258 ) -> Result<DescriptorInfo> {
259 let annotations = type_instruction.result_id.map_or(Ok(vec![]), |result_id| {
260 Reflection::find_annotations_for_id(&self.0.annotations, result_id)
261 })?;
262
263 match type_instruction.class.opcode {
265 spirv::Op::TypeArray => {
266 let element_type_id = get_operand_at!(type_instruction, Operand::IdRef, 0)?;
267 let num_elements_id = get_operand_at!(type_instruction, Operand::IdRef, 1)?;
268 let num_elements =
269 Self::find_assignment_for(&self.0.types_global_values, num_elements_id)?;
270 assert_eq!(num_elements.class.opcode, spirv::Op::Constant);
271 let num_elements_ty = Self::find_assignment_for(
272 &self.0.types_global_values,
273 num_elements.result_type.unwrap(),
274 )?;
275 assert_eq!(num_elements_ty.class.opcode, spirv::Op::TypeInt);
277 let num_elements = match get_operand_at!(num_elements_ty, Operand::LiteralBit32, 0)?
278 {
279 32 => get_operand_at!(num_elements, Operand::LiteralBit32, 0)?.try_into()?,
280 64 => get_operand_at!(num_elements, Operand::LiteralBit64, 0)?.try_into()?,
281 x => return Err(ReflectError::UnexpectedIntWidth(x)),
282 };
283 assert!(num_elements >= 1);
284 return Ok(DescriptorInfo {
285 binding_count: BindingCount::StaticSized(num_elements),
286 ..self.get_descriptor_type_for_var(element_type_id, storage_class)?
287 });
288 }
289 spirv::Op::TypeRuntimeArray => {
290 let element_type_id = get_operand_at!(type_instruction, Operand::IdRef, 0)?;
291 return Ok(DescriptorInfo {
292 binding_count: BindingCount::Unbounded,
293 ..self.get_descriptor_type_for_var(element_type_id, storage_class)?
294 });
295 }
296 spirv::Op::TypePointer => {
297 let ptr_storage_class =
298 get_operand_at!(type_instruction, Operand::StorageClass, 0)?;
299 let element_type_id = get_operand_at!(type_instruction, Operand::IdRef, 1)?;
300 assert_eq!(storage_class, ptr_storage_class);
301 return self.get_descriptor_type_for_var(element_type_id, storage_class);
302 }
303 spirv::Op::TypeSampledImage => {
304 let element_type_id = get_operand_at!(type_instruction, Operand::IdRef, 0)?;
305
306 let image_instruction =
307 Self::find_assignment_for(&self.0.types_global_values, element_type_id)?;
308
309 let descriptor = self.get_descriptor_type(image_instruction, storage_class)?;
310
311 let dim = get_operand_at!(image_instruction, Operand::Dim, 1)?;
312 assert_ne!(dim, spirv::Dim::DimSubpassData);
313
314 return Ok(if dim == spirv::Dim::DimBuffer {
315 if descriptor.ty != DescriptorType::UNIFORM_TEXEL_BUFFER
316 && descriptor.ty != DescriptorType::STORAGE_TEXEL_BUFFER
317 {
318 todo!("Unexpected sampled image type {:?}", descriptor.ty)
319 }
320 descriptor
321 } else {
322 DescriptorInfo {
323 ty: DescriptorType::COMBINED_IMAGE_SAMPLER,
324 ..descriptor
325 }
326 });
327 }
328 _ => {}
329 }
330
331 let descriptor_type = match type_instruction.class.opcode {
332 spirv::Op::TypeSampler => DescriptorType::SAMPLER,
333 spirv::Op::TypeImage => {
334 let dim = get_operand_at!(type_instruction, Operand::Dim, 1)?;
335
336 const IMAGE_SAMPLED: u32 = 1;
337 const IMAGE_STORAGE: u32 = 2;
338
339 let sampled = get_operand_at!(type_instruction, Operand::LiteralBit32, 5)?;
341
342 if dim == spirv::Dim::DimBuffer {
343 if sampled == IMAGE_SAMPLED {
344 DescriptorType::UNIFORM_TEXEL_BUFFER
345 } else if sampled == IMAGE_STORAGE {
346 DescriptorType::STORAGE_TEXEL_BUFFER
347 } else {
348 return Err(ReflectError::ImageSampledFieldUnknown(
349 type_instruction.clone(),
350 sampled,
351 ));
352 }
353 } else if dim == spirv::Dim::DimSubpassData {
354 DescriptorType::INPUT_ATTACHMENT
355 } else if sampled == IMAGE_SAMPLED {
356 DescriptorType::SAMPLED_IMAGE
357 } else if sampled == IMAGE_STORAGE {
358 DescriptorType::STORAGE_IMAGE
359 } else {
360 return Err(ReflectError::ImageSampledFieldUnknown(
361 type_instruction.clone(),
362 sampled,
363 ));
364 }
365 }
366 spirv::Op::TypeStruct => {
367 let mut is_uniform_buffer = false;
368 let mut is_storage_buffer = false;
369
370 for annotation in annotations {
371 for operand in &annotation.operands {
372 if let Operand::Decoration(decoration) = operand {
373 match decoration {
374 spirv::Decoration::Block => is_uniform_buffer = true,
375 spirv::Decoration::BufferBlock => is_storage_buffer = true,
376 _ => { }
377 }
378 }
379 }
380 }
381
382 let version = self
383 .0
384 .header
385 .as_ref()
386 .ok_or(ReflectError::MissingHeader)?
387 .version();
388
389 if version <= (1, 3) && is_storage_buffer {
390 DescriptorType::STORAGE_BUFFER
392 } else if version >= (1, 3) {
393 assert!(
395 !is_storage_buffer,
396 "BufferBlock decoration is obsolete in SPIRV > 1.3"
397 );
398 assert!(
399 is_uniform_buffer,
400 "Struct requires Block annotation in SPIRV > 1.3"
401 );
402 match storage_class {
403 spirv::StorageClass::Uniform | spirv::StorageClass::UniformConstant => {
404 DescriptorType::UNIFORM_BUFFER
405 }
406 spirv::StorageClass::StorageBuffer => DescriptorType::STORAGE_BUFFER,
407 _ => return Err(ReflectError::UnknownStorageClass(storage_class)),
408 }
409 } else if is_uniform_buffer {
410 DescriptorType::UNIFORM_BUFFER
411 } else {
412 return Err(ReflectError::UnknownStruct(type_instruction.clone()));
413 }
414 }
415 spirv::Op::TypeAccelerationStructureKHR => DescriptorType::ACCELERATION_STRUCTURE_KHR,
417 _ => {
418 return Err(ReflectError::UnhandledTypeInstruction(
419 type_instruction.clone(),
420 ))
421 }
422 };
423
424 Ok(DescriptorInfo {
425 ty: descriptor_type,
426 binding_count: BindingCount::One,
427 name: "".to_string(),
428 })
429 }
430
431 pub fn get_descriptor_sets(&self) -> Result<BTreeMap<u32, BTreeMap<u32, DescriptorInfo>>> {
434 let mut unique_sets = BTreeMap::new();
435 let reflect = &self.0;
436
437 let uniform_variables = reflect
438 .types_global_values
439 .iter()
440 .filter(|i| i.class.opcode == spirv::Op::Variable)
441 .filter_map(|i| {
442 let cls = get_operand_at!(i, Operand::StorageClass, 0);
443 match cls {
444 Ok(cls)
445 if cls == spirv::StorageClass::Uniform
446 || cls == spirv::StorageClass::UniformConstant
447 || cls == spirv::StorageClass::StorageBuffer =>
448 {
449 Some(Ok(i))
450 }
451 Err(e) => Some(Err(e)),
452 _ => None,
453 }
454 })
455 .collect::<Result<Vec<_>, _>>()?;
456
457 let names = reflect
458 .debug_names
459 .iter()
460 .filter(|i| i.class.opcode == spirv::Op::Name)
461 .map(|i| -> Result<(u32, String)> {
462 let element_type_id = get_operand_at!(i, Operand::IdRef, 0)?;
463 let name = get_ref_operand_at!(i, Operand::LiteralString, 1)?;
464 Ok((element_type_id, name.clone()))
465 })
466 .collect::<Result<BTreeMap<_, _>, _>>()?;
467
468 for var in uniform_variables {
469 if let Some(var_id) = var.result_id {
470 let annotations =
471 Reflection::find_annotations_for_id(&reflect.annotations, var_id)?;
472
473 let (set, binding) = annotations.iter().filter(|a| a.operands.len() >= 3).fold(
475 (None, None),
476 |state, a| {
477 if let Operand::Decoration(d) = a.operands[1] {
478 if let Operand::LiteralBit32(i) = a.operands[2] {
479 if d == spirv::Decoration::DescriptorSet {
480 assert!(state.0.is_none(), "Set already has a value!");
481 return (Some(i), state.1);
482 } else if d == spirv::Decoration::Binding {
483 assert!(state.1.is_none(), "Binding already has a value!");
484 return (state.0, Some(i));
485 }
486 }
487 }
488 state
489 },
490 );
491
492 let set = set.ok_or_else(|| ReflectError::MissingSetDecoration(var.clone()))?;
493 let binding =
494 binding.ok_or_else(|| ReflectError::MissingBindingDecoration(var.clone()))?;
495
496 let current_set = unique_sets
497 .entry(set)
498 .or_insert_with(BTreeMap::<u32, DescriptorInfo>::new);
499
500 let storage_class = get_operand_at!(var, Operand::StorageClass, 0)?;
501
502 let type_id = var
503 .result_type
504 .ok_or_else(|| ReflectError::VariableWithoutReturnType(var.clone()))?;
505 let mut descriptor_info =
506 self.get_descriptor_type_for_var(type_id, storage_class)?;
507
508 if let Some(name) = names.get(&var_id) {
509 if name == "$Globals" {
511 return Err(ReflectError::BindingGlobalParameterBuffer);
512 }
513
514 descriptor_info.name = name.to_owned();
515 }
516
517 let inserted = current_set.insert(binding, descriptor_info);
518 assert!(
519 inserted.is_none(),
520 "Can't bind to the same slot twice within the same shader"
521 );
522 }
523 }
524 Ok(unique_sets)
525 }
526
527 fn byte_offset_to_last_var(
528 reflect: &Module,
529 struct_instruction: &Instruction,
530 ) -> Result<u32, ReflectError> {
531 debug_assert!(struct_instruction.class.opcode == spirv::Op::TypeStruct);
532
533 if struct_instruction.operands.len() < 2 {
535 return Ok(0);
536 }
537
538 let result_id = struct_instruction
539 .result_id
540 .ok_or_else(|| ReflectError::MissingResultId(struct_instruction.clone()))?;
541
542 Ok(
544 Self::find_annotations_for_id(&reflect.annotations, result_id)?
545 .iter()
546 .filter(|i| i.class.opcode == spirv::Op::MemberDecorate)
547 .filter_map(|&i| match get_operand_at!(i, Operand::Decoration, 2) {
548 Ok(spirv::Decoration::Offset) => {
549 Some(get_operand_at!(i, Operand::LiteralBit32, 3))
550 }
551 Err(err) => Some(Err(err)),
552 _ => None,
553 })
554 .collect::<Result<Vec<_>>>()?
555 .into_iter()
556 .max()
557 .unwrap_or(0),
558 )
559 }
560
561 fn calculate_variable_size_bytes(
562 reflect: &Module,
563 type_instruction: &Instruction,
564 ) -> Result<u32, ReflectError> {
565 match type_instruction.class.opcode {
566 spirv::Op::TypeInt | spirv::Op::TypeFloat => {
567 debug_assert!(!type_instruction.operands.is_empty());
568 Ok(get_operand_at!(type_instruction, Operand::LiteralBit32, 0)? / 8)
569 }
570 spirv::Op::TypeVector | spirv::Op::TypeMatrix => {
571 debug_assert!(type_instruction.operands.len() == 2);
572 let type_id = get_operand_at!(type_instruction, Operand::IdRef, 0)?;
573 let var_type_instruction =
574 Self::find_assignment_for(&reflect.types_global_values, type_id)?;
575 let type_size_bytes =
576 Self::calculate_variable_size_bytes(reflect, var_type_instruction)?;
577
578 let type_constant_count =
579 get_operand_at!(type_instruction, Operand::LiteralBit32, 1)?;
580 Ok(type_size_bytes * type_constant_count)
581 }
582 spirv::Op::TypeArray => {
583 debug_assert!(type_instruction.operands.len() == 2);
584 let type_id = get_operand_at!(type_instruction, Operand::IdRef, 0)?;
585 let var_type_instruction =
586 Self::find_assignment_for(&reflect.types_global_values, type_id)?;
587 let type_size_bytes =
588 Self::calculate_variable_size_bytes(reflect, var_type_instruction)?;
589
590 let var_constant_id = get_operand_at!(type_instruction, Operand::IdRef, 1)?;
591 let constant_instruction =
592 Self::find_assignment_for(&reflect.types_global_values, var_constant_id)?;
593 let type_constant_count =
594 get_operand_at!(constant_instruction, Operand::LiteralBit32, 0)?;
595
596 Ok(type_size_bytes * type_constant_count)
597 }
598 spirv::Op::TypeStruct => {
599 if !type_instruction.operands.is_empty() {
600 let byte_offset = Self::byte_offset_to_last_var(reflect, type_instruction)?;
601 let last_var_idx = type_instruction.operands.len() - 1;
602 let id_ref = get_operand_at!(type_instruction, Operand::IdRef, last_var_idx)?;
603 let type_instruction =
604 Self::find_assignment_for(&reflect.types_global_values, id_ref)?;
605 Ok(byte_offset
606 + Self::calculate_variable_size_bytes(reflect, type_instruction)?)
607 } else {
608 Ok(0)
609 }
610 }
611 spirv::Op::TypePointer => {
612 let memory_model = reflect
613 .memory_model
614 .as_ref()
615 .ok_or(ReflectError::MissingMemoryModel)?;
616 let addressing_model = get_operand_at!(memory_model, Operand::AddressingModel, 0)?;
617
618 let storage_class = get_operand_at!(type_instruction, Operand::StorageClass, 0)?;
619
620 match (addressing_model, storage_class) {
623 (
624 spirv::AddressingModel::PhysicalStorageBuffer64,
626 spirv::StorageClass::PhysicalStorageBuffer,
627 ) => Ok(8),
628 (a, s) => Err(ReflectError::InvalidAddressingModelAndStorageClass(a, s)),
629 }
630 }
631 x => panic!("Size computation for {:?} unsupported", x),
632 }
633 }
634
635 pub fn get_push_constant_range(&self) -> Result<Option<PushConstantInfo>, ReflectError> {
636 let reflect = &self.0;
637
638 let push_constants = reflect
639 .types_global_values
640 .iter()
641 .filter(|i| i.class.opcode == spirv::Op::Variable)
642 .filter_map(|i| {
643 let cls = get_operand_at!(*i, Operand::StorageClass, 0);
644 match cls {
645 Ok(spirv::StorageClass::PushConstant) => Some(Ok(i)),
646 Err(err) => Some(Err(err)),
647 _ => None,
648 }
649 })
650 .collect::<Result<Vec<_>>>()?;
651
652 if push_constants.len() > 1 {
653 return Err(ReflectError::TooManyPushConstants);
654 }
655
656 let push_constant = match push_constants.into_iter().next() {
657 Some(push_constant) => push_constant,
658 None => return Ok(None),
659 };
660
661 let instruction = Reflection::find_assignment_for(
662 &reflect.types_global_values,
663 push_constant.result_type.unwrap(),
664 )?;
665
666 let instruction = if instruction.class.opcode == spirv::Op::TypePointer {
668 let ptr_storage_class = get_operand_at!(instruction, Operand::StorageClass, 0)?;
669 assert_eq!(spirv::StorageClass::PushConstant, ptr_storage_class);
670 let element_type_id = get_operand_at!(instruction, Operand::IdRef, 1)?;
671 Reflection::find_assignment_for(&reflect.types_global_values, element_type_id)?
672 } else {
673 instruction
674 };
675
676 let size_bytes = Self::calculate_variable_size_bytes(reflect, instruction)?;
677
678 Ok(Some(PushConstantInfo {
679 size: size_bytes,
680 offset: 0,
681 }))
682 }
683
684 pub fn disassemble(&self) -> String {
685 use rspirv::binary::Disassemble;
686 self.0.disassemble()
687 }
688}