Skip to content

fix(deps): KNLMeansCL v1.3 on every platform (#144) - #145

Merged
StuartCameronCode merged 8 commits into
mainfrom
fix-144-knlmeanscl
Nov 3, 2026
Merged

StuartCameronCode merged 8 commits into
mainfrom
fix-144-knlmeanscl

Conversation

@StuartCameronCode

Copy link
Copy Markdown
Owner

Fixes the KNLMeans failures reported in #144, and a separate KNLMeans bug on macOS that turned up while confirming the fix.

What was wrong

  • Windows shipped Khanattila's KNLMeansCL v1.1.1 release zip (2018). It accepts 10-bit only as packed YUV444P10/RGB30 with channels="YUV", and no 9/12/14-bit at all. Our channels="Y" / "UV" calls therefore failed on every 10-bit 4:2:0/4:2:2 source with only YUV444P10 and RGB30 are supported!, which is the error in the issue. QTGMC's knlmeanscl denoiser makes the same calls through havsfunc's wrapper.
  • macOS built Khanattila's unpinned master, which doesn't wait on its OpenCL buffer fills. Apple's OpenCL doesn't complete them in time. On an M1 Pro, 24 of 60 depth/layout/channel cases gave a different result on a repeat run (by 110–255 levels). Checked against a CPU reference of the kernel's maths, it denoised at h=1.2 where the algorithm leaves the picture alone.

The change

Every platform moves to Jaded Encoding Thaumaturgy's maintained fork, v1.3 (GPL-3.0). It carries pinterf's 9–16-bit support and the fill fix, and its OpenCL kernel is unchanged from master.

Platform Before After
Windows v1.1.1 release zip PyPI wheel vapoursynth-knlmeanscl 1.3
macOS arm64/x64 master, unpinned tag v1.3 from source (KNLM_TAG)
Linux x64/arm64 master, unpinned tag v1.3 from source (KNLM_TAG)
  • v1.3's meson.build assigns its VapourSynth include probe to a variable (r = run_command(...) … r.stdout().strip()). Both scripts' meson patchers now handle that form. I checked them against v1.3, BM3D r12, CAS and mvtools; the three existing forms patch exactly as before. On macOS the patcher moved into a patch_meson_vs_include function so the x86_64 KNLMeansCL block can use it too.
  • KNLMeans joins integration_high_bit_depth_filters_test.dart (heavy): every depth group, plus a 10-bit 4:2:0 group (preview parity and a full encode). These cases skip without an OpenCL device, so CI never runs them.
  • NOTICES, the About dialog and the README credit the fork. A CLAUDE.md rule, an ENGINEERING_NOTES entry and a report at docs/reports/issue-144-knlmeanscl/ (with scripts) record the evidence.

Behaviour changes

  • On macOS, KNLMeans now denoises less at low h. That matches Windows/Linux and the algorithm's design; the old result came from the race. Defaults are unchanged.
  • v1.3's device_type="auto" works on Apple Silicon, so QTGMC's knlmeanscl denoiser now really runs on a Mac instead of silently falling back to DFTTest.

Testing

On macOS arm64, with the local bundle's dylib replaced by a v1.3 build made with the script's recipe:

  • All 60 probe cases run and repeat bit-identically; the source build is bit-identical to the PyPI wheel; it is within 0.26 levels of the CPU reference.
  • High-bit-depth KNLMeans cases pass: 4:2:2 8- vs 10-bit parity 0.301; 10-bit 4:2:0 parity 1.394 (the fixtures alone differ by 1.717); 4:2:0 encode passes.
  • Existing KNLMeans and QTGMC knlmeanscl encodes pass; flutter test --exclude-tags heavy passes (1271); cargo test passes except the whisper subtitle test (add-on not installed locally, fails the same on main).

Not yet tested: Windows, the platform the issue is about, which will be verified on a Windows machine with a GPU next. macOS x64 and Linux builds are verified only by build-deps-* CI. pwsh wasn't available here, so the .ps1 change hasn't been parse-checked.

Before merging

Needs a deps 1.15.0 release. 1.14.0 is referenced by released apps, so it can't be rebuilt in place. deps-version.json is deliberately not bumped here.

🤖 Generated with Claude Code

StuartCameronCode and others added 8 commits November 3, 2026 14:35
Windows shipped Khanattila's v1.1.1 release (2018), which refuses every
9-15-bit clip except packed YUV444P10/RGB30, so the KNLMeans Noise
Reduction method (and QTGMC's knlmeanscl denoiser) failed on any 10-bit
4:2:0/4:2:2 source. macOS built Khanattila's unpinned master, whose
unsynchronised buffer fills on Apple's OpenCL gave unrepeatable results:
24 of 60 cases changed between runs on an M1 Pro, by 110-255 levels.

Move every platform to Jaded Encoding Thaumaturgy's maintained fork, v1.3:
the PyPI wheel on Windows, the v1.3 tag built from source on macOS and
Linux. Both meson patchers learn v1.3's assigned run_command include
probe. KNLMeans joins the high-bit-depth parity suite, with a 10-bit 4:2:0
group; the cases skip without an OpenCL device.

Measured on macOS arm64 only; Windows still needs a run on real hardware.
Report: docs/reports/issue-144-knlmeanscl/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI derives the asset version from the release tag, so the rc's zips are
VapourBox-deps-1.15.0-rc1-*.zip. Repoint at deps-v1.15.0 before merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows x64 (Radeon 780M, OpenCL probe gpu), the deps 1.14.0 DLL
(v1.1.1) reproduces the reported error on every 10-bit case, plus 12-bit,
and QTGMC's knlmeanscl denoiser fails the same way. The v1.3 DLL installed
by download-deps-windows.ps1 matches the wheel and the rc1 bundle, and
passes all of them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
integration_frame_mapping_test (#142) calls runPreview(subFrame: k), but
the harness change never landed, so the file failed to compile and every
nightly since has reported it as a load failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MVTools v24 (Windows, macOS x64) makes FlowFPS emit floor((n-1)*r)+1
frames; master (macOS arm64, Linux) emits floor(n*r), which is what
FrameMap::Retime counts. A FlowFPS job on Windows or an Intel Mac was one
frame short (or long, below 1x), so the progress total and preview index
were off at the tail. Found by the custom-rate test's first CI run: 178
frames for 179 on both macOS x64 jobs.

Both templates now compute floor(n*r) with Fraction and trim or pad (with
the last frame, which is what master returns past the source) after the
call. test_183 asserts it. The frame-mapping suite passes against the
arm64 bundle (master) and the macos-x64 bundle under Rosetta (v24).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the GPU-less Linux runners RIFE runs on lavapipe alongside the other
heavy files, and the group's 75-frame setup encode hit the harness's
default 5 minutes in four of main's last seven nightlies (it passed in
under two on other jobs). It now gets 20 minutes and the matching stall
limit, as the AI-upscale encodes do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
deps-v1.15.0 is the tested deps-v1.15.0-rc1 bundles with only the version
changed (file names, each zip's version.json, the deltas' baseSha256, the
sidecars); every other entry has the same contents, size and mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	docs/ENGINEERING_NOTES.md
#	docs/reports/README.md
#	worker/tests/filter_integration_test.rs
@StuartCameronCode
StuartCameronCode merged commit 039b775 into main Nov 3, 2026
4 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