Skip to content
dnjulek edited this page Jul 9, 2026 · 1 revision

tune is an optional int-array argument on every vszipcl filter that overrides its work-group shape / batching knobs. Defaults are the measured RTX 3070 Ti (Ampere) ship values, used on every vendor so you can retune at runtime instead of rebuilding.

core.vszipcl.EEDI3(clip, field=1, tune=[64, 16, -1, 256])

Semantics

  • Unset, shorter array, or negative entry → keep default (-1 = “leave this one”).
  • Validated at create (range, divisibility, max work-group, local-mem clamp). Bad tune → clear error, not a driver fault.
  • Bit-exact by design except ⚠ Bilateral smem_kib and GaussBlur threshold (path / border semantics). Always A/B hash (or eyeball ⚠) vs untuned before trusting a candidate.
  • Untuned = byte-identical ship behavior (same build opts + output).

Layouts and defaults

Defaults = Ampere ship values on all vendors.

EEDI3 / EEDI3H — [bx, run, run_hp, lws_floor]

# knob default range meaning
0 bx 48 8..256, multiple of run and run_hp Strip width for cooperative cost fill + DP. Wider → fewer barriers/row, more LDS (cst = bx*tpitch floats). Auto-clamped to device local mem; errors if minimum won’t fit.
1 run 8 1..64 Non-hp register-sliding fill length. Longer → fewer trips until register spill.
2 run_hp 4 1..64 Same for hp=True (most register-hungry). Lower first on spills.
3 lws_floor 128 32..1024 Interp WG width floor; clamped to device max WG.

Bilateral — [blk_x, blk_y, smem_kib, pinned]

# knob default range meaning
0 blk_x 16 1..64 Work-group / smem tile width.
1 blk_y 8 1..64 Tile height. blk_x*blk_y ≤ max WG.
2 smem_kib 48 1..1024 sm→gl crossover budget (KiB), clamped to device local mem. 48 = CUDA bilateralgpu parity.Not hash-neutral: sm duplicates borders, gl skips OOB taps. At ship 16×8 (f32 tile): radius ≤49 @48 KiB, ≤58 @64, ≤39 @32 (ref-mode halves budget → ≤33 / ≤39 / ≤26). Gate: (1+has_ref)·(2r+blk_y)·(2r+blk_x)·4 < budget.
3 pinned 1 0/1 1 = pinned host staging (Windows dGPU / WDDM win). 0 = pageable — measure on Linux/ROCm and unified-memory hosts.

GaussBlur — [blk_x, blk_y, vrt, large_r, threshold]

# knob default range meaning
0 blk_x 16 1..64 Work-group shape (both paths).
1 blk_y 8 1..64 blk_x*blk_y ≤ max WG.
2 vrt 3 1..8 Small-path (fused smem) rows per thread.
3 large_r 8 1..32 Large-path outputs/thread along taps. NV 1080p σ=250: 4→189, 8→201, 16→171 fps.
4 threshold 32 0..256 Small→large radius crossover. 0 = always large (one program/stream — helps slow JIT). ⚠ Not proven hash-neutral (NV agreed at boundary; re-check). Oversize fused tiles force large path.

NLMeans — [blk_x, blk_y, vrt, qb, pin_min_streams]

# knob default range meaning
0 blk_x 16 1..64 Weight tile + pointwise grid.
1 blk_y 8 1..64 Local tile (vrt*blk_y+2s)×(blk_x+2s) floats must fit device LDS.
2 vrt 3 1..8 Weight rows per thread.
3 qb 8 (≤~1080p) / 4 (above) 1..32 Displacements per (weight, acc) launch — cuts launch overhead (big on Windows). u4a = 2*qb slot planes/stream (~135 MB cap at adaptive default; tuned qb overrides — watch VRAM).
4 pin_min_streams 2 1..33 Pinned H2D when num_streams >= this. 2 = ship, 1 = always, 33 = never.

Deband — [blk_x, blk_y]

# knob default range meaning
0 blk_x 16 1..64 Work-group shape only (pointwise, no smem).
1 blk_y 8 1..64 blk_x*blk_y ≤ max WG.

Limits that actually differ by vendor (OpenCL)

