shader-tool is a C++ library that allows to parse, assemble and disassemble DirectX shader objects at runtime.
Currently it only supports DXBC shaders.
| version | supported | |
|---|---|---|
| SM1 | ❌ | DirectX 8 |
| SM2 | ❌ | DirectX 9 |
| SM3 | ❌ | DirectX 9 |
| SM4 | ✔️ | DirectX 10 |
| SM5 | ✔️ | DirectX 11 |
| SM5.1 | ✔️ | DirectX 12 |
| SM6 | ❌ | DirectX 12 |
- If you use premake:
Use this premake5 script
This code patches all constant buffer accesses by remapping the CB index.
#include <std_include.hpp>
#include <shader-tool/shader.hpp>
void main()
{
const auto data = utils::io::read_file("ps_test.cso");
auto shader = alys::shader::shader_object::parse(data);
const auto remap_cb_index = [&](const std::uint32_t index)
{
if (index >= 0x7F)
{
return index;
}
if (index >= 65)
{
return index - 3;
}
if (index >= 15)
{
return index - 1;
}
return index;
};
for (auto& instruction : shader.get_instructions())
{
if (instruction.opcode.type == D3D10_SB_OPCODE_DCL_CONSTANT_BUFFER)
{
continue;
}
for (auto& operand : instruction.operands)
{
if (operand.type == D3D10_SB_OPERAND_TYPE_CONSTANT_BUFFER && operand.indices[0].value.uint32 <= 4 &&
operand.indices[1].representation == D3D10_SB_OPERAND_INDEX_IMMEDIATE32)
{
operand.indices[1].value.uint32 = remap_cb_index(operand.indices[1].value.uint32);
}
}
}
shader.print();
const auto new_shader = shader.serialize();
}This code generates a shader using the assembler class.
#include <std_include.hpp>
#include <shader-tool/shader.hpp>
#include <utils/io.hpp>
using namespace alys::shader::literals;
void example1()
{
alys::shader::shader_object shader(alys::shader::pixelshader);
shader.add_input("SV_POSITION", 0, "xyzw", 0, alys::shader::POS, alys::shader::format_float, "");
shader.add_input("COLOR", 0, "xyzw", 1, alys::shader::NONE, alys::shader::format_float, "xyzw");
shader.add_input("TEXCOORD", 0, "xy", 2, alys::shader::NONE, alys::shader::format_float, "xy");
shader.add_input("TEXCOORD", 1, "xyzw", 3, alys::shader::NONE, alys::shader::format_float, "xyzw");
shader.add_input("TEXCOORD", 5, "xyz", 4, alys::shader::NONE, alys::shader::format_float, "xyz");
shader.add_output("SV_TARGET", 0, "xyzw", 0, alys::shader::TARGET, alys::shader::format_float, "");
auto a = shader.get_assembler();
a.dcl_globalFlags(refactoring_allowed | early_depth_stencil);
a.dcl_constantbuffer(cb1[6], immediate_indexed);
a.dcl_constantbuffer(cb2[2], immediate_indexed);
a.dcl_sampler(s0);
a.dcl_sampler(s3);
a.dcl_resource(t0, texture2d, c(t_float, t_float, t_float, t_float));
a.dcl_resource(t3, texture2d, c(t_float, t_float, t_float, t_float));
a.dcl_input_ps(v1.xyzw(), linear_centroid);
a.dcl_input_ps(v2.xy(), linear_centroid);
a.dcl_input_ps(v3.xyzw(), linear_centroid);
a.dcl_input_ps(v4.xyz(), linear_centroid);
a.dcl_output(o0.xyzw());
a.dcl_temps(4);
a.dp3(r0.x(), v3.xyzx(), v3.xyzx());
a.rsq(r0.x(), r0.x());
a.mul(r0.xyz(), r0.x(), v3.xyzx());
a.add(r1.xyz(), -v4.xyzx(), cb1[4].xyzx());
a.dp3(r0.w(), r1.xyzx(), r1.xyzx());
a.rsq(r1.w(), r0.w());
a.sqrt(r0.w(), r0.w());
a.mul_sat(r0.w(), r0.w(), cb1[4].w());
a.add_extension(res_dim, texture2d);
a.add_extension(res_return_type, t_float, t_float, t_float, t_float);
a.sample_indexable(r2.xyz(), r0.wwww(), t3.xyzw(), s3);
a.mul(r2.xyz(), r2.xyzx(), r2.xyzx());
a.mul(r1.xyz(), r1.wwww(), r1.xyzx());
a.dp3_sat(r0.x(), r1.xyzx(), r0.xyzx());
a.mul(r0.xyz(), r0.xxxx(), cb1[5].xyzx());
a.add_extension(res_dim, texture2d);
a.add_extension(res_return_type, t_float, t_float, t_float, t_float);
a.sample_indexable(r1.xyzw(), v2.xyxx(), t0.xyzw(), s0);
a.mul(r3.xyzw(), r1.xyzw(), v1.xyzw());
a.mad(r0.w(), -r1.w(), v1.w(), l(1.f));
a.add(r0.w(), -r0.w(), l(1.f));
a.mul(r1.xyz(), r0.wwww(), cb2[1].xyzx());
a.mul(r3.xyz(), r3.xyzx(), r3.xyzx());
a.mul(r3.xyz(), r3.xyzx(), r3.wwww());
a.mul(r2.xyz(), r2.xyzx(), r3.xyzx());
a.mad(r0.xyz(), r2.xyzx(), r0.xyzx(), -r1.xyzx());
a.mad(o0.xyz(), v3.wwww(), r0.xyzx(), r1.xyzx());
a.mov(o0.w(), l(0.f));
a.ret();
shader.print();
utils::io::write_file("ps_test.cso", shader.serialize());
}output (printf)
//
// Generated by https://github.com/alicealys/shader-tool
//
//
// Input signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_POSITION 0 xyzw 0 POS float
// COLOR 0 xyzw 1 TARGET float xyzw
// TEXCOORD 0 xy 2 TARGET float xy
// TEXCOORD 1 xyzw 3 TARGET float xyzw
// TEXCOORD 5 xyz 4 TARGET float xyz
//
// Output signature:
//
// Name Index Mask Register SysValue Format Used
// -------------------- ----- ------ -------- -------- ------- ------
// SV_TARGET 0 xyzw 0 TARGET float
//
ps_5_0
dcl_globalFlags refactoringAllowed | forceEarlyDepthStencil
dcl_constantbuffer CB1[6], immediateIndexed
dcl_constantbuffer CB2[2], immediateIndexed
dcl_sampler s0, mode_default
dcl_sampler s3, mode_default
dcl_resource_texture2d (float,float,float,float) t0
dcl_resource_texture2d (float,float,float,float) t3
dcl_input_ps linear centroid v1.xyzw
dcl_input_ps linear centroid v2.xy
dcl_input_ps linear centroid v3.xyzw
dcl_input_ps linear centroid v4.xyz
dcl_output o0.xyzw
dcl_temps 4
dp3 r0.x, v3.xyzx, v3.xyzx
rsq r0.x, r0.x
mul r0.xyz, r0.x, v3.xyzx
add r1.xyz, -v4.xyzx, cb1[4].xyzx
dp3 r0.w, r1.xyzx, r1.xyzx
rsq r1.w, r0.w
sqrt r0.w, r0.w
mul_sat r0.w, r0.w, cb1[4].w
sample_indexable(texture2d)(float,float,float,float) r2.xyz, r0.wwww, t3.xyzw, s3
mul r2.xyz, r2.xyzx, r2.xyzx
mul r1.xyz, r1.wwww, r1.xyzx
dp3_sat r0.x, r1.xyzx, r0.xyzx
mul r0.xyz, r0.xxxx, cb1[5].xyzx
sample_indexable(texture2d)(float,float,float,float) r1.xyzw, v2.xyxx, t0.xyzw, s0
mul r3.xyzw, r1.xyzw, v1.xyzw
mad r0.w, -r1.w, v1.w, l(1.000000)
add r0.w, -r0.w, l(1.000000)
mul r1.xyz, r0.wwww, cb2[1].xyzx
mul r3.xyz, r3.xyzx, r3.xyzx
mul r3.xyz, r3.xyzx, r3.wwww
mul r2.xyz, r2.xyzx, r3.xyzx
mad r0.xyz, r2.xyzx, r0.xyzx, -r1.xyzx
mad o0.xyz, v3.wwww, r0.xyzx, r1.xyzx
mov o0.w, l(0)
ret- x64 ZoneTool: usage can be found here: techset.cpp.
- H1-Mod
- "Parsing Direct3D shader bytecode" by Tim Jones.
- Windows Driver Kit: contains instruction formats.
- fxcd: HLSL decompiler.