imageworks/OpenShadingLanguage — pull request #172 by lgritz (2012-10-31 18:07:28+00:00)
Title: Smarter 'mix'

Up until now, mix(a,b,x) was defined in stdosl.h and basically expanded immediately into a*(1-x)+b*x, rather than being a true "built-in" function.

This patch makes it a built-in (i.e. oso instruction), with really good constant folding and LLVM IR generation.

The real point was to exploit an interesting observation: mix(a,b,x) would require evaluation of a and b *before* the arithmetic to produce the result. In the case where one or both of a,b were parameters that were connected to earlier shaders, it is possible for this to trigger execution of entire parts of the shader node network, only to go unused if x==0 or x==1. So it does something really clever... it generates code for

if (x==0)
r = a;
else if (x == 1)
r = b;
else
r = a*(1-x) + b*x

But only if a or b are connected parameters! Because if they are local computations, the 'if' is just wasted effort.

The hypothesis was that in our complicated shader networks, this might be a substantial savings by eliminating execution of pointless shader nodes.

In practice... results were somewhat disappointing. Making mix a built-in and having better constant folding gave me about a 3-4% speedup on some production scenes. The addition of the special sauce with the parameters did reduce the time, but by very little, essentially inconsequential (less than 1% for sure). I added a stat (not turned on by default) to count nodes executed -- it works, it really does reduce the number of nodes executed, but by a much smaller number than we'd thought.

I see no reason not to check this in -- we'll take the 3% gain! -- and even though the original idea didn't pan out, I don't see how it can hurt, and maybe other scenes in the future can benefit from it.

There is a bunch of additional RuntimeOptimizer and LLVM-related infrastructure that I implemented as part of this patch. It should be fairly straightforward to see what's going on.