Skip to content

MilkDrop: fresh blur levels, MilkDrop's sampler wrap rules and 8-bit feedback rounding (#752) - #754

Merged
CaYatur merged 4 commits into
mainfrom
fix/milkdrop-blur-chain-mip
Oct 14, 2026
Merged

CaYatur merged 4 commits into
mainfrom
fix/milkdrop-blur-chain-mip

Conversation

@CaYatur

@CaYatur CaYatur commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Second quality round for #752. Three engine fixes, both checked against MilkDrop's source and measured against BeatDrop 53d83ee with the #750 patch.

Blur levels 2 and 3 were one and two frames behind

Each blur level's out texture is mipmapped, and the mipmaps were generated after the whole chain. The next level read it with texture() at a smaller size, so it got last frame's mipmap. At a preset's start, blur2 was empty on its first frame and blur3 on its first two; MilkDrop fills both. MilkDrop's blur textures have a single level and are read bilinearly. Each level now reads its source with textureLod(…, 0).

sampler_main wraps in preset shaders

MilkDrop sets a sampler's wrap mode from its name:

  • a plain sampler_main is filtered and wraps (plugin.cpp:3445-3446);
  • fc_ and pc_ clamp;
  • bTexWrap is read only by the shader-less warp (milkdropfs.cpp:2217).

The engine followed bTexWrap in shaders too, so content leaving the screen stuck to the edge.

  • Synthetic check. On a preset with bTexWrap = 0 (a warp shader shifting uv, a comp shader zooming out), MilkDrop wraps and matches sampler_fw_main exactly. The engine's difference went 3.02 → 0.15.
  • Reach. 3,325 library presets (34%) have a shader and bTexWrap = 0.
  • Blur reads clamped. MilkDrop's BlurPasses sets CLAMP and never resets it. Blur now always reads clamped, through a sampler object.
  • Textured shapes. They are drawn after the blur, so they read clamped when a blur pass ran, as in MilkDrop.

Measured

Main → branch. Mean difference on a 16-pixel grid, median over presets without user textures or randomness.

set f2 f10 f30 f59
150 presets with a shader and bTexWrap = 0 (26 comparable) 2.33 → 1.52 3.44 → 3.48 10.23 → 8.97 13.60 → 11.51
150 random library presets (44 comparable) 1.19 → 0.98 2.18 → 1.89 3.64 → 3.64 4.75 → 4.75
120 presets that draw motion vectors (30 comparable) 1.64 → 1.64 2.54 → 2.49 3.86 → 3.86 5.25 → 5.00
#750's 21 presets, 1440×896 4.25 → 4.25
  • Wrap set, all 150. The frame-59 median fell 21.1 → 15.5; 41 presets are better by more than 2 and 9 worse.
  • Single wins:
    • "liquid firesticks" f2: 26.7 → 2.3
    • "Tricolors" f30: 71.0 → 8.8
    • "Furry Synthesis" f59: 40.7 → 24.2
    • "Accelerator" f59: 26.4 → 14.7
    • "truth, rarity edit05" f30: 23.8 → 8.4. It now has MilkDrop's colour and wire mesh.
    • "fed - DSC-011 LSD Edit 4" was black and now shows MilkDrop's coloured fields.

Looked at frame by frame:

  • "out of the paradigm" f30: 67.3 → 72.7. Its brightness is closer to MilkDrop and there is no artifact. It samples noise; see below.
  • "1200773722134" f59: 4.2 → 8.5, a few percent darker. The blur chain alone causes it, and the chain now reads what MilkDrop reads.

Tried and dropped: full light for motion-vector segments. All 21 of #750's presets came out equal or worse.

The feedback rounds to 8 bits, as MilkDrop's does

With the blur fix alone, "Dead CellWerx" went 17.3 → 20.1 at f59, with a stepped ring and a red background. The root is buffer precision. MilkDrop's buffer is 8-bit and rounds every write to the nearest 1/255; butterchurn and projectM are 8-bit too. Our buffer is 10-bit (RGB10_A2).

