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)]
867pub struct Binding {
868 pub range: Range,
870 pub name: String,
872 pub ty: Option<TypeId>,
874 pub sample_count: u32,
876}
877
878impl Binding {
879 fn view(&self) -> bool {
881 matches!(self.range.class, 0 | 1)
882 }
883
884 pub fn class_name(&self) -> &'static str {
886 match self.range.class {
887 0 => "texture",
888 1 => "UAV",
889 2 => "cbuffer",
890 _ => "sampler",
891 }
892 }
893
894 pub fn format_name(&self) -> &'static str {
897 match (self.view(), self.range.extra) {
898 (false, _) => "NA",
899 (true, 11) => "byte",
900 (true, 12) => "struct",
901 (true, _) => component_name(self.range.element.unwrap_or(0)),
902 }
903 }
904
905 pub fn dimension_name(&self) -> String {
909 let r = &self.range;
910 match (self.view(), r.extra) {
911 (false, _) => "NA".to_owned(),
912 (true, 11 | 12) if r.class == 0 => "r/o".to_owned(),
913 (true, 11 | 12) if r.counter => "r/w+cnt".to_owned(),
914 (true, 11 | 12) => "r/w".to_owned(),
915 (true, 3 | 8) if self.sample_count != 0 => {
916 format!("{}{}", dimension_name(r.extra), self.sample_count)
917 }
918 (true, kind) => dimension_name(kind).to_owned(),
919 }
920 }
921
922 pub fn id_name(&self) -> String {
924 let letter = match self.range.class {
925 0 => "T",
926 1 => "U",
927 2 => "CB",
928 _ => "S",
929 };
930 format!("{letter}{}", self.range.id)
931 }
932
933 pub fn bind_name(&self) -> String {
935 let letter = match self.range.class {
936 0 => "t",
937 1 => "u",
938 2 => "cb",
939 _ => "s",
940 };
941 if self.range.space == 0 {
942 format!("{letter}{}", self.range.lower)
943 } else {
944 format!("{letter}{},space{}", self.range.lower, self.range.space)
945 }
946 }
947}
948
949pub fn bindings(m: &Module) -> Result<Vec<Binding>> {
952 let extras = resource_extras(m)?;
953 let mut all: Vec<Binding> = ranges(m)?
954 .into_iter()
955 .map(|(range, name, ty)| {
956 let sample_count = extras
957 .iter()
958 .find(|e| e.class == range.class && e.id == range.id)
959 .and_then(|e| e.sample_count)
960 .unwrap_or(0);
961 Binding {
962 range,
963 name,
964 ty,
965 sample_count,
966 }
967 })
968 .collect();
969 let order = |class: u8| match class {
970 2 => 0,
971 3 => 1,
972 0 => 2,
973 _ => 3,
974 };
975 all.sort_by_key(|b| order(b.range.class));
977 Ok(all)
978}
979
980#[derive(Clone, Debug, PartialEq, Eq)]
982pub struct FieldAnnotation {
983 pub name: String,
985 pub offset: u32,
987 pub component: Option<u32>,
989 pub matrix: Option<(u32, u32, u32)>,
992}
993
994#[derive(Clone, Debug, PartialEq, Eq)]
996pub struct StructAnnotation {
997 pub size: u32,
999 pub fields: Vec<FieldAnnotation>,
1001}
1002
1003pub fn fields(m: &Module, t: TypeId) -> Result<Vec<FieldAnnotation>> {
1006 Ok(struct_annotation(m, t)?.map(|a| a.fields).unwrap_or_default())
1007}
1008
1009pub fn struct_annotation(m: &Module, t: TypeId) -> Result<Option<StructAnnotation>> {
1013 let Some((at, annotations)) = named(m, "dx.typeAnnotations") else {
1014 return Ok(None);
1015 };
1016 let field_count = match m.types.get(t) {
1017 Some(Type::Struct { elements, .. }) => elements.len(),
1018 _ => return Ok(None),
1019 };
1020 for &a in annotations {
1021 let a = Node::new(m, at, a, "type annotations")?;
1022 if a.int(0, "annotation kind")? != 0 {
1024 continue;
1025 }
1026 let (pairs, rest) = a.ops.get(1..).unwrap_or_default().as_chunks::<2>();
1027 if !rest.is_empty() {
1028 return Err(a.fail("a struct type without its annotation"));
1029 }
1030 for [struct_type, annotation] in pairs {
1031 let ty = match struct_type.and_then(|i| m.metadata.get(i)) {
1032 Some(Metadata::Value { ty, .. }) => *ty,
1033 _ => continue,
1034 };
1035 if ty != t {
1036 continue;
1037 }
1038 let annotation = a.node(*annotation, "struct annotation")?;
1039 let fields = match annotation.ops.len() {
1041 1 => &[][..],
1042 n if n == field_count.saturating_add(1) || n == field_count.saturating_add(2) => {
1043 annotation.ops.get(1..=field_count).unwrap_or_default()
1044 }
1045 n => {
1046 return Err(annotation.fail(format!("{n} operands for a struct of {field_count} fields")));
1047 }
1048 };
1049 let fields = fields
1050 .iter()
1051 .map(|f| {
1052 let f = annotation.node(*f, "field annotation")?;
1053 let (mut name, mut offset, mut component, mut matrix) = (String::new(), 0, None, None);
1054 let (tags, rest) = f.ops.as_chunks::<2>();
1055 if !rest.is_empty() {
1056 return Err(f.fail("a tag without its value"));
1057 }
1058 for [tag, value] in tags {
1059 match f.int_of(*tag, "field tag")? {
1060 6 => name = f.string_of(*value, "field name")?,
1061 3 => offset = f.number_of(*value, "field offset")?,
1062 2 => {
1063 let ops = f.node(*value, "matrix annotation")?;
1064 matrix = Some((
1065 ops.number(0, "matrix rows")?,
1066 ops.number(1, "matrix columns")?,
1067 ops.number(2, "matrix orientation")?,
1068 ));
1069 }
1070 7 => component = Some(f.number_of(*value, "field component type")?),
1071 _ => {}
1072 }
1073 }
1074 Ok(FieldAnnotation {
1075 name,
1076 offset,
1077 component,
1078 matrix,
1079 })
1080 })
1081 .collect::<Result<Vec<_>>>()?;
1082 return Ok(Some(StructAnnotation {
1083 size: annotation.number(0, "struct size")?,
1084 fields,
1085 }));
1086 }
1087 }
1088 Ok(None)
1089}
1090
1091pub fn shader_model(m: &Module) -> Result<Option<(String, u32, u32)>> {
1094 let Some((at, [model])) = named(m, "dx.shaderModel") else {
1095 return Ok(None);
1096 };
1097 let model = Node::new(m, at, *model, "dx.shaderModel")?;
1098 Ok(Some((
1099 model.string_of(model.op(0)?, "shader kind")?,
1100 model.number(1, "shader model major")?,
1101 model.number(2, "shader model minor")?,
1102 )))
1103}
1104
1105pub fn view_id_state(m: &Module) -> Result<Option<Vec<u32>>> {
1108 let Some((at, [state])) = named(m, "dx.viewIdState") else {
1109 return Ok(None);
1110 };
1111 let state = Node::new(m, at, *state, "dx.viewIdState")?;
1112 let value = match state.op(0)?.and_then(|i| m.metadata.get(i)) {
1113 Some(Metadata::Value { value, .. }) => *value,
1114 _ => return Err(state.fail("expected an array constant")),
1115 };
1116 let len = match m.value_types.get(value).and_then(|t| m.types.get(*t)) {
1117 Some(Type::Array(n, _)) => {
1118 usize::try_from(*n).map_err(|_| state.fail("an array past the address space"))?
1119 }
1120 _ => return Err(state.fail("expected an i32 array")),
1121 };
1122 let word = |v: &Value| -> Result<u32> {
1123 match v {
1124 Value::Constant(Constant::Integer(v)) => Ok(u32::try_from(v & 0xFFFF_FFFF).unwrap_or(0)),
1126 Value::Constant(Constant::Null) => Ok(0),
1127 _ => Err(state.fail("an element that is not an integer constant")),
1128 }
1129 };
1130 match m.values.get(value) {
1131 Some(Value::Constant(Constant::Null)) => Ok(Some(vec![0; len])),
1132 Some(Value::Constant(Constant::Data(words))) => {
1133 Ok(Some(words.iter().map(|w| (w & 0xFFFF_FFFF) as u32).collect()))
1134 }
1135 Some(Value::Constant(Constant::Aggregate(ids))) => ids
1136 .iter()
1137 .map(|id| word(m.values.get(*id).unwrap_or(&Value::Local)))
1138 .collect::<Result<Vec<_>>>()
1139 .map(Some),
1140 _ => Err(state.fail("expected an array constant")),
1141 }
1142}
1143
1144#[derive(Clone, Debug, PartialEq, Eq)]
1146pub struct SignatureElement {
1147 pub stream: u32,
1149 pub name: String,
1151 pub index: u32,
1153 pub system_value: u32,
1155 pub component_type: u32,
1157 pub register: u32,
1159 pub mask: u8,
1161 pub read_write_mask: u8,
1163 pub bit: usize,
1165}
1166
1167impl SignatureElement {
1168 pub fn system_value_name(&self) -> &'static str {
1171 match self.system_value {
1172 1 => "POS",
1173 2 => "CLIPDST",
1174 3 => "CULLDST",
1175 4 => "RTINDEX",
1176 5 => "VPINDEX",
1177 6 => "VERTID",
1178 7 => "PRIMID",
1179 8 => "INSTID",
1180 9 => "FFACE",
1181 10 => "SAMPLE",
1182 11 => "QUADEDGE",
1183 12 => "QUADINT",
1184 13 => "TRIEDGE",
1185 14 => "TRIINT",
1186 15 => "LINEDET",
1187 16 => "LINEDEN",
1188 23 => "BARYCEN",
1189 24 => "SHDINGRATE",
1190 25 => "CULLPRIM",
1191 64 => "TARGET",
1192 65 => "DEPTH",
1193 66 => "COVERAGE",
1194 67 => "DEPTHGE",
1195 68 => "DEPTHLE",
1196 69 => "STENCILREF",
1197 70 => "INNERCOV",
1198 _ => "NONE",
1199 }
1200 }
1201
1202 pub fn format_name(&self) -> &'static str {
1204 match self.component_type {
1205 1 => "uint",
1206 2 => "int",
1207 3 => "float",
1208 4 => "uint16",
1209 5 => "int16",
1210 6 => "fp16",
1211 7 => "uint64",
1212 8 => "int64",
1213 9 => "double",
1214 _ => "unknown",
1215 }
1216 }
1217}
1218
1219pub fn component_name(component: u32) -> &'static str {
1222 match component {
1223 1 => "i1",
1224 2 => "i16",
1225 3 => "u16",
1226 4 => "i32",
1227 5 => "u32",
1228 6 => "i64",
1229 7 => "u64",
1230 8 => "f16",
1231 9 => "f32",
1232 10 => "f64",
1233 11 => "snorm_f16",
1234 12 => "unorm_f16",
1235 13 => "snorm_f32",
1236 14 => "unorm_f32",
1237 15 => "snorm_f64",
1238 16 => "unorm_f64",
1239 17 => "p32i8",
1240 18 => "p32u8",
1241 _ => "invalid",
1242 }
1243}
1244
1245pub fn dimension_name(kind: u32) -> &'static str {
1248 match kind {
1249 1 => "1d",
1250 2 => "2d",
1251 3 => "2dMS",
1252 4 => "3d",
1253 5 => "cube",
1254 6 => "1darray",
1255 7 => "2darray",
1256 8 => "2darrayMS",
1257 9 => "cubearray",
1258 10 => "buf",
1259 11 => "rawbuf",
1260 12 => "structbuf",
1261 13 => "cbuffer",
1262 14 => "sampler",
1263 15 => "tbuffer",
1264 16 => "ras",
1265 17 => "fbtex2d",
1266 18 => "fbtex2darray",
1267 _ => "invalid",
1268 }
1269}
1270
1271pub fn signature(part: &[u8]) -> Result<Vec<SignatureElement>> {
1275 let bit = |byte: usize| byte.saturating_mul(8);
1276 let v = primase_foundations::bytes::View::new(part);
1277 let word = |at: usize| -> Result<u32> {
1278 v.array::<4>(at)
1279 .map(u32::from_le_bytes)
1280 .map_err(|e| error(bit(at), e.to_string()))
1281 };
1282 let field = |record: usize, delta: usize| -> Result<u32> {
1284 word(
1285 record
1286 .checked_add(delta)
1287 .ok_or_else(|| error(bit(record), "signature element offset"))?,
1288 )
1289 };
1290 let n = word(0)? as usize;
1291 let first = word(4)? as usize;
1292 let table = n
1294 .checked_mul(32)
1295 .and_then(|size| first.checked_add(size))
1296 .filter(|&end| end <= part.len())
1297 .ok_or_else(|| {
1298 error(
1299 0,
1300 format!(
1301 "{n} records of 32 bytes from byte {first} do not fit the part's {} bytes",
1302 part.len()
1303 ),
1304 )
1305 })?;
1306 let mut out = Vec::with_capacity(n);
1307 for i in 0..n {
1308 let e = i
1309 .checked_mul(32)
1310 .and_then(|o| o.checked_add(first))
1311 .filter(|&e| e < table)
1312 .ok_or_else(|| error(32, "signature element offset"))?;
1313 let name_at = field(e, 4)? as usize;
1314 let tail = part
1315 .get(name_at..)
1316 .ok_or_else(|| error(bit(name_at), "signature name offset"))?;
1317 let end = tail
1318 .iter()
1319 .position(|&b| b == 0)
1320 .filter(|&n| n <= 256)
1321 .ok_or_else(|| error(bit(name_at), "signature name"))?;
1322 let name = tail
1323 .get(..end)
1324 .and_then(|name| std::str::from_utf8(name).ok())
1325 .ok_or_else(|| error(bit(name_at), "signature name is not UTF-8"))?;
1326 let [mask, read_write_mask] = v
1327 .array::<2>(
1328 e.checked_add(24)
1329 .ok_or_else(|| error(bit(e), "signature element offset"))?,
1330 )
1331 .map_err(|x| error(bit(e), x.to_string()))?;
1332 out.push(SignatureElement {
1333 stream: field(e, 0)?,
1334 name: name.to_owned(),
1335 index: field(e, 8)?,
1336 system_value: field(e, 12)?,
1337 component_type: field(e, 16)?,
1338 register: field(e, 20)?,
1339 mask,
1340 read_write_mask,
1341 bit: bit(e),
1342 });
1343 }
1344 Ok(out)
1345}