17
\$\begingroup\$

I'm a bit confused. The official documentation (http://msdn.microsoft.com/en-us/library/windows/desktop/bb509588(v=vs.85).aspx) says that ddx(input) is the partial derivative of the input with respect to the "screen-space x-coordinate."

My calculus is fine, but how can it tell what the input is coming from? If I passed it a function, fine, I can imagine what it would do, but the derivative of a number is always zero...?

Is it just a scaling? Like, this would get scaled to one tenth of its size at this distance, so it returns one tenth? But then it doesn't need an input...

Can someone give a quick explanation of what's actually happening?

\$\endgroup\$
  • 2
    \$\begingroup\$ ddx and ddy do magic that you cannot do yourself. They have access to additional information from the rasterization pipeline that you can't acquire from within a pixel shader. I'm unsure exactly which formula they use; I've been led to believe they use an estimation technique, but I don't know for sure. \$\endgroup\$ – Sean Middleditch Sep 24 '13 at 17:33
  • \$\begingroup\$ That's deeply weird, but still. What does ddx(5) actually represent? If it says .1, how should I interpret that? Or -6? \$\endgroup\$ – Richard Rast Sep 24 '13 at 17:35
  • 1
    \$\begingroup\$ ddx(5) is kind of pointless. Use it with an input value, and that will give you the derivative of that value with respect to neighboring pixels in a block. Again, I don't know how precisely it calculates values, but asking for the derivative of a non-input may well just be undefined behavior and produce garbage. See fgiesen.wordpress.com/2011/07/10/… for more information than I can provide. \$\endgroup\$ – Sean Middleditch Sep 24 '13 at 17:41
  • \$\begingroup\$ I am not sure but it might well be that the HLSL compiler is actually doing full symbolic differentiation of the expression that you pass to the ddx/ddy. blogs.msdn.com/b/chuckw/archive/2011/03/08/… research.microsoft.com/pubs/146019/… \$\endgroup\$ – Ziriax Feb 1 '15 at 21:11
  • \$\begingroup\$ What does it do? The ONLY slightly reasonable thing, obviously. \$\endgroup\$ – MickLH Jul 10 '16 at 20:54
32
\$\begingroup\$

Internally, GPUs never run one instance of a pixel shader at a time. At the finest level of granularity, they are always running 32-64 pixels at the same time using a SIMD architecture. Within this, the pixels are further organized into 2x2 quads, so each group of 4 consecutive pixels in the SIMD vector corresponds to a 2x2 block of pixels on screen.

Derivatives are calculated by taking differences between the pixels in a quad. For instance, ddx will subtract the values in the pixels on the left side of the quad from the values on the right side, and ddy will subtract the bottom pixels from the top ones. The differences can then be returned as the derivative to all four pixels in the quad.

Since the pixel shader is running in SIMD, it's guaranteed that the corresponding value is in the same register at the same time for all the pixels in the quad. So whatever expression or value you put into ddx or ddy, it will be evaluated in all four pixels of the quad, then the values from different pixels subtracted as described above.

So taking the derivative of a constant value will give zero (as you'd expect from calculus, right?) because it's the same constant value in all four pixels.

Also note that there are "coarse" and "fine" derivatives, ddx_coarse/ddy_coarse and ddx_fine/ddy_fine. An explanation of the distinction is given here. Just plain ddx/ddy are aliases for the coarse versions.

BTW, the reason this functionality exists is that GPUs internally have to take derivatives of texture coordinates in order to do mipmap selection and anisotropic filtering. Since the hardware needs the capability anyway (you can use any arbitrary expression for texture coordinates in a shader), it was easy enough to also expose it to shader programmers directly.

\$\endgroup\$
  • \$\begingroup\$ +1; I've found this useful when applying a perturbation effect to a texture; the perturbed texcoords gave some colour distortion, but using tex2Dgrad with derivatives from the original (unperturbed) texcoords gave an undistorted result. \$\endgroup\$ – Maximus Minimus Sep 24 '13 at 20:07
  • \$\begingroup\$ Alright, after some thought I think I figured out my confusion. In my mind, ddx works like every other function in a traditional programming language; you evaluate the input, turn it to a float or whatever, then do the outside function (ddx) to it. But this is different- it takes the input string, evaluates it in several places, calculates a derivative, and reports the result. Is that about right? \$\endgroup\$ – Richard Rast Sep 24 '13 at 22:59
  • 1
    \$\begingroup\$ @RichardRast Right. You can think of ddx as operating on the expression you pass into it, rather than the value. \$\endgroup\$ – Nathan Reed Sep 24 '13 at 23:22
  • \$\begingroup\$ But see, that's weird enough that it should be in the documentation. I can think of no other time in my programming life where that has been the case. \$\endgroup\$ – Richard Rast Sep 24 '13 at 23:23
  • \$\begingroup\$ Nathan Reed mentioned that GPUs use these functions for mipmap selection. You can also use them to force the GPU to select a desired mipmap level or to prevent artifacts from incorrect mip-level selection. The process is described on pages 163 and 164 of Shader X3. \$\endgroup\$ – JamesHoux Dec 5 '13 at 17:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.