Measured in patched MilkDrop with synthetic warps:

warp MilkDrop 10-bit now
+0.0025/frame, f59 59 39 60
+0.0015/frame, f59 0 19 0
×0.9 decay floor, warp shader 4 1 4
×0.9 decay floor, no warp shader 4 1 4
  • What changed. In accurate mode the warp's output (preset shader and shader-less) is rounded to 1/255.
  • What didn't. The buffer stays 10-bit, so waves, shapes and lines drawn after the warp keep their soft edges on screen. The comp pass isn't fed back and isn't rounded.
  • "Dead CellWerx" f59: 20.0 → 8.7, with no ring and no red background.
  • A plain 8-bit buffer was measured and rejected. It rounds those soft edges away in the feedback and darkens line presets:
    • "EoS - 7th galaxy" 43.8 → 63.3;
    • the motion-vector median at f30 3.86 → 4.72.
  • Threshold. Round-to-nearest is used. On this card MilkDrop's threshold sits between 0.52 and 0.6, but D3D lets cards differ.

Before → after the rounding, same MilkDrop frames:

set f10 f59
shader + bTexWrap = 0 (26) 3.48 → 2.84 11.51 → 11.11
random 150 (44) 1.89 → 1.86 4.75 → 4.68
100 without a warp shader (68) 1.24 → 1.24 mean −0.06, none worse by more than 0.67
motion vectors (30) 2.49 → 2.54 5.00 → 4.73
#750's 21 4.25 → 4.19

Main → branch, all three fixes, noise-free comparable presets:

  • wrap set f30: 7.13 → 3.62;
  • random 150 f59: 3.93 → 3.60;
  • motion vectors f10: 2.16 → 2.00.

Nothing is worse by more than 1.03 except "fed - DSC-011", which went from black to MilkDrop's coloured fields.

Noise presets. BeatDrop seeds its noise textures from std::random_device and the performance counter (plugin.cpp:645-646, 2863). Two MilkDrop runs of a noise preset differ by up to 17.6, while noise-free presets come out byte-identical. Every main → branch delta above uses one copy of MilkDrop's frames for both sides.

Checks

  • Tests: npm test passes 3020 of 3020, with 10 new tests.
  • Isolated smoke (--user-data-dir): PASS. The real presets are unchanged; settings.json changed during the run because the installed app was open and writing it, while the smoke used its own profile.

Refs #752, #560, #695

CaYatur and others added 2 commits October 14, 2026 21:44
)

Blur levels 2 and 3 read the previous level through mipmaps that were
only rebuilt after the whole chain, so they lagged one and two frames.
Each level now reads its source's base level, as MilkDrop does.

A plain sampler_main wraps in preset shaders: MilkDrop derives the wrap
mode from the sampler name, and bTexWrap only applies to the shader-less
warp. Blur passes read clamped, and textured shapes read clamped after a
blur pass, both as in MilkDrop. 3,325 library presets have a shader and
bTexWrap=0.

Refs #752

Co-authored-by: caganturgut <271973834+caganturgut@users.noreply.github.com>
Refs #752

Co-authored-by: caganturgut <271973834+caganturgut@users.noreply.github.com>
@cayadraft

cayadraft Bot commented Oct 14, 2026

Copy link
Copy Markdown

@CaYatur

CaYaDraft — pull request

Summary
MilkDrop: fresh blur levels and MilkDrop's sampler wrap rules (#752)

