Skip to main content

dxbc_reader/
controls.rs

1//! Typed views of the control bits of an opcode token: the bits 11 to 23 that [`Instruction::control`] and
2//! [`Declaration::control`] keep as stored, read as the fields the token format gives them for the opcodes that use them.
3//! Only the bits some program of the games read carries are named: the `precise` mask of an arithmetic instruction, the
4//! flags of a `sync`, the return type of `resinfo` and `sample_info`, the flags of `dcl_globalFlags`, and the values the
5//! geometry and tessellation declarations hold in their control bits or in the word after the token.
6use crate::decode::{Declaration, Declared, Instruction};
7
8/// The low byte of `v`.
9fn low8(v: u32) -> u8 {
10    let [byte, ..] = v.to_le_bytes();
11    byte
12}
13
14/// What a `sync` instruction waits for (control bits 0 to 3; the listing of Microsoft's disassembler adds a suffix for
15/// each: `_t`, `_g`, `_ugroup`, `_uglobal`).
16#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
17pub struct SyncFlags {
18    /// Bit 0: every thread of the group reaches the instruction first (`_t`).
19    pub threads_in_group: bool,
20    /// Bit 1: group shared memory accesses are complete (`_g`).
21    pub group_shared_memory: bool,
22    /// Bit 2: unordered-access view accesses are complete within the group (`_ugroup`).
23    pub uav_group: bool,
24    /// Bit 3: unordered-access view accesses are complete across all groups (`_uglobal`).
25    pub uav_global: bool,
26}
27
28/// The type a `resinfo` or `sample_info` instruction returns (the `_uint` and `_rcpfloat` suffixes of Microsoft's
29/// listing; none suffix is a float).
30#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31pub enum InfoReturn {
32    /// A float.
33    Float,
34    /// The reciprocal of a float (`resinfo_rcpfloat`; the games' programs do not use it).
35    ReciprocalFloat,
36    /// An unsigned integer (`_uint`).
37    Uint,
38}
39
40/// The flags of a `dcl_globalFlags` declaration (its control bits). The names are the ones Microsoft's disassembler
41/// prints; the games' programs set bits 0 to 5.
42#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
43pub struct GlobalFlags {
44    /// Bit 0, `refactoringAllowed`.
45    pub refactoring_allowed: bool,
46    /// Bit 1, `enableDoublePrecisionFloatOps`.
47    pub double_precision: bool,
48    /// Bit 2, `forceEarlyDepthStencil`.
49    pub force_early_depth_stencil: bool,
50    /// Bit 3, `enableRawAndStructuredBuffers`.
51    pub raw_and_structured_buffers: bool,
52    /// Bit 4, `skipOptimization`.
53    pub skip_optimization: bool,
54    /// Bit 5, `enableMinimumPrecision`.
55    pub minimum_precision: bool,
56    /// The bits above bit 5, as stored (shifted down by six).
57    pub other: u32,
58}
59
60impl GlobalFlags {
61    /// The flags of the control bits `control` of a `dcl_globalFlags` declaration.
62    pub fn from_bits(control: u32) -> Self {
63        let bit = |n: u32| (control >> n) & 1 != 0;
64        GlobalFlags {
65            refactoring_allowed: bit(0),
66            double_precision: bit(1),
67            force_early_depth_stencil: bit(2),
68            raw_and_structured_buffers: bit(3),
69            skip_optimization: bit(4),
70            minimum_precision: bit(5),
71            other: control >> 6,
72        }
73    }
74}
75
76const RESINFO: u16 = 61;
77const SAMPLE_INFO: u16 = 111;
78const SYNC: u16 = 190;
79const GS_OUTPUT_TOPOLOGY: u16 = 92;
80const GS_INPUT_PRIMITIVE: u16 = 93;
81const GS_MAX_OUTPUT_VERTICES: u16 = 94;
82const INPUT_CONTROL_POINTS: u16 = 147;
83const OUTPUT_CONTROL_POINTS: u16 = 148;
84const TESS_DOMAIN: u16 = 149;
85const TESS_PARTITIONING: u16 = 150;
86const TESS_OUTPUT_PRIMITIVE: u16 = 151;
87const HS_MAX_TESS_FACTOR: u16 = 152;
88const HS_FORK_INSTANCES: u16 = 153;
89const HS_JOIN_INSTANCES: u16 = 154;
90
91impl Instruction {
92    /// The components the instruction is marked `precise` for (control bits 8 to 11, the token's bits 19 to 22): bit 0 x
93    /// to bit 3 w, zero where it is not marked. The compiler marks every instruction of a `precise` expression.
94    pub fn precise_mask(&self) -> u8 {
95        low8((self.control >> 8) & 15)
96    }
97
98    /// What a `sync` instruction waits for; `None` for any other instruction.
99    pub fn sync_flags(&self) -> Option<SyncFlags> {
100        (self.opcode == SYNC).then(|| {
101            let bit = |n: u32| (self.control >> n) & 1 != 0;
102            SyncFlags {
103                threads_in_group: bit(0),
104                group_shared_memory: bit(1),
105                uav_group: bit(2),
106                uav_global: bit(3),
107            }
108        })
109    }
110
111    /// The type a `resinfo` or `sample_info` returns; `None` for any other instruction, and for a return type the
112    /// token format does not define.
113    pub fn info_return(&self) -> Option<InfoReturn> {
114        match (self.opcode, self.control & 3) {
115            (RESINFO | SAMPLE_INFO, 0) => Some(InfoReturn::Float),
116            (RESINFO, 1) => Some(InfoReturn::ReciprocalFloat),
117            (RESINFO, 2) | (SAMPLE_INFO, 1) => Some(InfoReturn::Uint),
118            _ => None,
119        }
120    }
121}
122
123impl Declaration {
124    /// The flags of a `dcl_globalFlags` declaration; `None` for any other declaration.
125    pub fn global_flags(&self) -> Option<GlobalFlags> {
126        match self.kind {
127            Declared::GlobalFlags(bits) => Some(GlobalFlags::from_bits(bits)),
128            _ => None,
129        }
130    }
131
132    /// The value in the first word after the token of a declaration this reader does not type (the word of
133    /// `dcl_maxout`, `dcl_hs_max_tessfactor` and the phase instance counts).
134    fn first_word(&self, opcode: u16) -> Option<u32> {
135        match &self.kind {
136            Declared::Other { words, .. } if self.opcode == opcode => words.first().copied(),
137            _ => None,
138        }
139    }
140
141    /// The control value of the declaration `opcode`, `mask`ed.
142    fn control_value(&self, opcode: u16, mask: u32) -> Option<u32> {
143        (self.opcode == opcode).then_some(self.control & mask)
144    }
145
146    /// A geometry program's input primitive (`dcl_inputprimitive`; a `D3D_PRIMITIVE`: 1 point, 2 line, 3 triangle, 6 line
147    /// and 7 triangle with adjacency).
148    pub fn geometry_input_primitive(&self) -> Option<u32> {
149        self.control_value(GS_INPUT_PRIMITIVE, 63)
150    }
151
152    /// A geometry program's output topology (`dcl_outputtopology`; a `D3D_PRIMITIVE_TOPOLOGY`: 1 point list, 3 line strip,
153    /// 5 triangle strip).
154    pub fn geometry_output_topology(&self) -> Option<u32> {
155        self.control_value(GS_OUTPUT_TOPOLOGY, 63)
156    }
157
158    /// A geometry program's largest vertex count (`dcl_maxout`).
159    pub fn max_output_vertex_count(&self) -> Option<u32> {
160        self.first_word(GS_MAX_OUTPUT_VERTICES)
161    }
162
163    /// A hull or domain program's input control point count (`dcl_input_control_point_count`).
164    pub fn input_control_point_count(&self) -> Option<u32> {
165        self.control_value(INPUT_CONTROL_POINTS, 63)
166    }
167
168    /// A hull program's output control point count (`dcl_output_control_point_count`).
169    pub fn output_control_point_count(&self) -> Option<u32> {
170        self.control_value(OUTPUT_CONTROL_POINTS, 63)
171    }
172
173    /// The tessellator domain (`dcl_tessellator_domain`; a `D3D11_TESSELLATOR_DOMAIN`: 1 isoline, 2 triangle, 3 quad).
174    pub fn tessellator_domain(&self) -> Option<u32> {
175        self.control_value(TESS_DOMAIN, 7)
176    }
177
178    /// The tessellator partitioning (`dcl_tessellator_partitioning`; a `D3D11_TESSELLATOR_PARTITIONING`: 1 integer, 2 pow2,
179    /// 3 fractional odd, 4 fractional even).
180    pub fn tessellator_partitioning(&self) -> Option<u32> {
181        self.control_value(TESS_PARTITIONING, 7)
182    }
183
184    /// The tessellator output primitive (`dcl_tessellator_output_primitive`; a `D3D11_TESSELLATOR_OUTPUT_PRIMITIVE`: 1
185    /// point, 2 line, 3 triangle clockwise, 4 triangle counter-clockwise).
186    pub fn tessellator_output_primitive(&self) -> Option<u32> {
187        self.control_value(TESS_OUTPUT_PRIMITIVE, 7)
188    }
189
190    /// A hull program's largest tessellation factor (`dcl_hs_max_tessfactor`).
191    pub fn hull_max_tess_factor(&self) -> Option<f32> {
192        self.first_word(HS_MAX_TESS_FACTOR).map(f32::from_bits)
193    }
194
195    /// The instance count of a hull program's fork phase (`dcl_hs_fork_phase_instance_count`).
196    pub fn fork_phase_instance_count(&self) -> Option<u32> {
197        self.first_word(HS_FORK_INSTANCES)
198    }
199
200    /// The instance count of a hull program's join phase (`dcl_hs_join_phase_instance_count`).
201    pub fn join_phase_instance_count(&self) -> Option<u32> {
202        self.first_word(HS_JOIN_INSTANCES)
203    }
204}