How many shaders can (and should) I bind and unbind?

Started by
4 comments, last by d07RiV 5 years, 10 months ago

I've noticed in most post processing tutorials several shaders are used one after another: one for bloom, another for contrast, and so on. For example: 


postprocessing.quad.bind()

// Effect 1
effect1.shader.bind();
postprocessing.texture.bind();

postprocessing.quad.draw();

postprocessing.texture.unbind();
effect1.shader.unbind();

// Effect 2
effect2.shader.bind();
// ...and so on

postprocessing.quad.unbind()

Is this good practice, how many shaders can I bind and unbind before I hit performance issues? I'm afraid I don't know what the good practices are in open/webGL regarding binding and unbinding resources. 

I'm guessing binding many shaders at post processing is okay since the scene has already been updated and I'm just working on a quad and texture at that moment. Or is it more optimal to put shader code in chunks and bind less frequently? I'd love to use several shaders at post though. 

Another example of what I'm doing at the moment:

1) Loop through GameObjects, bind its phong shader (send color, shadow, spec, normal samplers), unbind all.

2) At post: bind post processor quad, and loop/bind through different shader effects, and so on ...

Thanks all! 

Advertisement

The number of expensive state changes will vary according to the hardware. Usually a good idea is to decide on your lowest spec target machine, and regularly test and optimize for that, and everything above it should run great. For instance I usually target low end mobile, so practically everything else will run super smooth.

Whatever your target, as you probably know you want to minimize state changes, particularly expensive ones. The most important thing is usually to batch your rendering by shader / texture:

  • i.e. Naive approach is to iterate through your game objects, and render them one by one.
  • This is usually completely the wrong thing to do, and especially so if a game object might contain multiple textures and shaders.
  • Instead maintain a list each frame for each 'batch' (shader / texture etc combination). As you iterate the gameobjects add its 1 or more render 'components' to the batch list, don't render yet.
  • After this, render each batch at a time, so you only have to change the states that are absolutely necessary, in order of state change expense.

With your post processing you may not be able to reduce it any further. But make sure you make it switchable for the user, so that if they have a low power machine they can get better performance.

Another thing you can often do is prebind your textures to the slots available on your hardware. I forget the opengl command, but there are a minimum of 8 active textures required in e.g. OpenGL ES 2.0, so I make use of them. On desktop you may have many more active textures available, so you may never need to bind a texture (aside from at startup). Whether this helps performance will depend on the OpenGL implementation.

You should also look into using instancing or tricks (pack extra supplementary info into the vertex buffers) to render several objects with one draw call, you can also sometimes use this to render at the same time objects that would otherwise need a shader switch.

Whether any particular optimization makes a big difference is highly dependent on the OpenGL implementation / driver / hardware. Presumably the GPU guys are aware of what can cause big bottlenecks, and will already be trying to minimize the effect of suboptimal calling code from their side.

It's possible to use thousands of shades let frame. 

As a rule of thumb, changing shaders probably costs under 1 microsecond of CPU time on your rendering thread (1 thousandth of a millisecond), and costs nothing on the GPU as long as your draw calls cover a few dozen pixels each (giving the GPU pipeline enough time to prepare the next Draw concurrently). 

Thanks a lot for the answer guys, greatly appreciated. I'll have to change my engine a little but that's fine, I prefer doing it right, thanks again!

If it's a fullscreen quad, then don't worry about it. The cost of processing a screen-full of pixels is much higher. But you should still try to squeeze multiple postprocessing steps into one where possible.

This topic is closed to new replies.

Advertisement