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).and_then(Metadata::operands) {
79 Some(ops) => Ok(Node { m, id, ops, what }),
80 None => 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 flags_of(the_entry(m)?)
239}
240
241pub fn module_shader_flags(m: &Module) -> Result<Option<u64>> {
244 let (at, entries) =
245 named(m, "dx.entryPoints").ok_or_else(|| error(m.at, "the module has no dx.entryPoints metadata"))?;
246 let first = entries
247 .first()
248 .ok_or_else(|| error(at, "dx.entryPoints lists no entry point"))?;
249 flags_of(Node::new(m, at, *first, "entry point")?)
250}
251
252fn flags_of(entry: Node<'_>) -> Result<Option<u64>> {
254 let Some(properties) = entry.maybe(4) else {
255 return Ok(None);
256 };
257 let properties = entry.node(Some(properties), "entry point properties")?;
258 for pair in properties.ops.chunks(2) {
259 let [tag, value] = pair else {
260 return Err(properties.fail("a tag without a value"));
261 };
262 if properties.int_of(*tag, "property tag")? == 0 {
263 let flags = properties.int_of(*value, "shader flags")?;
264 return Ok(Some(u64::from_le_bytes(flags.to_le_bytes())));
265 }
266 }
267 Ok(None)
268}
269
270pub fn usage_masks(m: &Module) -> Result<[Vec<Option<u8>>; 3]> {
276 Ok(element_tags(m)?.map(|list| list.into_iter().map(|tags| tags.usage_mask).collect()))
277}
278
279#[derive(Clone, Debug, Default, PartialEq, Eq)]
283pub struct ElementTags {
284 pub stream: Option<u32>,
286 pub dynamic_index_mask: Option<u8>,
288 pub usage_mask: Option<u8>,
290 pub other: Vec<(i64, i64)>,
292}
293
294pub fn element_tags(m: &Module) -> Result<[Vec<ElementTags>; 3]> {
297 let entry = the_entry(m)?;
298 let Some(signatures) = entry.op(2)? else {
299 return Ok([Vec::new(), Vec::new(), Vec::new()]);
300 };
301 let signatures = entry.node(Some(signatures), "signatures")?;
302 let list = |i: usize| -> Result<Vec<ElementTags>> {
303 let Some(list) = signatures.op(i)? else {
304 return Ok(Vec::new());
305 };
306 let list = signatures.node(Some(list), "signature")?;
307 list.ops
308 .iter()
309 .map(|e| {
310 let e = list.node(*e, "signature element")?;
311 let Some(tail) = e.maybe(10) else {
312 return Ok(ElementTags::default());
313 };
314 let tail = e.node(Some(tail), "element tags")?;
315 let (pairs, rest) = tail.ops.as_chunks::<2>();
316 if !rest.is_empty() {
317 return Err(tail.fail("a tag without its value"));
318 }
319 let mut tags = ElementTags::default();
320 for [tag, value] in pairs {
321 match tail.int_of(*tag, "element tag")? {
322 0 => tags.stream = Some(tail.number_of(*value, "element stream")?),
323 2 => tags.dynamic_index_mask = Some(tail.number_of(*value, "dynamic index mask")?),
324 3 => tags.usage_mask = Some(tail.number_of(*value, "usage mask")?),
325 other => {
326 if let Ok(v) = tail.int_of(*value, "element tag value") {
327 tags.other.push((other, v));
328 }
329 }
330 }
331 }
332 Ok(tags)
333 })
334 .collect()
335 };
336 Ok([list(0)?, list(1)?, list(2)?])
337}
338
339pub fn elements(m: &Module) -> Result<(Vec<Element>, Vec<Element>)> {
341 let [inputs, outputs, _] = signature_lists(m)?;
342 Ok((inputs, outputs))
343}
344
345pub fn patch_elements(m: &Module) -> Result<Vec<Element>> {
347 let [_, _, patch] = signature_lists(m)?;
348 Ok(patch)
349}
350
351#[derive(Clone, Debug, PartialEq)]
354pub struct HullState {
355 pub patch_constant: ValueId,
357 pub input_control_points: u32,
359 pub output_control_points: u32,
361 pub domain: u32,
363 pub partitioning: u32,
365 pub output_primitive: u32,
367 pub max_tess_factor: f32,
369}
370
371fn entry_property(m: &Module, wanted: i64) -> Result<Option<Node<'_>>> {
373 let entry = the_entry(m)?;
374 let Some(properties) = entry.maybe(4) else {
375 return Ok(None);
376 };
377 let properties = entry.node(Some(properties), "entry point properties")?;
378 for pair in properties.ops.chunks(2) {
379 let [tag, value] = pair else {
380 return Err(properties.fail("a tag without a value"));
381 };
382 if properties.int_of(*tag, "property tag")? == wanted {
383 return properties.node(*value, "property").map(Some);
384 }
385 }
386 Ok(None)
387}
388
389#[derive(Clone, Copy, Debug, PartialEq, Eq)]
394pub struct GeometryState {
395 pub input: u32,
397 pub max_vertices: u32,
399 pub streams: u32,
401 pub topology: u32,
403 pub instances: u32,
405}
406
407pub fn geometry_state(m: &Module) -> Result<GeometryState> {
409 let state = entry_property(m, 1)?.ok_or_else(|| {
410 the_entry(m).map_or_else(
411 |e| e,
412 |e| e.fail("a geometry program without its state (properties tag 1)"),
413 )
414 })?;
415 Ok(GeometryState {
416 input: state.number(0, "geometry input primitive")?,
417 max_vertices: state.number(1, "geometry maximum vertices")?,
418 streams: state.number(2, "geometry streams")?,
419 topology: state.number(3, "geometry topology")?,
420 instances: state.number(4, "geometry instances")?,
421 })
422}
423
424#[derive(Clone, Copy, Debug, PartialEq, Eq)]
428pub struct MeshState {
429 pub max_vertices: u32,
431 pub max_primitives: u32,
433 pub topology: u32,
435 pub payload: u32,
437}
438
439pub fn mesh_state(m: &Module) -> Result<MeshState> {
441 let state = entry_property(m, 9)?.ok_or_else(|| {
442 the_entry(m).map_or_else(
443 |e| e,
444 |e| e.fail("a mesh program without its state (properties tag 9)"),
445 )
446 })?;
447 let field = |k: usize, what: &str| -> Result<u32> {
449 match state.op(k)? {
450 None => Ok(0),
451 some => state.number_of(some, what),
452 }
453 };
454 Ok(MeshState {
455 max_vertices: field(1, "mesh maximum vertices")?,
456 max_primitives: field(2, "mesh maximum primitives")?,
457 topology: field(3, "mesh topology")?,
458 payload: field(4, "mesh payload size")?,
459 })
460}
461
462pub fn hull_state(m: &Module) -> Result<HullState> {
464 let state = entry_property(m, 3)?.ok_or_else(|| {
465 the_entry(m).map_or_else(
466 |e| e,
467 |e| e.fail("a hull program without its patch state (properties tag 3)"),
468 )
469 })?;
470 let small = |k: usize, what: &str| -> Result<u32> { state.number_of(state.maybe(k), what) };
471 let patch_constant = match state.maybe(0).and_then(|i| m.metadata.get(i)) {
472 Some(Metadata::Value { value, .. }) => *value,
473 _ => return Err(state.fail("a hull program without its patch-constant function")),
474 };
475 let max = match state.maybe(6).and_then(|i| m.metadata.get(i)) {
476 Some(Metadata::Value { value, .. }) => match m.values.get(*value) {
477 Some(Value::Constant(Constant::Float(bits))) => {
478 let bits = u32::try_from(*bits)
479 .map_err(|_| state.fail("the maximum tessellation factor is wider than a float"))?;
480 f32::from_bits(bits)
481 }
482 _ => return Err(state.fail("the maximum tessellation factor is not a float")),
483 },
484 _ => return Err(state.fail("hull state without a maximum tessellation factor")),
485 };
486 Ok(HullState {
487 patch_constant,
488 input_control_points: small(1, "hull input control points")?,
489 output_control_points: small(2, "hull output control points")?,
490 domain: small(3, "hull domain")?,
491 partitioning: small(4, "hull partitioning")?,
492 output_primitive: small(5, "hull output primitive")?,
493 max_tess_factor: max,
494 })
495}
496
497pub fn domain_state(m: &Module) -> Result<(u32, u32)> {
499 let state = entry_property(m, 2)?.ok_or_else(|| {
500 the_entry(m).map_or_else(
501 |e| e,
502 |e| e.fail("a domain program without its patch state (properties tag 2)"),
503 )
504 })?;
505 Ok((
506 state.number_of(state.maybe(0), "domain")?,
507 state.number_of(state.maybe(1), "domain input control points")?,
508 ))
509}
510
511#[derive(Clone, Copy, Debug, PartialEq, Eq)]
516pub struct EntryProperty {
517 pub tag: i64,
519 pub value: Option<MetadataId>,
521}
522
523pub fn entry_properties(m: &Module) -> Result<Vec<EntryProperty>> {
525 let entry = the_entry(m)?;
526 properties_of(entry)
527}
528
529fn properties_of(entry: Node<'_>) -> Result<Vec<EntryProperty>> {
530 let Some(properties) = entry.maybe(4) else {
531 return Ok(Vec::new());
532 };
533 let properties = entry.node(Some(properties), "entry point properties")?;
534 let (pairs, rest) = properties.ops.as_chunks::<2>();
535 if !rest.is_empty() {
536 return Err(properties.fail("a tag without a value"));
537 }
538 pairs
539 .iter()
540 .map(|[tag, value]| {
541 Ok(EntryProperty {
542 tag: properties.int_of(*tag, "property tag")?,
543 value: *value,
544 })
545 })
546 .collect()
547}
548
549fn property_integer(m: &Module, wanted: i64) -> Result<Option<u32>> {
552 let entry = the_entry(m)?;
553 for property in properties_of(entry)? {
554 if property.tag != wanted {
555 continue;
556 }
557 let record = entry.node(property.value, "property").ok();
558 return match record {
559 Some(node) => node.number(0, "property value").map(Some),
560 None => entry.number_of(property.value, "property value").map(Some),
561 };
562 }
563 Ok(None)
564}
565
566pub fn ray_payload_size(m: &Module) -> Result<Option<u32>> {
569 property_integer(m, 6)
570}
571
572pub fn ray_attribute_size(m: &Module) -> Result<Option<u32>> {
574 property_integer(m, 7)
575}
576
577pub fn auto_binding_space(m: &Module) -> Result<Option<u32>> {
581 property_integer(m, 5)
582}
583
584pub fn library_binding_space(m: &Module) -> Result<Option<u32>> {
587 let (at, entries) =
588 named(m, "dx.entryPoints").ok_or_else(|| error(m.at, "the module has no dx.entryPoints metadata"))?;
589 for &id in entries {
590 let entry = Node::new(m, at, id, "entry point")?;
591 if entry.op(0)?.is_some() {
592 continue;
593 }
594 for property in properties_of(entry)? {
595 if property.tag == 5 {
596 let node = entry.node(property.value, "auto binding space")?;
597 return node.number(0, "auto binding space").map(Some);
598 }
599 }
600 }
601 Ok(None)
602}
603
604pub fn wave_size(m: &Module) -> Result<Option<Vec<u32>>> {
607 let Some(node) = entry_property(m, 11)? else {
608 return Ok(None);
609 };
610 node.ops
611 .iter()
612 .map(|id| node.number_of(*id, "wave size"))
613 .collect::<Result<Vec<u32>>>()
614 .map(Some)
615}
616
617pub fn amplification_payload_size(m: &Module) -> Result<Option<u32>> {
620 let Some(state) = entry_property(m, 10)? else {
621 return Ok(None);
622 };
623 state.number(1, "amplification payload size").map(Some)
624}
625
626pub fn fp32_denorm_mode(m: &Module) -> Result<Option<String>> {
630 let function = entry(m)?;
631 Ok(m.string_attribute(function, "fp32-denorm-mode")
632 .map(str::to_owned))
633}
634
635fn signature_lists(m: &Module) -> Result<[Vec<Element>; 3]> {
636 let entry = the_entry(m)?;
637 let Some(signatures) = entry.op(2)? else {
638 return Ok([Vec::new(), Vec::new(), Vec::new()]);
639 };
640 let signatures = entry.node(Some(signatures), "signatures")?;
641 let list = |i: usize| -> Result<Vec<Element>> {
642 let Some(list) = signatures.op(i)? else {
643 return Ok(Vec::new());
644 };
645 let list = signatures.node(Some(list), "signature")?;
646 list.ops
647 .iter()
648 .map(|e| {
649 let e = list.node(*e, "signature element")?;
650 let row = e.int(8, "row")?;
651 let element = Element {
652 id: e.number(0, "element id")?,
653 name: e.string_of(e.op(1)?, "element name")?,
654 index: match e.op(4)? {
655 Some(list) => match e.node(Some(list), "semantic indices")?.ops.first() {
656 Some(first) => e.number_of(*first, "semantic index")?,
657 None => 0,
658 },
659 None => 0,
660 },
661 component_type: e.number(2, "component type")?,
662 system_value: e.number(3, "system value")?,
663 interpolation: e.number(5, "interpolation")?,
664 rows: e.number(6, "rows")?,
665 columns: e.number(7, "columns")?,
666 placed: row >= 0,
667 row: u32::try_from(row.max(0))
668 .map_err(|_| e.fail(format!("row {row} is out of range")))?,
669 column: u8::try_from(e.int(9, "column")?.max(0))
670 .map_err(|_| e.fail("column is out of range"))?,
671 };
672 if element.rows == 0
674 || element.columns == 0
675 || u32::from(element.column)
676 .checked_add(u32::from(element.columns))
677 .is_none_or(|end| end > 4)
678 || element.row.checked_add(element.rows).is_none_or(|end| end > 32)
679 {
680 return Err(e.fail("the element lies outside the 32 four-lane registers"));
681 }
682 Ok(element)
683 })
684 .collect()
685 };
686 Ok([list(0)?, list(1)?, list(2)?])
687}
688
689fn tags(resource: Node<'_>, list: Option<MetadataId>) -> Result<(Option<u32>, Option<u32>)> {
691 let Some(list) = list else {
692 return Ok((None, None));
693 };
694 let ops = resource.node(Some(list), "resource tags")?;
695 let (pairs, rest) = ops.ops.as_chunks::<2>();
696 if !rest.is_empty() {
697 return Err(ops.fail("a tag without its value"));
698 }
699 let (mut element, mut stride) = (None, None);
700 for [tag, value] in pairs {
701 let value = ops.number_of(*value, "tag value")?;
702 match ops.int_of(*tag, "tag")? {
703 0 => element = Some(value),
704 1 => stride = Some(value),
705 _ => {}
706 }
707 }
708 Ok((element, stride))
709}
710
711fn variable_of(m: &Module, id: Option<MetadataId>) -> Option<ValueId> {
713 let Some(Metadata::Value { value, .. }) = id.and_then(|i| m.metadata.get(i)) else {
714 return None;
715 };
716 match m.values.get(*value)? {
717 Value::Constant(Constant::Cast { value, .. }) => Some(*value),
718 Value::Global { .. } => Some(*value),
719 _ => None,
720 }
721}
722
723pub fn ranges(m: &Module) -> Result<Vec<(Range, String, Option<TypeId>)>> {
726 let Some((at, [resources])) = named(m, "dx.resources") else {
727 return Ok(Vec::new());
728 };
729 let lists = Node::new(m, at, *resources, "dx.resources")?;
730 let mut out = Vec::new();
731 for (class, list) in lists.ops.iter().enumerate() {
732 let Some(list) = list else { continue };
733 let class =
734 u8::try_from(class).map_err(|_| lists.fail(format!("resource class {class} is out of range")))?;
735 let list = lists.node(Some(*list), "resource list")?;
736 for r in list.ops {
737 let r = list.node(*r, "resource")?;
738 let ty = match r.op(1)?.and_then(|i| m.metadata.get(i)) {
740 Some(Metadata::Value { ty, .. }) => match m.types.get(*ty) {
741 Some(Type::Pointer(t, _)) => Some(*t),
742 _ => None,
743 },
744 _ => None,
745 };
746 let extra = r.number(6, "resource field")?;
747 let (element, stride) = match class {
748 0 => tags(r, r.maybe(8))?,
749 1 => tags(r, r.maybe(10))?,
750 _ => (None, None),
751 };
752 out.push((
753 Range {
754 class,
755 id: r.number(0, "resource id")?,
756 lower: r.number(4, "lower bound")?,
757 count: {
759 let v = r.int(5, "range size")?;
760 u32::try_from(v)
761 .or_else(|_| i32::try_from(v).map(|v| u32::from_ne_bytes(v.to_ne_bytes())))
762 .map_err(|_| r.fail(format!("range size {v} is out of range")))?
763 },
764 extra,
765 space: r.number(3, "register space")?,
766 element,
767 stride,
768 counter: class == 1 && r.maybe(8).is_some() && r.int(8, "counter")? != 0,
770 variable: variable_of(m, r.op(1)?),
771 },
772 r.string_of(r.op(2)?, "resource name")?,
773 ty,
774 ));
775 }
776 }
777 Ok(out)
778}
779
780#[derive(Clone, Debug, Default, PartialEq, Eq)]
782pub struct ResourceExtras {
783 pub class: u8,
785 pub id: u32,
787 pub sample_count: Option<u32>,
789 pub globally_coherent: bool,
791 pub rasterizer_ordered: bool,
793 pub sampler_feedback: Option<u32>,
795 pub atomic64: bool,
797 pub tags: Vec<(i64, i64)>,
800}
801
802pub fn resource_extras(m: &Module) -> Result<Vec<ResourceExtras>> {
805 let Some((at, [resources])) = named(m, "dx.resources") else {
806 return Ok(Vec::new());
807 };
808 let lists = Node::new(m, at, *resources, "dx.resources")?;
809 let mut out = Vec::new();
810 for (class, list) in lists.ops.iter().enumerate() {
811 let Some(list) = list else { continue };
812 let class =
813 u8::try_from(class).map_err(|_| lists.fail(format!("resource class {class} is out of range")))?;
814 let list = lists.node(Some(*list), "resource list")?;
815 for r in list.ops {
816 let r = list.node(*r, "resource")?;
817 let tag_operand = match class {
819 0 => 8,
820 1 => 10,
821 _ => 7,
822 };
823 let mut tags = Vec::new();
824 if let Some(tag_list) = r.maybe(tag_operand) {
825 let tag_list = r.node(Some(tag_list), "resource tags")?;
826 let (pairs, rest) = tag_list.ops.as_chunks::<2>();
827 if !rest.is_empty() {
828 return Err(tag_list.fail("a tag without its value"));
829 }
830 for [tag, value] in pairs {
831 tags.push((
832 tag_list.int_of(*tag, "tag")?,
833 tag_list.int_of(*value, "tag value")?,
834 ));
835 }
836 }
837 let tag_value = |wanted: i64| tags.iter().find(|(t, _)| *t == wanted).map(|(_, v)| *v);
838 out.push(ResourceExtras {
839 class,
840 id: r.number(0, "resource id")?,
841 sample_count: match (class, r.maybe(7)) {
842 (0, Some(_)) => Some(r.number(7, "sample count")?),
843 _ => None,
844 },
845 globally_coherent: class == 1 && r.maybe(7).is_some() && r.int(7, "globally coherent")? != 0,
846 rasterizer_ordered: class == 1
847 && r.maybe(9).is_some()
848 && r.int(9, "rasterizer ordered")? != 0,
849 sampler_feedback: match tag_value(2) {
850 Some(v) => Some(
851 u32::try_from(v)
852 .map_err(|_| r.fail(format!("sampler feedback kind {v} is out of range")))?,
853 ),
854 None => None,
855 },
856 atomic64: tag_value(3).is_some_and(|v| v != 0),
857 tags,
858 });
859 }
860 }
861 Ok(out)
862}
863
864#[derive(Clone, Debug, PartialEq, Eq)]
866pub struct FieldAnnotation {
867 pub name: String,
869 pub offset: u32,
871 pub component: Option<u32>,
873 pub matrix: Option<(u32, u32, u32)>,
876}
877
878#[derive(Clone, Debug, PartialEq, Eq)]
880pub struct StructAnnotation {
881 pub size: u32,
883 pub fields: Vec<FieldAnnotation>,
885}
886
887pub fn fields(m: &Module, t: TypeId) -> Result<Vec<FieldAnnotation>> {
890 Ok(struct_annotation(m, t)?.map(|a| a.fields).unwrap_or_default())
891}
892
893pub fn struct_annotation(m: &Module, t: TypeId) -> Result<Option<StructAnnotation>> {
897 let Some((at, annotations)) = named(m, "dx.typeAnnotations") else {
898 return Ok(None);
899 };
900 let field_count = match m.types.get(t) {
901 Some(Type::Struct { elements, .. }) => elements.len(),
902 _ => return Ok(None),
903 };
904 for &a in annotations {
905 let a = Node::new(m, at, a, "type annotations")?;
906 if a.int(0, "annotation kind")? != 0 {
908 continue;
909 }
910 let (pairs, rest) = a.ops.get(1..).unwrap_or_default().as_chunks::<2>();
911 if !rest.is_empty() {
912 return Err(a.fail("a struct type without its annotation"));
913 }
914 for [struct_type, annotation] in pairs {
915 let ty = match struct_type.and_then(|i| m.metadata.get(i)) {
916 Some(Metadata::Value { ty, .. }) => *ty,
917 _ => continue,
918 };
919 if ty != t {
920 continue;
921 }
922 let annotation = a.node(*annotation, "struct annotation")?;
923 let fields = match annotation.ops.len() {
925 1 => &[][..],
926 n if n == field_count.saturating_add(1) || n == field_count.saturating_add(2) => {
927 annotation.ops.get(1..=field_count).unwrap_or_default()
928 }
929 n => {
930 return Err(annotation.fail(format!("{n} operands for a struct of {field_count} fields")));
931 }
932 };
933 let fields = fields
934 .iter()
935 .map(|f| {
936 let f = annotation.node(*f, "field annotation")?;
937 let (mut name, mut offset, mut component, mut matrix) = (String::new(), 0, None, None);
938 let (tags, rest) = f.ops.as_chunks::<2>();
939 if !rest.is_empty() {
940 return Err(f.fail("a tag without its value"));
941 }
942 for [tag, value] in tags {
943 match f.int_of(*tag, "field tag")? {
944 6 => name = f.string_of(*value, "field name")?,
945 3 => offset = f.number_of(*value, "field offset")?,
946 2 => {
947 let ops = f.node(*value, "matrix annotation")?;
948 matrix = Some((
949 ops.number(0, "matrix rows")?,
950 ops.number(1, "matrix columns")?,
951 ops.number(2, "matrix orientation")?,
952 ));
953 }
954 7 => component = Some(f.number_of(*value, "field component type")?),
955 _ => {}
956 }
957 }
958 Ok(FieldAnnotation {
959 name,
960 offset,
961 component,
962 matrix,
963 })
964 })
965 .collect::<Result<Vec<_>>>()?;
966 return Ok(Some(StructAnnotation {
967 size: annotation.number(0, "struct size")?,
968 fields,
969 }));
970 }
971 }
972 Ok(None)
973}
974
975pub fn shader_model(m: &Module) -> Result<Option<(String, u32, u32)>> {
978 let Some((at, [model])) = named(m, "dx.shaderModel") else {
979 return Ok(None);
980 };
981 let model = Node::new(m, at, *model, "dx.shaderModel")?;
982 Ok(Some((
983 model.string_of(model.op(0)?, "shader kind")?,
984 model.number(1, "shader model major")?,
985 model.number(2, "shader model minor")?,
986 )))
987}
988
989pub fn view_id_state(m: &Module) -> Result<Option<Vec<u32>>> {
992 let Some((at, [state])) = named(m, "dx.viewIdState") else {
993 return Ok(None);
994 };
995 let state = Node::new(m, at, *state, "dx.viewIdState")?;
996 let value = match state.op(0)?.and_then(|i| m.metadata.get(i)) {
997 Some(Metadata::Value { value, .. }) => *value,
998 _ => return Err(state.fail("expected an array constant")),
999 };
1000 let len = match m.value_types.get(value).and_then(|t| m.types.get(*t)) {
1001 Some(Type::Array(n, _)) => {
1002 usize::try_from(*n).map_err(|_| state.fail("an array past the address space"))?
1003 }
1004 _ => return Err(state.fail("expected an i32 array")),
1005 };
1006 let word = |v: &Value| -> Result<u32> {
1007 match v {
1008 Value::Constant(Constant::Integer(v)) => Ok(u32::try_from(v & 0xFFFF_FFFF).unwrap_or(0)),
1010 Value::Constant(Constant::Null) => Ok(0),
1011 _ => Err(state.fail("an element that is not an integer constant")),
1012 }
1013 };
1014 match m.values.get(value) {
1015 Some(Value::Constant(Constant::Null)) => Ok(Some(vec![0; len])),
1016 Some(Value::Constant(Constant::Data(words))) => {
1017 Ok(Some(words.iter().map(|w| (w & 0xFFFF_FFFF) as u32).collect()))
1018 }
1019 Some(Value::Constant(Constant::Aggregate(ids))) => ids
1020 .iter()
1021 .map(|id| word(m.values.get(*id).unwrap_or(&Value::Local)))
1022 .collect::<Result<Vec<_>>>()
1023 .map(Some),
1024 _ => Err(state.fail("expected an array constant")),
1025 }
1026}
1027
1028#[derive(Clone, Debug, PartialEq, Eq)]
1030pub struct SignatureElement {
1031 pub stream: u32,
1033 pub name: String,
1035 pub index: u32,
1037 pub system_value: u32,
1039 pub component_type: u32,
1041 pub register: u32,
1043 pub mask: u8,
1045 pub read_write_mask: u8,
1047 pub bit: usize,
1049}
1050
1051impl SignatureElement {
1052 pub fn system_value_name(&self) -> &'static str {
1055 match self.system_value {
1056 1 => "POS",
1057 2 => "CLIPDST",
1058 3 => "CULLDST",
1059 4 => "RTINDEX",
1060 5 => "VPINDEX",
1061 6 => "VERTID",
1062 7 => "PRIMID",
1063 8 => "INSTID",
1064 9 => "FFACE",
1065 10 => "SAMPLE",
1066 11 => "QUADEDGE",
1067 12 => "QUADINT",
1068 13 => "TRIEDGE",
1069 14 => "TRIINT",
1070 15 => "LINEDET",
1071 16 => "LINEDEN",
1072 23 => "BARYCEN",
1073 24 => "SHDINGRATE",
1074 25 => "CULLPRIM",
1075 64 => "TARGET",
1076 65 => "DEPTH",
1077 66 => "COVERAGE",
1078 67 => "DEPTHGE",
1079 68 => "DEPTHLE",
1080 69 => "STENCILREF",
1081 70 => "INNERCOV",
1082 _ => "NONE",
1083 }
1084 }
1085
1086 pub fn format_name(&self) -> &'static str {
1088 match self.component_type {
1089 1 => "uint",
1090 2 => "int",
1091 3 => "float",
1092 4 => "uint16",
1093 5 => "int16",
1094 6 => "fp16",
1095 7 => "uint64",
1096 8 => "int64",
1097 9 => "double",
1098 _ => "unknown",
1099 }
1100 }
1101}
1102
1103pub fn component_name(component: u32) -> &'static str {
1106 match component {
1107 1 => "i1",
1108 2 => "i16",
1109 3 => "u16",
1110 4 => "i32",
1111 5 => "u32",
1112 6 => "i64",
1113 7 => "u64",
1114 8 => "f16",
1115 9 => "f32",
1116 10 => "f64",
1117 11 => "snorm_f16",
1118 12 => "unorm_f16",
1119 13 => "snorm_f32",
1120 14 => "unorm_f32",
1121 15 => "snorm_f64",
1122 16 => "unorm_f64",
1123 17 => "p32i8",
1124 18 => "p32u8",
1125 _ => "invalid",
1126 }
1127}
1128
1129pub fn dimension_name(kind: u32) -> &'static str {
1132 match kind {
1133 1 => "1d",
1134 2 => "2d",
1135 3 => "2dMS",
1136 4 => "3d",
1137 5 => "cube",
1138 6 => "1darray",
1139 7 => "2darray",
1140 8 => "2darrayMS",
1141 9 => "cubearray",
1142 10 => "buf",
1143 11 => "rawbuf",
1144 12 => "structbuf",
1145 13 => "cbuffer",
1146 14 => "sampler",
1147 15 => "tbuffer",
1148 16 => "ras",
1149 17 => "fbtex2d",
1150 18 => "fbtex2darray",
1151 _ => "invalid",
1152 }
1153}
1154
1155pub fn signature(part: &[u8]) -> Result<Vec<SignatureElement>> {
1159 let bit = |byte: usize| byte.saturating_mul(8);
1160 let v = primase_foundations::bytes::View::new(part);
1161 let word = |at: usize| -> Result<u32> {
1162 v.array::<4>(at)
1163 .map(u32::from_le_bytes)
1164 .map_err(|e| error(bit(at), e.to_string()))
1165 };
1166 let field = |record: usize, delta: usize| -> Result<u32> {
1168 word(
1169 record
1170 .checked_add(delta)
1171 .ok_or_else(|| error(bit(record), "signature element offset"))?,
1172 )
1173 };
1174 let n = word(0)? as usize;
1175 let first = word(4)? as usize;
1176 let table = n
1178 .checked_mul(32)
1179 .and_then(|size| first.checked_add(size))
1180 .filter(|&end| end <= part.len())
1181 .ok_or_else(|| {
1182 error(
1183 0,
1184 format!(
1185 "{n} records of 32 bytes from byte {first} do not fit the part's {} bytes",
1186 part.len()
1187 ),
1188 )
1189 })?;
1190 let mut out = Vec::with_capacity(n);
1191 for i in 0..n {
1192 let e = i
1193 .checked_mul(32)
1194 .and_then(|o| o.checked_add(first))
1195 .filter(|&e| e < table)
1196 .ok_or_else(|| error(32, "signature element offset"))?;
1197 let name_at = field(e, 4)? as usize;
1198 let tail = part
1199 .get(name_at..)
1200 .ok_or_else(|| error(bit(name_at), "signature name offset"))?;
1201 let end = tail
1202 .iter()
1203 .position(|&b| b == 0)
1204 .filter(|&n| n <= 256)
1205 .ok_or_else(|| error(bit(name_at), "signature name"))?;
1206 let name = tail
1207 .get(..end)
1208 .and_then(|name| std::str::from_utf8(name).ok())
1209 .ok_or_else(|| error(bit(name_at), "signature name is not UTF-8"))?;
1210 let [mask, read_write_mask] = v
1211 .array::<2>(
1212 e.checked_add(24)
1213 .ok_or_else(|| error(bit(e), "signature element offset"))?,
1214 )
1215 .map_err(|x| error(bit(e), x.to_string()))?;
1216 out.push(SignatureElement {
1217 stream: field(e, 0)?,
1218 name: name.to_owned(),
1219 index: field(e, 8)?,
1220 system_value: field(e, 12)?,
1221 component_type: field(e, 16)?,
1222 register: field(e, 20)?,
1223 mask,
1224 read_write_mask,
1225 bit: bit(e),
1226 });
1227 }
1228 Ok(out)
1229}