dPdx and dPdy from OpenGL 2.0 spec

mrbill and folker have been doing a sterling job of trying to explain the workings of dpdx and its family .

As the original author of the Shading Language White paper I must apologise if the explanation of this ‘function’ was not to Nakoruru’s satisfaction and we will add some language to clarify things.

Derivatives can be derived in two ways - analytically or numerically. In the analytic case you need an expression while in the numerical case you just need to be able to find the value of the ‘thing’ you want to differentiate local to the point of interest.

Analytic differentiation is clearly not workable as any of the elements of the expression may not be differentiable - a lookup table (bump mapping, for example) would fall into this category.

For numeric differentiation if you make the assumption that the fragments are being evaluated on a SIMD array and you have access to your neighbour’s values then the magic of the dPdx call is to ask their x neighbour’s for their value of the ‘thing’ and subtract yours from it. The dx value is one, by definition, so the result is simple to provide.

At the evaluation of dPdx the ‘thing’ is just a number so how it was derived is immaterial. The function does accept an expression (as all functions do for their input arguments) and the expression is evaluated to a value. The expression can be anything, even the framebuffer colour value, and the only uncertainty arises if the expression is evaluated as part of a conditional (because the neighbour’s values may not be defined).

Should it be a function or operator? There are a limited number of suitable characters in the 7 bit ASCII set and as soon as you start to reuse to ones defined by C the expressions can end up looking very confusing. Using a named function makes the operation more readable and allows the user to redefine it if they want to - operator overloading is not a supported language feature so if done through an operator user couldn’t redefine the meaning.

We make a point in the white paper of saying that the built in functions are provided for convenience and to access hardware specific features. The convenience functions can easily be emulated by the user but the hardware ones may be impossible or just very difficult.

dPdx is in the impossible category to do a general solution, but it is possible to do a specific solution if you know what the analytic solution is or you can manually calculate the local differences. For you to calculate the local differences you will have to know the complete expression tree, maybe starting 5 subroutine levels back for example, so you can substitute x+1 into it. The expression tree may also use data you don’t have access to, such as the framebuffer colour value of your neighbour.

I did have a long write up and how I finally get it and am sorry that I got carried away. But my browser hiccuped and I lost it. I think I’ll start writing my posts in Notepad from now on :slight_smile:

My misunderstanding ‘derived’ from the fact that I was imagining that dPdx worked by returning the delta of variables that are varying. In otherwords, it knew how the inputs to the fragment shader varied. That would mean that in addition to, say gl_TexCoord0, there is also somewhere a gl_TexCoord0_dx and a gl_TexCoord_dy. So, one theory of mine was that dPdx ‘dereferenced’ gl_TexCoord0 into gl_TexCoord0_dx. In that case, the function could only take lvalues (values associated with a storage location). I figured that since there was no mechanism in the grammar for understanding anything other than lvalues that it would not be able to take expressions.

If that mechanism did exist then it could do numerical derivation by rewriting any expression:

dPdx(f(gl_x)) = f(gl_x + gl_x_dv) - f(gl_x)

where gl_x is a varying,
f is some expression involving gl_x,
and gl_x_dv is the delta that I imagined

Its possible, but I didn’t see where it could be done (there was no grammar to support it), so I refused to believe that was what was going on without proof (and good thing too, because that is not whats going on).

Dave Baldwin gave me the piece of the puzzle that makes it all come together. Apparently, dPdx has access to the results of the expression that ‘will be’ passed into its neighbor (it knows the future because its all being done in parallel). If it knows the result of the expression with the next value plugged into it, then all it has to do is return the difference.

I knew it had to be something that simple, because its something that has to be done a couple of billion times a second.

The main reason I ask what would happen with sin(x) (especially if I redefined it to be very un-sin-like) is because I was rejecting the notion that an analytical derivation was taking place. I was under the impression that Folker believed that, because he kept talking about things that only make sense with analysis (maybe I misunderstood). Thats what I refered to as fantasy. Maybe he is just trying to give me a calculus lesson :slight_smile:

But, now I know what would happen if I gave it an arbitrary function. It would just work, because it has the parallel results of whatever function I write.

So, now I have new questions. Does this mean that an implementation would always have to run the fragment shader for one extra fragment in the x direction and one extra fragment in the y direction? meaning that if I draw a single point, that it actually has to calculate 4 pixels?

Next, what happens if I try to take the second derivative? dPdx(dPdx(blah))? Seems to me it would require you to calculate two fragments ahead to be correct.