Files
CHANGELOG.md (modified, +10/-0)
@@ -38,6 +38,16 @@ GitHub. The full reasoning and the measurements behind each change are in [ROADM

  • Measured against patched MilkDrop on 120 presets that draw motion vectors, over the presets
    without user textures or randomness: the median frame-59 difference fell from 28.1 to 5.3, with
    no preset worse at frames 2, 10, 30 or 59.
    +- **Blur levels 2 and 3 are no longer one and two fr…

README.md (modified, +2/-2)
@@ -17,7 +17,7 @@ layers and 40 GPU effects, an OBS overlay, Spout / Syphon, **project
Downloads
License: MIT
[![Platform](https://img.shields.io/badge/platform-Windows%20%…

README.tr.md (modified, +2/-2)
@@ -18,7 +18,7 @@ destesi**, ve MCP üzerinden yapay zekâ kontrolü.
İndirme
Lisans: MIT
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-111997.sv…

ROADMAP.md (modified, +30/-2)
@@ -130,10 +130,10 @@ npm test
npm start -- --smoke


-- **3010 unit tests, all passing** on `main`. 703 of those shipped in v3.1.0;
+- **3015 unit tests, all passing** on `main`. 703 of those shipped in v3.1.0;
  105 came with v3.1.1; 163 came with v3.1.2; 157 came with v3.1.3 — 1128 at
  that tag — 469 more with v3.1.4, most of them from the MilkDrop work — 1597 at
-  that tag — 1402 mo…

src/visualizer/modes/milkdrop.js (modified, +46/-3)
@@ -364,10 +364,16 @@ uniform float uBias;
uniform vec3 uEdge;      // kenar karartma: (1-b1ed, b1ed, 5.0); kapaliyken (1,0,5)
uniform float uSat;      // 1: sonuc [0,1]'e doyuyor (MilkDrop'un 8 bit hedefi, #580)
void main(){
-  vec3 c = texture(uSrc, vUV).rgb * uCenter;
+  /* KAYNAK HER ZAMAN TEMEL SEVIYEDEN (textureLod 0). Bir onceki kademenin
+     \`out\` dokusu mipmapli ve mipmap kademele…

tests/milkdrop-blur-edge.test.js (modified, +10/-0)
@@ -56,6 +56,16 @@ test('shader: karartma karekök eğrisiyle uygulanıyor', () => {
  assert.match(FRAG[1], /outColor = vec4\(uSat > 0\.5 \? clamp\(r, 0\.0, 1\.0\) : r, 1\.0\);/);
});

+test('shader: kaynak her zaman temel seviyeden okunuyor (bayat mipmap yok)',…

**Note**
The local model is unavailable, so this is only the title and file list.

---
Stopping here. I will not merge, push, or change this pull request until you ask.

`/caya explain` describes the diff. `/caya plan` previews the next edit. `/caya revise` adds a commit.

<!-- cayadraft -->

CaYatur and others added 2 commits October 14, 2026 23:56
MilkDrop's buffer is 8-bit and rounds every write to the nearest 1/255.
Measured in patched MilkDrop: +0.0025 a frame climbs one level a frame,
+0.0015 never moves, and a x0.9 decay stops at 4/255. The 10-bit buffer
ran all three differently; "Dead CellWerx" flooded with red (frame 59:
20.0 -> 8.7).

In accurate mode the warp's output, shader or shader-less, is rounded
to 1/255. The buffer stays 10-bit, so what is drawn after the warp keeps
its soft edges; a plain 8-bit buffer darkened line presets and was
rejected. The comp pass is not fed back and is not rounded.

Refs #752

Co-authored-by: caganturgut <271973834+caganturgut@users.noreply.github.com>
Refs #752

Co-authored-by: caganturgut <271973834+caganturgut@users.noreply.github.com>
@CaYatur CaYatur changed the title MilkDrop: fresh blur levels and MilkDrop's sampler wrap rules (#752) MilkDrop: fresh blur levels, MilkDrop's sampler wrap rules and 8-bit feedback rounding (#752) Oct 14, 2026
@CaYatur
CaYatur merged commit c2f34c5 into main Oct 14, 2026
5 checks passed
@CaYatur
CaYatur deleted the fix/milkdrop-blur-chain-mip branch October 14, 2026 21:03
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