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 bits32(&self, i: usize, what: &str) -> Result<u32> {
132 let v = self.int(i, what)?;
133 u32::try_from(v)
134 .or_else(|_| i32::try_from(v).map(i32::cast_unsigned))
135 .map_err(|_| self.fail(format!("{what}: {v} is out of range")))
136 }
137 fn string_of(&self, id: Option<MetadataId>, what: &str) -> Result<String> {
139 match id.and_then(|i| self.m.metadata.get(i)) {
140 Some(Metadata::String(s)) => std::str::from_utf8(s).map(str::to_owned).map_err(|e| {
141 self.fail(format!(
142 "{what}: not UTF-8 (valid up to byte {})",
143 e.valid_up_to()
144 ))
145 }),
146 _ => Ok(String::new()),
147 }
148 }
149}
150
151fn named<'a>(m: &'a Module, name: &str) -> Option<(usize, &'a [MetadataId])> {
153 let i = m.named_metadata.iter().position(|(n, _)| n == name)?;
154 let (_, ids) = m.named_metadata.get(i)?;
155 Some((
156 m.named_metadata_at.get(i).copied().unwrap_or(m.at),
157 ids.as_slice(),
158 ))
159}
160
161pub fn library_kind(m: &Module) -> Result<u32> {
164 let entry = the_entry(m)?;
165 let properties = entry
166 .node(entry.maybe(4), "entry point properties")
167 .map_err(|_| entry.fail("a library entry point without properties"))?;
168 for pair in properties.ops.chunks(2) {
169 let [tag, value] = pair else {
170 return Err(properties.fail("a tag without a value"));
171 };
172 if properties.int_of(*tag, "property tag")? == 8 {
173 return properties.number_of(*value, "shader kind");
174 }
175 }
176 Err(properties.fail("a library entry point that states no shader kind"))
177}
178
179fn the_entry(m: &Module) -> Result<Node<'_>> {
181 let (at, entries) =
182 named(m, "dx.entryPoints").ok_or_else(|| error(m.at, "the module has no dx.entryPoints metadata"))?;
183 let mut with_function = Vec::new();
184 for &id in entries {
185 let entry = Node::new(m, at, id, "entry point")?;
186 if entry.op(0)?.is_some() {
187 with_function.push(entry);
188 }
189 }
190 match with_function.as_slice() {
191 [entry] => Ok(*entry),
192 _ => Err(error(
193 at,
194 format!(
195 "dx.entryPoints: expected exactly one entry point with a function, found {}",
196 with_function.len()
197 ),
198 )),
199 }
200}
201
202pub fn entry(m: &Module) -> Result<ValueId> {
204 let entry = the_entry(m)?;
205 match entry.op(0)?.and_then(|i| m.metadata.get(i)) {
206 Some(Metadata::Value { value, .. }) => Ok(*value),
207 _ => Err(entry.fail("operand 0 names no function")),
208 }
209}
210
211pub fn num_threads(m: &Module) -> Result<Option<[u32; 3]>> {
215 let entry = the_entry(m)?;
216 let Some(properties) = entry.maybe(4) else {
217 return Ok(None);
218 };
219 let properties = entry.node(Some(properties), "entry point properties")?;
220 for pair in properties.ops.chunks(2) {
221 let [tag, value] = pair else {
222 return Err(properties.fail("a tag without a value"));
223 };
224 let tag = properties.int_of(*tag, "property tag")?;
225 if tag != 4 && tag != 9 && tag != 10 {
226 continue;
227 }
228 let mut size = properties.node(*value, "numthreads")?;
229 if tag == 9 || tag == 10 {
231 size = size.node(size.op(0)?, "numthreads")?;
232 }
233 return Ok(Some([
234 size.number(0, "numthreads x")?,
235 size.number(1, "numthreads y")?,
236 size.number(2, "numthreads z")?,
237 ]));
238 }
239 Ok(None)
240}
241
242pub fn shader_flags(m: &Module) -> Result<Option<u64>> {
246 flags_of(the_entry(m)?)
247}
248
249pub fn module_shader_flags(m: &Module) -> Result<Option<u64>> {
252 let (at, entries) =
253 named(m, "dx.entryPoints").ok_or_else(|| error(m.at, "the module has no dx.entryPoints metadata"))?;
254 let first = entries
255 .first()
256 .ok_or_else(|| error(at, "dx.entryPoints lists no entry point"))?;
257 flags_of(Node::new(m, at, *first, "entry point")?)
258}
259
260fn flags_of(entry: Node<'_>) -> Result<Option<u64>> {
262 let Some(properties) = entry.maybe(4) else {
263 return Ok(None);
264 };
265 let properties = entry.node(Some(properties), "entry point properties")?;
266 for pair in properties.ops.chunks(2) {
267 let [tag, value] = pair else {
268 return Err(properties.fail("a tag without a value"));
269 };
270 if properties.int_of(*tag, "property tag")? == 0 {
271 let flags = properties.int_of(*value, "shader flags")?;
272 return Ok(Some(u64::from_le_bytes(flags.to_le_bytes())));
273 }
274 }
275 Ok(None)
276}
277
278pub fn usage_masks(m: &Module) -> Result<[Vec<Option<u8>>; 3]> {
284 Ok(element_tags(m)?.map(|list| list.into_iter().map(|tags| tags.usage_mask).collect()))
285}
286
287#[derive(Clone, Debug, Default, PartialEq, Eq)]
291pub struct ElementTags {
292 pub stream: Option<u32>,
294 pub dynamic_index_mask: Option<u8>,
296 pub usage_mask: Option<u8>,
298 pub other: Vec<(i64, i64)>,
300}
301
302pub fn element_tags(m: &Module) -> Result<[Vec<ElementTags>; 3]> {
305 let entry = the_entry(m)?;
306 let Some(signatures) = entry.op(2)? else {
307 return Ok([Vec::new(), Vec::new(), Vec::new()]);
308 };
309 let signatures = entry.node(Some(signatures), "signatures")?;
310 let list = |i: usize| -> Result<Vec<ElementTags>> {
311 let Some(list) = signatures.op(i)? else {
312 return Ok(Vec::new());
313 };
314 let list = signatures.node(Some(list), "signature")?;
315 list.ops
316 .iter()
317 .map(|e| {
318 let e = list.node(*e, "signature element")?;
319 let Some(tail) = e.maybe(10) else {
320 return Ok(ElementTags::default());
321 };
322 let tail = e.node(Some(tail), "element tags")?;
323 let (pairs, rest) = tail.ops.as_chunks::<2>();
324 if !rest.is_empty() {
325 return Err(tail.fail("a tag without its value"));
326 }
327 let mut tags = ElementTags::default();
328 for [tag, value] in pairs {
329 match tail.int_of(*tag, "element tag")? {
330 0 => tags.stream = Some(tail.number_of(*value, "element stream")?),
331 2 => tags.dynamic_index_mask = Some(tail.number_of(*value, "dynamic index mask")?),
332 3 => tags.usage_mask = Some(tail.number_of(*value, "usage mask")?),
333 other => {
334 if let Ok(v) = tail.int_of(*value, "element tag value") {
335 tags.other.push((other, v));
336 }
337 }
338 }
339 }
340 Ok(tags)
341 })
342 .collect()
343 };
344 Ok([list(0)?, list(1)?, list(2)?])
345}
346
347pub fn elements(m: &Module) -> Result<(Vec<Element>, Vec<Element>)> {
349 let [inputs, outputs, _] = signature_lists(m)?;
350 Ok((inputs, outputs))
351}
352
353pub fn patch_elements(m: &Module) -> Result<Vec<Element>> {
355 let [_, _, patch] = signature_lists(m)?;
356 Ok(patch)
357}
358
359#[derive(Clone, Debug, PartialEq)]
362pub struct HullState {
363 pub patch_constant: ValueId,
365 pub input_control_points: u32,
367 pub output_control_points: u32,
369 pub domain: u32,
371 pub partitioning: u32,
373 pub output_primitive: u32,
375 pub max_tess_factor: f32,
377}
378
379fn entry_property(m: &Module, wanted: i64) -> Result<Option<Node<'_>>> {
381 let entry = the_entry(m)?;
382 let Some(properties) = entry.maybe(4) else {
383 return Ok(None);
384 };
385 let properties = entry.node(Some(properties), "entry point properties")?;
386 for pair in properties.ops.chunks(2) {
387 let [tag, value] = pair else {
388 return Err(properties.fail("a tag without a value"));
389 };
390 if properties.int_of(*tag, "property tag")? == wanted {
391 return properties.node(*value, "property").map(Some);
392 }
393 }
394 Ok(None)
395}
396
397#[derive(Clone, Copy, Debug, PartialEq, Eq)]
402pub struct GeometryState {
403 pub input: u32,
405 pub max_vertices: u32,
407 pub streams: u32,
409 pub topology: u32,
411 pub instances: u32,
413}
414
415pub fn geometry_state(m: &Module) -> Result<GeometryState> {
417 let state = entry_property(m, 1)?.ok_or_else(|| {
418 the_entry(m).map_or_else(
419 |e| e,
420 |e| e.fail("a geometry program without its state (properties tag 1)"),
421 )
422 })?;
423 Ok(GeometryState {
424 input: state.number(0, "geometry input primitive")?,
425 max_vertices: state.number(1, "geometry maximum vertices")?,
426 streams: state.number(2, "geometry streams")?,
427 topology: state.number(3, "geometry topology")?,
428 instances: state.number(4, "geometry instances")?,
429 })
430}
431
432#[derive(Clone, Copy, Debug, PartialEq, Eq)]
436pub struct MeshState {
437 pub max_vertices: u32,
439 pub max_primitives: u32,
441 pub topology: u32,
443 pub payload: u32,
445}
446
447pub fn mesh_state(m: &Module) -> Result<MeshState> {
449 let state = entry_property(m, 9)?.ok_or_else(|| {
450 the_entry(m).map_or_else(
451 |e| e,
452 |e| e.fail("a mesh program without its state (properties tag 9)"),
453 )
454 })?;
455 let field = |k: usize, what: &str| -> Result<u32> {
457 match state.op(k)? {
458 None => Ok(0),
459 some => state.number_of(some, what),
460 }
461 };
462 Ok(MeshState {
463 max_vertices: field(1, "mesh maximum vertices")?,
464 max_primitives: field(2, "mesh maximum primitives")?,
465 topology: field(3, "mesh topology")?,
466 payload: field(4, "mesh payload size")?,
467 })
468}
469
470pub fn hull_state(m: &Module) -> Result<HullState> {
472 let state = entry_property(m, 3)?.ok_or_else(|| {
473 the_entry(m).map_or_else(
474 |e| e,
475 |e| e.fail("a hull program without its patch state (properties tag 3)"),
476 )
477 })?;
478 let small = |k: usize, what: &str| -> Result<u32> { state.number_of(state.maybe(k), what) };
479 let patch_constant = match state.maybe(0).and_then(|i| m.metadata.get(i)) {
480 Some(Metadata::Value { value, .. }) => *value,
481 _ => return Err(state.fail("a hull program without its patch-constant function")),
482 };
483 let max = match state.maybe(6).and_then(|i| m.metadata.get(i)) {
484 Some(Metadata::Value { value, .. }) => match m.values.get(*value) {
485 Some(Value::Constant(Constant::Float(bits))) => {
486 let bits = u32::try_from(*bits)
487 .map_err(|_| state.fail("the maximum tessellation factor is wider than a float"))?;
488 f32::from_bits(bits)
489 }
490 _ => return Err(state.fail("the maximum tessellation factor is not a float")),
491 },
492 _ => return Err(state.fail("hull state without a maximum tessellation factor")),
493 };
494 Ok(HullState {
495 patch_constant,
496 input_control_points: small(1, "hull input control points")?,
497 output_control_points: small(2, "hull output control points")?,
498 domain: small(3, "hull domain")?,
499 partitioning: small(4, "hull partitioning")?,
500 output_primitive: small(5, "hull output primitive")?,
501 max_tess_factor: max,
502 })
503}
504
505pub fn domain_state(m: &Module) -> Result<(u32, u32)> {
507 let state = entry_property(m, 2)?.ok_or_else(|| {
508 the_entry(m).map_or_else(
509 |e| e,
510 |e| e.fail("a domain program without its patch state (properties tag 2)"),
511 )
512 })?;
513 Ok((
514 state.number_of(state.maybe(0), "domain")?,
515 state.number_of(state.maybe(1), "domain input control points")?,
516 ))
517}
518
519#[derive(Clone, Copy, Debug, PartialEq, Eq)]
524pub struct EntryProperty {
525 pub tag: i64,
527 pub value: Option<MetadataId>,
529}
530
531pub fn entry_properties(m: &Module) -> Result<Vec<EntryProperty>> {
533 let entry = the_entry(m)?;
534 properties_of(entry)
535}
536
537fn properties_of(entry: Node<'_>) -> Result<Vec<EntryProperty>> {
538 let Some(properties) = entry.maybe(4) else {
539 return Ok(Vec::new());
540 };
541 let properties = entry.node(Some(properties), "entry point properties")?;
542 let (pairs, rest) = properties.ops.as_chunks::<2>();
543 if !rest.is_empty() {
544 return Err(properties.fail("a tag without a value"));
545 }
546 pairs
547 .iter()
548 .map(|[tag, value]| {
549 Ok(EntryProperty {
550 tag: properties.int_of(*tag, "property tag")?,
551 value: *value,
552 })
553 })
554 .collect()
555}
556
557fn property_integer(m: &Module, wanted: i64) -> Result<Option<u32>> {
560 let entry = the_entry(m)?;
561 for property in properties_of(entry)? {
562 if property.tag != wanted {
563 continue;
564 }
565 let record = entry.node(property.value, "property").ok();
566 return match record {
567 Some(node) => node.number(0, "property value").map(Some),
568 None => entry.number_of(property.value, "property value").map(Some),
569 };
570 }
571 Ok(None)
572}
573
574pub fn ray_payload_size(m: &Module) -> Result<Option<u32>> {
577 property_integer(m, 6)
578}
579
580pub fn ray_attribute_size(m: &Module) -> Result<Option<u32>> {
582 property_integer(m, 7)
583}
584
585pub fn auto_binding_space(m: &Module) -> Result<Option<u32>> {
589 property_integer(m, 5)
590}
591
592pub fn library_binding_space(m: &Module) -> Result<Option<u32>> {
595 let (at, entries) =
596 named(m, "dx.entryPoints").ok_or_else(|| error(m.at, "the module has no dx.entryPoints metadata"))?;
597 for &id in entries {
598 let entry = Node::new(m, at, id, "entry point")?;
599 if entry.op(0)?.is_some() {
600 continue;
601 }
602 for property in properties_of(entry)? {
603 if property.tag == 5 {
604 let node = entry.node(property.value, "auto binding space")?;
605 return node.number(0, "auto binding space").map(Some);
606 }
607 }
608 }
609 Ok(None)
610}
611
612pub fn wave_size(m: &Module) -> Result<Option<Vec<u32>>> {
615 let Some(node) = entry_property(m, 11)? else {
616 return Ok(None);
617 };
618 node.ops
619 .iter()
620 .map(|id| node.number_of(*id, "wave size"))
621 .collect::<Result<Vec<u32>>>()
622 .map(Some)
623}
624
625pub fn amplification_payload_size(m: &Module) -> Result<Option<u32>> {
628 let Some(state) = entry_property(m, 10)? else {
629 return Ok(None);
630 };
631 state.number(1, "amplification payload size").map(Some)
632}
633
634pub fn fp32_denorm_mode(m: &Module) -> Result<Option<String>> {
638 let function = entry(m)?;
639 Ok(m.string_attribute(function, "fp32-denorm-mode")
640 .map(str::to_owned))
641}
642
643fn signature_lists(m: &Module) -> Result<[Vec<Element>; 3]> {
644 let entry = the_entry(m)?;
645 let Some(signatures) = entry.op(2)? else {
646 return Ok([Vec::new(), Vec::new(), Vec::new()]);
647 };
648 let signatures = entry.node(Some(signatures), "signatures")?;
649 let list = |i: usize| -> Result<Vec<Element>> {
650 let Some(list) = signatures.op(i)? else {
651 return Ok(Vec::new());
652 };
653 let list = signatures.node(Some(list), "signature")?;
654 list.ops
655 .iter()
656 .map(|e| {
657 let e = list.node(*e, "signature element")?;
658 let row = e.int(8, "row")?;
659 let element = Element {
660 id: e.number(0, "element id")?,
661 name: e.string_of(e.op(1)?, "element name")?,
662 index: match e.op(4)? {
663 Some(list) => match e.node(Some(list), "semantic indices")?.ops.first() {
664 Some(first) => e.number_of(*first, "semantic index")?,
665 None => 0,
666 },
667 None => 0,
668 },
669 component_type: e.number(2, "component type")?,
670 system_value: e.number(3, "system value")?,
671 interpolation: e.number(5, "interpolation")?,
672 rows: e.number(6, "rows")?,
673 columns: e.number(7, "columns")?,
674 placed: row >= 0,
675 row: u32::try_from(row.max(0))
676 .map_err(|_| e.fail(format!("row {row} is out of range")))?,
677 column: u8::try_from(e.int(9, "column")?.max(0))
678 .map_err(|_| e.fail("column is out of range"))?,
679 };
680 if element.rows == 0
682 || element.columns == 0
683 || u32::from(element.column)
684 .checked_add(u32::from(element.columns))
685 .is_none_or(|end| end > 4)
686 || element.row.checked_add(element.rows).is_none_or(|end| end > 32)
687 {
688 return Err(e.fail("the element lies outside the 32 four-lane registers"));
689 }
690 Ok(element)
691 })
692 .collect()
693 };
694 Ok([list(0)?, list(1)?, list(2)?])
695}
696
697fn tags(resource: Node<'_>, list: Option<MetadataId>) -> Result<(Option<u32>, Option<u32>)> {
699 let Some(list) = list else {
700 return Ok((None, None));
701 };
702 let ops = resource.node(Some(list), "resource tags")?;
703 let (pairs, rest) = ops.ops.as_chunks::<2>();
704 if !rest.is_empty() {
705 return Err(ops.fail("a tag without its value"));
706 }
707 let (mut element, mut stride) = (None, None);
708 for [tag, value] in pairs {
710 match ops.int_of(*tag, "tag")? {
711 0 => element = Some(ops.number_of(*value, "tag value")?),
712 1 => stride = Some(ops.number_of(*value, "tag value")?),
713 _ => {}
714 }
715 }
716 Ok((element, stride))
717}
718
719fn variable_of(m: &Module, id: Option<MetadataId>) -> Option<ValueId> {
721 let Some(Metadata::Value { value, .. }) = id.and_then(|i| m.metadata.get(i)) else {
722 return None;
723 };
724 match m.values.get(*value)? {
725 Value::Constant(Constant::Cast { value, .. }) => Some(*value),
726 Value::Global { .. } => Some(*value),
727 _ => None,
728 }
729}
730
731pub fn ranges(m: &Module) -> Result<Vec<(Range, String, Option<TypeId>)>> {
734 let Some((at, [resources])) = named(m, "dx.resources") else {
735 return Ok(Vec::new());
736 };
737 let lists = Node::new(m, at, *resources, "dx.resources")?;
738 let mut out = Vec::new();
739 for (class, list) in lists.ops.iter().enumerate() {
740 let Some(list) = list else { continue };
741 let class =
742 u8::try_from(class).map_err(|_| lists.fail(format!("resource class {class} is out of range")))?;
743 let list = lists.node(Some(*list), "resource list")?;
744 for r in list.ops {
745 let r = list.node(*r, "resource")?;
746 let ty = match r.op(1)?.and_then(|i| m.metadata.get(i)) {
748 Some(Metadata::Value { ty, .. }) => match m.types.get(*ty) {
749 Some(Type::Pointer(t, _)) => Some(*t),
750 _ => None,
751 },
752 _ => None,
753 };
754 let extra = r.number(6, "resource field")?;
755 let (element, stride) = match class {
756 0 => tags(r, r.maybe(8))?,
757 1 => tags(r, r.maybe(10))?,
758 _ => (None, None),
759 };
760 out.push((
761 Range {
762 class,
763 id: r.number(0, "resource id")?,
764 lower: r.bits32(4, "lower bound")?,
765 count: r.bits32(5, "range size")?,
766 extra,
767 space: r.bits32(3, "register space")?,
768 element,
769 stride,
770 counter: class == 1 && r.maybe(8).is_some() && r.int(8, "counter")? != 0,
772 variable: variable_of(m, r.op(1)?),
773 },
774 r.string_of(r.op(2)?, "resource name")?,
775 ty,
776 ));
777 }
778 }
779 Ok(out)
780}
781
782#[derive(Clone, Debug, Default, PartialEq, Eq)]
784pub struct ResourceExtras {
785 pub class: u8,
787 pub id: u32,
789 pub sample_count: Option<u32>,
792 pub globally_coherent: bool,
794 pub rasterizer_ordered: bool,
796 pub sampler_feedback: Option<u32>,
798 pub atomic64: bool,
800 pub tags: Vec<(i64, i64)>,
803}
804
805pub fn resource_extras(m: &Module) -> Result<Vec<ResourceExtras>> {
808 let Some((at, [resources])) = named(m, "dx.resources") else {
809 return Ok(Vec::new());
810 };
811 let lists = Node::new(m, at, *resources, "dx.resources")?;
812 let mut out = Vec::new();
813 for (class, list) in lists.ops.iter().enumerate() {
814 let Some(list) = list else { continue };
815 let class =
816 u8::try_from(class).map_err(|_| lists.fail(format!("resource class {class} is out of range")))?;
817 let list = lists.node(Some(*list), "resource list")?;
818 for r in list.ops {
819 let r = list.node(*r, "resource")?;
820 let tag_operand = match class {
822 0 => 8,
823 1 => 10,
824 _ => 7,
825 };
826 let mut tags = Vec::new();
827 if let Some(tag_list) = r.maybe(tag_operand) {
828 let tag_list = r.node(Some(tag_list), "resource tags")?;
829 let (pairs, rest) = tag_list.ops.as_chunks::<2>();
830 if !rest.is_empty() {
831 return Err(tag_list.fail("a tag without its value"));
832 }
833 for [tag, value] in pairs {
834 tags.push((
835 tag_list.int_of(*tag, "tag")?,
836 tag_list.int_of(*value, "tag value")?,
837 ));
838 }
839 }
840 let tag_value = |wanted: i64| tags.iter().find(|(t, _)| *t == wanted).map(|(_, v)| *v);
841 out.push(ResourceExtras {
842 class,
843 id: r.number(0, "resource id")?,
844 sample_count: match (class, r.maybe(7)) {
845 (0, Some(_)) => Some(r.bits32(7, "sample count")?),
847 _ => None,
848 },
849 globally_coherent: class == 1 && r.maybe(7).is_some() && r.int(7, "globally coherent")? != 0,
850 rasterizer_ordered: class == 1
851 && r.maybe(9).is_some()
852 && r.int(9, "rasterizer ordered")? != 0,
853 sampler_feedback: match tag_value(2) {
854 Some(v) => Some(
855 u32::try_from(v)
856 .map_err(|_| r.fail(format!("sampler feedback kind {v} is out of range")))?,
857 ),
858 None => None,
859 },
860 atomic64: tag_value(3).is_some_and(|v| v != 0),
861 tags,
862 });
863 }
864 }
865 Ok(out)
866}
867
868#[derive(Clone, Debug, PartialEq, Eq)]
871pub struct Binding {
872 pub range: Range,
874 pub name: String,
876 pub ty: Option<TypeId>,
878 pub sample_count: u32,
880}
881
882impl Binding {
883 fn view(&self) -> bool {
885 matches!(self.range.class, 0 | 1)
886 }
887
888 pub fn class_name(&self) -> &'static str {
890 match self.range.class {
891 0 => "texture",
892 1 => "UAV",
893 2 => "cbuffer",
894 _ => "sampler",
895 }
896 }
897
898 pub fn format_name(&self) -> &'static str {
901 match (self.view(), self.range.extra) {
902 (false, _) => "NA",
903 (true, 11) => "byte",
904 (true, 12) => "struct",
905 (true, _) => component_name(self.range.element.unwrap_or(0)),
906 }
907 }
908
909 pub fn dimension_name(&self) -> String {
913 let r = &self.range;
914 match (self.view(), r.extra) {
915 (false, _) => "NA".to_owned(),
916 (true, 11 | 12) if r.class == 0 => "r/o".to_owned(),
917 (true, 11 | 12) if r.counter => "r/w+cnt".to_owned(),
918 (true, 11 | 12) => "r/w".to_owned(),
919 (true, 3 | 8) if self.sample_count != 0 => {
920 format!("{}{}", dimension_name(r.extra), self.sample_count)
921 }
922 (true, kind) => dimension_name(kind).to_owned(),
923 }
924 }
925
926 pub fn id_name(&self) -> String {
928 let letter = match self.range.class {
929 0 => "T",
930 1 => "U",
931 2 => "CB",
932 _ => "S",
933 };
934 format!("{letter}{}", self.range.id)
935 }
936
937 pub fn bind_name(&self) -> String {
939 let letter = match self.range.class {
940 0 => "t",
941 1 => "u",
942 2 => "cb",
943 _ => "s",
944 };
945 if self.range.space == 0 {
946 format!("{letter}{}", self.range.lower)
947 } else {
948 format!("{letter}{},space{}", self.range.lower, self.range.space)
949 }
950 }
951}
952
953pub fn bindings(m: &Module) -> Result<Vec<Binding>> {
956 let extras = resource_extras(m)?;
957 let mut all: Vec<Binding> = ranges(m)?
958 .into_iter()
959 .map(|(range, name, ty)| {
960 let sample_count = extras
961 .iter()
962 .find(|e| e.class == range.class && e.id == range.id)
963 .and_then(|e| e.sample_count)
964 .unwrap_or(0);
965 Binding {
966 range,
967 name,
968 ty,
969 sample_count,
970 }
971 })
972 .collect();
973 let order = |class: u8| match class {
974 2 => 0,
975 3 => 1,
976 0 => 2,
977 _ => 3,
978 };
979 all.sort_by_key(|b| order(b.range.class));
981 Ok(all)
982}
983
984#[derive(Clone, Debug, PartialEq, Eq)]
986pub struct FieldAnnotation {
987 pub name: String,
989 pub offset: u32,
991 pub component: Option<u32>,
993 pub matrix: Option<(u32, u32, u32)>,
996}
997
998#[derive(Clone, Debug, PartialEq, Eq)]
1000pub struct StructAnnotation {
1001 pub size: u32,
1003 pub fields: Vec<FieldAnnotation>,
1005}
1006
1007pub fn fields(m: &Module, t: TypeId) -> Result<Vec<FieldAnnotation>> {
1010 Ok(struct_annotation(m, t)?.map(|a| a.fields).unwrap_or_default())
1011}
1012
1013pub fn struct_annotation(m: &Module, t: TypeId) -> Result<Option<StructAnnotation>> {
1017 let Some((at, annotations)) = named(m, "dx.typeAnnotations") else {
1018 return Ok(None);
1019 };
1020 let field_count = match m.types.get(t) {
1021 Some(Type::Struct { elements, .. }) => elements.len(),
1022 _ => return Ok(None),
1023 };
1024 for &a in annotations {
1025 let a = Node::new(m, at, a, "type annotations")?;
1026 if a.int(0, "annotation kind")? != 0 {
1028 continue;
1029 }
1030 let (pairs, rest) = a.ops.get(1..).unwrap_or_default().as_chunks::<2>();
1031 if !rest.is_empty() {
1032 return Err(a.fail("a struct type without its annotation"));
1033 }
1034 for [struct_type, annotation] in pairs {
1035 let ty = match struct_type.and_then(|i| m.metadata.get(i)) {
1036 Some(Metadata::Value { ty, .. }) => *ty,
1037 _ => continue,
1038 };
1039 if ty != t {
1040 continue;
1041 }
1042 let annotation = a.node(*annotation, "struct annotation")?;
1043 let fields = match annotation.ops.len() {
1045 1 => &[][..],
1046 n if n == field_count.saturating_add(1) || n == field_count.saturating_add(2) => {
1047 annotation.ops.get(1..=field_count).unwrap_or_default()
1048 }
1049 n => {
1050 return Err(annotation.fail(format!("{n} operands for a struct of {field_count} fields")));
1051 }
1052 };
1053 let fields = fields
1054 .iter()
1055 .map(|f| {
1056 let f = annotation.node(*f, "field annotation")?;
1057 let (mut name, mut offset, mut component, mut matrix) = (String::new(), 0, None, None);
1058 let (tags, rest) = f.ops.as_chunks::<2>();
1059 if !rest.is_empty() {
1060 return Err(f.fail("a tag without its value"));
1061 }
1062 for [tag, value] in tags {
1063 match f.int_of(*tag, "field tag")? {
1064 6 => name = f.string_of(*value, "field name")?,
1065 3 => offset = f.number_of(*value, "field offset")?,
1066 2 => {
1067 let ops = f.node(*value, "matrix annotation")?;
1068 matrix = Some((
1069 ops.number(0, "matrix rows")?,
1070 ops.number(1, "matrix columns")?,
1071 ops.number(2, "matrix orientation")?,
1072 ));
1073 }
1074 7 => component = Some(f.number_of(*value, "field component type")?),
1075 _ => {}
1076 }
1077 }
1078 Ok(FieldAnnotation {
1079 name,
1080 offset,
1081 component,
1082 matrix,
1083 })
1084 })
1085 .collect::<Result<Vec<_>>>()?;
1086 return Ok(Some(StructAnnotation {
1087 size: annotation.number(0, "struct size")?,
1088 fields,
1089 }));
1090 }
1091 }
1092 Ok(None)
1093}
1094
1095pub fn shader_model(m: &Module) -> Result<Option<(String, u32, u32)>> {
1098 let Some((at, [model])) = named(m, "dx.shaderModel") else {
1099 return Ok(None);
1100 };
1101 let model = Node::new(m, at, *model, "dx.shaderModel")?;
1102 Ok(Some((
1103 model.string_of(model.op(0)?, "shader kind")?,
1104 model.number(1, "shader model major")?,
1105 model.number(2, "shader model minor")?,
1106 )))
1107}
1108
1109pub fn view_id_state(m: &Module) -> Result<Option<Vec<u32>>> {
1112 let Some((at, [state])) = named(m, "dx.viewIdState") else {
1113 return Ok(None);
1114 };
1115 let state = Node::new(m, at, *state, "dx.viewIdState")?;
1116 let value = match state.op(0)?.and_then(|i| m.metadata.get(i)) {
1117 Some(Metadata::Value { value, .. }) => *value,
1118 _ => return Err(state.fail("expected an array constant")),
1119 };
1120 let len = match m.value_types.get(value).and_then(|t| m.types.get(*t)) {
1121 Some(Type::Array(n, _)) => {
1122 usize::try_from(*n).map_err(|_| state.fail("an array past the address space"))?
1123 }
1124 _ => return Err(state.fail("expected an i32 array")),
1125 };
1126 let word = |v: &Value| -> Result<u32> {
1127 match v {
1128 Value::Constant(Constant::Integer(v)) => Ok(u32::try_from(v & 0xFFFF_FFFF).unwrap_or(0)),
1130 Value::Constant(Constant::Null) => Ok(0),
1131 _ => Err(state.fail("an element that is not an integer constant")),
1132 }
1133 };
1134 match m.values.get(value) {
1135 Some(Value::Constant(Constant::Null)) => Ok(Some(vec![0; len])),
1136 Some(Value::Constant(Constant::Data(words))) => {
1137 Ok(Some(words.iter().map(|w| (w & 0xFFFF_FFFF) as u32).collect()))
1138 }
1139 Some(Value::Constant(Constant::Aggregate(ids))) => ids
1140 .iter()
1141 .map(|id| word(m.values.get(*id).unwrap_or(&Value::Local)))
1142 .collect::<Result<Vec<_>>>()
1143 .map(Some),
1144 _ => Err(state.fail("expected an array constant")),
1145 }
1146}
1147
1148#[derive(Clone, Debug, PartialEq, Eq)]
1150pub struct SignatureElement {
1151 pub stream: u32,
1153 pub name: String,
1155 pub index: u32,
1157 pub system_value: u32,
1159 pub component_type: u32,
1161 pub register: u32,
1163 pub mask: u8,
1165 pub read_write_mask: u8,
1167 pub bit: usize,
1169}
1170
1171impl SignatureElement {
1172 pub fn system_value_name(&self) -> &'static str {
1175 match self.system_value {
1176 1 => "POS",
1177 2 => "CLIPDST",
1178 3 => "CULLDST",
1179 4 => "RTINDEX",
1180 5 => "VPINDEX",
1181 6 => "VERTID",
1182 7 => "PRIMID",
1183 8 => "INSTID",
1184 9 => "FFACE",
1185 10 => "SAMPLE",
1186 11 => "QUADEDGE",
1187 12 => "QUADINT",
1188 13 => "TRIEDGE",
1189 14 => "TRIINT",
1190 15 => "LINEDET",
1191 16 => "LINEDEN",
1192 23 => "BARYCEN",
1193 24 => "SHDINGRATE",
1194 25 => "CULLPRIM",
1195 64 => "TARGET",
1196 65 => "DEPTH",
1197 66 => "COVERAGE",
1198 67 => "DEPTHGE",
1199 68 => "DEPTHLE",
1200 69 => "STENCILREF",
1201 70 => "INNERCOV",
1202 _ => "NONE",
1203 }
1204 }
1205
1206 pub fn format_name(&self) -> &'static str {
1208 match self.component_type {
1209 1 => "uint",
1210 2 => "int",
1211 3 => "float",
1212 4 => "uint16",
1213 5 => "int16",
1214 6 => "fp16",
1215 7 => "uint64",
1216 8 => "int64",
1217 9 => "double",
1218 _ => "unknown",
1219 }
1220 }
1221}
1222
1223pub fn component_name(component: u32) -> &'static str {
1226 match component {
1227 1 => "i1",
1228 2 => "i16",
1229 3 => "u16",
1230 4 => "i32",
1231 5 => "u32",
1232 6 => "i64",
1233 7 => "u64",
1234 8 => "f16",
1235 9 => "f32",
1236 10 => "f64",
1237 11 => "snorm_f16",
1238 12 => "unorm_f16",
1239 13 => "snorm_f32",
1240 14 => "unorm_f32",
1241 15 => "snorm_f64",
1242 16 => "unorm_f64",
1243 17 => "p32i8",
1244 18 => "p32u8",
1245 _ => "invalid",
1246 }
1247}
1248
1249pub fn dimension_name(kind: u32) -> &'static str {
1252 match kind {
1253 1 => "1d",
1254 2 => "2d",
1255 3 => "2dMS",
1256 4 => "3d",
1257 5 => "cube",
1258 6 => "1darray",
1259 7 => "2darray",
1260 8 => "2darrayMS",
1261 9 => "cubearray",
1262 10 => "buf",
1263 11 => "rawbuf",
1264 12 => "structbuf",
1265 13 => "cbuffer",
1266 14 => "sampler",
1267 15 => "tbuffer",
1268 16 => "ras",
1269 17 => "fbtex2d",
1270 18 => "fbtex2darray",
1271 _ => "invalid",
1272 }
1273}
1274
1275pub fn signature(part: &[u8]) -> Result<Vec<SignatureElement>> {
1279 let bit = |byte: usize| byte.saturating_mul(8);
1280 let v = primase_foundations::bytes::View::new(part);
1281 let word = |at: usize| -> Result<u32> {
1282 v.array::<4>(at)
1283 .map(u32::from_le_bytes)
1284 .map_err(|e| error(bit(at), e.to_string()))
1285 };
1286 let field = |record: usize, delta: usize| -> Result<u32> {
1288 word(
1289 record
1290 .checked_add(delta)
1291 .ok_or_else(|| error(bit(record), "signature element offset"))?,
1292 )
1293 };
1294 let n = word(0)? as usize;
1295 let first = word(4)? as usize;
1296 let table = n
1298 .checked_mul(32)
1299 .and_then(|size| first.checked_add(size))
1300 .filter(|&end| end <= part.len())
1301 .ok_or_else(|| {
1302 error(
1303 0,
1304 format!(
1305 "{n} records of 32 bytes from byte {first} do not fit the part's {} bytes",
1306 part.len()
1307 ),
1308 )
1309 })?;
1310 let mut out = Vec::with_capacity(n);
1311 for i in 0..n {
1312 let e = i
1313 .checked_mul(32)
1314 .and_then(|o| o.checked_add(first))
1315 .filter(|&e| e < table)
1316 .ok_or_else(|| error(32, "signature element offset"))?;
1317 let name_at = field(e, 4)? as usize;
1318 let tail = part
1319 .get(name_at..)
1320 .ok_or_else(|| error(bit(name_at), "signature name offset"))?;
1321 let end = tail
1322 .iter()
1323 .position(|&b| b == 0)
1324 .filter(|&n| n <= 256)
1325 .ok_or_else(|| error(bit(name_at), "signature name"))?;
1326 let name = tail
1327 .get(..end)
1328 .and_then(|name| std::str::from_utf8(name).ok())
1329 .ok_or_else(|| error(bit(name_at), "signature name is not UTF-8"))?;
1330 let [mask, read_write_mask] = v
1331 .array::<2>(
1332 e.checked_add(24)
1333 .ok_or_else(|| error(bit(e), "signature element offset"))?,
1334 )
1335 .map_err(|x| error(bit(e), x.to_string()))?;
1336 out.push(SignatureElement {
1337 stream: field(e, 0)?,
1338 name: name.to_owned(),
1339 index: field(e, 8)?,
1340 system_value: field(e, 12)?,
1341 component_type: field(e, 16)?,
1342 register: field(e, 20)?,
1343 mask,
1344 read_write_mask,
1345 bit: bit(e),
1346 });
1347 }
1348 Ok(out)
1349}