Overview
Better Blur DX implements the Dual Kawase blur algorithm, a highly efficient two-pass blur technique that achieves high-quality blur effects with minimal texture samples. The algorithm consists of downsampling and upsampling passes that progressively blur the image.Dual Kawase Algorithm
The Dual Kawase algorithm works by:- Downsampling Pass: Progressively scales the image down by 50% each iteration while applying blur
- Upsampling Pass: Scales the image back up by 200% each iteration with additional blur
- Final Render: Applies color correction, noise, and rounded corners to the blurred result
Why Dual Kawase?
- Performance: Requires only 8 texture samples per pass (vs. Gaussian blur’s many more)
- Quality: Produces smooth, natural-looking blur without visible artifacts
- Scalability: Blur strength can be easily adjusted by changing iteration count and offset values
Blur Strength System
The blur strength is controlled by two primary parameters configured inblur.cpp:259-314:
Iteration Count
The number of downsample/upsample iterations determines the base blur radius. Better Blur DX supports up to 4 iterations:Number of downsample/upsample passes (1-4). Each iteration halves then doubles the texture size.
Offset Values
The offset controls how far apart texture samples are taken. Each iteration has min/max offset constraints:Blur Strength Values
TheinitBlurStrengthValues() function (blur.cpp:259) creates 15 evenly-distributed blur strength presets:
Downsample Pass
Implemented inblur.cpp:993-1019 and shaders/downsample.frag.
Algorithm
The downsample pass uses a weighted 5-sample pattern:Process
- Render to progressively smaller framebuffers (each 50% of previous size)
- Sample the center pixel heavily (weight 4) plus 4 diagonal neighbors (weight 1 each)
- Apply offset multiplier to control blur spread
- Repeat for
m_iterationCountiterations
Half the pixel size of the source texture, used for precise texture coordinate offsets
Upsample Pass
Implemented inblur.cpp:1021-1046 and shaders/upsample.frag.
Algorithm
The upsample pass uses an 8-sample tent filter pattern:Pattern
The tent filter creates a weighted cross pattern:Noise Pass
Implemented inblur.cpp:1138-1166 and shaders/noise.frag.
Purpose
Adds subtle grain to prevent color banding in smooth gradients. Banding occurs when 8-bit color depth creates visible steps in smooth blur transitions.Implementation
- Is generated once and cached (blur.cpp:719)
- Scales with screen DPI for consistent appearance
- Uses
GL_REPEATwrap mode for seamless tiling - Is rendered additively over the blurred result
Maximum random value per pixel (0-255). Higher values create more visible grain.
Contrast Pass
Applies color transformation for brightness, saturation, and contrast adjustment.Color Matrix
Implemented inblur.cpp:82-113 as colorTransformMatrix():
Application
The color matrix is applied in the onscreen pass shader:Per-Window Override
Windows can override the global color matrix via Wayland protocols:Render Pipeline
The complete blur rendering pipeline inblur() (blur.cpp:755-1173):
- Capture Background (blur.cpp:875-886): Copy screen region to framebuffer 0
- Downsample (blur.cpp:993-1019): Apply downsample shader
m_iterationCounttimes - Upsample (blur.cpp:1021-1046): Apply upsample shader back to original size
- Onscreen Pass (blur.cpp:1094-1136): Render with color matrix and opacity
- Noise Pass (blur.cpp:1138-1166): Add grain if
m_noiseStrength > 0 - Rounded Corners (blur.cpp:1168-1170): Apply corner masking if needed
Framebuffer Management
Framebuffers are allocated dynamically (blur.cpp:843-873):- Framebuffer 0: Contains unblurred background (cache)
- Framebuffers 1-N: Downsampled textures at 1/2, 1/4, 1/8, 1/16 size
- Textures match render target format (usually GL_RGBA8)
Performance Characteristics
Texture Samples per Pixel
- Downsample: 5 samples ×
m_iterationCountiterations - Upsample: 8 samples × (
m_iterationCount- 1) iterations - Onscreen: 8 samples
- Noise: 1 sample (if enabled)
- Downsample: 5 samples × 2 = 10
- Upsample: 8 samples × 1 = 8
- Onscreen: 8 samples
- Total: ~26 texture samples
Memory Usage
Framebuffer memory per window (assuming 1920×1080 RGBA8):- Iteration 1: 960×540 × 4 bytes = 2.07 MB
- Iteration 2: 480×270 × 4 bytes = 0.52 MB
- Iteration 3: 240×135 × 4 bytes = 0.13 MB
- Iteration 4: 120×67 × 4 bytes = 0.03 MB
- Total: ~2.75 MB per blurred window
Framebuffers are cached per RenderView and only reallocated when window size, iteration count, or texture format changes.