Class D3d9BytecodePatcher
- Namespace
- ShadowDusk.Core
- Assembly
- ShadowDusk.Core.dll
Post-pass over vkd3d's D3D9 SM2/SM3 token streams that canonicalizes the two instruction forms MojoShader rejects but vkd3d 1.17 emits (found by the Phase 39 rung-3/4 FNA harness — real FNA load failures where the fxc oracle loads fine):
texkillwith a partial destination writemask (vkd3d writes e.g..x; fxc always writes.xyzwand MojoShader hard-fails anything else: "TEXKILL writemask must be .xyzw").texldwhose coordinate source MojoShader rejects: a swizzle below SM3 ("TEXLD src0 must not swizzle" for ps_1_x/ps_2_x; SM3 allows it), or a source modifier at ANY SM2+ major ("TEXLD src0 must have no modifiers").deffloat literals with |f| ≥ 2³² — vkd3d'sdiscardsentinel is −2³² (0xCF800000), and MojoShader'sMOJOSHADER_printFloatconverts the magnitude through a 32-bitunsigned longon Windows (LLP64), overflowing to±0.0in the translated source — sotexkill's< 0test never fires (Dissolve rendered un-discarded). Clamped in place to the same-signed largest float BELOW 2³² (±4294967040.0, 0x4F7FFFFF), which MojoShader prints exactly; the sentinel's only observable property is its sign. fxc's largest def literals are ±1, so the oracle never trips this. Empirically proven pixel-identical in real FNA by the rung-4 harness's clamped-Dissolve experiment.
The rewrite is semantics-preserving, not a blind mask/swizzle flip: the offending
operand is routed through a fresh temporary —
mov rK, reg.<replicated-masked-components> followed by the canonical
texkill rK.xyzw (each tested lane now holds one of the originally-tested
values), or mov rK, src0.<swizzle> followed by texld dst, rK, s#.
Blindly widening the texkill mask would test garbage lanes; this never does.
D3D9 SM2+ instruction tokens carry an explicit operand-count field and the format has no byte-offset branches, so inserting instructions is purely mechanical; comment blocks (the CTAB) pass through byte-identical. Streams below SM2 are returned unchanged: MojoShader's ps_1_x rules differ wholesale (and ps_1_x tokens carry no instruction-length fields to walk), and the FNA pipeline rejects literal SM1 profiles upstream (SD0300), so this pass never has to reason about them.
Applied ONLY on the FNA path — the DirectX SM5 path never sees this code, and the blob handed to the fx_2_0 writer stays exactly what MojoShader will consume.
public static class D3d9BytecodePatcher
- Inheritance
-
D3d9BytecodePatcher
- Inherited Members
Methods
PatchForMojoShader(byte[], string)
Patches bytecode for MojoShader compatibility. Returns the
original array when nothing needs patching (the common case — zero-copy).
public static Result<byte[], ShaderError> PatchForMojoShader(byte[] bytecode, string sourceFile)
Parameters
Returns
- Result<byte[], ShaderError>