NVIDIA (OpenCL) AMD RDNA Apple Silicon (OpenCL)
Warp / wave 32 Wave32 primary (Wave64 mode exists); GCN/Vega = 64 SIMD width 32
Local mem (CL_DEVICE_LOCAL_MEM_SIZE) usually 48 KiB usually 64 KiB (WGP physical LDS ~128 KiB) ~32 KiB
Max work-group often 1024 often 256 (query it) often 256 (Metal max can be 1024)
Host path PCIe; Windows WDDM adds launch cost PCIe or APU unified memory — no PCIe to hide

CUDA on NVIDIA can use more than 48 KiB shared with a dynamic opt-in; this plugin’s OpenCL path does not. Prefer WG sizes that are multiples of the wave (32 on RDNA/NVIDIA/Apple OpenCL; 64 on GCN/Vega).


Starting points by GPU

Only Ampere is measured. Everything else is a starting guess — sweep and keep the winner. ⚠ rows can change borders / path output.

NVIDIA RTX 30 (Ampere) — machine of record

Leave tune unset. Ship defaults are the optimum here.

Dead ends (don’t re-try without new evidence): GaussBlur/NLMeans pinned D2H; Bilateral VRT / -D-baked params; EEDI3 bx=64 (48 KiB residency wall); run=16 on hp (−17%).

NVIDIA RTX 40 (Ada) / RTX 50 (Blackwell) — unmeasured

Start unset. Same OpenCL envelope (warp 32, ~48 KiB local). Silicon can differ; codegen and Windows launch cost are not guaranteed identical to Ampere.

Filter First try Then
EEDI3 [-1, 16, -1, -1] run 8/16; then run_hp 4/8 (hp separately — Ampere 16 hurt hp)
NLMeans [-1, -1, -1, 12, -1] qb 8/12/16 (driver/OS-bound)
GaussBlur [-1, -1, -1, 12, -1] large_r 8/12/16 at large sigma
Bilateral / Deband unset Optional blk shape A/B; no strong gen prior

AMD RDNA 3 / 4 — unmeasured

Wave32 + ~64 KiB OpenCL local → a bit more room than NVIDIA’s 48 KiB gate. No -cl-nv-maxrregcount — watch VGPR spills (RGP / rocprof). RDNA 4: same starting table.

Filter First try Then
EEDI3 (non-hp) [64, -1, -1, -1] bx 48/64/96 · run 8/16 · lws_floor 128/256
EEDI3 (hp) unset bx 32/48/64 · lower run_hp on spill
Bilateral [16, 16] 16×8 / 16×16 / 8×8 · pinned=0 on Linux · ⚠ smem_kib=64 only if border parity with bilateralgpu doesn’t matter
GaussBlur [16, 16, -1, 4] vrt 1..3 · large_r 4/8/16 · ⚠ threshold 24..48
NLMeans [16, 16] vrt 1..3 · qb 4..16 · pin_min_streams 1/2/33
Deband [8, 8] [8,8] / [16,16] / [32,8]

GCN/Vega: prefer WG multiples of 64.

Apple Silicon (M1+) — unmeasured, OpenCL compatibility only

OpenCL is deprecated (frozen 1.2; Metal is first-class). Expect correct results, not dGPU fps. Untuned is already LDS/max-WG safe (clamps + large-path fallback).

One starting preset for all M-series (same OpenCL surface; faster chips just run it faster):

Filter First try Notes
EEDI3 [32, -1, -1, -1] bx=32 leaves LDS headroom under ~32 KiB; lower run/run_hp if spills
Bilateral [-1, -1, -1, 0] Disable pinned (UMA). sm budget auto-clamps (~r≤39 @32 KiB)
GaussBlur unset Slow create()? try threshold=0 (always-large, one JIT) then A/B fps
NLMeans [-1, -1, -1, 4, 33] Never pin; smaller qb saves RAM
Deband unset Optional [16,16] / [8,8]

Also: num_streams=1 first; pick GPU device_id if a CPU CL device exists; f16 works via core half storage but is less exercised here. Deband blue-noise / native_sin parity claims are Windows/NVIDIA-oriented — don’t expect identical dither bits.


Sweep protocol

  1. Correctness: hash/diff vs untuned (except ⚠ → accept or visual-check borders).
    num_streams 1-vs-4 identical; create/destroy clean.
  2. Speed: vspipe -p, candidate vs unset interleaved same window, median ≥3 pairs, real params (mdis, d/a/s, …).
  3. Change one knob at a time; re-check combo winners.
  4. AMD mystery slowdown → check spills / LDS occupancy; usually lower run / run_hp / vrt / bx.