Skip to content

MilkDrop fidelity: hue colour and the fixed composite as MilkDrop draws them - #612

Merged
CaYatur merged 2 commits into
mainfrom
milkdrop/hue-580
Sep 23, 2026
Merged

CaYatur merged 2 commits into
mainfrom
milkdrop/hue-580

Conversation

@CaYatur

@CaYatur CaYatur commented Sep 23, 2026

Copy link
Copy Markdown
Owner

What

This fixes a regression from 52c7391 (#560) that #611 found, and checks the fixed composite's hue against MilkDrop's source: Nullsoft's D3D11 code (jecassis/foo_vis_milk2 5b44cea) and the D3D9 code (BeatDrop 53d83ee).

  • Preset shaders get the full hue colour. MilkDrop gives every composite shader the full hue_shader colour, whatever the preset's fShader says ("since we don't know if shader uses it or not", milkdropfs.cpp:4122). fShader scales the colour only on the fixed pipeline and in the shader MilkDrop generates. 52c7391 applied the fixed pipeline's rule to shaders too, so with fidelity on:
    • 914 corpus presets that read hue_shader with fShader = 0 lost their colour;
    • 36 others got only part of it.
  • Corners in the right place. MilkDrop weighs the four corners by the composite grid's screen position, with y = 1 on top (plugin.cpp:1475-1490). Our translation flipped y, which mirrored the colours top to bottom. A screen readback with four pure colours now puts corner 0 at the top right, as in MilkDrop; before this change it was at the bottom right.
  • The fixed pipeline, drawn as MilkDrop draws it.
    • The amount is the file's fShader, blended linearly through a transition, and it is no longer clamped.
    • Each draw's corner colour (gamma × layer share × hue) goes through COLOR_NORM, so fShader = 10 wraps into other colours as in MilkDrop. fixedCompWeights replaces fixedGammaGain.
    • The quad is interpolated as MilkDrop's two triangles instead of bilinearly.
    • A generated composite stage no longer applies a partial fShader twice.
  • Echo fade during a blend. The fade now uses the new side's blended echo alpha, as MilkDrop does, so the old echo fades out even when the new preset has none.

The legacy path keeps its own shader branch.

Measured

  • Screen readback: shader corners sit where MilkDrop puts them. The fixed quad's centre is the mean of corners 1 and 2, as with MilkDrop's two triangles.

  • Fixed pipeline render check: 72 of 72 cases within 2/255 of the model, including gamma 3.7 with echo 0.5 (eight draws). Rounding after every draw, as MilkDrop's 8-bit buffer does, differs from our single rounding by at most 0.6/255 in those cases.

  • Against main (last 10 of 60 frames, up to 120 presets per group):

    Group Result
    Shader presets that don't read hue_shader 120/120 identical
    fShader = 0 readers 95/120 changed; median saturation 0.006 → 0.150
    Full readers (orientation only) 90/120 changed, median 4.3%
    Partial readers 28/36 changed
    Fixed pipeline with fShader > 0.001 30/120 changed, median 0.5%; 12 of the 16 at 10
    Fixed pipeline without fShader rounding only, largest 0.36%
  • Fidelity off: the fixed pipeline and the control group are byte-identical. Presets that read hue_shader differ by one level in at most 0.1% of pixels, because four equal corner colours are summed in a different order.

  • 900-preset sample: one preset moves from blown to clean. It scales its picture by hue_shader, which was white on main. Rendered alone, its brightness goes from 0.555 to 0.507.

Tests

  • 3 new tests and 3 test files reworked; 18 of 18 mutations are caught.
  • npm test: 2184 of 2184 pass, and each commit passes on its own. The changed files also pass under Linux emulation.
  • npm run smoke: PASS, with the userData checksums unchanged.

Next

The remaining missing-key fallbacks, and MilkDrop's linear blending of its other file-only values. The orientation of uv in preset shaders is listed as an open check.

Refs #580
Refs #560

MilkDrop gives every composite shader the full hue colour, whatever
the preset's fShader says ("since we don't know if shader uses it or
not", milkdropfs.cpp:4122; BeatDrop's D3D9 code 4318). fShader scales
the colour only on the fixed pipeline and in the shader MilkDrop
writes for a preset without one. 52c7391 (#560) applied the fixed
pipeline's rule to preset shaders as well, so with fidelity on the 914
corpus presets whose composite shader reads hue_shader and leaves
fShader at 0 lost their colour, and 36 got only part of it.

- Preset shaders get the colour at amount 1 again.
- hueAt no longer flips y. MilkDrop weighs the corners by the
  composite grid's screen position with y at 1 on top
  (plugin.cpp:1475-1490), and our uv.y is 1 on top as well. Read back
  from the screen, corner 0 now sits top right as in MilkDrop; it sat
  bottom right.
- On the fixed pipeline the amount is the file's fShader, blended
  linearly through a transition like MilkDrop's CBlendableFloat, not
  the per-frame value (16 presets write fshader in code), and it is no
  longer clamped to 1.
- The fixed pipeline is drawn the way MilkDrop draws it. Every pass's
  corner colour is gamma x layer share x hue, put through COLOR_NORM
  (fixedCompWeights, replacing fixedGammaGain), so fShader 10 wraps
  into other colours as in MilkDrop (16 presets). The quad is
  interpolated as two triangles sharing the top-right/bottom-left
  edge, not bilinearly. A generated composite stage no longer gets a
  partial fShader twice.
- Echo fading during a blend looks at the new side's blended echo
  alpha (m_fVideoEchoAlpha.eval), so an echo fades out even when the
  new preset has none.

The legacy path keeps its own shader branch.

Measured against main over the last 10 of 60 frames, up to 120
presets a group:
- shader presets that do not read hue_shader: 120/120 identical;
- fShader-0 readers: 95/120 changed, and median saturation went from
  0.006 to 0.150;
- full readers (orientation only): 90/120 changed;
- partial readers: 28/36 changed;
- fixed pipeline with fShader: 30/120 changed (median 0.5%);
- fixed pipeline without fShader: rounding only (largest 0.36%).

With fidelity off, the fixed pipeline and control presets are
identical, and hue readers differ by one level in at most 0.1% of
pixels (equal corner colours summed in another order). The fixed
pipeline render check passes 72/72 within 2/255.

3 new tests and 3 test files reworked; 18/18 mutations are caught.
The docs' test count follows.

Refs #580, #560
README (both languages): composite shaders get the hue colour MilkDrop
gives them, and the fixed pipeline draws it through MilkDrop's passes.
ROADMAP: the #560 hue_shader entry is corrected in place, since its
first version read the fixed pipeline's rule as applying to shaders.
The #580 entry records the fix and its measurements, including the one
class flip on the 900-preset sample, checked alone. The remaining
missing-key fallbacks come next, and the orientation of uv in preset
shaders is listed as an open check.

Refs #580, #560
@CaYatur
CaYatur merged commit 17121fe into main Sep 23, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant