Skip to content

MilkDrop fidelity: stage rule, fixed composite and defaults - #611

Merged
CaYatur merged 7 commits into
mainfrom
milkdrop/fidelity-580
Sep 23, 2026
Merged

CaYatur merged 7 commits into
mainfrom
milkdrop/fidelity-580

Conversation

@CaYatur

@CaYatur CaYatur commented Sep 23, 2026

Copy link
Copy Markdown
Owner

What

Follow-ups from the MilkDrop fidelity audit, checked against Nullsoft's code (jecassis/foo_vis_milk2 5b44cea) and, for the fixed pipeline's blend passes, the D3D9 code it was ported from (BeatDrop 53d83ee). Everything is behind the fidelity switch; with it off the legacy paths are unchanged.

  • Stages by version. MilkDrop picks the warp and composite shaders from the file's version, not from whether shader text exists.
    • No MILKDROP_PRESET_VERSION, or one below 200, means a MilkDrop 1 preset, and its shader text is ignored.
    • Version 200 uses PSVERSION; 201 and above use PSVERSION_WARP and PSVERSION_COMP, each 2 when missing.
    • A stage with a version but no text gets the shader MilkDrop writes at load time, with the file's values baked in.
    • Switching fidelity rebuilds the running preset's stages.
  • Fixed composite (2,128 corpus presets). Brighten is 1−(1−c)² and solarize is 2c(1−c); the engine used sqrt(c) and 4c(1−c), the formulas of the generated shader. Echo orientation is (int)x % 4. Gamma below 1 is not applied while echo is on. When two presets whose echoes point different ways blend, the echo fades out and back in instead of flipping.
  • Defaults for keys a file does not write: decay 0.98, gamma 2.0, echo zoom 2.0, wave alpha 0.8, mv_l 0.9, grid 12×9, border sizes 0.01, inner border colour 0.25. Init code sees them too, and a centre of 0 stays in the corner.
  • Our own presets write fWaveAlpha, the key MilkDrop reads. The builtins and the generator wrote wave_a, so MilkDrop drew their wave at its default 0.8. Nothing changes on screen in our engine; the generator's golden hashes were renewed and its version kept.
  • Mash-ups follow the stage rule. The mash-up rule version goes to 2, so saving a recipe from before this change again writes a new file instead of overwriting the old one.

Measured

  • Rendered cases: 66 of 66 flag, gamma and orientation cases are within 2/255 of MilkDrop's formula, with fidelity on and off.
  • 900-preset sample with the texture pack: the same classes before and after (882 clean, 12 blown, 4 frozen, 2 black).
  • Last 10 of 60 frames against main:
    • 99 of 120 brighten presets changed (median 3.8%).
    • 63 of 83 solarize presets changed; median brightness went from 0.291 to 0.112.
    • 23 of 49 presets with a centre of 0 changed, and 7 of 38 that write echo_orient per frame.
  • Mash-up part tests: they agree with the engine's stage choice for every corpus preset and part.

Found here, fixed in the next PR

  • Hue colour in preset shaders. MilkDrop gives every composite shader the full hue colour ("since we don't know if shader uses it or not"). fShader scales it only on the fixed path and in the generated shader. The hue_shader change in 52c7391 (MilkDrop Phase 2: visual rendering quality, user textures (img.ini), motion vectors, and smooth preset transitions #560) applied fShader to preset shaders too. With fidelity on, 914 corpus presets lost the colour and 36 got part of it.

  • Keys that fall back to other values when missing:

    • wave_r, wave_g, wave_b, wave_x and wave_y: MilkDrop reads them as rot's value, which is 0.
    • Wave smoothing is 0.75, and the volume fade runs from 0.75 to 0.95.
    • Shape colours.

    No corpus preset and none of ours leaves them out.

Tests

  • 14 new tests and 8 updated test files; 36 of 36 mutations are caught.
  • npm test: 2181 of 2181 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.

Refs #580
Refs #579
Refs #560

MilkDrop reads the main wave's alpha from the file as `fWaveAlpha`
(state.cpp:1383). `wave_a` is the name per-frame code uses, and a
header line with it is never read. The five builtin presets and the
generator wrote `wave_a`, so in MilkDrop they drew the wave at its
default 0.8. All 10,347 corpus files write fWaveAlpha and none writes
wave_a in the header.

The engine reads both names, so nothing changes on screen here: for
the builtins and 199 generated presets the engine's wave_a is the same
with either key. The generator's golden hashes change with the text;
its version stays 1 because the same code still gives the same picture.

Refs #580, #560
Checked against Nullsoft's code (jecassis/foo_vis_milk2 5b44cea) and,
for the fixed pipeline's blend passes, the D3D9 code it was ported
from (BeatDrop 53d83ee).

Stages by version. MilkDrop picks the warp and composite shaders by
the file's version, not by whether shader text exists. A file with no
MILKDROP_PRESET_VERSION, or one below 200, is a MilkDrop 1 preset. 200
uses PSVERSION; 201 and above use PSVERSION_WARP and PSVERSION_COMP,
each 2 when missing (state.cpp:1328-1348). A stage with a version and
no text gets a shader written at load time with the file's values
baked in (plugin.cpp GenWarpPShaderText, GenCompPShaderText). With
fidelity on the engine follows this, and switching fidelity rebuilds
the running preset's stages.

Fixed composite. MilkDrop draws the flags with blend passes of a white
quad: brighten is 1-(1-c)^2 and solarize 2c(1-c). The engine used
sqrt(c) and 4c(1-c), the formulas of the shader MilkDrop generates.
Echo orientation is (int)x % 4 with C's sign rule. Gamma is drawn as
additive redraws, so below 1 it is not applied while echo is on. When
two presets whose echoes point different ways blend, the echo fades
out before the snap point and back in after it.

Defaults. Keys a file does not write take MilkDrop's defaults: decay
0.98, gamma 2.0, echo zoom 2.0, wave alpha 0.8, mv_l 0.9, grid 12x9,
border sizes 0.01 and inner border colour 0.25. The per-frame reset
wrote the pool's 0 back every frame. Init code sees the defaults too,
and a centre of 0 stays in the corner.

Measured. Every flag, gamma and orientation case rendered through the
engine reads back within 2/255 of MilkDrop's formula, with fidelity on
and off (66 of 66). The 900-preset sample sorts into the same classes
before and after. Of 120 brighten presets 99 changed (median 3.8%);
of 83 solarize presets 63 changed, and median brightness went from
0.291 to 0.112.

14 new tests, 5 updated; the docs' test count follows.

Refs #580, #560
A preset gives a mash-up its warp or composite part only if MilkDrop
would draw that shader: besides a non-empty shader line, the stage's
version under MilkDrop's rule must be above 0. A MilkDrop 1 file, or
one whose PSVERSION_WARP is 0, no longer gives a part that neither
MilkDrop nor the engine draws. The version written for a donated
shader is the one its preset has for that stage, by the same rule as
the engine's md2Versions. A version value that is not wholly a number
counts as missing, as in the parser, and takes the rule's default.

The part test is now checked against the engine's stage choice rather
than against the presence of shader text, with eight version edge
cases added; composing with a warp from a PSVERSION_WARP=0 preset
writes version 0 and the engine draws the fixed warp.

Refs #579, #580, #560
The stage rule, the fixed composite, MilkDrop's defaults and the
fWaveAlpha key, with the measurements, in the README feature list and
a ROADMAP entry under #580. The entry also records what was found
while finishing this and comes next: preset shaders get the full hue
colour in MilkDrop whatever fShader says, and a few keys fall back to
other values when a file leaves them out.

Refs #580
The mash-up id carries the rule version so that a rule change which
alters the text of the same recipe never overwrites an earlier save.
Following MilkDrop's stage rule is such a change: a warp from a preset
with PSVERSION_WARP=0 used to be written as version 2, and the engine
drew it; it is now written as 0. A recipe from the history saved again
now writes md_mix2_... next to the old md_mix1_... file instead of
replacing it. Nothing recognises mash-ups by the prefix, so older
saves stay ordinary presets.

Refs #579, #580
The README now says most values a preset leaves out take MilkDrop's
defaults, not all of them: the five wave keys MilkDrop reads as 0 when
missing, wave smoothing, the volume fade range and shape colours come
in the next change. The ROADMAP records the mash-up rule version, the
part test's stage condition, and that a partial fShader lands twice in
a generated composite stage until the hue fix (the one corpus preset
with such a stage has fShader at 0).

Refs #580, #579
@CaYatur
CaYatur merged commit 82ab5ec 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