How dFdx Computes Screen-Space Derivatives in GLSL

The dFdx function in GLSL calculates the horizontal rate of change of any arbitrary value across adjacent screen-space pixels. Rather than performing mathematical differentiation through analytical formulas, GPUs approximate this derivative using discrete finite differences. By organizing fragment shader execution into small 2x2 pixel clusters known as quads, the hardware can evaluate the difference between a variable's value in neighboring fragments along the horizontal axis in real time.

The 2x2 Pixel Quad Architecture

Modern GPUs do not process fragment shaders as completely isolated threads. Instead, they execute fragments in lockstep groups called quads (2x2 pixel grids). A single quad consists of four fragments:

  • Top-left \((x, y)\)
  • Top-right \((x+1, y)\)
  • Bottom-left \((x, y+1)\)
  • Bottom-right \((x+1, y+1)\)

Because all four fragments inside a quad execute instructions simultaneously on Single Instruction, Multiple Data (SIMD) hardware, their registers can be read by neighboring lanes within the execution unit without costly memory transactions.

The Finite Difference Calculation

To evaluate dFdx(p) for an arbitrary expression or variable p, the GPU reads the value of p computed by horizontally adjacent fragments in the quad and computes the difference:

\[\Delta p = p_{\text{right}} - p_{\text{left}}\]

In GLSL, the exact calculation depends on whether the GPU applies a "coarse" or "fine" derivative:

  1. Coarse Derivatives (dFdxCoarse): The GPU calculates a single horizontal delta across the quad (typically p(top-right) - p(top-left)) and shares that exact result with all fragments in the quad.
  2. Fine Derivatives (dFdxFine): The GPU evaluates separate horizontal deltas for both rows:
    • Top row: p(top-right) - p(top-left)
    • Bottom row: p(bottom-right) - p(bottom-left)

The standard dFdx function is typically mapped by the GPU driver to either the fine or coarse implementation depending on the target hardware and driver optimizations.

Helper Invocations at Primitive Edges

If a rendered triangle only covers a portion of a 2x2 quad, the unshaded pixels still execute the fragment shader as helper invocations (or dummy fragments). These helper fragments compute values strictly to provide valid data for derivative operations like dFdx and dFdy. Once shading finishes, the output of helper invocations is discarded and never written to the framebuffer.

Practical Applications

Because dFdx calculates rates of change directly in window coordinates, it is essential for several core graphics techniques:

  • Texture Filtering (Mipmapping): Determines how fast texture coordinates \((u, v)\) change per screen pixel to select the appropriate mipmap Level of Detail (LOD).
  • Faceted Normal Reconstruction: Generates flat-shading surface normals dynamically using the cross product of horizontal and vertical position derivatives: normalize(cross(dFdx(vPosition), dFdy(vPosition))).
  • Procedural Anti-Aliasing: Sets filter widths for procedural shapes (such as stripes, grids, or circles) to avoid aliasing artifacts.

Divergent Control Flow Limitation

Because dFdx depends on inter-lane communication within a 2x2 quad, calling it inside non-uniform control flow (such as an if statement where only some fragments in a quad take the branch) results in undefined behavior. The GPU cannot reliably compare neighbor values if those neighbors executed a different set of instructions.