I have a feeling the answers to these questions are related, and they reflect the fact that while what I explain above may be basically true, that its not the whole story about how this works.

Here is an example of how I could do something like what I now believe is going on using a C++ functor:

struct D {
float prev;

D(float first) : prev(first) {}

float operator () (float expression_result)
{
float rv = expression_result - prev;
prev = expression_result;
return rv;
}

};

You can see that with this class, I can call an instance of D like a function and it will remember the last value I passed to it and return the difference. For example:

float x = 0;
D Dsinx(x);

for (x = 0 + SMALL_DELTA; x < PI2; x += SMALL_DELTA)
{
mysin = sin(x);
mycos = Dsinx(mysin); // derivative of sine is cosine
}

Originally posted by Nakoruru:
[b]So, now I have new questions. Does this mean that an implementation would always have to run the fragment shader for one extra fragment in the x direction and one extra fragment in the y direction? meaning that if I draw a single point, that it actually has to calculate 4 pixels?

[/b][/QUOTE]

That would be one way to do this, although it really is an implementation detail.

Originally posted by Nakoruru:
[b]
Next, what happens if I try to take the second derivative? dPdx(dPdx(blah))? Seems to me it would require you to calculate two fragments ahead to be correct.

[/b][/QUOTE]

You can next the dPdx as many times as you like and it will all hang together. The inner most dPdx is evaluated as already discussed for all the neighbouring fragments. This gets fed into the next evaluation as a value - recall dPdx doesn’t know or care about where a value came from - and the process repeated.

Dave.
3Dlabs.

Thanks.

My main concern about second (and third) derivatives was that they would not be accurate until a few iterations in. Meaning that the left and top edge of primitives would be wrong. (assuming that things are rasterized from left to right, top to bottom…).

That makes me thing of something else. In RenderMan these derivatives are in the U and V directions of the primative, meaning that they are relative to the surface.

I assume that dPdx and dPdy are relative to the framebuffer, this means that they change relative to the surface when the surface is rotated. Doesn’t this introduce some amount of variance under rotation? Would this be something to keep in mind as one translates RenderMan shaders to OpenGL SL?

Originally posted by Nakoruru:
Dave Baldwin gave me the piece of the puzzle that makes it all come together. Apparently, dPdx has access to the results of the expression that ‘will be’ passed into its neighbor (it knows the future because its all being done in parallel). If it knows the result of the expression with the next value plugged into it, then all it has to do is return the difference

Dave gave an explanation how dPdx can be implemented. My aim was different, I want to explain why the language is well-defined formally and mathematically.

Note that your questions about how dPdx functions are exactly defined formally is valid and very important. The first question is if there is a senseful behaviour and implementation of dPdx, for example also including expressions. This is answered by Dave. The second question is if you can define the language formally consisten, which is not possible by considering values simply as numbers as you correctly pointed out. And indeed it maybe that even if the meaning is plausible, a particular formal definition is not working out. I wanted to try to explain such a well-defined definition.

BTW, Daves implementation explanation and my formalism explanation expresses the same: Daves explanation means that in a shader program, a value is not only a single number but also the neighboring values are relevant. So a value is a collection of multiple numbers of neighboring fragments. Formally this means exactly that a value is a function of the (neighboring) fragment coordinates x and y.

Originally posted by Dave Baldwin:
[b]mrbill and folker have been doing a sterling job of trying to explain the workings of dpdx and its family .

As the original author of the Shading Language White paper I must apologise if the explanation of this ‘function’ was not to Nakoruru’s satisfaction and we will add some language to clarify things.

Derivatives can be derived in two ways - analytically or numerically. In the analytic case you need an expression while in the numerical case you just need to be able to find the value of the ‘thing’ you want to differentiate local to the point of interest.

Analytic differentiation is clearly not workable as any of the elements of the expression may not be differentiable - a lookup table (bump mapping, for example) would fall into this category.

For numeric differentiation if you make the assumption that the fragments are being evaluated on a SIMD array and you have access to your neighbour’s values then the magic of the dPdx call is to ask their x neighbour’s for their value of the ‘thing’ and subtract yours from it. The dx value is one, by definition, so the result is simple to provide.

At the evaluation of dPdx the ‘thing’ is just a number so how it was derived is immaterial. The function does accept an expression (as all functions do for their input arguments) and the expression is evaluated to a value. The expression can be anything, even the framebuffer colour value, and the only uncertainty arises if the expression is evaluated as part of a conditional (because the neighbour’s values may not be defined).

