Skip to content

v1.2.0 - matrix folds that were silently left expanded, and output names that fit

Choose a tag to compare

@StArraySharp StArraySharp released this 28 Oct 02:41
· 41 commits to main since this release

What changed

The library was rewritten: grammar (ANTLR) → AST → HLSL → ShaderLab is now the
only route, and src/ShaderRipper.Old is a local reference copy that is no
longer built.

Correctness

  • Matrix products whose closing row is not adjacent. fxc schedules the work
    that feeds a product's vector components between the rows, so the completing
    mad sat four instructions after the third row. The scan window grows to
    eight instructions but still stops at the first write of the accumulator, so a
    wider window cannot swallow an unrelated product.
  • A component that is overwritten before it is re-read. ADOFAI_HoldShader
    reads r0.x in the first row, overwrites it with the object-to-world w, then
    reads it again in the closing row. Such a component is now pinned into a local
    where it still held its old value:
    float pin0 = r0.x;
    r0.x = mul(unity_ObjectToWorld, float4(v0.x, v0.y, v0.z, v0.w)).w;
    o0.xyzw = mul(unity_MatrixVP, float4(r0.y, pin0, r1.z, r0.x));
    This is also why such a shader is deliberately not folded into
    UnityObjectToClipPos — its clip position is built from object-space xyz
    plus the w of the object-to-world product, which is a different computation.
  • Texture names. The asset's texture reflection names what it can; the
    registers it does not cover (a [PerRendererData] texture, or a built-in the
    variant samples without a property) now take the remaining property names in
    declaration order instead of degrading to t1, t2, …. A name already bound
    is never handed out twice (redefinition of '_MainTex').
  • ShaderLab tag casing. Tag keys and LightMode values are canonicalised —
    LIGHTMODE/FORWARDBASE → LightMode/ForwardBase. Unity matches the key
    case-insensitively but the value exactly, so the value mattered.

Output layout

  • Output paths follow one rule: a fixed path wins (Resources container or
    bundle AssetBundle.m_Container), else the shader's own name with / → _,
    else a _1 suffix. Longest file name on the reference corpus dropped from 100
    to 59 characters, and the numeric suffix never fires.
  • Fixed paths claim first, so a shader the build placed at an explicit path keeps
    it.
  • Bundle container paths are read now (AssetBundle.m_Container, class 142) —
    the field existed but was never assigned.

CLI

  • Options are position-independent: --shaderlab out --game <dir> is the same
    run as --game <dir> --shaderlab out. Reading args[0] as the command word
    made the first form fail with "unknown option --shaderlab".
  • --game and --file-bare are registered as value-taking options, so their
    values no longer land in the positional list where order decided their meaning.
  • A mistyped option reports itself instead of printing the usage text.

Packaging

  • linux-x64 publishes embed libdxcompiler.so / libdxil.so; before this the
    Linux build carried only Windows DLLs and could not decompile anything.
  • Native libraries are embedded per RID again, so each binary carries only what
    its platform can load.

Verification

  • scripts/cmp_logic.py removed: every invariant it checked is a subset of
    verify_logic.py, and its sample count came from the compiled program (which
    fxc merges by coordinate) rather than the source.
  • gen_grammar.py regenerates all five grammars, not just DxbcAsm.
  • Corpus (425 shaders / 3,263 programs): verify_logic.py 3195/3195,
    scan_unwritten 0, scan_nan 0 escaping / 0 contained, scan_props 0 leaked.