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}