1#![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
17pub type Id = op::IdRef;
21
22#[derive(Clone, Copy, Debug)]
24pub enum Constant<'a> {
25 Bool(bool),
27 Constant(Literal),
29 Composite {
31 ty: Id,
33 constituents: &'a [Id],
35 },
36 Sampler {
38 addressing_mode: op::SamplerAddressingMode,
40 is_normalized: bool,
42 filter_mode: op::SamplerFilterMode,
44 },
45 Null {
47 ty: Id,
49 },
50 SpecBool(bool),
52 SpecConstant(Literal),
54 SpecConstantComposite {
56 ty: Id,
58 constituents: &'a [Id],
60 },
61 SpecConstantOp {
63 ty: Id,
65 opcode: op::Code,
69 operands: &'a [Id]
71 },
72}
73
74#[derive(Clone, Copy, Debug)]
76pub enum ScalarType {
77 Bool,
79 Int {
81 width: u32,
83 is_signed: bool,
85 },
86 Float {
88 width: u32,
90 },
91}
92
93#[derive(Clone, Copy, Debug)]
95pub struct SpecConstant {
96 pub ty: ScalarType,
98 pub constant_id: u32,
100 pub id: Id,
102}
103
104pub struct Resource<'a> {
106 pub type_id: Id,
108 pub base_type_id: Id,
110 pub variable_id: Id,
112 pub name: Option<CompilerStr<'a>>,
114}
115
116#[derive(Clone, Copy, Debug)]
120pub enum ResourceType {
121 UniformBuffer,
123 StorageBuffer,
125 PushConstant,
127 AtomicCounter,
129 InputAttachment,
131 StorageImage,
133 CombinedImageSampler,
135 SeparateImage,
137 SeparateSampler,
139 UniformTexelBuffer,
141 StorageTexelBuffer,
143}
144
145#[derive(Clone, Debug)]
149pub struct RuntimeArrayHole {
150 stride: usize,
151}
152
153impl RuntimeArrayHole {
154
155 #[inline]
157 pub fn declared(&self) -> usize {
158 self.stride
159 }
160
161 #[inline]
163 pub fn resolve(&self, count: usize) -> usize {
164 self.stride * count
165 }
166}
167
168#[derive(Clone, Debug)]
175pub struct UnknownArrayStrideHole {
176 element: Box<TypeSizeHint>,
177 count: usize,
178}
179
180impl UnknownArrayStrideHole {
181
182 #[inline]
184 pub fn declared(&self) -> usize {
185 self.element.declared() * self.count
186 }
187
188 #[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#[derive(Clone, Debug)]
205pub struct MatrixStrideHole {
206 columns: u32,
207 rows: u32,
208 declared: usize
209}
210
211impl MatrixStrideHole {
212
213 #[inline]
215 pub fn declared(&self) -> usize {
216 self.declared
217 }
218
219 #[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#[derive(Clone, Debug)]
234pub struct StructHole {
235 last_offset: usize,
236 hole: RuntimeArrayHole,
237}
238
239impl StructHole {
240
241 #[inline]
243 pub fn declared(&self) -> usize {
244 self.last_offset + self.hole.declared()
245 }
246
247 #[inline]
249 pub fn resolve(&self, count: usize) -> usize {
250 self.last_offset + self.hole.resolve(count)
251 }
252}
253
254#[derive(Clone, Debug)]
258pub enum TypeSizeHint {
259 Static(usize),
261 RuntimeArray(RuntimeArrayHole),
263 Matrix(MatrixStrideHole),
265 UnknownArrayStride(UnknownArrayStrideHole),
267 Struct(StructHole),
269}
270
271impl TypeSizeHint {
272
273 #[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
286pub struct Type<'a> {
288 pub id: Id,
290 pub name: Option<CompilerStr<'a>>,
292 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#[derive(Clone, Copy)]
307pub struct Decorate<'a> {
308 pub target: Id,
310 pub member: Option<u32>,
314 pub decoration: op::Decoration<'a>,
318}
319
320#[derive(Clone, Copy)]
322pub struct Variable {
323 pub result_type: Id,
325 pub id_result: Id,
327 pub storage_class: op::StorageClass,
329}
330
331pub 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 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 #[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 #[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 #[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 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 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 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 #[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 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}