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
104#[derive(Clone, Copy, Debug)]
108pub enum ResourceType {
109 UniformBuffer,
111 StorageBuffer,
113 PushConstant,
115 AtomicCounter,
117 InputAttachment,
119 StorageImage,
121 CombinedImageSampler,
123 SeparateImage,
125 SeparateSampler,
127 UniformTexelBuffer,
129 StorageTexelBuffer,
131}
132
133#[derive(Clone, Debug)]
137pub struct RuntimeArrayHole {
138 stride: usize,
139}
140
141impl RuntimeArrayHole {
142
143 #[inline]
145 pub fn declared(&self) -> usize {
146 self.stride
147 }
148
149 #[inline]
151 pub fn resolve(&self, count: usize) -> usize {
152 self.stride * count
153 }
154}
155
156#[derive(Clone, Debug)]
163pub struct UnknownArrayStrideHole {
164 element: Box<TypeSizeHint>,
165 count: usize,
166}
167
168impl UnknownArrayStrideHole {
169
170 #[inline]
172 pub fn declared(&self) -> usize {
173 self.element.declared() * self.count
174 }
175
176 #[inline]
180 pub fn resolve<F>(&self, f: F) -> usize
181 where F: FnOnce(&TypeSizeHint) -> usize
182 {
183 f(&self.element) * self.count
184 }
185}
186
187#[derive(Clone, Debug)]
193pub struct MatrixStrideHole {
194 columns: u32,
195 rows: u32,
196 declared: usize
197}
198
199impl MatrixStrideHole {
200
201 #[inline]
203 pub fn declared(&self) -> usize {
204 self.declared
205 }
206
207 #[inline]
209 pub fn resolve(&self, stride: usize, is_row_major: bool) -> usize {
210 if is_row_major {
211 stride * self.rows as usize
212 } else {
213 stride * self.columns as usize
214 }
215 }
216}
217
218#[derive(Clone, Debug)]
222pub struct StructHole {
223 last_offset: usize,
224 hole: RuntimeArrayHole,
225}
226
227impl StructHole {
228
229 #[inline]
231 pub fn declared(&self) -> usize {
232 self.last_offset + self.hole.declared()
233 }
234
235 #[inline]
237 pub fn resolve(&self, count: usize) -> usize {
238 self.last_offset + self.hole.resolve(count)
239 }
240}
241
242#[derive(Clone, Debug)]
246pub enum TypeSizeHint {
247 Static(usize),
249 RuntimeArray(RuntimeArrayHole),
251 Matrix(MatrixStrideHole),
253 UnknownArrayStride(UnknownArrayStrideHole),
255 Struct(StructHole),
257}
258
259impl TypeSizeHint {
260
261 #[inline]
263 pub fn declared(&self) -> usize {
264 match self {
265 &Self::Static(size) => size,
266 Self::RuntimeArray(hole) => hole.declared(),
267 Self::Matrix(hole) => hole.declared(),
268 Self::UnknownArrayStride(hole) => hole.declared(),
269 Self::Struct(hole) => hole.declared(),
270 }
271 }
272}
273
274#[derive(Clone, Copy, Debug)]
276pub enum ResourceCount {
277 Static(usize),
279 Runtime {
281 declared: usize,
283 },
284}
285
286impl ResourceCount {
287
288 #[inline]
290 pub fn declared(self) -> usize {
291 match self {
292 Self::Static(size) => size,
293 Self::Runtime { declared } => declared,
294 }
295 }
296}
297
298#[derive(Clone, Copy)]
300pub struct Resource<'a> {
301 pub type_id: Id,
303 pub base_type_id: Id,
305 pub variable_id: Id,
307 pub name: Option<CompilerStr<'a>>,
309 pub count: ResourceCount,
313 pub offset: Option<u32>,
317}
318
319pub struct Type<'a> {
321 pub id: Id,
323 pub name: Option<CompilerStr<'a>>,
325 pub size_hint: TypeSizeHint,
329}
330
331#[derive(Clone, Copy)]
332struct Name<'a> {
333 target: Id,
334 member: Option<u32>,
335 name: CompilerStr<'a>,
336}
337
338#[derive(Clone, Copy)]
340pub struct Decorate<'a> {
341 pub target: Id,
343 pub member: Option<u32>,
347 pub decoration: op::Decoration<'a>,
351}
352
353#[derive(Clone, Copy)]
355pub struct Variable {
356 pub result_type: Id,
358 pub id_result: Id,
360 pub storage_class: op::StorageClass,
362}
363
364#[derive(Clone, Copy)]
365struct EntryPoint<'a> {
366 _execution_model: op::ExecutionModel,
367 _name: CompilerStr<'a>,
368 interface: &'a [Id],
369}
370
371pub struct Reflector<'a> {
394 module: Module<'a>,
395 decorates: Vec<Decorate<'a>>,
396 current_entry_point: Option<EntryPoint<'a>>,
397}
398
399impl<'a> Reflector<'a> {
400
401 pub fn new(
408 mut module: Module<'a>,
409 ) -> ReflectResult<Reflector<'a>>
410 {
411 module.parse_full()?;
412 let mut decorates = vec![];
413 for mut stream in module.all_instructions() {
414 if matches!(
415 stream.code(),
416 op::Code::DECORATE | op::Code::DECORATE_ID | op::Code::DECORATE_STRING,
417 ) {
418 decorates.push(Decorate {
419 target: Id::parse_one(&mut stream)?,
420 member: None,
421 decoration: op::Decoration::parse_one(&mut stream)?,
422 });
423 } else if matches!(
424 stream.code(),
425 op::Code::MEMBER_DECORATE | op::Code::MEMBER_DECORATE_STRING,
426 ) {
427 decorates.push(Decorate {
428 target: Id::parse_one(&mut stream)?,
429 member: Some(stream.read()?),
430 decoration: op::Decoration::parse_one(&mut stream)?,
431 });
432 }
433 }
434 Ok(Self {
435 decorates,
436 module,
437 current_entry_point: None,
438 })
439 }
440
441 #[inline]
443 pub fn variables(&self) -> impl Iterator<Item = ReflectResult<Variable>>
444 {
445 self.module
446 .results()
447 .map(|(_, mut stream)| {
448 if stream.code() == op::Code::VARIABLE {
449 let result_type = Id::parse_one(&mut stream)?;
450 let id_result = Id::parse_one(&mut stream)?;
451 let storage_class = op::StorageClass::parse_one(&mut stream)?;
452 return Ok(Some(Variable {
453 result_type,
454 id_result,
455 storage_class,
456 }))
457 }
458 ReflectResult::Ok(None)
459 }).filter_map(|variable| {
460 match variable {
461 Ok(var) => var.map(Ok),
462 Err(err) => Some(Err(err)),
463 }
464 })
465 }
466
467 #[inline]
473 pub fn set_entry_point(
474 &mut self,
475 name: &CStr,
476 execution_model: op::ExecutionModel,
477 ) -> ReflectResult<()>
478 {
479 for mut stream in self.module.all_instructions() {
480 if stream.code() == op::Code::ENTRY_POINT {
481 let exec_model = op::ExecutionModel::parse_one(&mut stream)?;
482 if exec_model == execution_model {
483 stream.advance(1)?;
484 let cname = stream.read_string()?;
485 if name == cname.to_cstr()? {
486 self.current_entry_point = Some(EntryPoint {
487 _execution_model: exec_model,
488 _name: cname,
489 interface: Id::parse_eos(&mut stream)?,
490 });
491 return Ok(())
492 }
493 }
494 }
495 }
496 Err(ReflectError::EntryPointNotFound(name.to_owned(), execution_model))
497 }
498
499 fn names(&self) -> impl Iterator<Item = ReflectResult<Name<'a>>> {
500 self.module
501 .all_instructions()
502 .filter(|stream| matches!(stream.code(), op::Code::NAME | op::Code::MEMBER_NAME))
503 .map(|mut stream| {
504 if stream.code() == op::Code::NAME {
505 Ok(Name {
506 target: Id::parse_one(&mut stream)?,
507 member: None,
508 name: stream.read_string()?,
509 })
510 } else if stream.code() == op::Code::MEMBER_NAME {
511 Ok(Name {
512 target: Id::parse_one(&mut stream)?,
513 member: Some(stream.read()?),
514 name: stream.read_string()?,
515 })
516 } else { unreachable!() }
517 })
518 }
519
520 #[inline]
523 pub fn spec_constants(&self) -> impl Iterator<Item = ReflectResult<SpecConstant>> {
524 self.module
525 .results()
526 .filter_map(|(id, stream)|
527 if matches!(
528 stream.code(),
529 op::Code::SPEC_CONSTANT_TRUE
530 | op::Code::SPEC_CONSTANT_FALSE
531 | op::Code::SPEC_CONSTANT
532 ) {
533 self.decorates
534 .iter()
535 .find_map(|&decorate|
536 if decorate.target == id &&
537 let op::Decoration::SpecId { specialization_constant_id } =
538 decorate.decoration
539 {
540 Some(specialization_constant_id)
541 } else { None }
542 ).map(|constant_id| (constant_id, id, stream))
543 } else { None }
544 )
545 .map(|(constant_id, id, mut stream)| {
546 match stream.code() {
547 op::Code::SPEC_CONSTANT_TRUE | op::Code::SPEC_CONSTANT_FALSE => {
548 Ok(SpecConstant {
549 ty: ScalarType::Bool,
550 constant_id,
551 id,
552 })
553 },
554 op::Code::SPEC_CONSTANT => {
555 let result_type = op::IdResultType::parse_one(&mut stream)?;
556 stream.advance(1)?;
557 let ctx = &mut ParseContext {
558 result_type: Some(result_type)
559 };
560 let value = Literal::parse_one(&self.module, &mut stream, ctx)?;
561 let ty = match value {
562 Literal::F16(_) => ScalarType::Float { width: 2, },
563 Literal::F32(_) => ScalarType::Float { width: 4, },
564 Literal::F64(_) => ScalarType::Float { width: 8, },
565 Literal::I8(_) => ScalarType::Int { width: 1, is_signed: true, },
566 Literal::I16(_) => ScalarType::Int { width: 2, is_signed: true, },
567 Literal::I32(_) => ScalarType::Int { width: 4, is_signed: true, },
568 Literal::I64(_) => ScalarType::Int { width: 8, is_signed: true, },
569 Literal::U8(_) => ScalarType::Int { width: 1, is_signed: false, },
570 Literal::U16(_) => ScalarType::Int { width: 2, is_signed: false, },
571 Literal::U32(_) => ScalarType::Int { width: 4, is_signed: false, },
572 Literal::U64(_) => ScalarType::Int { width: 8, is_signed: false, },
573 };
574 Ok(SpecConstant {
575 ty,
576 constant_id,
577 id,
578 })
579 },
580 _ => unreachable!(),
581 }
582 })
583 }
584
585 pub fn constant(
587 &self,
588 id: Id,
589 ) -> ReflectResult<Constant<'a>>
590 {
591 let mut stream = self.module.get_result(id).ok_or(ReflectError::InvalidConstantId(id))?;
592 match stream.code() {
593 op::Code::CONSTANT_TRUE => {
594 Ok(Constant::Bool(true))
595 },
596 op::Code::CONSTANT_FALSE => {
597 Ok(Constant::Bool(false))
598 },
599 op::Code::CONSTANT => {
600 let result_type = op::IdResultType::parse_one(&mut stream)?;
601 stream.advance(1)?;
602 let ctx = &mut ParseContext {
603 result_type: Some(result_type)
604 };
605 let value = Literal::parse_one(&self.module, &mut stream, ctx)?;
606 Ok(Constant::Constant(value))
607 },
608 op::Code::CONSTANT_COMPOSITE => {
609 let ty = Id::parse_one(&mut stream)?;
610 stream.advance(1)?;
611 Ok(Constant::Composite {
612 ty,
613 constituents: Id::parse_eos(&mut stream)?,
614 })
615 },
616 op::Code::CONSTANT_SAMPLER => {
617 stream.advance(2)?;
618 let addressing_mode = op::SamplerAddressingMode::parse_one(&mut stream)?;
619 let is_normalized = stream.read()? == 1;
620 let filter_mode = op::SamplerFilterMode::parse_one(&mut stream)?;
621 Ok(Constant::Sampler { addressing_mode, is_normalized, filter_mode })
622 },
623 op::Code::CONSTANT_NULL => {
624 let ty = Id::parse_one(&mut stream)?;
625 Ok(Constant::Null { ty })
626 },
627 op::Code::SPEC_CONSTANT_TRUE => {
628 Ok(Constant::SpecBool(true))
629 },
630 op::Code::SPEC_CONSTANT_FALSE => {
631 Ok(Constant::SpecBool(false))
632 },
633 op::Code::SPEC_CONSTANT => {
634 let result_type = op::IdResultType::parse_one(&mut stream)?;
635 stream.advance(1)?;
636 let ctx = &mut ParseContext {
637 result_type: Some(result_type)
638 };
639 let value = Literal::parse_one(&self.module, &mut stream, ctx)?;
640 Ok(Constant::SpecConstant(value))
641 },
642 op::Code::SPEC_CONSTANT_COMPOSITE => {
643 let ty = Id::parse_one(&mut stream)?;
644 stream.advance(1)?;
645 Ok(Constant::SpecConstantComposite {
646 ty,
647 constituents: Id::parse_eos(&mut stream)?,
648 })
649 },
650 op::Code::SPEC_CONSTANT_OP => {
651 let ty = Id::parse_one(&mut stream)?;
652 stream.advance(1)?;
653 let code = op::LiteralSpecConstantOpInteger::parse_one(&mut stream)?;
654 Ok(Constant::SpecConstantOp { ty, opcode: code.code, operands: code.operands })
655 },
656 _ => Err(ReflectError::InvalidConstantId(id))
657 }
658 }
659
660 #[inline]
661 pub fn name(&self, id: Id, struct_member: Option<u32>) -> ReflectResult<Option<CompilerStr<'a>>> {
663 for name in self.names() {
664 let name = name?;
665 if name.target == id &&
666 name.member == struct_member
667 {
668 return Ok(Some(name.name))
669 }
670 }
671 Ok(None)
672 }
673
674 pub fn resources_for_type(&self, ty: ResourceType) -> ReflectResult<
684 impl Iterator<Item = ReflectResult<Resource<'a>>>
685 >
686 {
687 let Some(entry_point) = self.current_entry_point else {
688 return Err(ReflectError::EntryPointNotSet)
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 if self.module.version() >= VERSION_1_4 &&
706 !entry_point.interface.contains(&op_variable.id_result)
707 {
708 return Ok(None)
709 }
710 let mut base_type = op_variable.result_type;
711 let mut count = ResourceCount::Static(1);
712 loop {
713 let mut stream = self.module
714 .get_result(base_type)
715 .ok_or(ReflectError::InvalidTypeId(base_type))?;
716 base_type = match stream.code()
717 {
718 op::Code::TYPE_POINTER => {
719 stream.advance(1)?;
720 let storage_class = op::StorageClass::parse_one(&mut stream)?;
721 if storage_class != resource_class {
722 return Ok(None)
723 }
724 Id::parse_one(&mut stream)?
725 },
726 op::Code::TYPE_ARRAY => {
727 stream.advance(1)?;
728 let id = Id::parse_one(&mut stream)?;
729 let length = match self.constant(Id::parse_one(&mut stream)?)?
730 {
731 Constant::Constant(literal) => {
732 literal.as_usize()
733 .ok_or(ReflectError::NonIntegerLiteral(literal))?
734 },
735 Constant::SpecConstant(literal) => {
736 literal.as_usize()
737 .ok_or(ReflectError::NonIntegerLiteral(literal))?
738 },
739 x => return Err(ReflectError::ExpectedConstantLiteral {
740 found: format!("{x:?}")
741 })
742 };
743 count = match count {
744 ResourceCount::Static(count) =>
745 ResourceCount::Static(count * length),
746 ResourceCount::Runtime { declared } =>
747 ResourceCount::Runtime { declared: declared * length },
748 };
749 id
750 },
751 op::Code::TYPE_RUNTIME_ARRAY => {
752 stream.advance(1)?;
753 count = match count {
754 ResourceCount::Static(count) =>
755 ResourceCount::Runtime { declared: count, },
756 ResourceCount::Runtime { declared } =>
757 ResourceCount::Runtime { declared },
758
759 };
760 Id::parse_one(&mut stream)?
761 },
762 _ => {
763 break
764 },
765 };
766 }
767 let name = self.names().find_map(|op_name| {
768 if let Ok(name) = op_name &&
769 name.target == op_variable.id_result
770 {
771 Some(name.name)
772 } else { None }
773 });
774 if resource_class == op::StorageClass::UNIFORM_CONSTANT {
775 match ty {
776 ResourceType::InputAttachment => {
777 let mut stream = self.module
778 .get_result(base_type)
779 .ok_or(ReflectError::InvalidTypeId(base_type))?;
780 if matches!(stream.code(), op::Code::TYPE_IMAGE) {
781 stream.advance(2)?;
782 let dim = op::Dim::parse_one(&mut stream)?;
783 if dim == op::Dim::SUBPASS_DATA {
784 return Ok(Some(Resource {
785 type_id: op_variable.result_type,
786 base_type_id: base_type,
787 variable_id: op_variable.id_result,
788 name,
789 count,
790 offset: None,
791 }))
792 }
793 }
794 Ok(None)
795 },
796 ResourceType::StorageImage => {
797 let mut stream = self.module
798 .get_result(base_type)
799 .ok_or(ReflectError::InvalidTypeId(base_type))?;
800 if matches!(stream.code(), op::Code::TYPE_IMAGE) {
801 stream.advance(2)?;
802 let dim = op::Dim::parse_one(&mut stream)?;
803 stream.advance(3)?;
804 let sampled = stream.read()?;
805 if sampled == 2 &&
806 matches!(
807 dim,
808 op::Dim::TYPE_1D
809 | op::Dim::TYPE_2D
810 | op::Dim::TYPE_3D
811 | op::Dim::CUBE
812 | op::Dim::RECT
813 )
814 {
815 return Ok(Some(Resource {
816 type_id: op_variable.result_type,
817 base_type_id: base_type,
818 variable_id: op_variable.id_result,
819 name,
820 count,
821 offset: None,
822 }))
823 }
824 }
825 Ok(None)
826 },
827 ResourceType::CombinedImageSampler => {
828 let stream = self.module
829 .get_result(base_type)
830 .ok_or(ReflectError::InvalidTypeId(base_type))?;
831 if matches!(stream.code(), op::Code::TYPE_SAMPLED_IMAGE) {
832 Ok(Some(Resource {
833 type_id: op_variable.result_type,
834 base_type_id: base_type,
835 variable_id: op_variable.id_result,
836 name,
837 count,
838 offset: None,
839 }))
840 } else { Ok(None) }
841 },
842 ResourceType::SeparateImage => {
843 let mut stream = self.module
844 .get_result(base_type)
845 .ok_or(ReflectError::InvalidTypeId(base_type))?;
846 if matches!(
847 stream.code(),
848 op::Code::TYPE_IMAGE,
849 ) {
850 stream.advance(2)?;
851 let dim = op::Dim::parse_one(&mut stream)?;
852 stream.advance(3)?;
853 let sampled = stream.read()?;
854 if sampled != 2 &&
855 matches!(
856 dim,
857 op::Dim::TYPE_1D
858 | op::Dim::TYPE_2D
859 | op::Dim::TYPE_3D
860 | op::Dim::CUBE
861 | op::Dim::RECT
862 )
863 {
864 return Ok(Some(Resource {
865 type_id: op_variable.result_type,
866 base_type_id: base_type,
867 variable_id: op_variable.id_result,
868 name,
869 count,
870 offset: None,
871 }))
872 }
873 }
874 Ok(None)
875 },
876 ResourceType::SeparateSampler => {
877 let stream = self.module
878 .get_result(base_type)
879 .ok_or(ReflectError::InvalidTypeId(base_type))?;
880 if matches!(stream.code(), op::Code::TYPE_SAMPLER) {
881 Ok(Some(Resource {
882 type_id: op_variable.result_type,
883 base_type_id: base_type,
884 variable_id: op_variable.id_result,
885 name,
886 count,
887 offset: None,
888 }))
889 } else { Ok(None) }
890 },
891 ResourceType::UniformTexelBuffer => {
892 let mut stream = self.module
893 .get_result(base_type)
894 .ok_or(ReflectError::InvalidTypeId(base_type))?;
895 if matches!(stream.code(), op::Code::TYPE_IMAGE) {
896 stream.advance(2)?;
897 let dim = op::Dim::parse_one(&mut stream)?;
898 stream.advance(3)?;
899 let sampled = stream.read()?;
900 if sampled != 2 && dim == op::Dim::BUFFER {
901 return Ok(Some(Resource {
902 type_id: op_variable.result_type,
903 base_type_id: base_type,
904 variable_id: op_variable.id_result,
905 name,
906 count,
907 offset: None,
908 }))
909 }
910 }
911 Ok(None)
912 },
913 ResourceType::StorageTexelBuffer => {
914 let mut stream = self.module
915 .get_result(base_type)
916 .ok_or(ReflectError::InvalidTypeId(base_type))?;
917 if matches!(stream.code(), op::Code::TYPE_IMAGE) {
918 stream.advance(2)?;
919 let dim = op::Dim::parse_one(&mut stream)?;
920 stream.advance(3)?;
921 let sampled = stream.read()?;
922 if sampled == 2 && dim == op::Dim::BUFFER {
923 return Ok(Some(Resource {
924 type_id: op_variable.result_type,
925 base_type_id: base_type,
926 variable_id: op_variable.id_result,
927 name,
928 count,
929 offset: None,
930 }))
931 }
932 }
933 Ok(None)
934 },
935 _ => unreachable!()
936 }
937 } else {
938 let offset = match ty {
939 ResourceType::PushConstant => {
940 self.decorations(base_type)
941 .filter_map(|dec| {
942 if let op::Decoration::Offset { byte_offset } = dec.decoration {
943 Some(byte_offset)
944 } else { None }
945 }).min()
946 },
947 _ => None
948 };
949 Ok((op_variable.storage_class == resource_class).then_some(
950 Resource {
951 type_id: op_variable.result_type,
952 base_type_id: base_type,
953 variable_id: op_variable.id_result,
954 name,
955 count,
956 offset,
957 }
958 ))
959 }
960 }).filter_map(|resource| {
961 match resource {
962 Ok(res) => res.map(Ok),
963 Err(err) => Some(Err(err)),
964 }
965 }))
966 }
967
968 #[inline]
972 pub fn decorations(
973 &self,
974 target_id: Id,
975 ) -> impl Iterator<Item = Decorate<'a>>
976 {
977 self.decorates
978 .iter()
979 .filter_map(move |&dec|
980 (dec.target == target_id).then_some(
981 dec
982 )
983 )
984 }
985
986 pub fn type_description(&self, id: Id) -> ReflectResult<Type<'a>> {
994 let mut stream = self.module
995 .get_result(id)
996 .ok_or(ReflectError::InvalidTypeId(id))?;
997 let name = self.names().find_map(|name| {
998 if let Ok(name) = name &&
999 name.target == id
1000 {
1001 Some(name.name)
1002 } else { None }
1003 });
1004 match stream.code() {
1005 op::Code::TYPE_BOOL => Ok(Type {
1006 id,
1007 name,
1008 size_hint: TypeSizeHint::Static(4),
1009 }),
1010 op::Code::TYPE_INT | op::Code::TYPE_FLOAT => {
1011 stream.advance(1)?;
1012 let width = stream.read()?;
1013 Ok(Type {
1014 id,
1015 name,
1016 size_hint: TypeSizeHint::Static(width as usize / 8),
1017 })
1018 },
1019 op::Code::TYPE_VECTOR => {
1020 stream.advance(1)?;
1021 let component_type = Id::parse_one(&mut stream)?;
1022 let component_count = stream.read()?;
1023 let mut stream = self.module
1024 .get_result(component_type)
1025 .ok_or(ReflectError::InvalidTypeId(id))?;
1026 let size = match stream.code() {
1027 op::Code::TYPE_BOOL => 4 * component_count as usize,
1028 op::Code::TYPE_INT | op::Code::TYPE_FLOAT => {
1029 stream.advance(1)?;
1030 let width = stream.read()?;
1031 (width / 8 * component_count) as usize
1032 },
1033 x => return Err(ReflectError::ExpectedScalarType { found: x })
1034 };
1035 Ok(Type {
1036 id,
1037 name,
1038 size_hint: TypeSizeHint::Static(size),
1039 })
1040 }
1041 op::Code::TYPE_MATRIX => {
1042 stream.advance(1)?;
1043 let column_type = Id::parse_one(&mut stream)?;
1044 let column_count = stream.read()?;
1045 let mut stream = self.module
1046 .get_result(column_type)
1047 .ok_or(ReflectError::InvalidTypeId(id))?;
1048 if !matches!(stream.code(), op::Code::TYPE_VECTOR) {
1049 return Err(ReflectError::ExpectedVectorType { found: stream.code() })
1050 }
1051 stream.advance(1)?;
1052 let component_type = Id::parse_one(&mut stream)?;
1053 let component_count = stream.read()?;
1054 let mut stream = self.module
1055 .get_result(component_type)
1056 .ok_or(ReflectError::InvalidTypeId(id))?;
1057 let column_size = match stream.code() {
1058 op::Code::TYPE_BOOL => 4,
1059 op::Code::TYPE_INT | op::Code::TYPE_FLOAT => {
1060 stream.advance(1)?;
1061 stream.read()? as usize / 8
1062 },
1063 x => return Err(ReflectError::ExpectedScalarType {
1064 found: x,
1065 })
1066 };
1067 Ok(Type {
1068 id,
1069 name,
1070 size_hint: TypeSizeHint::Matrix(MatrixStrideHole {
1071 columns: column_count,
1072 rows: component_count,
1073 declared: column_size * (column_count as usize),
1074 }),
1075 })
1076 },
1077 op::Code::TYPE_ARRAY => {
1078 stream.advance(1)?;
1079 let element_type = Id::parse_one(&mut stream)?;
1080 let length = Id::parse_one(&mut stream)?;
1081 let count = {
1082 let constant = self.constant(length)?;
1083 match constant {
1084 Constant::Constant(literal) =>
1085 literal.as_usize().ok_or(ReflectError::NonIntegerLiteral(literal))?,
1086 Constant::SpecConstant(literal) =>
1087 literal.as_usize().ok_or(ReflectError::NonIntegerLiteral(literal))?,
1088 x => return Err(ReflectError::ExpectedConstantLiteral {
1089 found: format!("{x:?}"),
1090 })
1091 }
1092 };
1093 let size_hint = {
1094 if let Some(stride) =
1095 self.decorations(id)
1096 .find_map(|dec| {
1097 let op::Decoration::ArrayStride { array_stride } = dec.decoration else {
1098 return None
1099 };
1100 Some(array_stride as usize)
1101 })
1102 {
1103 TypeSizeHint::Static(count * stride)
1104 } else {
1105 let element_size_hint = self.type_description(element_type)?.size_hint;
1106 match element_size_hint {
1107 TypeSizeHint::Static(size) => TypeSizeHint::Static(count * size),
1108 TypeSizeHint::Matrix(_) | TypeSizeHint::UnknownArrayStride(_)
1109 => TypeSizeHint::UnknownArrayStride(
1110 UnknownArrayStrideHole {
1111 element: Box::new(element_size_hint),
1112 count,
1113 }),
1114 TypeSizeHint::RuntimeArray(_) | TypeSizeHint::Struct(_)
1115 => return Err(ReflectError::InvalidRuntimeArray),
1116 }
1117 }
1118 };
1119 Ok(Type {
1120 id,
1121 name,
1122 size_hint
1123 })
1124 },
1125 op::Code::TYPE_RUNTIME_ARRAY => {
1126 let stride = self
1127 .decorations(id)
1128 .find_map(|dec| {
1129 let op::Decoration::ArrayStride { array_stride } = dec.decoration else {
1130 return None
1131 };
1132 Some(array_stride as usize)
1133 }).ok_or(ReflectError::MissingRequiredDecoration("ArrayStride"))?;
1134 Ok(Type {
1135 id,
1136 name,
1137 size_hint: TypeSizeHint::RuntimeArray(RuntimeArrayHole { stride }),
1138 })
1139 }
1140 op::Code::TYPE_STRUCT => {
1141 stream.advance(1)?;
1142 let member_types = Id::parse_eos(&mut stream)?;
1143 let mut min = u32::MAX;
1144 let (desc, member, offset) = self.decorations(id)
1145 .filter_map(|dec| {
1146 if let Some(member) = dec.member &&
1147 let op::Decoration::Offset { byte_offset } = dec.decoration &&
1148 let Some(&ty) = member_types.get(member as usize) &&
1149 let Ok(desc) = self.type_description(ty)
1150 {
1151 Some((desc, member, byte_offset))
1152 } else { None }
1153 }).max_by_key(|(_, _, offset)| {
1154 min = min.min(*offset);
1155 *offset
1156 }).ok_or(ReflectError::MissingRequiredDecoration("Offset"))?;
1157 let offset = (offset - min) as usize;
1158 let size_hint = match desc.size_hint {
1159 TypeSizeHint::Static(size) => TypeSizeHint::Static(offset + size),
1160 TypeSizeHint::RuntimeArray(hole) => TypeSizeHint::Struct(StructHole {
1161 last_offset: offset,
1162 hole,
1163 }),
1164 TypeSizeHint::Matrix(hole) => {
1165 let mut stride = None;
1166 let mut is_row_major = false;
1167 for dec in self.decorations(id) {
1168 if let Some(dec_member) = dec.member &&
1169 dec_member == member
1170 {
1171 if let op::Decoration::MatrixStride { matrix_stride } = dec.decoration {
1172 stride = Some(matrix_stride)
1173 } else if let op::Decoration::RowMajor = dec.decoration {
1174 is_row_major = true
1175 }
1176 }
1177 }
1178 TypeSizeHint::Static(offset + hole
1179 .resolve(
1180 stride.ok_or(
1181 ReflectError::MissingRequiredDecoration("MatrixStride")
1182 )? as usize,
1183 is_row_major
1184 ))
1185 },
1186 TypeSizeHint::UnknownArrayStride(_) => return Err(
1187 ReflectError::MissingRequiredDecoration("ArrayStride")
1188 ),
1189 TypeSizeHint::Struct(_) => return Err(
1190 ReflectError::InvalidRuntimeArray
1191 ),
1192 };
1193 Ok(Type {
1194 id,
1195 name,
1196 size_hint,
1197 })
1198 },
1199 op::Code::TYPE_POINTER => {
1200 stream.advance(2)?;
1201 self.type_description(Id::parse_one(&mut stream)?)
1202 },
1203 _ => Ok(Type {
1204 id,
1205 name,
1206 size_hint: TypeSizeHint::Static(0),
1207 })
1208 }
1209 }
1210}