Should it be a function or operator? There are a limited number of suitable characters in the 7 bit ASCII set and as soon as you start to reuse to ones defined by C the expressions can end up looking very confusing. Using a named function makes the operation more readable and allows the user to redefine it if they want to - operator overloading is not a supported language feature so if done through an operator user couldn’t redefine the meaning.

We make a point in the white paper of saying that the built in functions are provided for convenience and to access hardware specific features. The convenience functions can easily be emulated by the user but the hardware ones may be impossible or just very difficult.

dPdx is in the impossible category to do a general solution, but it is possible to do a specific solution if you know what the analytic solution is or you can manually calculate the local differences. For you to calculate the local differences you will have to know the complete expression tree, maybe starting 5 subroutine levels back for example, so you can substitute x+1 into it. The expression tree may also use data you don’t have access to, such as the framebuffer colour value of your neighbour.

[/b]

Dave, one comment:
You explained possible implementations of dPdx. But Nakoruru point remains: How is dPdx is defined formally in the language specification? You cannot simply consider dPdx as function of atomic float values like 3.14 as pointed out correctly by Nakoruru.

So indeed it may be a good idea to solve this formal gap in the OpenGL 2.0 shader language spec. According to my explanations in previous posts, I would suggest the following:

a) Add a statement that formally all values are not only simple values (e.g. conventional floats), but functions of the fragment positions x and y. All usual arithmetic which works for plain values formally can be easily applied to values depending on x and y, too.

A note may clarify that this definition has formal reasons. The typical reader may still consider values as simple values, e.g. simple floats like 3.14. Unless he goes more in formal detail as Nakoruru… :wink:

b) For control statements like “if(value)”, the value is collaped to a simple value by taking the simple value at the current fragment x and y.

This second statement is necessary to make control statements well-defined.

Note that the purpose of these additions to the specs is NOT to define the meaning or implementation of dPdx, but make the formal definition of the language precise and well-defined.

Folker,

I’m am now completely satisfied that dPdx is a function, with no syntactical specialness. It really does take the result of an expression as its argument, and it is able to return the delta because it knows what the value will passed to its neighbor in the next iteration.

Look at my example code where I show how you can calculate the derivative of sin(x) just because you remember the result of sin(x - epsilon).

The explaination that this function has special access to its ‘neighbors’ due to the nature of SIMD hardware and can thus know how much it differs from them is enough.

Even that is an implementation detail.

For some reason I cannot edit my posts (tells me the password is invalid).

Anyway, just wanted to add that I do believe that the grammar is correct, because dPdx can be implemented by passing a value. There is nothing which prevents functions from having access to special information (at least, not in computer science, I think that Calculus might object to ‘magic’ ^_^).

I find it exceedingly strange that you would now insist that I should not be satisfied after I have my answers LOL.

All it really has to say is that it returns the difference between the value passed to it for the fragment at position x, and the value passed to it for the fragment at position x+1. In fact, if it said that I would have been completely satisfied and never would have had any doubt in my mind as to its usefulness and behavior.

Some warnings about conditionals, and maybe some explanation about what happens if those x+1 fragments get killed by per fragment tests or the edge of the primitive and we’re golden :slight_smile:

Originally posted by Nakoruru:
[b]For some reason I cannot edit my posts (tells me the password is invalid).

Anyway, just wanted to add that I do believe that the grammar is correct, because dPdx can be implemented by passing a value. There is nothing which prevents functions from having access to special information (at least, not in computer science, I think that Calculus might object to ‘magic’ ^_^).

I find it exceedingly strange that you would now insist that I should not be satisfied after I have my answers LOL.

All it really has to say is that it returns the difference between the value passed to it for the fragment at position x, and the value passed to it for the fragment at position x+1. In fact, if it said that I would have been completely satisfied and never would have had any doubt in my mind as to its usefulness and behavior.

Some warnings about conditionals, and maybe some explanation about what happens if those x+1 fragments get killed by per fragment tests or the edge of the primitive and we’re golden :slight_smile:

[/b]

Talking about neighboring values exactly means that formally each value depends also on x and y (x and y are used to address the value), so we indeed agree completely. BTW, as soon as you define this precisely (e.g. as in my previous post), conditionals, kills, the question of border pixels etc. are also well-defined.

Maybe my posts look very formal and pedantic and that the sound like a lession of algebra ;-), but I think that is important to not stop with “it is plausible and will work somehow”, but to make specifications precise.