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): bit 6 `enable11_1DoubleExtensions`, bit 7
57    /// `enable11_1ShaderExtensions`, bit 8 `allResourcesBound` (a Shader Model 5.1 program built with
58    /// `/all_resources_bound`); bits 9 to 12 have no name.
59    pub other: u32,
60}
61
62impl GlobalFlags {
63    /// The flags of the control bits `control` of a `dcl_globalFlags` declaration.
64    pub fn from_bits(control: u32) -> Self {
65        let bit = |n: u32| (control >> n) & 1 != 0;
66        GlobalFlags {
67            refactoring_allowed: bit(0),
68            double_precision: bit(1),
69            force_early_depth_stencil: bit(2),
70            raw_and_structured_buffers: bit(3),
71            skip_optimization: bit(4),
72            minimum_precision: bit(5),
73            other: control >> 6,
74        }
75    }
76}
77
78const RESINFO: u16 = 61;
79const SAMPLE_INFO: u16 = 111;
80const SYNC: u16 = 190;
81const GS_OUTPUT_TOPOLOGY: u16 = 92;
82const GS_INPUT_PRIMITIVE: u16 = 93;
83const GS_MAX_OUTPUT_VERTICES: u16 = 94;
84const INPUT_CONTROL_POINTS: u16 = 147;
85const OUTPUT_CONTROL_POINTS: u16 = 148;
86const TESS_DOMAIN: u16 = 149;
87const TESS_PARTITIONING: u16 = 150;
88const TESS_OUTPUT_PRIMITIVE: u16 = 151;
89const HS_MAX_TESS_FACTOR: u16 = 152;
90const HS_FORK_INSTANCES: u16 = 153;
91const HS_JOIN_INSTANCES: u16 = 154;
92
93impl Instruction {
94    /// The components the instruction is marked `precise` for (control bits 8 to 11, the token's bits 19 to 22): bit 0 x
95    /// to bit 3 w, zero where it is not marked. The compiler marks every instruction of a `precise` expression.
96    pub fn precise_mask(&self) -> u8 {
97        low8((self.control >> 8) & 15)
98    }
99
100    /// What a `sync` instruction waits for; `None` for any other instruction.
101    pub fn sync_flags(&self) -> Option<SyncFlags> {
102        (self.opcode == SYNC).then(|| {
103            let bit = |n: u32| (self.control >> n) & 1 != 0;
104            SyncFlags {
105                threads_in_group: bit(0),
106                group_shared_memory: bit(1),
107                uav_group: bit(2),
108                uav_global: bit(3),
109            }
110        })
111    }
112
113    /// The type a `resinfo` or `sample_info` returns; `None` for any other instruction, and for a return type the
114    /// token format does not define.
115    pub fn info_return(&self) -> Option<InfoReturn> {
116        match (self.opcode, self.control & 3) {
117            (RESINFO | SAMPLE_INFO, 0) => Some(InfoReturn::Float),
118            (RESINFO, 1) => Some(InfoReturn::ReciprocalFloat),
119            (RESINFO, 2) | (SAMPLE_INFO, 1) => Some(InfoReturn::Uint),
120            _ => None,
121        }
122    }
123}
124
125impl Declaration {
126    /// The flags of a `dcl_globalFlags` declaration; `None` for any other declaration.
127    pub fn global_flags(&self) -> Option<GlobalFlags> {
128        match self.kind {
129            Declared::GlobalFlags(bits) => Some(GlobalFlags::from_bits(bits)),
130            _ => None,
131        }
132    }
133
134    /// The value in the first word after the token of a declaration this reader does not type (the word of
135    /// `dcl_maxout`, `dcl_hs_max_tessfactor` and the phase instance counts).
136    fn first_word(&self, opcode: u16) -> Option<u32> {
137        match &self.kind {
138            Declared::Other { words, .. } if self.opcode == opcode => words.first().copied(),
139            _ => None,
140        }
141    }
142
143    /// The control value of the declaration `opcode`, `mask`ed.
144    fn control_value(&self, opcode: u16, mask: u32) -> Option<u32> {
145        (self.opcode == opcode).then_some(self.control & mask)
146    }
147
148    /// A geometry program's input primitive (`dcl_inputprimitive`; a `D3D_PRIMITIVE`: 1 point, 2 line, 3 triangle, 6 line
149    /// and 7 triangle with adjacency).
150    pub fn geometry_input_primitive(&self) -> Option<u32> {
151        self.control_value(GS_INPUT_PRIMITIVE, 63)
152    }
153
154    /// A geometry program's output topology (`dcl_outputtopology`; a `D3D_PRIMITIVE_TOPOLOGY`: 1 point list, 3 line strip,
155    /// 5 triangle strip).
156    pub fn geometry_output_topology(&self) -> Option<u32> {
157        self.control_value(GS_OUTPUT_TOPOLOGY, 63)
158    }
159
160    /// A geometry program's largest vertex count (`dcl_maxout`).
161    pub fn max_output_vertex_count(&self) -> Option<u32> {
162        self.first_word(GS_MAX_OUTPUT_VERTICES)
163    }
164
165    /// A hull or domain program's input control point count (`dcl_input_control_point_count`).
166    pub fn input_control_point_count(&self) -> Option<u32> {
167        self.control_value(INPUT_CONTROL_POINTS, 63)
168    }
169
170    /// A hull program's output control point count (`dcl_output_control_point_count`).
171    pub fn output_control_point_count(&self) -> Option<u32> {
172        self.control_value(OUTPUT_CONTROL_POINTS, 63)
173    }
174
175    /// The tessellator domain (`dcl_tessellator_domain`; a `D3D11_TESSELLATOR_DOMAIN`: 1 isoline, 2 triangle, 3 quad):
176    /// control bits 0 to 1, as `D3D11_SB_TESS_DOMAIN_MASK` and Microsoft's listing read it.
177    pub fn tessellator_domain(&self) -> Option<u32> {
178        self.control_value(TESS_DOMAIN, 3)
179    }
180
181    /// The tessellator partitioning (`dcl_tessellator_partitioning`; a `D3D11_TESSELLATOR_PARTITIONING`: 1 integer, 2 pow2,
182    /// 3 fractional odd, 4 fractional even).
183    pub fn tessellator_partitioning(&self) -> Option<u32> {
184        self.control_value(TESS_PARTITIONING, 7)
185    }
186
187    /// The tessellator output primitive (`dcl_tessellator_output_primitive`; a `D3D11_TESSELLATOR_OUTPUT_PRIMITIVE`: 1
188    /// point, 2 line, 3 triangle clockwise, 4 triangle counter-clockwise).
189    pub fn tessellator_output_primitive(&self) -> Option<u32> {
190        self.control_value(TESS_OUTPUT_PRIMITIVE, 7)
191    }
192
193    /// A hull program's largest tessellation factor (`dcl_hs_max_tessfactor`).
194    pub fn hull_max_tess_factor(&self) -> Option<f32> {
195        self.first_word(HS_MAX_TESS_FACTOR).map(f32::from_bits)
196    }
197
198    /// The instance count of a hull program's fork phase (`dcl_hs_fork_phase_instance_count`).
199    pub fn fork_phase_instance_count(&self) -> Option<u32> {
200        self.first_word(HS_FORK_INSTANCES)
201    }
202
203    /// The instance count of a hull program's join phase (`dcl_hs_join_phase_instance_count`).
204    pub fn join_phase_instance_count(&self) -> Option<u32> {
205        self.first_word(HS_JOIN_INSTANCES)
206    }
207}