1use crate::module::{Constant, Metadata, MetadataId, Module, Type, TypeId, Value, ValueId};
7use crate::{Result, error};
8
9#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct Element {
13 pub id: u32,
15 pub name: String,
17 pub index: u32,
19 pub component_type: u8,
21 pub system_value: u8,
23 pub interpolation: u8,
25 pub row: u32,
27 pub column: u8,
29 pub rows: u32,
31 pub columns: u8,
33 pub placed: bool,
35}
36#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38pub struct Range {
39 pub class: u8,
41 pub id: u32,
43 pub lower: u32,
45 pub count: u32,
47 pub extra: u32,
50 pub space: u32,
52 pub element: Option<u32>,
54 pub stride: Option<u32>,
56 pub counter: bool,
58 pub variable: Option<ValueId>,
61}
62
63#[derive(Clone, Copy, Debug)]
65struct Node<'a> {
66 m: &'a Module,
67 id: MetadataId,
68 ops: &'a [Option<MetadataId>],
69 what: &'static str,
71}
72
73impl<'a> Node<'a> {
74 fn new(m: &'a Module, at: usize, id: MetadataId, what: &'static str) -> Result<Self> {
76 match m.metadata.get(id) {
77 Some(Metadata::Node(ops)) => Ok(Node { m, id, ops, what }),
78 _ => Err(error(at, format!("{what}: expected a metadata node"))),
79 }
80 }
81 fn bit(&self) -> usize {
82 self.m.metadata_at.get(self.id).copied().unwrap_or(self.m.at)
83 }
84 fn fail(&self, detail: impl std::fmt::Display) -> crate::Error {
86 error(self.bit(), format!("{}: {detail}", self.what))
87 }
88 fn node(&self, id: Option<MetadataId>, what: &'static str) -> Result<Node<'a>> {
90 let id = id.ok_or_else(|| self.fail(format!("{what} is null")))?;
91 Node::new(self.m, self.bit(), id, what)
92 }
93 fn op(&self, i: usize) -> Result<Option<MetadataId>> {
95 self.ops
96 .get(i)
97 .copied()
98 .ok_or_else(|| self.fail(format!("node has no operand {i}")))
99 }
100 fn maybe(&self, i: usize) -> Option<MetadataId> {
102 self.ops.get(i).copied().flatten()
103 }
104 fn int_of(&self, id: Option<MetadataId>, what: &str) -> Result<i64> {
106 match id.and_then(|i| self.m.metadata.get(i)) {
107 Some(Metadata::Value { value, .. }) => match self.m.values.get(*value) {
108 Some(Value::Constant(Constant::Integer(v))) => Ok(*v),
109 Some(Value::Constant(Constant::Null)) => Ok(0),
111 _ => Err(self.fail(format!("{what}: expected an integer constant"))),
112 },
113 _ => Err(self.fail(format!("{what}: expected an integer"))),
114 }
115 }
116 fn int(&self, i: usize, what: &str) -> Result<i64> {
117 self.int_of(self.op(i)?, what)
118 }
119 fn number_of<T: TryFrom<i64>>(&self, id: Option<MetadataId>, what: &str) -> Result<T> {
121 let v = self.int_of(id, what)?;
122 T::try_from(v).map_err(|_| self.fail(format!("{what}: {v} is out of range")))
123 }
124 fn number<T: TryFrom<i64>>(&self, i: usize, what: &str) -> Result<T> {
125 self.number_of(self.op(i)?, what)
126 }
127 fn string_of(&self, id: Option<MetadataId>, what: &str) -> Result<String> {
129 match id.and_then(|i| self.m.metadata.get(i)) {
130 Some(Metadata::String(s)) => std::str::from_utf8(s).map(str::to_owned).map_err(|e| {
131 self.fail(format!(
132 "{what}: not UTF-8 (valid up to byte {})",
133 e.valid_up_to()
134 ))
135 }),
136 _ => Ok(String::new()),
137 }
138 }
139}
140
141fn named<'a>(m: &'a Module, name: &str) -> Option<(usize, &'a [MetadataId])> {
143 let i = m.named_metadata.iter().position(|(n, _)| n == name)?;
144 let (_, ids) = m.named_metadata.get(i)?;
145 Some((
146 m.named_metadata_at.get(i).copied().unwrap_or(m.at),
147 ids.as_slice(),
148 ))
149}
150
151pub fn library_kind(m: &Module) -> Result<u32> {
154 let entry = the_entry(m)?;
155 let properties = entry
156 .node(entry.maybe(4), "entry point properties")
157 .map_err(|_| entry.fail("a library entry point without properties"))?;
158 for pair in properties.ops.chunks(2) {
159 let [tag, value] = pair else {
160 return Err(properties.fail("a tag without a value"));
161 };
162 if properties.int_of(*tag, "property tag")? == 8 {
163 return properties.number_of(*value, "shader kind");
164 }
165 }
166 Err(properties.fail("a library entry point that states no shader kind"))
167}
168
169fn the_entry(m: &Module) -> Result<Node<'_>> {
171 let (at, entries) =
172 named(m, "dx.entryPoints").ok_or_else(|| error(m.at, "the module has no dx.entryPoints metadata"))?;
173 let mut with_function = Vec::new();
174 for &id in entries {
175 let entry = Node::new(m, at, id, "entry point")?;
176 if entry.op(0)?.is_some() {
177 with_function.push(entry);
178 }
179 }
180 match with_function.as_slice() {
181 [entry] => Ok(*entry),
182 _ => Err(error(
183 at,
184 format!(
185 "dx.entryPoints: expected exactly one entry point with a function, found {}",
186 with_function.len()
187 ),
188 )),
189 }
190}
191
192pub fn entry(m: &Module) -> Result<ValueId> {
194 let entry = the_entry(m)?;
195 match entry.op(0)?.and_then(|i| m.metadata.get(i)) {
196 Some(Metadata::Value { value, .. }) => Ok(*value),
197 _ => Err(entry.fail("operand 0 names no function")),
198 }
199}
200
201pub fn num_threads(m: &Module) -> Result<Option<[u32; 3]>> {
204 let entry = the_entry(m)?;
205 let Some(properties) = entry.maybe(4) else {
206 return Ok(None);
207 };
208 let properties = entry.node(Some(properties), "entry point properties")?;
209 for pair in properties.ops.chunks(2) {
210 let [tag, value] = pair else {
211 return Err(properties.fail("a tag without a value"));
212 };
213 let tag = properties.int_of(*tag, "property tag")?;
214 if tag != 4 && tag != 9 {
215 continue;
216 }
217 let mut size = properties.node(*value, "numthreads")?;
218 if tag == 9 {
220 size = size.node(size.op(0)?, "numthreads")?;
221 }
222 return Ok(Some([
223 size.number(0, "numthreads x")?,
224 size.number(1, "numthreads y")?,
225 size.number(2, "numthreads z")?,
226 ]));
227 }
228 Ok(None)
229}
230
231pub fn shader_flags(m: &Module) -> Result<Option<u64>> {
235 let entry = the_entry(m)?;
236 let Some(properties) = entry.maybe(4) else {
237 return Ok(None);
238 };
239 let properties = entry.node(Some(properties), "entry point properties")?;
240 for pair in properties.ops.chunks(2) {
241 let [tag, value] = pair else {
242 return Err(properties.fail("a tag without a value"));
243 };
244 if properties.int_of(*tag, "property tag")? == 0 {
245 let flags = properties.int_of(*value, "shader flags")?;
246 return Ok(Some(u64::from_le_bytes(flags.to_le_bytes())));
247 }
248 }
249 Ok(None)
250}
251
252pub fn usage_masks(m: &Module) -> Result<[Vec<Option<u8>>; 3]> {
258 let entry = the_entry(m)?;
259 let Some(signatures) = entry.op(2)? else {
260 return Ok([Vec::new(), Vec::new(), Vec::new()]);
261 };
262 let signatures = entry.node(Some(signatures), "signatures")?;
263 let list = |i: usize| -> Result<Vec<Option<u8>>> {
264 let Some(list) = signatures.op(i)? else {
265 return Ok(Vec::new());
266 };
267 let list = signatures.node(Some(list), "signature")?;
268 list.ops
269 .iter()
270 .map(|e| {
271 let e = list.node(*e, "signature element")?;
272 let Some(tail) = e.maybe(10) else {
273 return Ok(None);
274 };
275 let tail = e.node(Some(tail), "element tags")?;
276 let (pairs, rest) = tail.ops.as_chunks::<2>();
277 if !rest.is_empty() {
278 return Err(tail.fail("a tag without its value"));
279 }
280 let mut mask = None;
281 for [tag, value] in pairs {
282 if tail.int_of(*tag, "element tag")? == 3 {
283 mask = Some(tail.number_of(*value, "usage mask")?);
284 }
285 }
286 Ok(mask)
287 })
288 .collect()
289 };
290 Ok([list(0)?, list(1)?, list(2)?])
291}
292
293pub fn elements(m: &Module) -> Result<(Vec<Element>, Vec<Element>)> {
295 let [inputs, outputs, _] = signature_lists(m)?;
296 Ok((inputs, outputs))
297}
298
299pub fn patch_elements(m: &Module) -> Result<Vec<Element>> {
301 let [_, _, patch] = signature_lists(m)?;
302 Ok(patch)
303}
304
305#[derive(Clone, Debug, PartialEq)]
308pub struct HullState {
309 pub patch_constant: ValueId,
311 pub input_control_points: u32,
313 pub output_control_points: u32,
315 pub domain: u32,
317 pub partitioning: u32,
319 pub output_primitive: u32,
321 pub max_tess_factor: f32,
323}
324
325fn entry_property(m: &Module, wanted: i64) -> Result<Option<Node<'_>>> {
327 let entry = the_entry(m)?;
328 let Some(properties) = entry.maybe(4) else {
329 return Ok(None);
330 };
331 let properties = entry.node(Some(properties), "entry point properties")?;
332 for pair in properties.ops.chunks(2) {
333 let [tag, value] = pair else {
334 return Err(properties.fail("a tag without a value"));
335 };
336 if properties.int_of(*tag, "property tag")? == wanted {
337 return properties.node(*value, "property").map(Some);
338 }
339 }
340 Ok(None)
341}
342
343#[derive(Clone, Copy, Debug, PartialEq, Eq)]
348pub struct GeometryState {
349 pub input: u32,
351 pub max_vertices: u32,
353 pub streams: u32,
355 pub topology: u32,
357 pub instances: u32,
359}
360
361pub fn geometry_state(m: &Module) -> Result<GeometryState> {
363 let state = entry_property(m, 1)?.ok_or_else(|| {
364 the_entry(m).map_or_else(
365 |e| e,
366 |e| e.fail("a geometry program without its state (properties tag 1)"),
367 )
368 })?;
369 Ok(GeometryState {
370 input: state.number(0, "geometry input primitive")?,
371 max_vertices: state.number(1, "geometry maximum vertices")?,
372 streams: state.number(2, "geometry streams")?,
373 topology: state.number(3, "geometry topology")?,
374 instances: state.number(4, "geometry instances")?,
375 })
376}
377
378#[derive(Clone, Copy, Debug, PartialEq, Eq)]
382pub struct MeshState {
383 pub max_vertices: u32,
385 pub max_primitives: u32,
387 pub topology: u32,
389 pub payload: u32,
391}
392
393pub fn mesh_state(m: &Module) -> Result<MeshState> {
395 let state = entry_property(m, 9)?.ok_or_else(|| {
396 the_entry(m).map_or_else(
397 |e| e,
398 |e| e.fail("a mesh program without its state (properties tag 9)"),
399 )
400 })?;
401 let field = |k: usize, what: &str| -> Result<u32> {
403 match state.op(k)? {
404 None => Ok(0),
405 some => state.number_of(some, what),
406 }
407 };
408 Ok(MeshState {
409 max_vertices: field(1, "mesh maximum vertices")?,
410 max_primitives: field(2, "mesh maximum primitives")?,
411 topology: field(3, "mesh topology")?,
412 payload: field(4, "mesh payload size")?,
413 })
414}
415
416pub fn hull_state(m: &Module) -> Result<HullState> {
418 let state = entry_property(m, 3)?.ok_or_else(|| {
419 the_entry(m).map_or_else(
420 |e| e,
421 |e| e.fail("a hull program without its patch state (properties tag 3)"),
422 )
423 })?;
424 let small = |k: usize, what: &str| -> Result<u32> { state.number_of(state.maybe(k), what) };
425 let patch_constant = match state.maybe(0).and_then(|i| m.metadata.get(i)) {
426 Some(Metadata::Value { value, .. }) => *value,
427 _ => return Err(state.fail("a hull program without its patch-constant function")),
428 };
429 let max = match state.maybe(6).and_then(|i| m.metadata.get(i)) {
430 Some(Metadata::Value { value, .. }) => match m.values.get(*value) {
431 Some(Value::Constant(Constant::Float(bits))) => {
432 let bits = u32::try_from(*bits)
433 .map_err(|_| state.fail("the maximum tessellation factor is wider than a float"))?;
434 f32::from_bits(bits)
435 }
436 _ => return Err(state.fail("the maximum tessellation factor is not a float")),
437 },
438 _ => return Err(state.fail("hull state without a maximum tessellation factor")),
439 };
440 Ok(HullState {
441 patch_constant,
442 input_control_points: small(1, "hull input control points")?,
443 output_control_points: small(2, "hull output control points")?,
444 domain: small(3, "hull domain")?,
445 partitioning: small(4, "hull partitioning")?,
446 output_primitive: small(5, "hull output primitive")?,
447 max_tess_factor: max,
448 })
449}
450
451pub fn domain_state(m: &Module) -> Result<(u32, u32)> {
453 let state = entry_property(m, 2)?.ok_or_else(|| {
454 the_entry(m).map_or_else(
455 |e| e,
456 |e| e.fail("a domain program without its patch state (properties tag 2)"),
457 )
458 })?;
459 Ok((
460 state.number_of(state.maybe(0), "domain")?,
461 state.number_of(state.maybe(1), "domain input control points")?,
462 ))
463}
464
465fn signature_lists(m: &Module) -> Result<[Vec<Element>; 3]> {
466 let entry = the_entry(m)?;
467 let Some(signatures) = entry.op(2)? else {
468 return Ok([Vec::new(), Vec::new(), Vec::new()]);
469 };
470 let signatures = entry.node(Some(signatures), "signatures")?;
471 let list = |i: usize| -> Result<Vec<Element>> {
472 let Some(list) = signatures.op(i)? else {
473 return Ok(Vec::new());
474 };
475 let list = signatures.node(Some(list), "signature")?;
476 list.ops
477 .iter()
478 .map(|e| {
479 let e = list.node(*e, "signature element")?;
480 let row = e.int(8, "row")?;
481 let element = Element {
482 id: e.number(0, "element id")?,
483 name: e.string_of(e.op(1)?, "element name")?,
484 index: match e.op(4)? {
485 Some(list) => match e.node(Some(list), "semantic indices")?.ops.first() {
486 Some(first) => e.number_of(*first, "semantic index")?,
487 None => 0,
488 },
489 None => 0,
490 },
491 component_type: e.number(2, "component type")?,
492 system_value: e.number(3, "system value")?,
493 interpolation: e.number(5, "interpolation")?,
494 rows: e.number(6, "rows")?,
495 columns: e.number(7, "columns")?,
496 placed: row >= 0,
497 row: u32::try_from(row.max(0))
498 .map_err(|_| e.fail(format!("row {row} is out of range")))?,
499 column: u8::try_from(e.int(9, "column")?.max(0))
500 .map_err(|_| e.fail("column is out of range"))?,
501 };
502 if element.rows == 0
504 || element.columns == 0
505 || u32::from(element.column)
506 .checked_add(u32::from(element.columns))
507 .is_none_or(|end| end > 4)
508 || element.row.checked_add(element.rows).is_none_or(|end| end > 32)
509 {
510 return Err(e.fail("the element lies outside the 32 four-lane registers"));
511 }
512 Ok(element)
513 })
514 .collect()
515 };
516 Ok([list(0)?, list(1)?, list(2)?])
517}
518
519fn tags(resource: Node<'_>, list: Option<MetadataId>) -> Result<(Option<u32>, Option<u32>)> {
521 let Some(list) = list else {
522 return Ok((None, None));
523 };
524 let ops = resource.node(Some(list), "resource tags")?;
525 let (pairs, rest) = ops.ops.as_chunks::<2>();
526 if !rest.is_empty() {
527 return Err(ops.fail("a tag without its value"));
528 }
529 let (mut element, mut stride) = (None, None);
530 for [tag, value] in pairs {
531 let value = ops.number_of(*value, "tag value")?;
532 match ops.int_of(*tag, "tag")? {
533 0 => element = Some(value),
534 1 => stride = Some(value),
535 _ => {}
536 }
537 }
538 Ok((element, stride))
539}
540
541fn variable_of(m: &Module, id: Option<MetadataId>) -> Option<ValueId> {
543 let Some(Metadata::Value { value, .. }) = id.and_then(|i| m.metadata.get(i)) else {
544 return None;
545 };
546 match m.values.get(*value)? {
547 Value::Constant(Constant::Cast { value, .. }) => Some(*value),
548 Value::Global { .. } => Some(*value),
549 _ => None,
550 }
551}
552
553pub fn ranges(m: &Module) -> Result<Vec<(Range, String, Option<TypeId>)>> {
556 let Some((at, [resources])) = named(m, "dx.resources") else {
557 return Ok(Vec::new());
558 };
559 let lists = Node::new(m, at, *resources, "dx.resources")?;
560 let mut out = Vec::new();
561 for (class, list) in lists.ops.iter().enumerate() {
562 let Some(list) = list else { continue };
563 let class =
564 u8::try_from(class).map_err(|_| lists.fail(format!("resource class {class} is out of range")))?;
565 let list = lists.node(Some(*list), "resource list")?;
566 for r in list.ops {
567 let r = list.node(*r, "resource")?;
568 let ty = match r.op(1)?.and_then(|i| m.metadata.get(i)) {
570 Some(Metadata::Value { ty, .. }) => match m.types.get(*ty) {
571 Some(Type::Pointer(t, _)) => Some(*t),
572 _ => None,
573 },
574 _ => None,
575 };
576 let extra = r.number(6, "resource field")?;
577 let (element, stride) = match class {
578 0 => tags(r, r.maybe(8))?,
579 1 => tags(r, r.maybe(10))?,
580 _ => (None, None),
581 };
582 out.push((
583 Range {
584 class,
585 id: r.number(0, "resource id")?,
586 lower: r.number(4, "lower bound")?,
587 count: {
589 let v = r.int(5, "range size")?;
590 u32::try_from(v)
591 .or_else(|_| i32::try_from(v).map(|v| u32::from_ne_bytes(v.to_ne_bytes())))
592 .map_err(|_| r.fail(format!("range size {v} is out of range")))?
593 },
594 extra,
595 space: r.number(3, "register space")?,
596 element,
597 stride,
598 counter: class == 1 && r.maybe(8).is_some() && r.int(8, "counter")? != 0,
600 variable: variable_of(m, r.op(1)?),
601 },
602 r.string_of(r.op(2)?, "resource name")?,
603 ty,
604 ));
605 }
606 }
607 Ok(out)
608}
609
610#[derive(Clone, Debug, PartialEq, Eq)]
612pub struct FieldAnnotation {
613 pub name: String,
615 pub offset: u32,
617 pub component: Option<u32>,
619 pub matrix: Option<(u32, u32, u32)>,
622}
623
624pub fn fields(m: &Module, t: TypeId) -> Result<Vec<FieldAnnotation>> {
627 let Some((at, annotations)) = named(m, "dx.typeAnnotations") else {
628 return Ok(Vec::new());
629 };
630 for &a in annotations {
631 let a = Node::new(m, at, a, "type annotations")?;
632 if a.int(0, "annotation kind")? != 0 {
634 continue;
635 }
636 let (pairs, rest) = a.ops.get(1..).unwrap_or_default().as_chunks::<2>();
637 if !rest.is_empty() {
638 return Err(a.fail("a struct type without its annotation"));
639 }
640 for [struct_type, annotation] in pairs {
641 let ty = match struct_type.and_then(|i| m.metadata.get(i)) {
642 Some(Metadata::Value { ty, .. }) => *ty,
643 _ => continue,
644 };
645 if ty != t {
646 continue;
647 }
648 let annotation = a.node(*annotation, "struct annotation")?;
649 let fields = annotation
651 .ops
652 .get(1..)
653 .ok_or_else(|| annotation.fail("a struct annotation without its first operand"))?;
654 return fields
655 .iter()
656 .map(|f| {
657 let f = annotation.node(*f, "field annotation")?;
658 let (mut name, mut offset, mut component, mut matrix) = (String::new(), 0, None, None);
659 let (tags, rest) = f.ops.as_chunks::<2>();
660 if !rest.is_empty() {
661 return Err(f.fail("a tag without its value"));
662 }
663 for [tag, value] in tags {
664 match f.int_of(*tag, "field tag")? {
665 6 => name = f.string_of(*value, "field name")?,
666 3 => offset = f.number_of(*value, "field offset")?,
667 2 => {
668 let ops = f.node(*value, "matrix annotation")?;
669 matrix = Some((
670 ops.number(0, "matrix rows")?,
671 ops.number(1, "matrix columns")?,
672 ops.number(2, "matrix orientation")?,
673 ));
674 }
675 7 => component = Some(f.number_of(*value, "field component type")?),
676 _ => {}
677 }
678 }
679 Ok(FieldAnnotation {
680 name,
681 offset,
682 component,
683 matrix,
684 })
685 })
686 .collect();
687 }
688 }
689 Ok(Vec::new())
690}
691
692#[derive(Clone, Debug, PartialEq, Eq)]
694pub struct SignatureElement {
695 pub name: String,
697 pub index: u32,
699 pub system_value: u32,
701 pub component_type: u32,
703 pub register: u32,
705 pub mask: u8,
707 pub read_write_mask: u8,
709 pub bit: usize,
711}
712
713pub fn signature(part: &[u8]) -> Result<Vec<SignatureElement>> {
717 let bit = |byte: usize| byte.saturating_mul(8);
718 let v = primase_foundations::bytes::View::new(part);
719 let word = |at: usize| -> Result<u32> {
720 v.array::<4>(at)
721 .map(u32::from_le_bytes)
722 .map_err(|e| error(bit(at), e.to_string()))
723 };
724 let field = |record: usize, delta: usize| -> Result<u32> {
726 word(
727 record
728 .checked_add(delta)
729 .ok_or_else(|| error(bit(record), "signature element offset"))?,
730 )
731 };
732 let n = word(0)? as usize;
733 let first = word(4)? as usize;
734 let table = n
736 .checked_mul(32)
737 .and_then(|size| first.checked_add(size))
738 .filter(|&end| end <= part.len())
739 .ok_or_else(|| {
740 error(
741 0,
742 format!(
743 "{n} records of 32 bytes from byte {first} do not fit the part's {} bytes",
744 part.len()
745 ),
746 )
747 })?;
748 let mut out = Vec::with_capacity(n);
749 for i in 0..n {
750 let e = i
751 .checked_mul(32)
752 .and_then(|o| o.checked_add(first))
753 .filter(|&e| e < table)
754 .ok_or_else(|| error(32, "signature element offset"))?;
755 let name_at = field(e, 4)? as usize;
756 let tail = part
757 .get(name_at..)
758 .ok_or_else(|| error(bit(name_at), "signature name offset"))?;
759 let end = tail
760 .iter()
761 .position(|&b| b == 0)
762 .filter(|&n| n <= 256)
763 .ok_or_else(|| error(bit(name_at), "signature name"))?;
764 let name = tail
765 .get(..end)
766 .and_then(|name| std::str::from_utf8(name).ok())
767 .ok_or_else(|| error(bit(name_at), "signature name is not UTF-8"))?;
768 let [mask, read_write_mask] = v
769 .array::<2>(
770 e.checked_add(24)
771 .ok_or_else(|| error(bit(e), "signature element offset"))?,
772 )
773 .map_err(|x| error(bit(e), x.to_string()))?;
774 out.push(SignatureElement {
775 name: name.to_owned(),
776 index: field(e, 8)?,
777 system_value: field(e, 12)?,
778 component_type: field(e, 16)?,
779 register: field(e, 20)?,
780 mask,
781 read_write_mask,
782 bit: bit(e),
783 });
784 }
785 Ok(out)
786}