Dual-Filtering Blur vs Gaussian Blur in DirectX

Dual-filtering blur is a highly efficient post-processing technique designed to generate large, smooth blur radii—such as those used in bloom, depth of field, and user interface backdrops—by chaining together alternating downsampling and upsampling passes. While standard Gaussian blur relies on gathering many texture taps across full- or half-resolution surfaces, dual filtering drastically reduces GPU workload by processing lower-resolution render targets using fixed, sparse sample patterns. In DirectX rendering pipelines, this approach minimizes texture sample counts and memory bandwidth, delivering visually comparable results at a fraction of the computational cost.

What Is Dual-Filtering Blur?

First introduced by Marius Bjørge at ARM during SIGGRAPH 2015, dual-filtering blur achieves a wide blur footprint through a pyramidal downsampling and upsampling pipeline. Rather than calculating a blur across a large pixel radius directly, it decomposes the operation into two distinct filtering phases:

  1. Downsampling Filter: The source texture is downscaled through several successive passes (typically down to 1/2, 1/4, 1/8, or 1/16 of the original resolution). At each step, a compact filter kernel (often 5 texture samples) gathers surrounding pixels to avoid aliasing and reduce the image size.
  2. Upsampling Filter: Once the lowest resolution is reached, the image is incrementally upscaled back to the target resolution. At each upsampling step, an expanded filter kernel (typically 8 to 9 texture samples) blends the lower-resolution data with smooth interpolation.

By spreading the blur across spatial scales rather than increasing the kernel footprint at a static resolution, the effective blur radius expands exponentially with each iteration.

The Bottlenecks of Standard Gaussian Blur

Traditional Gaussian blur applies a normal distribution curve across surrounding pixels. While standard implementations optimize the 2D convolution by separating it into two 1D passes (horizontal and vertical), performance issues still emerge in real-time DirectX applications:

  • Linear Cost Scaling (\(O(N)\)): As the desired blur radius expands, the number of required texture samples per pixel scales linearly. Simulating large bloom effects often requires dozens of taps per pixel across both passes.
  • Full-Resolution Memory Bandwidth: Executing horizontal and vertical blur passes at full or half resolution demands continuous reading and writing to large render target views (RTVs) and shader resource views (SRVs).
  • Cache Misses: Wide sampling kernels read texels located far apart in memory, leading to texture cache thrashing and stalled GPU compute or pixel shader warps.

Why Dual Filtering Is Faster in DirectX

Dual-filtering blur bypasses the architectural limits of modern graphics hardware through several specific optimizations:

1. Significant Bandwidth Reduction

GPUs are predominantly memory-bandwidth bound during post-processing. Because dual filtering halves the resolution at each downsampling step, the pixel count drops by 75% for each level. The bulk of the texture reads and writes take place on tiny render targets, drastically reducing the total gigabytes per second pushed through the DirectX pipeline.

2. Exploitation of Hardware Bilinear Sampling

Dual-filtering kernels are specifically designed to sample between texel centers. By offsetting UV coordinates by half-texels, the shader leverages the GPU's fixed-function texture filtering hardware to fetch four texels with a single bilinear tap. Consequently, an 8-tap upsampling kernel effectively processes 32 texels of spatial information in a single pass without extra ALU cost.

3. Fixed Sample Count per Pass

In standard Gaussian blur, increasing the blur size requires increasing the number of loop iterations or hardcoded texture fetches. With dual filtering, the sample count remains completely constant (typically 5 taps down, 8 taps up). To widen the blur, the engine simply adds another downsample/upsample mip level, which executes on an exponentially smaller grid of pixels.

4. Optimal Wavefront Occupancy in DirectX

DirectX pixel shaders and compute shaders execute threads in waves or warps. Because dual-filter kernels only sample immediate neighbors relative to the current mip scale, memory access remains localized. This spatial locality preserves texture cache hit rates and keeps GPU execution units saturated, avoiding the execution bubbles common in wide-radius Gaussian passes.