Skip to content
dnjulek edited this page Jul 7, 2026 · 3 revisions

OpenCL port of placebo.Debandlibplacebo's pl_shader_deband (the shader behind vs-placebo's Deband).

Deband removes banding by low-pass filtering with random-directional reference sampling: for each pixel it takes a few random-angle 4-tap averages and replaces the pixel with the average where the local difference is below threshold (smoothing banding while keeping detail). Optional grain is then added to dither out residual banding.

Each plane is debanded independently as a grayscale pass (exactly like placebo.Deband, which uploads every plane as its own texture).

Usage

vszipcl.Deband(vnode clip[, int iterations=1, float threshold=3.0, float radius=16.0, float grain=4.0, int planes=1, int dither, int dither_algo=0, int device_id=0, int num_streams=1])

Parameters

  • clip:
    Clip to process. 8/16-bit integer, 16-bit half, or 32-bit float; Gray / YUV / RGB.
  • iterations: (Default: 1, range: 0..32)
    Number of debanding passes. Each pass samples at a larger radius. iterations=0 disables debanding and applies only grain (a pure grain function).
  • threshold: (Default: 3.0)
    Debanding threshold — the maximum difference (before the /1000 internal scale) below which a pixel is considered banding and smoothed. Higher values deband more aggressively (and blur more detail).
  • radius: (Default: 16.0)
    Sampling radius of the initial pass (grows with each iteration). Larger values catch wider bands.
  • grain: (Default: 4.0)
    Strength of the added grain. 0 disables grain.
  • planes: (Default: 1)
    Bitmask of planes to process (placebo-style): bit 0 = plane 0, bit 1 = plane 1, bit 2 = plane 2. Default 1 = luma only. 7 = all three planes. Unselected planes are copied through untouched.
  • dither: (Default: on for 8-bit, off otherwise)
    Enable dithering. Like placebo.Deband, dither is applied only to 8-bit input (and only to the first processed plane) — it is ignored for 16-bit and float.
  • dither_algo: (Default: 0)
    Dither method (8-bit only): 0 blue noise, 1 ordered (bayer) LUT, 2 ordered fixed, 3 white noise.
  • device_id / num_streams: see Home.

Examples

out = core.vszipcl.Deband(src)                              # luma, default strength
out = core.vszipcl.Deband(src, planes=7)                    # all planes
out = core.vszipcl.Deband(src, iterations=0, grain=8)       # grain only
out = core.vszipcl.Deband(src, threshold=6, radius=24, grain=8)  # stronger

Notes on parity with placebo.Deband

The reference runs GLSL→SPIR-V on Vulkan, so bit-exactness across GPU APIs is not generally possible — the bar is faithful reproduction. In practice:

  • iterations=0 (pure grain) is bit-exact (max|Δ| = 0) — this validates the PRNG (pcg3d), the seed, the grain math, and the integer store rounding.
  • iterations ≥ 1 differs on ~0.03% of pixels (each ±1-threshold), an inherent consequence of matching Vulkan's fixed-function rasterizer sampling in OpenCL.

For an A/B against placebo.Deband, set core.num_threads = 1 and render sequentially from frame 0 (placebo's grain seed is a render-order counter); blue-noise dither additionally needs placebo to be the first dither instance in a fresh process.

Benchmark

1080p YUV420, fps (higher is better), RTX 3070 Ti — see Benchmarks for method. iterations=1, threshold=4, radius=16, grain=6, planes=1.

Implementation u16 f16 f32
vszipcl (s=1) 882.8 889.3 458.3
vszipcl (s=2) 877.2 887.3 458.7
placebo 569.5 585.9 337.2

~1.35–1.55× placebo. The single deband kernel saturates the GPU, so num_streams=2 is neutral.

Clone this wiki locally