Skip to main content

nox_spirv/
reflect.rs

1//! Contains everything reflection relevant.
2
3#![warn(missing_docs)]
4
5mod error;
6
7use ::core::ffi::CStr;
8
9use crate::{
10    core::*,
11    op,
12    module::*,
13};
14
15pub use error::*;
16
17/// An Id to an instruction, which has a [`result`][1].
18///
19/// [1]: op::IdResult
20pub type Id = op::IdRef;
21
22/// Represents an constant value.
23#[derive(Clone, Copy, Debug)]
24pub enum Constant<'a> {
25    /// A non-specialization constant boolean.
26    Bool(bool),
27    /// A non-specialization constant.
28    Constant(Literal),
29    /// A non-specialization constant composite.
30    Composite {
31        /// The type [`Id`] of the composite type.
32        ty: Id,
33        /// The constituents of composite value.
34        constituents: &'a [Id],
35    },
36    /// A constant sampler.
37    Sampler {
38        /// The addressing mode of the sampler.
39        addressing_mode: op::SamplerAddressingMode,
40        /// Whether the sampler uses normalized coordinates.
41        is_normalized: bool,
42        /// The filter mode of the sampler.
43        filter_mode: op::SamplerFilterMode,
44    },
45    /// A null value.
46    Null {
47        /// The type [`Id`] of the null value.
48        ty: Id,
49    },
50    /// A specialization constant boolean.
51    SpecBool(bool),
52    /// A specialization constant.
53    SpecConstant(Literal),
54    /// A specialization constant composite.
55    SpecConstantComposite {
56        /// The type [`Id`] of the composite type.
57        ty: Id,
58        /// The constituents of the composite value.
59        constituents: &'a [Id],
60    },
61    /// A specialization constant operation.
62    SpecConstantOp {
63        /// The resultant type [`Id`] of the operation.
64        ty: Id,
65        /// The [`code`][1] specifying the operation.
66        ///
67        /// [1]: op::Code
68        opcode: op::Code,
69        /// The operands of the operation.
70        operands: &'a [Id]
71    },
72}
73
74/// Represents a scalar type.
75#[derive(Clone, Copy, Debug)]
76pub enum ScalarType {
77    /// A boolean type.
78    Bool,
79    /// An integer type.
80    Int {
81        /// The width of the integer in bits.
82        width: u32,
83        /// Whether the integer is signed.
84        is_signed: bool,
85    },
86    /// A floating point type.
87    Float {
88        /// The width of the floating point type in bits.
89        width: u32,
90    },
91}
92
93/// Represents a specialization constant
94#[derive(Clone, Copy, Debug)]
95pub struct SpecConstant {
96    /// The scalar type of the constant.
97    pub ty: ScalarType,
98    /// The constant id assigned to the constant.
99    pub constant_id: u32,
100    /// The [`Id`] of the constant.
101    pub id: Id,
102}
103
104/// Represents a shader resource.
105pub struct Resource<'a> {
106    /// The outer type of the resource.
107    pub type_id: Id,
108    /// The inner type of the resource.
109    pub base_type_id: Id,
110    /// The variable id of the resource.
111    pub variable_id: Id,
112    /// The variable name string of the resource.
113    pub name: Option<CompilerStr<'a>>,
114}
115
116/// A type specifying, which resources to [`reflect`][1].
117///
118/// [1]: Reflector::resources_for_type
119#[derive(Clone, Copy, Debug)]
120pub enum ResourceType {
121    /// A uniform buffer resource.
122    UniformBuffer,
123    /// A storage buffer resource.
124    StorageBuffer,
125    /// A push constant resource.
126    PushConstant,
127    /// An atomic counter resource.
128    AtomicCounter,
129    /// An input attachment resource.
130    InputAttachment,
131    /// A storage image resource.
132    StorageImage,
133    /// A sampled image containing both a sampler and an image.
134    CombinedImageSampler,
135    /// A sampled image without a sampler.
136    SeparateImage,
137    /// An sampler without an image.
138    SeparateSampler,
139    /// A uniform buffer interpreted as an image.
140    UniformTexelBuffer,
141    /// A storage buffer interpreted as an image.
142    StorageTexelBuffer,
143}
144
145/// A hole created from the declaration of a runtime array.
146///
147/// Can be [`resolved`][1] by giving the number of elements in the array.
148#[derive(Clone, Debug)]
149pub struct RuntimeArrayHole {
150    stride: usize,
151}
152
153impl RuntimeArrayHole {
154
155    /// Gets the declared size of runtime array, which is just its stride.
156    #[inline]
157    pub fn declared(&self) -> usize {
158        self.stride
159    }
160
161    /// Resolves the hole with `count`.
162    #[inline]
163    pub fn resolve(&self, count: usize) -> usize {
164        self.stride * count
165    }
166}
167
168/// A hole created from the declaration of an array without the [`array stride decoration`][1].
169///
170/// Can be resolved, by resolving the element type [`hint`][2].
171///
172/// [1]: op::Decoration::ArrayStride
173/// [2]: TypeSizeHint
174#[derive(Clone, Debug)]
175pub struct UnknownArrayStrideHole {
176    element: Box<TypeSizeHint>,
177    count: usize,
178}
179
180impl UnknownArrayStrideHole {
181
182    /// Gets the declared size of the hole.
183    #[inline]
184    pub fn declared(&self) -> usize {
185        self.element.declared() * self.count
186    }
187
188    /// Resolves the hole by resolving the element type [`hint`][1].
189    ///
190    /// [1]: TypeSizeHint
191    #[inline]
192    pub fn resolve<F>(&self, f: F) -> usize
193        where F: FnOnce(&TypeSizeHint) -> usize
194    {
195        f(&self.element) * self.count
196    }
197}
198
199/// A hole created from the usage of a matrix type without the [`matrix stride decoration`][1].
200///
201/// Can be resolved by specifying the stride and whether the matrix is row-major.
202///
203/// [1]: op::Decoration::MatrixStride
204#[derive(Clone, Debug)]
205pub struct MatrixStrideHole {
206    columns: u32,
207    rows: u32,
208    declared: usize
209}
210
211impl MatrixStrideHole {
212
213    /// Gets the declared size of the hole.
214    #[inline]
215    pub fn declared(&self) -> usize {
216        self.declared
217    }
218
219    /// Resolves the hole with a stride and whether the matrix is row-major.
220    #[inline]
221    pub fn resolve(&self, stride: usize, is_row_major: bool) -> usize {
222        if is_row_major {
223            stride * self.rows as usize
224        } else {
225            stride * self.columns as usize
226        }
227    }
228}
229
230/// A hole created from a struct, which has a runtime array as the last member.
231///
232/// Can be resolved by giving the number of elements in the runtime array.
233#[derive(Clone, Debug)]
234pub struct StructHole {
235    last_offset: usize,
236    hole: RuntimeArrayHole,
237}
238
239impl StructHole {
240
241    /// Gets the declared size of the hole.
242    #[inline]
243    pub fn declared(&self) -> usize {
244        self.last_offset + self.hole.declared()
245    }
246
247    /// Resolves the hole by giving the number of elements in the runtime array.
248    #[inline]
249    pub fn resolve(&self, count: usize) -> usize {
250        self.last_offset + self.hole.resolve(count)
251    }
252}
253
254/// A hint of the size of a [`Type`].
255///
256/// It can be a statically known size, or it can have a hole which can be resolved.
257#[derive(Clone, Debug)]
258pub enum TypeSizeHint {
259    /// A statically known size.
260    Static(usize),
261    /// A runtime array hole.
262    RuntimeArray(RuntimeArrayHole),
263    /// A matrix stride hole.
264    Matrix(MatrixStrideHole),
265    /// An unknown array stride hole.
266    UnknownArrayStride(UnknownArrayStrideHole),
267    /// A hole caused by a runtime array at the end of a struct.
268    Struct(StructHole),
269}
270
271impl TypeSizeHint {
272
273    /// Gets the declared minimum size of the type.
274    #[inline]
275    pub fn declared(&self) -> usize {
276        match self {
277            &Self::Static(size) => size,
278            Self::RuntimeArray(hole) => hole.declared(),
279            Self::Matrix(hole) => hole.declared(),
280            Self::UnknownArrayStride(hole) => hole.declared(),
281            Self::Struct(hole) => hole.declared(),
282        }
283    } 
284}
285
286/// Describes a type.
287pub struct Type<'a> {
288    /// The [`Id`] of the type.
289    pub id: Id,
290    /// The name of the type.
291    pub name: Option<CompilerStr<'a>>,
292    /// A [`hint`][1] of the size of the type.
293    ///
294    /// [1]: TypeSizeHint
295    pub size_hint: TypeSizeHint,
296}
297
298#[derive(Clone, Copy)]
299struct Name<'a> {
300    target: Id,
301    member: Option<u32>,
302    name: CompilerStr<'a>,
303}
304
305/// A decoration added to an instruction, or a member of a struct.
306#[derive(Clone, Copy)]
307pub struct Decorate<'a> {
308    /// The target [`Id`] of the decoration.
309    pub target: Id,
310    /// The member index within [`target`][1], if this decorates a member of a struct.
311    ///
312    /// [1]: Self::target
313    pub member: Option<u32>,
314    /// The [`Decoration`][1] assigned to the target.
315    ///
316    /// [1]: op::Decoration
317    pub decoration: op::Decoration<'a>,
318}
319
320/// Represents a variable.
321#[derive(Clone, Copy)]
322pub struct Variable {
323    /// The resultant type [`Id`] of the variable.
324    pub result_type: Id,
325    /// The [`Id`] of the variable.
326    pub id_result: Id,
327    /// The storage class of the variable.
328    pub storage_class: op::StorageClass,
329}
330
331/// Reflects SPIR-V code through a [`module`][1].
332///
333/// # Example
334/// ``` rust
335/// use nox_spirv::op;
336/// use nox_spirv::Module;
337/// use nox_spirv::reflect::{Reflector, ResourceType};
338/// 
339/// let spirv: &[u32] = ...;
340/// let module = Module::new(spirv);
341/// let mut reflector = Reflector::new(module).unwrap();
342/// reflector.set_entry_point(c"main", op::ExecutionModel::FRAGMENT).unwrap();
343/// for push_constant in reflector.resources_for_type(ResourceType::PushConstant).unwrap() {
344///     let push_constant = push_constant.unwrap();
345///     let size = reflector
346///         .type_description(push_constant.base_type_id)
347///         .unwrap().size_hint.declared();
348///     println!("Push constant (size {size}): {}", push_constant.name.unwrap_or_default());
349/// }
350/// ```
351///
352/// [1]: Module
353pub struct Reflector<'a> {
354    module: Module<'a>,
355    decorates: Vec<Decorate<'a>>,
356    current_entry_point: Option<op::InstEntryPoint<'a>>,
357}
358
359impl<'a> Reflector<'a> {
360
361    /// Creates a reflector from a [`module`][1].
362    ///
363    /// Automatically calls [`parse_full`][2].
364    ///
365    /// [1]: Module
366    /// [2]: Module::parse_full
367    pub fn new(
368        mut module: Module<'a>,
369    ) -> ReflectResult<Reflector<'a>>
370    {
371        module.parse_full()?;
372        let mut decorates = vec![];
373        for mut stream in module.all_instructions() { 
374            if stream.code() == op::Code::DECORATE {
375                let decorate = op::InstDecorate {
376                    target: Id::parse_one(&mut stream)?,
377                    decoration: op::Decoration::parse_one(&mut stream)?,
378                };
379                decorates.push(Decorate {
380                    target: decorate.target,
381                    member: None,
382                    decoration: decorate.decoration,
383                });
384            } else if stream.code() == op::Code::DECORATE_ID {
385                let decorate = op::InstDecorateId {
386                    target: Id::parse_one(&mut stream)?,
387                    decoration: op::Decoration::parse_one(&mut stream)?,
388                };
389                decorates.push(Decorate {
390                    target: decorate.target,
391                    member: None,
392                    decoration: decorate.decoration,
393                });
394            } else if stream.code() == op::Code::DECORATE_STRING {
395                let decorate = op::InstDecorateString {
396                    target: Id::parse_one(&mut stream)?,
397                    decoration: op::Decoration::parse_one(&mut stream)?,
398                };
399                decorates.push(Decorate {
400                    target: decorate.target,
401                    member: None,
402                    decoration: decorate.decoration,
403                });
404            } else if stream.code() == op::Code::MEMBER_DECORATE {
405                let decorate = op::InstMemberDecorate {
406                    structure_type: Id::parse_one(&mut stream)?,
407                    member: stream.read()?,
408                    decoration: op::Decoration::parse_one(&mut stream)?,
409                };
410                decorates.push(Decorate {
411                    target: decorate.structure_type,
412                    member: Some(decorate.member),
413                    decoration: decorate.decoration,
414                });
415            } else if stream.code() == op::Code::MEMBER_DECORATE_STRING {
416                let decorate = op::InstMemberDecorateString {
417                    struct_type: Id::parse_one(&mut stream)?,
418                    member: stream.read()?,
419                    decoration: op::Decoration::parse_one(&mut stream)?,
420                };
421                decorates.push(Decorate {
422                    target: decorate.struct_type,
423                    member: Some(decorate.member),
424                    decoration: decorate.decoration,
425                });
426            }
427        }
428        Ok(Self {
429            decorates,
430            module,
431            current_entry_point: None,
432        })
433    }
434
435    /// Sets the entry point to reflect.
436    ///
437    /// This *must* be set before doing any reflection.
438    #[inline]
439    pub fn set_entry_point(
440        &mut self,
441        entry_point: &CStr,
442        execution_model: op::ExecutionModel,
443    ) -> ReflectResult<()> {
444        for mut stream in self.module.all_instructions() {
445            if stream.code() == op::Code::ENTRY_POINT {
446                let model = op::ExecutionModel::parse_one(&mut stream)?;
447                let entry_point_id = Id::parse_one(&mut stream)?;
448                let name = stream.read_string()?;
449                if name.to_cstr()? == entry_point && model == execution_model {
450                    self.current_entry_point = Some(op::InstEntryPoint {
451                        execution_model: model,
452                        entry_point: entry_point_id,
453                        name,
454                        interface: Id::parse_eos(&mut stream)?,
455                    });
456                    return Ok(())
457                }
458            }
459        }
460        Err(ReflectError::UnknownEntryPoint)
461    }
462
463    /// Returns an iterator over all variables contained in the [`current entry point`][1].
464    ///
465    /// [1]: Self::set_entry_point
466    #[inline]
467    pub fn variables(&self) -> ReflectResult<impl Iterator<Item = ReflectResult<Variable>>>
468    {
469        let Some(entry_point) = self.current_entry_point else {
470            return Err(ReflectError::NoEntryPointSet)
471        };
472        Ok(self.module
473            .results()
474            .map(|(_, mut stream)| {
475                if stream.code() == op::Code::VARIABLE {
476                    let result_type = Id::parse_one(&mut stream)?;
477                    let id_result = Id::parse_one(&mut stream)?;
478                    if entry_point.interface.contains(&id_result) {
479                        return Ok(Some(Variable {
480                            result_type,
481                            id_result,
482                            storage_class: op::StorageClass::parse_one(&mut stream)?,
483                        }))
484                    }
485                }
486                ReflectResult::Ok(None)
487            }).filter_map(|variable| {
488                match variable {
489                    Ok(var) => var.map(Ok),
490                    Err(err) => Some(Err(err)),
491                }
492            })
493        )
494    }
495
496    fn names(&self) -> impl Iterator<Item = ReflectResult<Name<'a>>> {
497        self.module
498            .all_instructions()
499            .filter(|stream| matches!(stream.code(), op::Code::NAME | op::Code::MEMBER_NAME))
500            .map(|mut stream| {
501                if stream.code() == op::Code::NAME {
502                    let name = op::InstName {
503                        target: Id::parse_one(&mut stream)?,
504                        name: stream.read_string()?,
505                    };
506                    Ok(Name {
507                        target: name.target,
508                        member: None,
509                        name: name.name,
510                    })
511                } else if stream.code() == op::Code::MEMBER_NAME {
512                    let name = op::InstMemberName {
513                        ty: Id::parse_one(&mut stream)?,
514                        member: stream.read()?,
515                        name: stream.read_string()?,
516                    };
517                    Ok(Name {
518                        target: name.ty,
519                        member: Some(name.member),
520                        name: name.name,
521                    })
522                } else { unreachable!() }
523            })
524        }
525
526        /// Returns an iterator over the statically known values and constant ids of all
527        /// specialization constants.
528        #[inline]
529        pub fn spec_constants(&self) -> impl Iterator<Item = ReflectResult<SpecConstant>> {
530            self.module
531                .results()
532                .filter_map(|(id, stream)| 
533                    if matches!(
534                        stream.code(),
535                        op::Code::SPEC_CONSTANT_TRUE
536                        | op::Code::SPEC_CONSTANT_FALSE
537                        | op::Code::SPEC_CONSTANT
538                    ) {
539                        self.decorates
540                            .iter()
541                            .find_map(|&decorate|
542                                if decorate.target == id &&
543                                    let op::Decoration::SpecId { specialization_constant_id } =
544                                    decorate.decoration
545                                {
546                                    Some(specialization_constant_id)
547                                } else { None }
548                            ).map(|constant_id| (constant_id, id, stream))
549                    } else { None }
550                )
551                .map(|(constant_id, id, mut stream)| {
552                    match stream.code() {
553                        op::Code::SPEC_CONSTANT_TRUE | op::Code::SPEC_CONSTANT_FALSE => {
554                            Ok(SpecConstant {
555                                ty: ScalarType::Bool,
556                                constant_id,
557                                id,
558                            })
559                        },
560                        op::Code::SPEC_CONSTANT => {
561                            let result_type = op::IdResultType::parse_one(&mut stream)?;
562                            let _ = stream.read()?;
563                            let ctx = &mut ParseContext {
564                                result_type: Some(result_type)
565                            };
566                            let value = Literal::parse_one(&self.module, &mut stream, ctx)?;
567                            let ty = match value {
568                                Literal::F16(_) => ScalarType::Float { width: 2, },
569                                Literal::F32(_) => ScalarType::Float { width: 4, },
570                                Literal::F64(_) => ScalarType::Float { width: 8, },
571                                Literal::I8(_) => ScalarType::Int { width: 1, is_signed: true, },
572                                Literal::I16(_) => ScalarType::Int { width: 2, is_signed: true, },
573                                Literal::I32(_) => ScalarType::Int { width: 4, is_signed: true, },
574                                Literal::I64(_) => ScalarType::Int { width: 8, is_signed: true, },
575                                Literal::U8(_) => ScalarType::Int { width: 1, is_signed: false, },
576                                Literal::U16(_) => ScalarType::Int { width: 2, is_signed: false, },
577                                Literal::U32(_) => ScalarType::Int { width: 4, is_signed: false, },
578                                Literal::U64(_) => ScalarType::Int { width: 8, is_signed: false, },
579                            };
580                            Ok(SpecConstant {
581                                ty,
582                                constant_id,
583                                id,
584                            })
585                        },
586                        _ => unreachable!(),
587                    }
588                })
589        }
590
591        /// Gets a specific constant with `id`.
592        pub fn constant(
593            &self,
594            id: Id,
595        ) -> ReflectResult<Constant<'a>>
596        {
597            let mut stream = self.module.get_result(id).ok_or(ReflectError::InvalidConstantId(id))?;
598            match stream.code() {
599                op::Code::CONSTANT_TRUE => {
600                    Ok(Constant::Bool(true))
601                },
602                op::Code::CONSTANT_FALSE => {
603                    Ok(Constant::Bool(false))
604                },
605                op::Code::CONSTANT => {
606                    let result_type = op::IdResultType::parse_one(&mut stream)?;
607                    let _ = stream.read()?;
608                    let ctx = &mut ParseContext {
609                        result_type: Some(result_type)
610                    };
611                    let value = Literal::parse_one(&self.module, &mut stream, ctx)?;
612                    Ok(Constant::Constant(value))
613                },
614                op::Code::CONSTANT_COMPOSITE => {
615                    let ty = Id::parse_one(&mut stream)?;
616                    let _ = stream.read()?;
617                    Ok(Constant::Composite {
618                        ty,
619                        constituents: Id::parse_eos(&mut stream)?,
620                    })
621                },
622                op::Code::CONSTANT_SAMPLER => {
623                    let _ = stream.read()?;
624                    let _ = stream.read()?;
625                    let addressing_mode = op::SamplerAddressingMode::parse_one(&mut stream)?;
626                    let is_normalized = stream.read()? == 1;
627                    let filter_mode = op::SamplerFilterMode::parse_one(&mut stream)?;
628                    Ok(Constant::Sampler { addressing_mode, is_normalized, filter_mode })
629                },
630                op::Code::CONSTANT_NULL => {
631                    let ty = Id::parse_one(&mut stream)?;
632                    Ok(Constant::Null { ty })
633                },
634                op::Code::SPEC_CONSTANT_TRUE => {
635                    Ok(Constant::SpecBool(true))
636                },
637                op::Code::SPEC_CONSTANT_FALSE => {
638                    Ok(Constant::SpecBool(false))
639                },
640                op::Code::SPEC_CONSTANT => {
641                    let result_type = op::IdResultType::parse_one(&mut stream)?;
642                    let _ = stream.read()?;
643                    let ctx = &mut ParseContext {
644                        result_type: Some(result_type)
645                    };
646                    let value = Literal::parse_one(&self.module, &mut stream, ctx)?;
647                    Ok(Constant::SpecConstant(value))
648                },
649                op::Code::SPEC_CONSTANT_COMPOSITE => {
650                    let ty = Id::parse_one(&mut stream)?;
651                    let _ = stream.read()?;
652                    Ok(Constant::SpecConstantComposite {
653                        ty,
654                        constituents: Id::parse_eos(&mut stream)?,
655                    })
656                },
657                op::Code::SPEC_CONSTANT_OP => {
658                    let ty = Id::parse_one(&mut stream)?;
659                    let _ = stream.read()?;
660                    let code = op::LiteralSpecConstantOpInteger::parse_one(&mut stream)?;
661                    Ok(Constant::SpecConstantOp { ty, opcode: code.code, operands: code.operands })
662                },
663                _ => Err(ReflectError::InvalidConstantId(id))
664            }
665        }
666
667        #[inline]
668        /// Returns a name assigned to an [`Id`], or optionally to a struct member of a struct.
669        pub fn name(&self, id: Id, struct_member: Option<u32>) -> ReflectResult<Option<CompilerStr<'a>>> {
670            for name in self.names() {
671                let name = name?;
672                if name.target == id &&
673                    name.member == struct_member
674                {
675                    return Ok(Some(name.name))
676                }
677            }
678            Ok(None)
679        }
680
681        /// Returns an iterator over all resources used in the current [`entry point`][1] of
682        /// type [`ty`][2].
683        ///
684        /// [1]: Self::set_entry_point
685        /// [2]: ResourceType
686        pub fn resources_for_type(&self, ty: ResourceType) -> ReflectResult<
687            impl Iterator<Item = ReflectResult<Resource<'a>>>
688        >
689        {
690            let resource_class = match ty {
691                ResourceType::UniformBuffer => op::StorageClass::UNIFORM,
692                ResourceType::StorageBuffer => op::StorageClass::STORAGE_BUFFER,
693                ResourceType::PushConstant => op::StorageClass::PUSH_CONSTANT,
694                ResourceType::AtomicCounter => op::StorageClass::ATOMIC_COUNTER,
695                ResourceType::InputAttachment
696                | ResourceType::StorageImage
697                | ResourceType::CombinedImageSampler
698                | ResourceType::SeparateImage
699                | ResourceType::SeparateSampler
700                | ResourceType::UniformTexelBuffer
701                | ResourceType::StorageTexelBuffer => op::StorageClass::UNIFORM_CONSTANT,
702            };
703            Ok(self.variables()?.map(move |op_variable| {
704                let op_variable = op_variable?;
705                let name = self.names().find_map(|op_name| {
706                    if let Ok(name) = op_name &&
707                        name.target == op_variable.id_result
708                    {
709                        Some(name.name)
710                    } else { None }
711                });
712                let mut base_type = op_variable.result_type;
713                loop {
714                    let mut stream = self.module
715                        .get_result(base_type)
716                        .ok_or(ReflectError::InvalidTypeId(base_type))?;
717                    base_type = match stream.code()
718                    {
719                        op::Code::TYPE_POINTER => {
720                            let _ = stream.read()?;
721                            let storage_class = op::StorageClass::parse_one(&mut stream)?;
722                            if storage_class != resource_class {
723                                return Ok(None)
724                            }
725                            Id::parse_one(&mut stream)?
726                        },
727                        op::Code::TYPE_ARRAY | op::Code::TYPE_RUNTIME_ARRAY => {
728                            let _ = stream.read()?;
729                            Id::parse_one(&mut stream)?
730                        }
731                        _ => {
732                            break
733                        },
734                    };
735                }
736                if resource_class == op::StorageClass::UNIFORM_CONSTANT {
737                    match ty {
738                        ResourceType::InputAttachment => {
739                            let mut stream = self.module
740                                .get_result(base_type)
741                                .ok_or(ReflectError::InvalidTypeId(base_type))?;
742                            if matches!(stream.code(), op::Code::TYPE_IMAGE) {
743                                let _ = stream.read_words(Some(2))?;
744                                let dim = op::Dim::parse_one(&mut stream)?;
745                                if dim == op::Dim::SUBPASS_DATA {
746                                    return Ok(Some(Resource {
747                                        type_id: op_variable.result_type,
748                                        base_type_id: base_type,
749                                        variable_id: op_variable.id_result,
750                                        name,
751                                    }))
752                                }
753                            }
754                            Ok(None)
755                        },
756                        ResourceType::StorageImage => {
757                            let mut stream = self.module
758                                .get_result(base_type)
759                                .ok_or(ReflectError::InvalidTypeId(base_type))?;
760                            if matches!(stream.code(), op::Code::TYPE_IMAGE) {
761                                let _ = stream.read_words(Some(2))?;
762                                let dim = op::Dim::parse_one(&mut stream)?;
763                                let _ = stream.read_words(Some(3))?;
764                                let sampled = stream.read()?;
765                                if sampled == 2 &&
766                                    matches!(
767                                        dim,
768                                        op::Dim::TYPE_1D
769                                        | op::Dim::TYPE_2D
770                                        | op::Dim::TYPE_3D
771                                        | op::Dim::CUBE
772                                        | op::Dim::RECT
773                                    )
774                                {
775                                    return Ok(Some(Resource {
776                                        type_id: op_variable.result_type,
777                                        base_type_id: base_type,
778                                        variable_id: op_variable.id_result,
779                                        name,
780                                    }))
781                                }
782                            }
783                            Ok(None)
784                        },
785                        ResourceType::CombinedImageSampler => {
786                            let stream = self.module
787                                .get_result(base_type)
788                                .ok_or(ReflectError::InvalidTypeId(base_type))?;
789                            if matches!(stream.code(), op::Code::TYPE_SAMPLED_IMAGE) {
790                                Ok(Some(Resource {
791                                    type_id: op_variable.result_type,
792                                    base_type_id: base_type,
793                                    variable_id: op_variable.id_result,
794                                    name,
795                                }))
796                            } else { Ok(None) }
797                        },
798                        ResourceType::SeparateImage => {
799                            let mut stream = self.module
800                                .get_result(base_type)
801                                .ok_or(ReflectError::InvalidTypeId(base_type))?;
802                            if matches!(
803                                stream.code(),
804                                op::Code::TYPE_IMAGE,
805                            ) {
806                                let _ = stream.read_words(Some(2))?;
807                                let dim = op::Dim::parse_one(&mut stream)?;
808                                let _ = stream.read_words(Some(3))?;
809                                let sampled = stream.read()?;
810                                if sampled != 2 &&
811                                    matches!(
812                                        dim,
813                                        op::Dim::TYPE_1D
814                                        | op::Dim::TYPE_2D
815                                        | op::Dim::TYPE_3D
816                                        | op::Dim::CUBE
817                                        | op::Dim::RECT
818                                    )
819                                {
820                                    return Ok(Some(Resource {
821                                        type_id: op_variable.result_type,
822                                        base_type_id: base_type,
823                                        variable_id: op_variable.id_result,
824                                        name,
825                                    }))
826                                }
827                            } 
828                            Ok(None)
829                        },
830                        ResourceType::SeparateSampler => {
831                            let stream = self.module
832                                .get_result(base_type)
833                                .ok_or(ReflectError::InvalidTypeId(base_type))?;
834                            if matches!(stream.code(), op::Code::TYPE_SAMPLER) {
835                                Ok(Some(Resource {
836                                    type_id: op_variable.result_type,
837                                    base_type_id: base_type,
838                                    variable_id: op_variable.id_result,
839                                    name,
840                                }))
841                            } else { Ok(None) }
842                        },
843                        ResourceType::UniformTexelBuffer => {
844                            let mut stream = self.module
845                                .get_result(base_type)
846                                .ok_or(ReflectError::InvalidTypeId(base_type))?;
847                            if matches!(stream.code(), op::Code::TYPE_IMAGE) {
848                                let _ = stream.read_words(Some(2))?;
849                                let dim = op::Dim::parse_one(&mut stream)?;
850                                let _ = stream.read_words(Some(3))?;
851                                let sampled = stream.read()?;
852                                if sampled != 2 && dim == op::Dim::BUFFER {
853                                    return  Ok(Some(Resource {
854                                        type_id: op_variable.result_type,
855                                        base_type_id: base_type,
856                                        variable_id: op_variable.id_result, 
857                                        name,
858                                    }))
859                                }
860                            }
861                            Ok(None)
862                        },
863                        ResourceType::StorageTexelBuffer => {
864                            let mut stream = self.module
865                                .get_result(base_type)
866                                .ok_or(ReflectError::InvalidTypeId(base_type))?;
867                            if matches!(stream.code(), op::Code::TYPE_IMAGE) {
868                                let _ = stream.read_words(Some(2))?;
869                                let dim = op::Dim::parse_one(&mut stream)?;
870                                let _ = stream.read_words(Some(3))?;
871                                let sampled = stream.read()?;
872                                if sampled == 2 && dim == op::Dim::BUFFER {
873                                    return  Ok(Some(Resource {
874                                        type_id: op_variable.result_type,
875                                        base_type_id: base_type,
876                                        variable_id: op_variable.id_result, 
877                                        name,
878                                    }))
879                                }
880                            }
881                            Ok(None)
882                        },
883                        _ => unreachable!()
884                    }
885                } else {
886                    Ok((op_variable.storage_class == resource_class).then_some(
887                        Resource {
888                            type_id: op_variable.result_type,
889                            base_type_id: base_type,
890                            variable_id: op_variable.id_result,
891                            name,
892                        }
893                    ))
894                }
895            }).filter_map(|resource| {
896                match resource {
897                    Ok(res) => res.map(Ok),
898                    Err(err) => Some(Err(err)),
899                }
900            }))
901        } 
902
903        /// Returns all [`decorations`][1] added to a given [`Id`].
904        ///
905        /// [1]: op::Decoration
906        #[inline]
907        pub fn decorations(
908            &self,
909            target_id: Id,
910        ) -> impl Iterator<Item = Decorate<'a>>
911        {
912            self.decorates
913                .iter()
914                .filter_map(move |&dec|
915                    (dec.target == target_id).then_some(
916                        dec
917                    )
918                )
919        }
920
921        /// Gets a [`type description`][1] of a given [`Id`] pointing to a type.
922        ///
923        /// The description notably contains a [`hint`][2] of its size, which can be resolved to a
924        /// known size when needed.
925        ///
926        /// [1]: Type
927        /// [2]: TypeSizeHint
928        pub fn type_description(&self, id: Id) -> ReflectResult<Type<'a>> {
929            let mut stream = self.module
930                .get_result(id)
931                .ok_or(ReflectError::InvalidTypeId(id))?;
932            let name = self.names().find_map(|name| {
933                if let Ok(name) = name &&
934                    name.target == id
935                {
936                    Some(name.name)
937                } else { None }
938            });
939            match stream.code() {
940                op::Code::TYPE_BOOL => Ok(Type {
941                    id,
942                    name,
943                    size_hint: TypeSizeHint::Static(4),
944                }),
945                op::Code::TYPE_INT | op::Code::TYPE_FLOAT => {
946                    let _ = stream.read()?;
947                    let width = stream.read()?;
948                    Ok(Type {
949                        id,
950                        name,
951                        size_hint: TypeSizeHint::Static(width.div_ceil(8) as usize),
952                    })
953                },
954                op::Code::TYPE_VECTOR => {
955                    let _ = stream.read()?;
956                    let component_type = Id::parse_one(&mut stream)?;
957                    let component_count = stream.read()?;
958                    let mut stream = self.module
959                        .get_result(component_type)
960                        .ok_or(ReflectError::InvalidTypeId(id))?;
961                    let size = match stream.code() {
962                        op::Code::TYPE_BOOL => 4 * component_count as usize,
963                        op::Code::TYPE_INT | op::Code::TYPE_FLOAT => {
964                            let _ = stream.read()?;
965                            let width = stream.read()?;
966                            (width.div_ceil(8) * component_count) as usize
967                        },
968                        x => return Err(ReflectError::ExpectedScalarType { found: x })
969                    };
970                    Ok(Type {
971                        id,
972                        name,
973                        size_hint: TypeSizeHint::Static(size),
974                    })
975                }
976                op::Code::TYPE_MATRIX => {
977                    let _ = stream.read()?;
978                    let column_type = Id::parse_one(&mut stream)?;
979                    let column_count = stream.read()?;
980                    let mut stream = self.module
981                        .get_result(column_type)
982                        .ok_or(ReflectError::InvalidTypeId(id))?;
983                    if !matches!(stream.code(), op::Code::TYPE_VECTOR) {
984                        return Err(ReflectError::ExpectedVectorType { found: stream.code() })
985                    }
986                    let _ = stream.read()?;
987                    let component_type = Id::parse_one(&mut stream)?;
988                    let component_count = stream.read()?;
989                    let mut stream = self.module
990                        .get_result(component_type)
991                        .ok_or(ReflectError::InvalidTypeId(id))?;
992                    let column_size = match stream.code() {
993                        op::Code::TYPE_BOOL => 4,
994                        op::Code::TYPE_INT | op::Code::TYPE_FLOAT => {
995                            let _ = stream.read()?;
996                            stream.read()?.div_ceil(8) as usize
997                        },
998                        x => return Err(ReflectError::ExpectedScalarType {
999                            found: x,
1000                        })
1001                    };
1002                    Ok(Type {
1003                        id,
1004                        name,
1005                        size_hint: TypeSizeHint::Matrix(MatrixStrideHole {
1006                            columns: column_count,
1007                            rows: component_count,
1008                            declared: column_size * (column_count as usize),
1009                        }),
1010                    })
1011                },
1012                op::Code::TYPE_ARRAY => {
1013                    let _ = stream.read()?;
1014                    let element_type = Id::parse_one(&mut stream)?;
1015                    let length = Id::parse_one(&mut stream)?;
1016                    let count = {
1017                        let constant = self.constant(length)?;
1018                        match constant {
1019                            Constant::Constant(literal) =>
1020                                literal.as_usize().ok_or(ReflectError::NonIntegerLiteral(literal))?,
1021                            Constant::SpecConstant(literal) =>
1022                                literal.as_usize().ok_or(ReflectError::NonIntegerLiteral(literal))?,
1023                            x => return Err(ReflectError::ExpectedConstantLiteral {
1024                                found: format!("{x:?}"),
1025                            })
1026                        }
1027                    };
1028                    let size_hint = {
1029                        if let Some(stride) =
1030                            self.decorations(id)
1031                            .find_map(|dec| {
1032                                let op::Decoration::ArrayStride { array_stride } = dec.decoration else {
1033                                    return None
1034                                };
1035                                Some(array_stride as usize)
1036                            })
1037                        {
1038                            TypeSizeHint::Static(count * stride)
1039                        } else {
1040                            let element_size_hint = self.type_description(element_type)?.size_hint;
1041                            match element_size_hint {
1042                                TypeSizeHint::Static(size) => TypeSizeHint::Static(count * size),
1043                                TypeSizeHint::Matrix(_) | TypeSizeHint::UnknownArrayStride(_)
1044                                    => TypeSizeHint::UnknownArrayStride(
1045                                    UnknownArrayStrideHole {
1046                                        element: Box::new(element_size_hint),
1047                                        count,
1048                                    }),
1049                                TypeSizeHint::RuntimeArray(_) | TypeSizeHint::Struct(_)
1050                                    => return Err(ReflectError::InvalidRuntimeArray),
1051                            }
1052                        }
1053                    };
1054                    Ok(Type {
1055                        id,
1056                        name,
1057                        size_hint
1058                    })
1059                },
1060                op::Code::TYPE_RUNTIME_ARRAY => {
1061                    let stride = self
1062                        .decorations(id)
1063                        .find_map(|dec| {
1064                            let op::Decoration::ArrayStride { array_stride } = dec.decoration else {
1065                                return None
1066                            };
1067                            Some(array_stride as usize)
1068                        }).ok_or(ReflectError::MissingRequiredDecoration("ArrayStride"))?;
1069                    Ok(Type {
1070                        id,
1071                        name,
1072                        size_hint: TypeSizeHint::RuntimeArray(RuntimeArrayHole { stride }),
1073                    })
1074                }
1075                op::Code::TYPE_STRUCT => {
1076                    let _ = stream.read()?;
1077                    let member_types = Id::parse_eos(&mut stream)?;
1078                    let mut min = u32::MAX;
1079                    let (desc, member, offset) = self.decorations(id)
1080                        .filter_map(|dec| {
1081                            if let Some(member) = dec.member &&
1082                                let op::Decoration::Offset { byte_offset } = dec.decoration &&
1083                                let Some(&ty) = member_types.get(member as usize) &&
1084                                let Ok(desc) = self.type_description(ty)
1085                            {
1086                                Some((desc, member, byte_offset))
1087                            } else { None }
1088                        }).max_by_key(|(_, _, offset)| {
1089                            min = min.min(*offset);
1090                            *offset
1091                        }).ok_or(ReflectError::MissingRequiredDecoration("Offset"))?;
1092                    let offset = (offset - min) as usize;
1093                    let size_hint = match desc.size_hint {
1094                        TypeSizeHint::Static(size) => TypeSizeHint::Static(offset + size),
1095                        TypeSizeHint::RuntimeArray(hole) => TypeSizeHint::Struct(StructHole {
1096                            last_offset: offset,
1097                            hole,
1098                        }),
1099                        TypeSizeHint::Matrix(hole) => {
1100                            let mut stride = None;
1101                            let mut is_row_major = false;
1102                            for dec in self.decorations(id) {
1103                                if let Some(dec_member) = dec.member &&
1104                                    dec_member == member
1105                                {
1106                                    if let op::Decoration::MatrixStride { matrix_stride } = dec.decoration {
1107                                        stride = Some(matrix_stride)
1108                                    } else if let op::Decoration::RowMajor = dec.decoration {
1109                                        is_row_major = true
1110                                    }
1111                                }
1112                            }
1113                            TypeSizeHint::Static(offset + hole
1114                                .resolve(
1115                                    stride.ok_or(
1116                                        ReflectError::MissingRequiredDecoration("MatrixStride")
1117                                    )? as usize,
1118                                    is_row_major
1119                                ))
1120                        },
1121                        TypeSizeHint::UnknownArrayStride(_) => return Err(
1122                            ReflectError::MissingRequiredDecoration("ArrayStride")
1123                        ),
1124                        TypeSizeHint::Struct(_) => return Err(
1125                            ReflectError::InvalidRuntimeArray
1126                        ),
1127                    };
1128                    Ok(Type {
1129                        id,
1130                        name,
1131                        size_hint,
1132                    })
1133                },
1134                op::Code::TYPE_POINTER => {
1135                    let _ = stream.read_words(Some(2));
1136                    self.type_description(Id::parse_one(&mut stream)?)
1137                },
1138                _ => Ok(Type {
1139                    id,
1140                    name,
1141                    size_hint: TypeSizeHint::Static(0),
1142                })
1143            }
1144        }
1145}