Direct3D shader bytecode disassembler and encoder written in Rust.
Parses DXBC containers, decodes SM4/SM5 shader bytecode into a fully typed intermediate representation, and produces fxc.exe-compatible disassembly output. The IR can be re-encoded to byte-identical bytecode, enabling shader inspection and modification workflows.
- Full SM4/SM5 bytecode round-trip — Decode → IR → encode produces bit-identical output across all 217+ opcodes. Verified against 1,152 real-world shader chunks with zero failures.
- Typed IR with no raw bit hacks — Every opcode field (GS primitives, topologies, component selection modes, sample counts, test conditions) is represented as a proper Rust enum. No raw bit buckets.
- 18 chunk types parsed — RDEF, ISGN/ISG1, OSGN/OSG1/OSG5, PCSG/PSG1, SHEX/SHDR, STAT, RTS0, SFI0, HASH/XHSH, ILDN, ILDB, PRIV, DXIL, PSV0, RDAT, LIBF, LFS0, LIBH. Unknown chunks are preserved for lossless container rebuilds.
- Zero-copy reads, copy-on-write mutation — Metadata strings and raw blobs borrow directly from the input buffer via
Cow<'a, T>. AssigningCow::Owned(...)transparently enables shader patching without touching the original data. - Lazy chunk parsing — Non-program chunks (RDEF, signatures, STAT, etc.) are parsed on first access via
OnceCell. Callers that only need the shader program pay zero overhead for metadata. - Container scanning — Automatically finds DXBC containers in arbitrary byte streams (raw files,
fxb0wrappers, custom game archives). no_stdcore — Thedxbccrate uses onlycoreandalloc, no filesystem or I/O.- All shader stages — VS, PS, GS, HS, DS, CS.
| Crate | Type | Purpose |
|---|---|---|
dxbc |
lib (no_std) |
DXBC container parser/writer, chunk decoders, SM4/SM5 bytecode decode/encode/format |
d3dasm |
lib | High-level disassembly interface — wraps dxbc with typed accessors and Display formatting |
d3dasm-cli |
bin | Command-line disassembler |
cargo run -- <shader.bin>let data = std::fs::read("shader.bin").unwrap();
for shader in d3dasm::parse(&data) {
// Typed access to parsed chunks.
if let Some(program) = shader.program() {
println!("SM {}.{}", program.version_major, program.version_minor);
}
// fxc.exe-compatible disassembly via Display.
print!("{shader}");
}let container = dxbc::DxbcContainer::parse(bytes).unwrap();
for chunk in &container.chunks {
match chunk.parse() {
dxbc::ChunkData::Shader(program) => {
// Round-trip: decode → encode → identical bytes.
let encoded = dxbc::shex::encode(&program);
assert_eq!(chunk.data, &encoded[..]);
}
_ => {}
}
}// Compiled with: Microsoft (R) HLSL Shader Compiler 10.0.10011.0
//
// Resource Bindings:
// Name Type Dim Slot Count Flags
// --------------- ------------ -------- ---- ----- -----
// gPointSampler0 sampler NA 0 1
// gPointTexture0 texture 2dMS 0 1 comparisonSampler;texComp0
//
// Input Signature:
// TEXCOORD float v0.xy
// TEXCOORD1 float v0.zw
// SV_Position float v1.xyzw
//
// Output Signature:
// SV_Target float v0.xyzw
//
ps_5_0
dcl_globalFlags refactoringAllowed
dcl_sampler s0, mode_default
dcl_resource_texture2d (float,float,float,float) t0
dcl_input_ps linear v0.xy
dcl_input_ps linear v0.zw
dcl_output o0.xyzw
dcl_temps 1
sample r0.xyzw, v0.xyxx, t0.xyzw, s0
mov o0.xyzw, r0.xyzw
retd3dasm/
├── Cargo.toml # Workspace root (resolver = "3")
├── crates/
│ ├── dxbc/ # no_std DXBC library
│ │ └── src/
│ │ ├── container.rs # DXBC container parser, writer, scanner
│ │ ├── chunks/ # Per-chunk parsers (rdef, signature, stat, …)
│ │ ├── shex/ # SM4/SM5 bytecode
│ │ │ ├── ir.rs # Typed intermediate representation
│ │ │ ├── decode.rs # Bytes → IR
│ │ │ ├── encode.rs # IR → bytes
│ │ │ ├── fmt.rs # IR → disassembly text
│ │ │ └── opcodes.rs # Opcode enum (217+ opcodes)
│ │ └── util.rs # Shared helpers
│ ├── d3dasm/ # High-level disassembly API
│ │ └── src/lib.rs
│ └── d3dasm-cli/ # CLI binary
│ └── src/main.rs
cargo buildRequires Rust 1.85+ (edition 2024, core::cell::OnceCell stable since 1.70).
cargo test --workspaceRound-trip verification across all test shaders:
cargo run --release --example roundtrip -- shaders/shaders-pc/**/*.bin