dxbc_reader/decode.rs
1//! The token-level decoder of Shader Model 4 and 5 programs: any stage's version token, declarations as typed
2//! records, and instructions as an opcode, its control bits, its extended tokens and its operands. It decides nothing
3//! about which instructions a caller can use: they are all returned, and the caller refuses what it cannot handle. A token
4//! it cannot read at all (an operand register type it has no name for, a truncated instruction) is an error naming the
5//! word.
6
7use crate::format::{OperandFormat, operand_format};
8use crate::opcodes::NAMES;
9use crate::{Result, error};
10
11/// The low byte of `v`.
12fn low8(v: u32) -> u8 {
13 let [byte, ..] = v.to_le_bytes();
14 byte
15}
16
17/// The low 16 bits of `v`.
18fn low16(v: u32) -> u16 {
19 let [a, b, ..] = v.to_le_bytes();
20 u16::from_le_bytes([a, b])
21}
22
23/// The kind of register an operand names.
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub enum Register {
26 /// A temporary register (`r<n>`).
27 Temp,
28 /// An input register (`v<n>`).
29 Input,
30 /// An output register (`o<n>`).
31 Output,
32 /// An indexable temporary (`x<n>[i]`).
33 IndexableTemp,
34 /// A constant buffer register (`cb<slot>[row]`).
35 Constant,
36 /// An immediate operand (`l(...)`), its values in [`Operand::immediate`].
37 Immediate,
38 /// A 64-bit immediate operand (`d(...)`): its values in [`Operand::immediate`] as pairs of words, low word first
39 /// (two words for one value, four for two).
40 Immediate64,
41 /// A subroutine label (`label <n>`), the operand of `label`, `call` and `callc`.
42 Label,
43 /// A function body of a program with class linkage (`fb<n>`).
44 FunctionBody,
45 /// A function table of a program with class linkage (`ft<n>`).
46 FunctionTable,
47 /// An interface pointer of a program with class linkage (`fp<n>[array][call site]`).
48 Interface,
49 /// A function's input register (`fi<n>`).
50 FunctionInput,
51 /// A function's output register (`fo<n>`).
52 FunctionOutput,
53 /// The `this` pointer of a class instance (`this[<n>]`).
54 ThisPointer,
55 /// `vCycleCounter`, the input clock counter.
56 CycleCounter,
57 /// `vInnerCoverage`, the input inner coverage.
58 InnerCoverage,
59 /// A shader resource view (`t<n>`).
60 Texture,
61 /// A sampler (`s<n>`).
62 Sampler,
63 /// The null register (`null`), a destination that discards its result.
64 Null,
65 /// An unordered-access view (`u<n>`).
66 Uav,
67 /// The immediate constant buffer (`icb[n]`), its rows the program's own.
68 ImmediateBuffer,
69 /// `vPrim`, the primitive id input.
70 PrimitiveId,
71 /// `vCoverage`, the input coverage mask.
72 InputCoverage,
73 /// `oDepth`.
74 OutputDepth,
75 /// `oDepthGE`, a depth output that is at least the rasterized depth.
76 OutputDepthGreaterEqual,
77 /// `oDepthLE`, a depth output that is at most the rasterized depth.
78 OutputDepthLessEqual,
79 /// `oMask`, the coverage output.
80 OutputCoverage,
81 /// `rasterizer`: the render target as `sample_info` and `sample_pos` query it (its sample count, a sample's position).
82 Rasterizer,
83 /// `oStencilRef`, the stencil reference output.
84 OutputStencilRef,
85 /// `g<n>`, thread-group shared memory.
86 GroupSharedMemory,
87 /// `vThreadID`, `vThreadGroupID`, `vThreadIDInGroup` and `vThreadIDInGroupFlattened`, a compute program's ids.
88 ThreadId,
89 /// `vThreadGroupID`.
90 ThreadGroupId,
91 /// `vThreadIDInGroup`.
92 ThreadIdInGroup,
93 /// `vThreadIDInGroupFlattened`.
94 ThreadIdInGroupFlattened,
95 /// `vOutputControlPointID`, `vForkInstanceID`, `vJoinInstanceID`: a hull program's phase numbers.
96 OutputControlPointId,
97 /// `vForkInstanceID`, a hull program's fork phase instance.
98 ForkInstanceId,
99 /// `vJoinInstanceID`, a hull program's join phase instance.
100 JoinInstanceId,
101 /// `vicp[point][register]`: an input control point of a hull or domain program.
102 InputControlPoint,
103 /// `vocp[point][register]`: an output control point a hull program's patch-constant phases read.
104 OutputControlPoint,
105 /// `vpc[register]`: a patch constant a domain program reads (or a hull program's join phase).
106 InputPatchConstant,
107 /// `vDomain`: a domain program's location in its patch.
108 InputDomainPoint,
109 /// `m<n>`: a geometry program's output stream.
110 Stream,
111 /// `vGSInstanceID`.
112 GsInstanceId,
113}
114impl Register {
115 /// How many index dimensions an operand of this register has in Shader Model 4 and 5.0.
116 fn dimensions(self) -> u32 {
117 match self {
118 Register::Immediate
119 | Register::Immediate64
120 | Register::CycleCounter
121 | Register::InnerCoverage
122 | Register::Null
123 | Register::PrimitiveId
124 | Register::InputCoverage
125 | Register::OutputDepth
126 | Register::OutputDepthGreaterEqual
127 | Register::OutputDepthLessEqual
128 | Register::OutputCoverage
129 | Register::Rasterizer
130 | Register::OutputStencilRef
131 | Register::ThreadId
132 | Register::ThreadGroupId
133 | Register::ThreadIdInGroup
134 | Register::ThreadIdInGroupFlattened
135 | Register::OutputControlPointId
136 | Register::ForkInstanceId
137 | Register::JoinInstanceId
138 | Register::InputDomainPoint
139 | Register::GsInstanceId => 0,
140 Register::Constant
141 | Register::IndexableTemp
142 | Register::InputControlPoint
143 | Register::OutputControlPoint
144 | Register::Interface => 2,
145 _ => 1,
146 }
147 }
148 fn from_type(n: u32) -> Option<Self> {
149 Some(match n {
150 0 => Register::Temp,
151 1 => Register::Input,
152 2 => Register::Output,
153 3 => Register::IndexableTemp,
154 4 => Register::Immediate,
155 5 => Register::Immediate64,
156 10 => Register::Label,
157 17 => Register::FunctionBody,
158 18 => Register::FunctionTable,
159 19 => Register::Interface,
160 20 => Register::FunctionInput,
161 21 => Register::FunctionOutput,
162 29 => Register::ThisPointer,
163 40 => Register::CycleCounter,
164 42 => Register::InnerCoverage,
165 6 => Register::Sampler,
166 7 => Register::Texture,
167 8 => Register::Constant,
168 9 => Register::ImmediateBuffer,
169 11 => Register::PrimitiveId,
170 12 => Register::OutputDepth,
171 13 => Register::Null,
172 14 => Register::Rasterizer,
173 15 => Register::OutputCoverage,
174 16 => Register::Stream,
175 22 => Register::OutputControlPointId,
176 23 => Register::ForkInstanceId,
177 24 => Register::JoinInstanceId,
178 25 => Register::InputControlPoint,
179 26 => Register::OutputControlPoint,
180 27 => Register::InputPatchConstant,
181 28 => Register::InputDomainPoint,
182 37 => Register::GsInstanceId,
183 30 => Register::Uav,
184 31 => Register::GroupSharedMemory,
185 32 => Register::ThreadId,
186 33 => Register::ThreadGroupId,
187 34 => Register::ThreadIdInGroup,
188 36 => Register::ThreadIdInGroupFlattened,
189 35 => Register::InputCoverage,
190 38 => Register::OutputDepthGreaterEqual,
191 39 => Register::OutputDepthLessEqual,
192 41 => Register::OutputStencilRef,
193 _ => return None,
194 })
195 }
196}
197
198/// The components of a register an operand selects.
199#[derive(Clone, Copy, Debug, PartialEq, Eq)]
200pub enum Selection {
201 /// A write mask: bit 0 is x, bit 3 is w.
202 Mask(u8),
203 /// A four-component swizzle: for each result lane the source lane (0 x to 3 w) it reads.
204 Swizzle([u8; 4]),
205 /// One source lane (0 x to 3 w).
206 Scalar(u8),
207 /// No selection: the operand has no components (a sampler, a resource).
208 None,
209}
210
211/// An instruction's or declaration's operand.
212#[derive(Clone, Debug)]
213pub struct Operand {
214 /// The kind of register it names.
215 pub register: Register,
216 /// Which components it selects.
217 pub selection: Selection,
218 /// The number of components the operand token states (0, 1 or 4): a one-component operand (`oDepth`, `l(1.0)`)
219 /// and a four-component register selecting one lane (`r0.x`) both have a [`Selection::Scalar`].
220 pub components: u8,
221 /// Its index dimensions, outermost first: none for an immediate, one for `r3`, two for `cb2[5]`.
222 pub indices: Vec<Index>,
223 /// The values of an immediate operand (one lane repeated, or four); zero for other registers.
224 pub immediate: [u32; 4],
225 /// 0 none, 1 negate, 2 absolute, 3 negated absolute (float sources); 4 two's-complement negate (an integer
226 /// instruction's negate; the encoding has no such value, so a caller that knows the instruction reads integers sets it).
227 pub modifier: u8,
228 /// The minimum-precision annotation of the operand's extended token (0 none; 1 float16, 2 float2_8, 4 sint16,
229 /// 5 uint16).
230 pub precision: u8,
231 /// The operand's extended token marks it as a non-uniform resource index.
232 pub non_uniform: bool,
233}
234
235/// One index dimension of an operand: a constant, a relative register, or both added.
236#[derive(Clone, Debug)]
237pub struct Index {
238 /// The constant part of the index.
239 pub base: u32,
240 /// The register whose component is added to `base`, where the index is relative.
241 pub relative: Option<Box<Operand>>,
242}
243
244/// An instruction as stored.
245#[derive(Clone, Debug)]
246pub struct Instruction {
247 /// The instruction's first word in the token stream.
248 pub word: usize,
249 /// The opcode token's low 11 bits (see [`opcode_name`]).
250 pub opcode: u16,
251 /// The opcode token's bits 11 to 23: saturate and the opcode-specific controls (the test of a conditional, a
252 /// `resinfo` return type, the interpolation of a declaration).
253 pub control: u32,
254 /// The opcode's extended tokens as stored (sample offsets, resource dimension, return type).
255 pub extensions: Vec<u32>,
256 /// The saturate bit of the opcode token.
257 pub saturate: bool,
258 /// A conditional (`if`, `breakc`, `continuec`) taken when its operand is nonzero (`_nz`), else when zero (`_z`).
259 pub nonzero: bool,
260 /// The operands, destination first where the instruction has one.
261 pub operands: Vec<Operand>,
262 /// The call site an `interface_call` stores before its operand (the entry of the interface's function tables it
263 /// calls); `None` for other instructions.
264 pub function: Option<u32>,
265}
266impl Instruction {
267 /// The opcode's name (`mov`, `add`, ...), or `unknown opcode`.
268 pub fn name(&self) -> &'static str {
269 opcode_name(self.opcode)
270 }
271 /// The sample offsets of the extended token of kind 1 (`_aoffimmi`), if the instruction has one.
272 pub fn sample_offset(&self) -> Option<[i32; 3]> {
273 self.extension(1).map(|e| {
274 let field = |shift: u32| (((e >> shift) & 15) as i32) << 28 >> 28;
275 [field(9), field(13), field(17)]
276 })
277 }
278 /// The resource dimension and structured stride of the extended token of kind 2.
279 pub fn resource_dimension(&self) -> Option<(u32, u32)> {
280 self.extension(2).map(|e| ((e >> 6) & 31, (e >> 11) & 0xf_ffff))
281 }
282 /// The return-type token of the extended token of kind 3 (four nibbles).
283 pub fn return_type(&self) -> Option<u32> {
284 self.extension(3).map(|e| (e >> 6) & 0xffff)
285 }
286 fn extension(&self, kind: u32) -> Option<u32> {
287 self.extensions.iter().copied().find(|e| e & 63 == kind)
288 }
289 /// The opcode's operand format (see [`operand_format`]); `decode` has checked the operand count against it.
290 pub fn format(&self) -> Option<OperandFormat> {
291 operand_format(self.opcode)
292 }
293 /// The destination operands and the source operands, split as the opcode's format says; `None` for an opcode
294 /// with no format.
295 pub fn destinations_and_sources(&self) -> Option<(&[Operand], &[Operand])> {
296 self.operands.split_at_checked(self.format()?.destinations.len())
297 }
298}
299
300/// The name of an opcode in Microsoft's tokenized program format, or `unknown opcode`.
301pub fn opcode_name(opcode: u16) -> &'static str {
302 NAMES
303 .get(usize::from(opcode))
304 .copied()
305 .unwrap_or("unknown opcode")
306}
307
308/// The type of a program's four return lanes in a resource declaration, as D3D_RETURN_TYPE codes (1 unorm, 2 snorm,
309/// 3 sint, 4 uint, 5 float, 6 mixed, 7 double, 8 continued, 9 unused), one nibble per lane.
310pub type ReturnCodes = [u8; 4];
311
312/// The registers a Shader Model 5.1 declaration binds and their register space (`T1[4:7], space=1`). Shader Model 5.0
313/// declares each register on its own, in space 0; 5.1 names the resource by an id (the declaration's `slot`) and
314/// instructions by the id and a register of the range.
315#[derive(Clone, Copy, Debug, PartialEq, Eq)]
316pub struct RegisterRange {
317 /// The first register.
318 pub lower: u32,
319 /// The last register; `u32::MAX` for an array of no stated size (`T0[0:*]`).
320 pub upper: u32,
321 /// The register space.
322 pub space: u32,
323}
324
325/// A declaration, typed. `word` is where it starts.
326#[derive(Clone, Debug)]
327pub struct Declaration {
328 /// The word of the token stream the declaration starts at.
329 pub word: usize,
330 /// The opcode token's low 11 bits (see [`opcode_name`]).
331 pub opcode: u16,
332 /// The opcode token's bits 11 to 23 as stored.
333 pub control: u32,
334 /// The declaration, typed.
335 pub kind: Declared,
336}
337
338/// A declaration, typed where this reader knows its layout.
339#[derive(Clone, Debug)]
340pub enum Declared {
341 /// `dcl_temps`.
342 Temps(u32),
343 /// `dcl_indexable_temp x<register>[size], components`.
344 IndexableTemp {
345 /// The register index (the `<n>` of `x<n>`).
346 register: u32,
347 /// The number of elements.
348 size: u32,
349 /// The number of components per element.
350 components: u32,
351 },
352 /// `dcl_globalFlags`: the control bits as stored (bit 0 refactoring allowed, 1 double precision, 2 forced early
353 /// depth-stencil, 3 raw and structured buffers, 4 skip optimization, 5 min precision, 6 double extensions, 7
354 /// shader extensions).
355 GlobalFlags(u32),
356 /// `dcl_constantbuffer cb<slot>[rows]`; `dynamic` when it is dynamically indexed.
357 ConstantBuffer {
358 /// The buffer slot (the `<slot>` of `cb<slot>`) in Shader Model 5.0, its id in 5.1 (its registers in `range`).
359 slot: u32,
360 /// The number of 16-byte rows; 0 where the declaration states none.
361 rows: u32,
362 /// The buffer is dynamically indexed.
363 dynamic: bool,
364 /// The number of index dimensions the operand had (1 or 2; 3 in Shader Model 5.1).
365 dimensions: usize,
366 /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
367 range: Option<RegisterRange>,
368 },
369 /// `dcl_sampler s<slot>`, its mode: 0 default, 1 comparison, 2 mono.
370 Sampler {
371 /// The sampler slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
372 slot: u32,
373 /// 0 default, 1 comparison, 2 mono.
374 mode: u32,
375 /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
376 range: Option<RegisterRange>,
377 },
378 /// `dcl_resource_<dimension> t<slot>`: the D3D resource dimension code, the sample count of a multisampled one,
379 /// and the four return types.
380 Resource {
381 /// The resource slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
382 slot: u32,
383 /// The D3D resource dimension code.
384 dimension: u32,
385 /// The sample count of a multisampled resource.
386 samples: u32,
387 /// The return type of each lane.
388 returns: ReturnCodes,
389 /// The return type token as stored.
390 token: u32,
391 /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
392 range: Option<RegisterRange>,
393 },
394 /// `dcl_resource_raw t<slot>`.
395 ResourceRaw {
396 /// The resource slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
397 slot: u32,
398 /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
399 range: Option<RegisterRange>,
400 },
401 /// `dcl_resource_structured t<slot>, stride`.
402 ResourceStructured {
403 /// The resource slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
404 slot: u32,
405 /// The element stride in bytes.
406 stride: u32,
407 /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
408 range: Option<RegisterRange>,
409 },
410 /// `dcl_uav_typed` and its control bits (dimension in bits 0 to 4, globally coherent bit 5).
411 UavTyped {
412 /// The UAV slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
413 slot: u32,
414 /// The D3D resource dimension code.
415 dimension: u32,
416 /// The return type of each lane.
417 returns: ReturnCodes,
418 /// The return type token as stored.
419 token: u32,
420 /// The opcode token's control bits.
421 control: u32,
422 /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
423 range: Option<RegisterRange>,
424 },
425 /// `dcl_uav_raw u<slot>`.
426 UavRaw {
427 /// The UAV slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
428 slot: u32,
429 /// The opcode token's control bits.
430 control: u32,
431 /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
432 range: Option<RegisterRange>,
433 },
434 /// `dcl_uav_structured u<slot>, stride`.
435 UavStructured {
436 /// The UAV slot in Shader Model 5.0, its id in 5.1 (its registers in `range`).
437 slot: u32,
438 /// The element stride in bytes.
439 stride: u32,
440 /// The opcode token's control bits.
441 control: u32,
442 /// The registers and space of a Shader Model 5.1 declaration; `None` before 5.1.
443 range: Option<RegisterRange>,
444 },
445 /// `dcl_thread_group x, y, z`.
446 ThreadGroup([u32; 3]),
447 /// `dcl_tgsm_raw g<register>, bytes`.
448 GroupSharedRaw {
449 /// The shared memory register index.
450 register: u32,
451 /// Its size in bytes.
452 bytes: u32,
453 },
454 /// `dcl_tgsm_structured g<register>, stride, count`.
455 GroupSharedStructured {
456 /// The shared memory register index.
457 register: u32,
458 /// The element stride in bytes.
459 stride: u32,
460 /// The number of elements.
461 count: u32,
462 },
463 /// An input register or special input: `dcl_input`, `_sgv`, `_siv`, `_ps`, `_ps_sgv`, `_ps_siv`. `interpolation` is
464 /// a pixel input's `D3D_INTERPOLATION_MODE`; `name` a system value's `D3D_NAME`.
465 Input {
466 /// The input register or special input the declaration names.
467 operand: Operand,
468 /// The interpolation mode of a pixel input.
469 interpolation: Option<u32>,
470 /// The system value (`D3D_NAME`) of a `_sgv` or `_siv` declaration.
471 name: Option<u32>,
472 },
473 /// An output register or special output: `dcl_output`, `_sgv`, `_siv`.
474 Output {
475 /// The output register or special output the declaration names.
476 operand: Operand,
477 /// The system value (`D3D_NAME`) of a `_siv` or `_sgv` declaration.
478 name: Option<u32>,
479 },
480 /// `dcl_indexrange`: registers from the operand's that a program indexes as one array, `count` of them.
481 IndexRange {
482 /// The first register of the range.
483 operand: Operand,
484 /// The number of registers.
485 count: u32,
486 },
487 /// `dcl_stream m<n>`: a geometry program's output stream that the declarations after it describe.
488 Stream(Operand),
489 /// `dcl_immediateConstantBuffer`.
490 ImmediateBuffer(Vec<[u32; 4]>),
491 /// Custom data that is not an immediate constant buffer (a comment, debug information, ...) by its class.
492 CustomData {
493 /// The custom data class.
494 class: u32,
495 },
496 /// `dcl_function_body fb<id>`: a function body of a program with class linkage.
497 FunctionBody(u32),
498 /// `dcl_function_table ft<id> = {fb..}`: the function bodies a class's table holds, one per call site.
499 FunctionTable {
500 /// The table's id.
501 id: u32,
502 /// The function bodies, by call site.
503 bodies: Vec<u32>,
504 },
505 /// `dcl_interface fp<id>[array][sites] = {ft..}`: an interface pointer, or an array of them.
506 Interface {
507 /// The interface pointer's id, its first where it is an array.
508 id: u32,
509 /// The pointer is indexed by a register (`dcl_interface_dynamicindexed`).
510 dynamic: bool,
511 /// The number of call sites, the length of each of its tables.
512 sites: u32,
513 /// The array length, 1 for one pointer.
514 length: u32,
515 /// The function tables of the classes that can be bound to it.
516 tables: Vec<u32>,
517 },
518 /// A declaration this decoder does not type: stage-specific (geometry, tessellation, compute).
519 Other {
520 /// The opcode.
521 opcode: u16,
522 /// The opcode token's control bits.
523 control: u32,
524 /// The declaration's words after its operands, as stored.
525 words: Vec<u32>,
526 },
527}
528
529impl Declared {
530 /// The registers and space a resource, sampler, view or constant buffer declaration binds: its `range` in Shader
531 /// Model 5.1, its one register in space 0 before; `None` for the other declarations.
532 pub fn registers(&self) -> Option<RegisterRange> {
533 let (slot, range) = match self {
534 Declared::ConstantBuffer { slot, range, .. }
535 | Declared::Sampler { slot, range, .. }
536 | Declared::Resource { slot, range, .. }
537 | Declared::ResourceRaw { slot, range }
538 | Declared::ResourceStructured { slot, range, .. }
539 | Declared::UavTyped { slot, range, .. }
540 | Declared::UavRaw { slot, range, .. }
541 | Declared::UavStructured { slot, range, .. } => (*slot, *range),
542 _ => return None,
543 };
544 Some(range.unwrap_or(RegisterRange {
545 lower: slot,
546 upper: slot,
547 space: 0,
548 }))
549 }
550}
551
552/// The kind of program a version token names.
553#[derive(Clone, Copy, Debug, PartialEq, Eq)]
554pub enum ProgramType {
555 /// A pixel program (`ps`).
556 Pixel,
557 /// A vertex program (`vs`).
558 Vertex,
559 /// A geometry program (`gs`).
560 Geometry,
561 /// A hull program (`hs`).
562 Hull,
563 /// A domain program (`ds`).
564 Domain,
565 /// A compute program (`cs`).
566 Compute,
567}
568impl ProgramType {
569 /// The target's stage prefix (`ps`, `vs`, ...).
570 pub fn name(self) -> &'static str {
571 match self {
572 ProgramType::Pixel => "ps",
573 ProgramType::Vertex => "vs",
574 ProgramType::Geometry => "gs",
575 ProgramType::Hull => "hs",
576 ProgramType::Domain => "ds",
577 ProgramType::Compute => "cs",
578 }
579 }
580}
581
582/// A decoded token stream: its target, declarations and instructions, each in program order.
583#[derive(Clone, Debug)]
584pub struct Decoded {
585 /// The program's stage.
586 pub kind: ProgramType,
587 /// The shader model's major version.
588 pub major: u32,
589 /// The shader model's minor version.
590 pub minor: u32,
591 /// The declarations, in program order.
592 pub declarations: Vec<Declaration>,
593 /// The instructions, in program order.
594 pub instructions: Vec<Instruction>,
595}
596impl Decoded {
597 /// The target's name (`ps_5_0`).
598 pub fn target(&self) -> String {
599 format!("{}_{}_{}", self.kind.name(), self.major, self.minor)
600 }
601}
602
603#[derive(Clone)]
604struct Cursor<'a> {
605 words: &'a [u32],
606 at: usize,
607 origin: usize,
608 /// The program's type: the input registers of a geometry, hull or domain program are indexed by vertex.
609 kind: ProgramType,
610 /// The program is Shader Model 5.1, whose resource, sampler, view and constant buffer operands carry the
611 /// resource's id as their first index.
612 sm51: bool,
613 /// The words are a declaration's, whose 5.1 binding operands give the id and the first and last register.
614 declaration: bool,
615}
616impl Cursor<'_> {
617 /// The word the cursor is at, in the token stream.
618 fn position(&self) -> usize {
619 self.origin.saturating_add(self.at)
620 }
621 fn take(&mut self) -> Result<u32> {
622 let value = *self
623 .words
624 .get(self.at)
625 .ok_or_else(|| error(self.position(), "truncated instruction"))?;
626 self.at = self.at.saturating_add(1);
627 Ok(value)
628 }
629 fn rest(&mut self) -> Vec<u32> {
630 let rest = self.words.get(self.at..).unwrap_or_default().to_vec();
631 self.at = self.words.len();
632 rest
633 }
634 fn operand(&mut self, depth: usize) -> Result<Operand> {
635 let pos = self.position();
636 if depth > 2 {
637 return Err(error(pos, "nested relative operand exceeds bound"));
638 }
639 let t = self.take()?;
640 let components = t & 3;
641 let selection = match components {
642 0 => Selection::None,
643 1 => Selection::Scalar(0),
644 2 => match (t >> 2) & 3 {
645 0 => Selection::Mask(low8((t >> 4) & 15)),
646 1 => Selection::Swizzle([4, 6, 8, 10].map(|shift| low8((t >> shift) & 3))),
647 2 => Selection::Scalar(low8((t >> 4) & 3)),
648 _ => return Err(error(pos, "reserved component selection")),
649 },
650 _ => return Err(error(pos, "unsupported N-component operand")),
651 };
652 let kind = (t >> 12) & 255;
653 let register = Register::from_type(kind)
654 .ok_or_else(|| error(pos, format!("unsupported operand register type {kind}")))?;
655 let (mut modifier, mut precision, mut non_uniform) = (0, 0, false);
656 if t >> 31 != 0 {
657 let mut ext = self.take()?;
658 loop {
659 // only the modifier extension (kind 1) exists in Shader Model 4 and 5
660 if ext & 63 != 1 {
661 return Err(error(pos, "unsupported extended operand (precision/modifier)"));
662 }
663 modifier = low8((ext >> 6) & 255);
664 precision = low8((ext >> 14) & 7);
665 non_uniform = ext & (1 << 17) != 0;
666 if ext >> 31 == 0 {
667 break;
668 }
669 ext = self.take()?;
670 }
671 if modifier > 3 {
672 return Err(error(pos, "unsupported extended operand (precision/modifier)"));
673 }
674 }
675 let dimensions = (t >> 20) & 3;
676 let expected = match (register, self.kind) {
677 (Register::Input, ProgramType::Geometry | ProgramType::Hull | ProgramType::Domain) => 2,
678 // Shader Model 5.1: `CB1[id][register][row]`, `T0[id][register]`; declared `CB1[id][lower:upper]`
679 (Register::Constant, _) if self.sm51 => 3,
680 (Register::Texture | Register::Sampler | Register::Uav, _) if self.sm51 => {
681 if self.declaration && depth == 0 { 3 } else { 2 }
682 }
683 _ => register.dimensions(),
684 };
685 if dimensions != expected {
686 return Err(error(pos, "unexpected operand index dimensions"));
687 }
688 let mut indices = Vec::new();
689 // the representation of each index dimension, three bits each from bit 22
690 for shift in [22, 25, 28].into_iter().take(dimensions as usize) {
691 let representation = (t >> shift) & 7;
692 if !matches!(representation, 0 | 2 | 3) {
693 return Err(error(pos, "unsupported 64-bit/reserved index"));
694 }
695 let base = if representation != 2 { self.take()? } else { 0 };
696 let relative = if representation != 0 {
697 Some(Box::new(self.operand(depth.saturating_add(1))?))
698 } else {
699 None
700 };
701 indices.push(Index { base, relative });
702 }
703 let mut immediate = [0; 4];
704 match (register, components) {
705 (Register::Immediate, 1) => immediate = [self.take()?; 4],
706 // four 32-bit values, or two 64-bit values of two words each
707 (Register::Immediate | Register::Immediate64, 2) => {
708 for x in &mut immediate {
709 *x = self.take()?;
710 }
711 }
712 (Register::Immediate64, 1) => {
713 let (low, high) = (self.take()?, self.take()?);
714 immediate = [low, high, low, high];
715 }
716 (Register::Immediate | Register::Immediate64, _) => {
717 return Err(error(pos, "immediate requires one or four components"));
718 }
719 _ => {}
720 }
721 Ok(Operand {
722 register,
723 selection,
724 components: match components {
725 0 => 0,
726 1 => 1,
727 _ => 4,
728 },
729 indices,
730 immediate,
731 modifier,
732 precision,
733 non_uniform,
734 })
735 }
736}
737
738/// `interface_call`, whose first word after its token is a call site, not an operand.
739const INTERFACE_CALL: u16 = 120;
740
741fn return_codes(token: u32) -> ReturnCodes {
742 [0, 4, 8, 12].map(|shift| low8((token >> shift) & 15))
743}
744
745/// The opcodes that are declarations (and the phase markers of hull programs, which carry none): 88-106 and
746/// 143-162 of Direct3D 10 and 11, the hull phases 113-116 and `dcl_gs_instance_count` (206).
747fn is_declaration(opcode: u16) -> bool {
748 matches!(opcode, 88..=106 | 113..=116 | 143..=162 | 206)
749}
750
751/// Decode a complete token stream: version token, length, then declarations and instructions to its end.
752pub fn decode(words: &[u32]) -> Result<Decoded> {
753 let (Some(&version), Some(&length)) = (words.first(), words.get(1)) else {
754 return Err(error(0, "token stream shorter than its header"));
755 };
756 if usize::try_from(length).ok() != Some(words.len()) {
757 return Err(error(
758 1,
759 format!(
760 "the declared length is {length} words, the stream has {}",
761 words.len()
762 ),
763 ));
764 }
765 let kind = match version >> 16 {
766 0 => ProgramType::Pixel,
767 1 => ProgramType::Vertex,
768 2 => ProgramType::Geometry,
769 3 => ProgramType::Hull,
770 4 => ProgramType::Domain,
771 5 => ProgramType::Compute,
772 other => {
773 return Err(error(
774 0,
775 format!("program type {other} is not a Shader Model 4 or 5 stage"),
776 ));
777 }
778 };
779 let mut result = Decoded {
780 kind,
781 major: (version >> 4) & 15,
782 minor: version & 15,
783 declarations: Vec::new(),
784 instructions: Vec::new(),
785 };
786 let sm51 = (result.major, result.minor) >= (5, 1);
787 let mut at = 2;
788 while let Some(&t) = words.get(at) {
789 let opcode = low16(t & 0x7ff);
790 // customdata: its class in the opcode token's upper bits, its length (these two words included) the next word
791 if opcode == 53 {
792 let len = words
793 .get(at.saturating_add(1))
794 .and_then(|&n| usize::try_from(n).ok())
795 .unwrap_or(0);
796 let end = at.checked_add(len).filter(|&end| len >= 2 && end <= words.len());
797 let (Some(end), Some(body)) = (end, words.get(at.saturating_add(2)..at.saturating_add(len)))
798 else {
799 return Err(error(
800 at,
801 format!(
802 "customdata of {len} words (at least 2) does not fit the stream's {} words",
803 words.len()
804 ),
805 ));
806 };
807 let class = t >> 11;
808 let kind = if class == 3 {
809 let (rows, rest) = body.as_chunks::<4>();
810 if !rest.is_empty() {
811 return Err(error(
812 at,
813 format!(
814 "an immediate constant buffer of {} words, expected whole rows of 4",
815 body.len()
816 ),
817 ));
818 }
819 Declared::ImmediateBuffer(rows.to_vec())
820 } else {
821 Declared::CustomData { class }
822 };
823 result.declarations.push(Declaration {
824 word: at,
825 opcode,
826 control: class,
827 kind,
828 });
829 at = end;
830 continue;
831 }
832 let len = usize::try_from((t >> 24) & 127).unwrap_or(0);
833 if len == 0 {
834 return Err(error(at, "instruction length 0 (at least 1)"));
835 }
836 let end = at.checked_add(len).filter(|&end| end <= words.len());
837 let (Some(end), Some(body)) = (end, words.get(at.saturating_add(1)..at.saturating_add(len))) else {
838 return Err(error(
839 at,
840 format!(
841 "instruction length {len} (at least 1) does not fit the stream's {} words",
842 words.len()
843 ),
844 ));
845 };
846 let mut c = Cursor {
847 words: body,
848 at: 0,
849 origin: at.saturating_add(1),
850 kind,
851 sm51,
852 declaration: is_declaration(opcode),
853 };
854 let mut extensions = Vec::new();
855 let mut extended = t >> 31 != 0;
856 while extended {
857 let ext = c.take()?;
858 extensions.push(ext);
859 extended = ext >> 31 != 0;
860 }
861 let control = (t >> 11) & 0x1fff;
862 if is_declaration(opcode) {
863 let kind = declaration(opcode, control, &mut c)?;
864 if c.at != c.words.len() {
865 return Err(error(
866 at,
867 format!(
868 "{} words after the operands of {}",
869 c.words.len().saturating_sub(c.at),
870 opcode_name(opcode)
871 ),
872 ));
873 }
874 result.declarations.push(Declaration {
875 word: at,
876 opcode,
877 control,
878 kind,
879 });
880 } else {
881 let function = (opcode == INTERFACE_CALL).then(|| c.take()).transpose()?;
882 let format = operand_format(opcode);
883 let mut operands = Vec::new();
884 while c.at < c.words.len() && format.is_none_or(|f| operands.len() < f.operands()) {
885 operands.push(c.operand(0)?);
886 }
887 // fxc pads some instructions (`samplepos`, `sample_cl_s`) with zero words after their operands; words that
888 // read as further operands are an instruction with another operand count than its format's
889 if let Some(nonzero) = c
890 .words
891 .get(c.at..)
892 .unwrap_or_default()
893 .iter()
894 .position(|&w| w != 0)
895 {
896 let mut extra = c.clone();
897 let mut count = operands.len();
898 while extra.at < extra.words.len() && extra.operand(0).is_ok() {
899 count = count.saturating_add(1);
900 }
901 if let Some(format) = format
902 && extra.at == extra.words.len()
903 {
904 return Err(error(
905 at,
906 format!(
907 "{} has {count} operands, its format is {} destinations and {} sources",
908 opcode_name(opcode),
909 format.destinations.len(),
910 format.sources.len()
911 ),
912 ));
913 }
914 return Err(error(
915 c.position().saturating_add(nonzero),
916 format!(
917 "a nonzero word after the {} operands of {}",
918 operands.len(),
919 opcode_name(opcode)
920 ),
921 ));
922 }
923 if let Some(format) = format
924 && operands.len() != format.operands()
925 {
926 return Err(error(
927 at,
928 format!(
929 "{} has {} operands, its format is {} destinations and {} sources",
930 opcode_name(opcode),
931 operands.len(),
932 format.destinations.len(),
933 format.sources.len()
934 ),
935 ));
936 }
937 result.instructions.push(Instruction {
938 word: at,
939 opcode,
940 control,
941 extensions,
942 saturate: t & 0x2000 != 0,
943 nonzero: t & 0x4_0000 != 0,
944 operands,
945 function,
946 });
947 }
948 at = end;
949 }
950 Ok(result)
951}
952
953fn declaration(opcode: u16, control: u32, c: &mut Cursor<'_>) -> Result<Declared> {
954 let pos = c.origin.saturating_sub(1);
955 let slot = |o: &Operand| -> Result<u32> {
956 o.indices
957 .first()
958 .map(|i| i.base)
959 .ok_or_else(|| error(pos, "missing declaration index"))
960 };
961 // A Shader Model 5.1 binding declaration's operand is `[id][lower][upper]`, and its last word the register space.
962 let range = |o: &Operand, c: &mut Cursor<'_>| -> Result<Option<RegisterRange>> {
963 if !c.sm51 {
964 return Ok(None);
965 }
966 let [_, lower, upper] = o.indices.as_slice() else {
967 return Err(error(
968 pos,
969 "a Shader Model 5.1 binding declaration without its three indices",
970 ));
971 };
972 Ok(Some(RegisterRange {
973 lower: lower.base,
974 upper: upper.base,
975 space: c.take()?,
976 }))
977 };
978 Ok(match opcode {
979 104 => Declared::Temps(c.take()?),
980 105 => Declared::IndexableTemp {
981 register: c.take()?,
982 size: c.take()?,
983 components: c.take()?,
984 },
985 106 => Declared::GlobalFlags(control),
986 88 => {
987 let o = c.operand(0)?;
988 let token = c.take()?;
989 Declared::Resource {
990 slot: slot(&o)?,
991 dimension: control & 31,
992 samples: (control >> 5) & 127,
993 returns: return_codes(token),
994 token,
995 range: range(&o, c)?,
996 }
997 }
998 89 => {
999 let o = c.operand(0)?;
1000 // 5.1 states the rows in a word of their own; before, they are the operand's second index
1001 let rows = if c.sm51 {
1002 c.take()?
1003 } else {
1004 o.indices.get(1).map_or(0, |i| i.base)
1005 };
1006 Declared::ConstantBuffer {
1007 slot: slot(&o)?,
1008 rows,
1009 dynamic: control & 1 != 0,
1010 dimensions: o.indices.len(),
1011 range: range(&o, c)?,
1012 }
1013 }
1014 90 => {
1015 let o = c.operand(0)?;
1016 Declared::Sampler {
1017 slot: slot(&o)?,
1018 mode: control & 15,
1019 range: range(&o, c)?,
1020 }
1021 }
1022 95..=100 => {
1023 let operand = c.operand(0)?;
1024 let pixel = matches!(opcode, 98..=100);
1025 let interpolation = pixel.then_some(control & 15);
1026 let name = matches!(opcode, 96 | 97 | 99 | 100)
1027 .then(|| c.take())
1028 .transpose()?;
1029 Declared::Input {
1030 operand,
1031 interpolation,
1032 name,
1033 }
1034 }
1035 101..=103 => {
1036 let operand = c.operand(0)?;
1037 let name = (opcode != 101).then(|| c.take()).transpose()?;
1038 Declared::Output { operand, name }
1039 }
1040 156 => {
1041 let o = c.operand(0)?;
1042 let token = c.take()?;
1043 Declared::UavTyped {
1044 slot: slot(&o)?,
1045 dimension: control & 31,
1046 returns: return_codes(token),
1047 token,
1048 control,
1049 range: range(&o, c)?,
1050 }
1051 }
1052 157 => {
1053 let o = c.operand(0)?;
1054 Declared::UavRaw {
1055 slot: slot(&o)?,
1056 control,
1057 range: range(&o, c)?,
1058 }
1059 }
1060 158 => {
1061 let o = c.operand(0)?;
1062 Declared::UavStructured {
1063 slot: slot(&o)?,
1064 stride: c.take()?,
1065 control,
1066 range: range(&o, c)?,
1067 }
1068 }
1069 155 => Declared::ThreadGroup([c.take()?, c.take()?, c.take()?]),
1070 91 => Declared::IndexRange {
1071 operand: c.operand(0)?,
1072 count: c.take()?,
1073 },
1074 143 => Declared::Stream(c.operand(0)?),
1075 159 => {
1076 let o = c.operand(0)?;
1077 Declared::GroupSharedRaw {
1078 register: slot(&o)?,
1079 bytes: c.take()?,
1080 }
1081 }
1082 160 => {
1083 let o = c.operand(0)?;
1084 Declared::GroupSharedStructured {
1085 register: slot(&o)?,
1086 stride: c.take()?,
1087 count: c.take()?,
1088 }
1089 }
1090 161 => {
1091 let o = c.operand(0)?;
1092 Declared::ResourceRaw {
1093 slot: slot(&o)?,
1094 range: range(&o, c)?,
1095 }
1096 }
1097 162 => {
1098 let o = c.operand(0)?;
1099 Declared::ResourceStructured {
1100 slot: slot(&o)?,
1101 stride: c.take()?,
1102 range: range(&o, c)?,
1103 }
1104 }
1105 144 => Declared::FunctionBody(c.take()?),
1106 145 => {
1107 let id = c.take()?;
1108 let count = c.take()?;
1109 let bodies = c.rest();
1110 if bodies.len() != count as usize {
1111 return Err(error(
1112 pos.saturating_add(2),
1113 format!(
1114 "dcl_function_table word 2 counts {count} function bodies, the declaration holds {}",
1115 bodies.len()
1116 ),
1117 ));
1118 }
1119 Declared::FunctionTable { id, bodies }
1120 }
1121 146 => {
1122 let id = c.take()?;
1123 let sites = c.take()?;
1124 let lengths = c.take()?;
1125 let tables = c.rest();
1126 // the array length in the high half, the table count in the low
1127 let count = lengths & 0xffff;
1128 if tables.len() != count as usize {
1129 return Err(error(
1130 pos.saturating_add(3),
1131 format!(
1132 "dcl_interface word 3 counts {count} function tables in its low 16 bits, the declaration \
1133 holds {}",
1134 tables.len()
1135 ),
1136 ));
1137 }
1138 Declared::Interface {
1139 id,
1140 dynamic: control & 1 != 0,
1141 sites,
1142 length: lengths >> 16,
1143 tables,
1144 }
1145 }
1146 _ => Declared::Other {
1147 opcode,
1148 control,
1149 words: c.rest(),
1150 },
1151 })
1152}