1use crate::module::{Constant, Metadata, MetadataId, Module, Type, TypeId, Value, ValueId};
9use crate::{Result, error};
10
11#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct Element {
15 pub id: u32,
17 pub name: String,
19 pub index: u32,
21 pub component_type: u8,
23 pub system_value: u8,
25 pub interpolation: u8,
27 pub row: u32,
29 pub column: u8,
31 pub rows: u32,
33 pub columns: u8,
35 pub placed: bool,
37}
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub struct Range {
41 pub class: u8,
43 pub id: u32,
45 pub lower: u32,
47 pub count: u32,
49 pub extra: u32,
52 pub space: u32,
54 pub element: Option<u32>,
56 pub stride: Option<u32>,
58 pub counter: bool,
60 pub variable: Option<ValueId>,
63}
64
65#[derive(Clone, Copy, Debug)]
67struct Node<'a> {
68 m: &'a Module,
69 id: MetadataId,
70 ops: &'a [Option<MetadataId>],
71 what: &'static str,
73}
74
75impl<'a> Node<'a> {
76 fn new(m: &'a Module, at: usize, id: MetadataId, what: &'static str) -> Result<Self> {
78 match m.metadata.get(id) {
79 Some(Metadata::Node(ops)) => Ok(Node { m, id, ops, what }),
80 _ => Err(error(at, format!("{what}: expected a metadata node"))),
81 }
82 }
83 fn bit(&self) -> usize {
84 self.m.metadata_at.get(self.id).copied().unwrap_or(self.m.at)
85 }
86 fn fail(&self, detail: impl std::fmt::Display) -> crate::Error {
88 error(self.bit(), format!("{}: {detail}", self.what))
89 }
90 fn node(&self, id: Option<MetadataId>, what: &'static str) -> Result<Node<'a>> {
92 let id = id.ok_or_else(|| self.fail(format!("{what} is null")))?;
93 Node::new(self.m, self.bit(), id, what)
94 }
95 fn op(&self, i: usize) -> Result<Option<MetadataId>> {
97 self.ops
98 .get(i)
99 .copied()
100 .ok_or_else(|| self.fail(format!("node has no operand {i}")))
101 }
102 fn maybe(&self, i: usize) -> Option<MetadataId> {
104 self.ops.get(i).copied().flatten()
105 }
106 fn int_of(&self, id: Option<MetadataId>, what: &str) -> Result<i64> {
108 match id.and_then(|i| self.m.metadata.get(i)) {
109 Some(Metadata::Value { value, .. }) => match self.m.values.get(*value) {
110 Some(Value::Constant(Constant::Integer(v))) => Ok(*v),
111 Some(Value::Constant(Constant::Null)) => Ok(0),
113 _ => Err(self.fail(format!("{what}: expected an integer constant"))),
114 },
115 _ => Err(self.fail(format!("{what}: expected an integer"))),
116 }
117 }
118 fn int(&self, i: usize, what: &str) -> Result<i64> {
119 self.int_of(self.op(i)?, what)
120 }
121 fn number_of<T: TryFrom<i64>>(&self, id: Option<MetadataId>, what: &str) -> Result<T> {
123 let v = self.int_of(id, what)?;
124 T::try_from(v).map_err(|_| self.fail(format!("{what}: {v} is out of range")))
125 }
126 fn number<T: TryFrom<i64>>(&self, i: usize, what: &str) -> Result<T> {
127 self.number_of(self.op(i)?, what)
128 }
129 fn string_of(&self, id: Option<MetadataId>, what: &str) -> Result<String> {
131 match id.and_then(|i| self.m.metadata.get(i)) {
132 Some(Metadata::String(s)) => std::str::from_utf8(s).map(str::to_owned).map_err(|e| {
133 self.fail(format!(
134 "{what}: not UTF-8 (valid up to byte {})",
135 e.valid_up_to()
136 ))
137 }),
138 _ => Ok(String::new()),
139 }
140 }
141}
142
143fn named<'a>(m: &'a Module, name: &str) -> Option<(usize, &'a [MetadataId])> {
145 let i = m.named_metadata.iter().position(|(n, _)| n == name)?;
146 let (_, ids) = m.named_metadata.get(i)?;
147 Some((
148 m.named_metadata_at.get(i).copied().unwrap_or(m.at),
149 ids.as_slice(),
150 ))
151}
152
153pub fn library_kind(m: &Module) -> Result<u32> {
156 let entry = the_entry(m)?;
157 let properties = entry
158 .node(entry.maybe(4), "entry point properties")
159 .map_err(|_| entry.fail("a library entry point without properties"))?;
160 for pair in properties.ops.chunks(2) {
161 let [tag, value] = pair else {
162 return Err(properties.fail("a tag without a value"));
163 };
164 if properties.int_of(*tag, "property tag")? == 8 {
165 return properties.number_of(*value, "shader kind");
166 }
167 }
168 Err(properties.fail("a library entry point that states no shader kind"))
169}
170
171fn the_entry(m: &Module) -> Result<Node<'_>> {
173 let (at, entries) =
174 named(m, "dx.entryPoints").ok_or_else(|| error(m.at, "the module has no dx.entryPoints metadata"))?;
175 let mut with_function = Vec::new();
176 for &id in entries {
177 let entry = Node::new(m, at, id, "entry point")?;
178 if entry.op(0)?.is_some() {
179 with_function.push(entry);
180 }
181 }
182 match with_function.as_slice() {
183 [entry] => Ok(*entry),
184 _ => Err(error(
185 at,
186 format!(
187 "dx.entryPoints: expected exactly one entry point with a function, found {}",
188 with_function.len()
189 ),
190 )),
191 }
192}
193
194pub fn entry(m: &Module) -> Result<ValueId> {
196 let entry = the_entry(m)?;
197 match entry.op(0)?.and_then(|i| m.metadata.get(i)) {
198 Some(Metadata::Value { value, .. }) => Ok(*value),
199 _ => Err(entry.fail("operand 0 names no function")),
200 }
201}
202
203pub fn num_threads(m: &Module) -> Result<Option<[u32; 3]>> {
207 let entry = the_entry(m)?;
208 let Some(properties) = entry.maybe(4) else {
209 return Ok(None);
210 };
211 let properties = entry.node(Some(properties), "entry point properties")?;
212 for pair in properties.ops.chunks(2) {
213 let [tag, value] = pair else {
214 return Err(properties.fail("a tag without a value"));
215 };
216 let tag = properties.int_of(*tag, "property tag")?;
217 if tag != 4 && tag != 9 && tag != 10 {
218 continue;
219 }
220 let mut size = properties.node(*value, "numthreads")?;
221 if tag == 9 || tag == 10 {
223 size = size.node(size.op(0)?, "numthreads")?;
224 }
225 return Ok(Some([
226 size.number(0, "numthreads x")?,
227 size.number(1, "numthreads y")?,
228 size.number(2, "numthreads z")?,
229 ]));
230 }
231 Ok(None)
232}
233
234pub fn shader_flags(m: &Module) -> Result<Option<u64>> {
238 let entry = the_entry(m)?;
239 let Some(properties) = entry.maybe(4) else {
240 return Ok(None);
241 };
242 let properties = entry.node(Some(properties), "entry point properties")?;
243 for pair in properties.ops.chunks(2) {
244 let [tag, value] = pair else {
245 return Err(properties.fail("a tag without a value"));
246 };
247 if properties.int_of(*tag, "property tag")? == 0 {
248 let flags = properties.int_of(*value, "shader flags")?;
249 return Ok(Some(u64::from_le_bytes(flags.to_le_bytes())));
250 }
251 }
252 Ok(None)
253}
254
255pub fn usage_masks(m: &Module) -> Result<[Vec<Option<u8>>; 3]> {
261 Ok(element_tags(m)?.map(|list| list.into_iter().map(|tags| tags.usage_mask).collect()))
262}
263
264#[derive(Clone, Debug, Default, PartialEq, Eq)]
268pub struct ElementTags {
269 pub stream: Option<u32>,
271 pub dynamic_index_mask: Option<u8>,
273 pub usage_mask: Option<u8>,
275 pub other: Vec<(i64, i64)>,
277}
278
279pub fn element_tags(m: &Module) -> Result<[Vec<ElementTags>; 3]> {
282 let entry = the_entry(m)?;
283 let Some(signatures) = entry.op(2)? else {
284 return Ok([Vec::new(), Vec::new(), Vec::new()]);
285 };
286 let signatures = entry.node(Some(signatures), "signatures")?;
287 let list = |i: usize| -> Result<Vec<ElementTags>> {
288 let Some(list) = signatures.op(i)? else {
289 return Ok(Vec::new());
290 };
291 let list = signatures.node(Some(list), "signature")?;
292 list.ops
293 .iter()
294 .map(|e| {
295 let e = list.node(*e, "signature element")?;
296 let Some(tail) = e.maybe(10) else {
297 return Ok(ElementTags::default());
298 };
299 let tail = e.node(Some(tail), "element tags")?;
300 let (pairs, rest) = tail.ops.as_chunks::<2>();
301 if !rest.is_empty() {
302 return Err(tail.fail("a tag without its value"));
303 }
304 let mut tags = ElementTags::default();
305 for [tag, value] in pairs {
306 match tail.int_of(*tag, "element tag")? {
307 0 => tags.stream = Some(tail.number_of(*value, "element stream")?),
308 2 => tags.dynamic_index_mask = Some(tail.number_of(*value, "dynamic index mask")?),
309 3 => tags.usage_mask = Some(tail.number_of(*value, "usage mask")?),
310 other => {
311 if let Ok(v) = tail.int_of(*value, "element tag value") {
312 tags.other.push((other, v));
313 }
314 }
315 }
316 }
317 Ok(tags)
318 })
319 .collect()
320 };
321 Ok([list(0)?, list(1)?, list(2)?])
322}
323
324pub fn elements(m: &Module) -> Result<(Vec<Element>, Vec<Element>)> {
326 let [inputs, outputs, _] = signature_lists(m)?;
327 Ok((inputs, outputs))
328}
329
330pub fn patch_elements(m: &Module) -> Result<Vec<Element>> {
332 let [_, _, patch] = signature_lists(m)?;
333 Ok(patch)
334}
335
336#[derive(Clone, Debug, PartialEq)]
339pub struct HullState {
340 pub patch_constant: ValueId,
342 pub input_control_points: u32,
344 pub output_control_points: u32,
346 pub domain: u32,
348 pub partitioning: u32,
350 pub output_primitive: u32,
352 pub max_tess_factor: f32,
354}
355
356fn entry_property(m: &Module, wanted: i64) -> Result<Option<Node<'_>>> {
358 let entry = the_entry(m)?;
359 let Some(properties) = entry.maybe(4) else {
360 return Ok(None);
361 };
362 let properties = entry.node(Some(properties), "entry point properties")?;
363 for pair in properties.ops.chunks(2) {
364 let [tag, value] = pair else {
365 return Err(properties.fail("a tag without a value"));
366 };
367 if properties.int_of(*tag, "property tag")? == wanted {
368 return properties.node(*value, "property").map(Some);
369 }
370 }
371 Ok(None)
372}
373
374#[derive(Clone, Copy, Debug, PartialEq, Eq)]
379pub struct GeometryState {
380 pub input: u32,
382 pub max_vertices: u32,
384 pub streams: u32,
386 pub topology: u32,
388 pub instances: u32,
390}
391
392pub fn geometry_state(m: &Module) -> Result<GeometryState> {
394 let state = entry_property(m, 1)?.ok_or_else(|| {
395 the_entry(m).map_or_else(
396 |e| e,
397 |e| e.fail("a geometry program without its state (properties tag 1)"),
398 )
399 })?;
400 Ok(GeometryState {
401 input: state.number(0, "geometry input primitive")?,
402 max_vertices: state.number(1, "geometry maximum vertices")?,
403 streams: state.number(2, "geometry streams")?,
404 topology: state.number(3, "geometry topology")?,
405 instances: state.number(4, "geometry instances")?,
406 })
407}
408
409#[derive(Clone, Copy, Debug, PartialEq, Eq)]
413pub struct MeshState {
414 pub max_vertices: u32,
416 pub max_primitives: u32,
418 pub topology: u32,
420 pub payload: u32,
422}
423
424pub fn mesh_state(m: &Module) -> Result<MeshState> {
426 let state = entry_property(m, 9)?.ok_or_else(|| {
427 the_entry(m).map_or_else(
428 |e| e,
429 |e| e.fail("a mesh program without its state (properties tag 9)"),
430 )
431 })?;
432 let field = |k: usize, what: &str| -> Result<u32> {
434 match state.op(k)? {
435 None => Ok(0),
436 some => state.number_of(some, what),
437 }
438 };
439 Ok(MeshState {
440 max_vertices: field(1, "mesh maximum vertices")?,
441 max_primitives: field(2, "mesh maximum primitives")?,
442 topology: field(3, "mesh topology")?,
443 payload: field(4, "mesh payload size")?,
444 })
445}
446
447pub fn hull_state(m: &Module) -> Result<HullState> {
449 let state = entry_property(m, 3)?.ok_or_else(|| {
450 the_entry(m).map_or_else(
451 |e| e,
452 |e| e.fail("a hull program without its patch state (properties tag 3)"),
453 )
454 })?;
455 let small = |k: usize, what: &str| -> Result<u32> { state.number_of(state.maybe(k), what) };
456 let patch_constant = match state.maybe(0).and_then(|i| m.metadata.get(i)) {
457 Some(Metadata::Value { value, .. }) => *value,
458 _ => return Err(state.fail("a hull program without its patch-constant function")),
459 };
460 let max = match state.maybe(6).and_then(|i| m.metadata.get(i)) {
461 Some(Metadata::Value { value, .. }) => match m.values.get(*value) {
462 Some(Value::Constant(Constant::Float(bits))) => {
463 let bits = u32::try_from(*bits)
464 .map_err(|_| state.fail("the maximum tessellation factor is wider than a float"))?;
465 f32::from_bits(bits)
466 }
467 _ => return Err(state.fail("the maximum tessellation factor is not a float")),
468 },
469 _ => return Err(state.fail("hull state without a maximum tessellation factor")),
470 };
471 Ok(HullState {
472 patch_constant,
473 input_control_points: small(1, "hull input control points")?,
474 output_control_points: small(2, "hull output control points")?,
475 domain: small(3, "hull domain")?,
476 partitioning: small(4, "hull partitioning")?,
477 output_primitive: small(5, "hull output primitive")?,
478 max_tess_factor: max,
479 })
480}
481
482pub fn domain_state(m: &Module) -> Result<(u32, u32)> {
484 let state = entry_property(m, 2)?.ok_or_else(|| {
485 the_entry(m).map_or_else(
486 |e| e,
487 |e| e.fail("a domain program without its patch state (properties tag 2)"),
488 )
489 })?;
490 Ok((
491 state.number_of(state.maybe(0), "domain")?,
492 state.number_of(state.maybe(1), "domain input control points")?,
493 ))
494}
495
496#[derive(Clone, Copy, Debug, PartialEq, Eq)]
501pub struct EntryProperty {
502 pub tag: i64,
504 pub value: Option<MetadataId>,
506}
507
508pub fn entry_properties(m: &Module) -> Result<Vec<EntryProperty>> {
510 let entry = the_entry(m)?;
511 properties_of(entry)
512}
513
514fn properties_of(entry: Node<'_>) -> Result<Vec<EntryProperty>> {
515 let Some(properties) = entry.maybe(4) else {
516 return Ok(Vec::new());
517 };
518 let properties = entry.node(Some(properties), "entry point properties")?;
519 let (pairs, rest) = properties.ops.as_chunks::<2>();
520 if !rest.is_empty() {
521 return Err(properties.fail("a tag without a value"));
522 }
523 pairs
524 .iter()
525 .map(|[tag, value]| {
526 Ok(EntryProperty {
527 tag: properties.int_of(*tag, "property tag")?,
528 value: *value,
529 })
530 })
531 .collect()
532}
533
534fn property_integer(m: &Module, wanted: i64) -> Result<Option<u32>> {
537 let entry = the_entry(m)?;
538 for property in properties_of(entry)? {
539 if property.tag != wanted {
540 continue;
541 }
542 let record = entry.node(property.value, "property").ok();
543 return match record {
544 Some(node) => node.number(0, "property value").map(Some),
545 None => entry.number_of(property.value, "property value").map(Some),
546 };
547 }
548 Ok(None)
549}
550
551pub fn ray_payload_size(m: &Module) -> Result<Option<u32>> {
554 property_integer(m, 6)
555}
556
557pub fn ray_attribute_size(m: &Module) -> Result<Option<u32>> {
559 property_integer(m, 7)
560}
561
562pub fn auto_binding_space(m: &Module) -> Result<Option<u32>> {
566 property_integer(m, 5)
567}
568
569pub fn library_binding_space(m: &Module) -> Result<Option<u32>> {
572 let (at, entries) =
573 named(m, "dx.entryPoints").ok_or_else(|| error(m.at, "the module has no dx.entryPoints metadata"))?;
574 for &id in entries {
575 let entry = Node::new(m, at, id, "entry point")?;
576 if entry.op(0)?.is_some() {
577 continue;
578 }
579 for property in properties_of(entry)? {
580 if property.tag == 5 {
581 let node = entry.node(property.value, "auto binding space")?;
582 return node.number(0, "auto binding space").map(Some);
583 }
584 }
585 }
586 Ok(None)
587}
588
589pub fn wave_size(m: &Module) -> Result<Option<Vec<u32>>> {
592 let Some(node) = entry_property(m, 11)? else {
593 return Ok(None);
594 };
595 node.ops
596 .iter()
597 .map(|id| node.number_of(*id, "wave size"))
598 .collect::<Result<Vec<u32>>>()
599 .map(Some)
600}
601
602pub fn amplification_payload_size(m: &Module) -> Result<Option<u32>> {
605 let Some(state) = entry_property(m, 10)? else {
606 return Ok(None);
607 };
608 state.number(1, "amplification payload size").map(Some)
609}
610
611pub fn fp32_denorm_mode(m: &Module) -> Result<Option<String>> {
615 let function = entry(m)?;
616 Ok(m.string_attribute(function, "fp32-denorm-mode")
617 .map(str::to_owned))
618}
619
620fn signature_lists(m: &Module) -> Result<[Vec<Element>; 3]> {
621 let entry = the_entry(m)?;
622 let Some(signatures) = entry.op(2)? else {
623 return Ok([Vec::new(), Vec::new(), Vec::new()]);
624 };
625 let signatures = entry.node(Some(signatures), "signatures")?;
626 let list = |i: usize| -> Result<Vec<Element>> {
627 let Some(list) = signatures.op(i)? else {
628 return Ok(Vec::new());
629 };
630 let list = signatures.node(Some(list), "signature")?;
631 list.ops
632 .iter()
633 .map(|e| {
634 let e = list.node(*e, "signature element")?;
635 let row = e.int(8, "row")?;
636 let element = Element {
637 id: e.number(0, "element id")?,
638 name: e.string_of(e.op(1)?, "element name")?,
639 index: match e.op(4)? {
640 Some(list) => match e.node(Some(list), "semantic indices")?.ops.first() {
641 Some(first) => e.number_of(*first, "semantic index")?,
642 None => 0,
643 },
644 None => 0,
645 },
646 component_type: e.number(2, "component type")?,
647 system_value: e.number(3, "system value")?,
648 interpolation: e.number(5, "interpolation")?,
649 rows: e.number(6, "rows")?,
650 columns: e.number(7, "columns")?,
651 placed: row >= 0,
652 row: u32::try_from(row.max(0))
653 .map_err(|_| e.fail(format!("row {row} is out of range")))?,
654 column: u8::try_from(e.int(9, "column")?.max(0))
655 .map_err(|_| e.fail("column is out of range"))?,
656 };
657 if element.rows == 0
659 || element.columns == 0
660 || u32::from(element.column)
661 .checked_add(u32::from(element.columns))
662 .is_none_or(|end| end > 4)
663 || element.row.checked_add(element.rows).is_none_or(|end| end > 32)
664 {
665 return Err(e.fail("the element lies outside the 32 four-lane registers"));
666 }
667 Ok(element)
668 })
669 .collect()
670 };
671 Ok([list(0)?, list(1)?, list(2)?])
672}
673
674fn tags(resource: Node<'_>, list: Option<MetadataId>) -> Result<(Option<u32>, Option<u32>)> {
676 let Some(list) = list else {
677 return Ok((None, None));
678 };
679 let ops = resource.node(Some(list), "resource tags")?;
680 let (pairs, rest) = ops.ops.as_chunks::<2>();
681 if !rest.is_empty() {
682 return Err(ops.fail("a tag without its value"));
683 }
684 let (mut element, mut stride) = (None, None);
685 for [tag, value] in pairs {
686 let value = ops.number_of(*value, "tag value")?;
687 match ops.int_of(*tag, "tag")? {
688 0 => element = Some(value),
689 1 => stride = Some(value),
690 _ => {}
691 }
692 }
693 Ok((element, stride))
694}
695
696fn variable_of(m: &Module, id: Option<MetadataId>) -> Option<ValueId> {
698 let Some(Metadata::Value { value, .. }) = id.and_then(|i| m.metadata.get(i)) else {
699 return None;
700 };
701 match m.values.get(*value)? {
702 Value::Constant(Constant::Cast { value, .. }) => Some(*value),
703 Value::Global { .. } => Some(*value),
704 _ => None,
705 }
706}
707
708pub fn ranges(m: &Module) -> Result<Vec<(Range, String, Option<TypeId>)>> {
711 let Some((at, [resources])) = named(m, "dx.resources") else {
712 return Ok(Vec::new());
713 };
714 let lists = Node::new(m, at, *resources, "dx.resources")?;
715 let mut out = Vec::new();
716 for (class, list) in lists.ops.iter().enumerate() {
717 let Some(list) = list else { continue };
718 let class =
719 u8::try_from(class).map_err(|_| lists.fail(format!("resource class {class} is out of range")))?;
720 let list = lists.node(Some(*list), "resource list")?;
721 for r in list.ops {
722 let r = list.node(*r, "resource")?;
723 let ty = match r.op(1)?.and_then(|i| m.metadata.get(i)) {
725 Some(Metadata::Value { ty, .. }) => match m.types.get(*ty) {
726 Some(Type::Pointer(t, _)) => Some(*t),
727 _ => None,
728 },
729 _ => None,
730 };
731 let extra = r.number(6, "resource field")?;
732 let (element, stride) = match class {
733 0 => tags(r, r.maybe(8))?,
734 1 => tags(r, r.maybe(10))?,
735 _ => (None, None),
736 };
737 out.push((
738 Range {
739 class,
740 id: r.number(0, "resource id")?,
741 lower: r.number(4, "lower bound")?,
742 count: {
744 let v = r.int(5, "range size")?;
745 u32::try_from(v)
746 .or_else(|_| i32::try_from(v).map(|v| u32::from_ne_bytes(v.to_ne_bytes())))
747 .map_err(|_| r.fail(format!("range size {v} is out of range")))?
748 },
749 extra,
750 space: r.number(3, "register space")?,
751 element,
752 stride,
753 counter: class == 1 && r.maybe(8).is_some() && r.int(8, "counter")? != 0,
755 variable: variable_of(m, r.op(1)?),
756 },
757 r.string_of(r.op(2)?, "resource name")?,
758 ty,
759 ));
760 }
761 }
762 Ok(out)
763}
764
765#[derive(Clone, Debug, Default, PartialEq, Eq)]
767pub struct ResourceExtras {
768 pub class: u8,
770 pub id: u32,
772 pub sample_count: Option<u32>,
774 pub globally_coherent: bool,
776 pub rasterizer_ordered: bool,
778 pub sampler_feedback: Option<u32>,
780 pub atomic64: bool,
782 pub tags: Vec<(i64, i64)>,
785}
786
787pub fn resource_extras(m: &Module) -> Result<Vec<ResourceExtras>> {
790 let Some((at, [resources])) = named(m, "dx.resources") else {
791 return Ok(Vec::new());
792 };
793 let lists = Node::new(m, at, *resources, "dx.resources")?;
794 let mut out = Vec::new();
795 for (class, list) in lists.ops.iter().enumerate() {
796 let Some(list) = list else { continue };
797 let class =
798 u8::try_from(class).map_err(|_| lists.fail(format!("resource class {class} is out of range")))?;
799 let list = lists.node(Some(*list), "resource list")?;
800 for r in list.ops {
801 let r = list.node(*r, "resource")?;
802 let tag_operand = match class {
804 0 => 8,
805 1 => 10,
806 _ => 7,
807 };
808 let mut tags = Vec::new();
809 if let Some(tag_list) = r.maybe(tag_operand) {
810 let tag_list = r.node(Some(tag_list), "resource tags")?;
811 let (pairs, rest) = tag_list.ops.as_chunks::<2>();
812 if !rest.is_empty() {
813 return Err(tag_list.fail("a tag without its value"));
814 }
815 for [tag, value] in pairs {
816 tags.push((
817 tag_list.int_of(*tag, "tag")?,
818 tag_list.int_of(*value, "tag value")?,
819 ));
820 }
821 }
822 let tag_value = |wanted: i64| tags.iter().find(|(t, _)| *t == wanted).map(|(_, v)| *v);
823 out.push(ResourceExtras {
824 class,
825 id: r.number(0, "resource id")?,
826 sample_count: match (class, r.maybe(7)) {
827 (0, Some(_)) => Some(r.number(7, "sample count")?),
828 _ => None,
829 },
830 globally_coherent: class == 1 && r.maybe(7).is_some() && r.int(7, "globally coherent")? != 0,
831 rasterizer_ordered: class == 1
832 && r.maybe(9).is_some()
833 && r.int(9, "rasterizer ordered")? != 0,
834 sampler_feedback: match tag_value(2) {
835 Some(v) => Some(
836 u32::try_from(v)
837 .map_err(|_| r.fail(format!("sampler feedback kind {v} is out of range")))?,
838 ),
839 None => None,
840 },
841 atomic64: tag_value(3).is_some_and(|v| v != 0),
842 tags,
843 });
844 }
845 }
846 Ok(out)
847}
848
849#[derive(Clone, Debug, PartialEq, Eq)]
851pub struct FieldAnnotation {
852 pub name: String,
854 pub offset: u32,
856 pub component: Option<u32>,
858 pub matrix: Option<(u32, u32, u32)>,
861}
862
863pub fn fields(m: &Module, t: TypeId) -> Result<Vec<FieldAnnotation>> {
866 let Some((at, annotations)) = named(m, "dx.typeAnnotations") else {
867 return Ok(Vec::new());
868 };
869 for &a in annotations {
870 let a = Node::new(m, at, a, "type annotations")?;
871 if a.int(0, "annotation kind")? != 0 {
873 continue;
874 }
875 let (pairs, rest) = a.ops.get(1..).unwrap_or_default().as_chunks::<2>();
876 if !rest.is_empty() {
877 return Err(a.fail("a struct type without its annotation"));
878 }
879 for [struct_type, annotation] in pairs {
880 let ty = match struct_type.and_then(|i| m.metadata.get(i)) {
881 Some(Metadata::Value { ty, .. }) => *ty,
882 _ => continue,
883 };
884 if ty != t {
885 continue;
886 }
887 let annotation = a.node(*annotation, "struct annotation")?;
888 let fields = annotation
890 .ops
891 .get(1..)
892 .ok_or_else(|| annotation.fail("a struct annotation without its first operand"))?;
893 return fields
894 .iter()
895 .map(|f| {
896 let f = annotation.node(*f, "field annotation")?;
897 let (mut name, mut offset, mut component, mut matrix) = (String::new(), 0, None, None);
898 let (tags, rest) = f.ops.as_chunks::<2>();
899 if !rest.is_empty() {
900 return Err(f.fail("a tag without its value"));
901 }
902 for [tag, value] in tags {
903 match f.int_of(*tag, "field tag")? {
904 6 => name = f.string_of(*value, "field name")?,
905 3 => offset = f.number_of(*value, "field offset")?,
906 2 => {
907 let ops = f.node(*value, "matrix annotation")?;
908 matrix = Some((
909 ops.number(0, "matrix rows")?,
910 ops.number(1, "matrix columns")?,
911 ops.number(2, "matrix orientation")?,
912 ));
913 }
914 7 => component = Some(f.number_of(*value, "field component type")?),
915 _ => {}
916 }
917 }
918 Ok(FieldAnnotation {
919 name,
920 offset,
921 component,
922 matrix,
923 })
924 })
925 .collect();
926 }
927 }
928 Ok(Vec::new())
929}
930
931#[derive(Clone, Debug, PartialEq, Eq)]
933pub struct SignatureElement {
934 pub name: String,
936 pub index: u32,
938 pub system_value: u32,
940 pub component_type: u32,
942 pub register: u32,
944 pub mask: u8,
946 pub read_write_mask: u8,
948 pub bit: usize,
950}
951
952pub fn signature(part: &[u8]) -> Result<Vec<SignatureElement>> {
956 let bit = |byte: usize| byte.saturating_mul(8);
957 let v = primase_foundations::bytes::View::new(part);
958 let word = |at: usize| -> Result<u32> {
959 v.array::<4>(at)
960 .map(u32::from_le_bytes)
961 .map_err(|e| error(bit(at), e.to_string()))
962 };
963 let field = |record: usize, delta: usize| -> Result<u32> {
965 word(
966 record
967 .checked_add(delta)
968 .ok_or_else(|| error(bit(record), "signature element offset"))?,
969 )
970 };
971 let n = word(0)? as usize;
972 let first = word(4)? as usize;
973 let table = n
975 .checked_mul(32)
976 .and_then(|size| first.checked_add(size))
977 .filter(|&end| end <= part.len())
978 .ok_or_else(|| {
979 error(
980 0,
981 format!(
982 "{n} records of 32 bytes from byte {first} do not fit the part's {} bytes",
983 part.len()
984 ),
985 )
986 })?;
987 let mut out = Vec::with_capacity(n);
988 for i in 0..n {
989 let e = i
990 .checked_mul(32)
991 .and_then(|o| o.checked_add(first))
992 .filter(|&e| e < table)
993 .ok_or_else(|| error(32, "signature element offset"))?;
994 let name_at = field(e, 4)? as usize;
995 let tail = part
996 .get(name_at..)
997 .ok_or_else(|| error(bit(name_at), "signature name offset"))?;
998 let end = tail
999 .iter()
1000 .position(|&b| b == 0)
1001 .filter(|&n| n <= 256)
1002 .ok_or_else(|| error(bit(name_at), "signature name"))?;
1003 let name = tail
1004 .get(..end)
1005 .and_then(|name| std::str::from_utf8(name).ok())
1006 .ok_or_else(|| error(bit(name_at), "signature name is not UTF-8"))?;
1007 let [mask, read_write_mask] = v
1008 .array::<2>(
1009 e.checked_add(24)
1010 .ok_or_else(|| error(bit(e), "signature element offset"))?,
1011 )
1012 .map_err(|x| error(bit(e), x.to_string()))?;
1013 out.push(SignatureElement {
1014 name: name.to_owned(),
1015 index: field(e, 8)?,
1016 system_value: field(e, 12)?,
1017 component_type: field(e, 16)?,
1018 register: field(e, 20)?,
1019 mask,
1020 read_write_mask,
1021 bit: bit(e),
1022 });
1023 }
1024 Ok(out)
1025}