3
votes

I have a situation where I have two particular shaders:

The first shader casts shadows from all objects in a scene and renders to a single fullscreen 8 bit shadow texture. The glsl code is very short.

The second shader performs deferred lighting calculations onto the g-buffer and renders to a single full screen 32bit texture buffer. It uses several full screen textures (32bit position, 16bit normal, 32bit diffuse, 8bit specular, 8bit shadow). The glsl code is also quite verbose.

As you can see, for each light, these two shaders must execute subsequently. A then B, A then B, A then B. This results in a lot of swapping.

I've read that shader swapping has some high relative overhead, but im unfamiliar with how a GPU would deal with swapping between only two shaders.

Will the two shader programs be cached effectively enough that this shouldn't be a problem?

Should I combine the two shaders and direct the output using glDrawBuffers()? If I combine them, the 5 textures that are loaded from the previous g-buffer illumination stage would be left stale for the next shadow casting stage, would this cause any performance overhead?

1
Questions like these are very difficult to answer for your specific case. In general, if you create, compile and link the programs once, they'll be cached until you delete them. You should get the uniform locations once and save them. But beyond that, it will probably depend on the GPU, the CPU, the OS, and various other factors. Your best bet is to profile your code under the various scenarios you're concerned about and see which is faster.user1118321
@user1118321: The problem is not compiling or linking but the performance hit on the GPU execution pipeline caused by swapping the shaders. This causes a full pipeline flush, which is what you want to avoid where possible.datenwolf
@datenwolf: Changing shaders doesn't flush the pipeline. At least, it doesn't have to. What changing shaders does have to do is load a new set of instructions, as well as a new set of uniforms, possibly texture/UBO/etc settings (which image units/binding points are active), and so forth. This involves some overhead, as new uniform data is loaded into GPU registers.Nicol Bolas
@NicolBolas: I'm not thinking about the OpenGL pipeline here, but the GPU's instruction execution pipeline, on the metal, which involves things like branch predictors, instruction reordering and such. After switching a program, every pipelined execution system takes at least the number of pipeline stages in clock cycles to achieve full throughput (pipeline lead time). I guess modern GPU provide methods to query for OOE stats and BP state after shader execution, so that those can be restored after a shader program switch. But the lead time can not be avoided.datenwolf
Virtually every shader execution unit for every GPU is an in-order, non-branch predicting processor. They don't do things like "branch predictors" or "instruction reordering". The main thing that has to happen is that new source code and data has to be uploaded to the various individual execution elements. Whereas if you don't change programs, only new data (if any) has to be uploaded.Nicol Bolas

1 Answers

4
votes

Will the two shader programs be cached effectively enough that this shouldn't be a problem?

The problem is not caching, but that changing the shaders flushes the GPU execution pipelines. And it takes a few dozen clock cycles for the pipelines and branch predictors to settle for the newly switched to shader.

Should I combine the two shaders and direct the output using glDrawBuffers()?

In a deferred shader setup, if you can do it without too much problems? Definitely!