4
$\begingroup$

I have managed to greatly minimize draw calls on my recent project, but as I'm adding more shaders, I see that I have to switch more times and that is causing a lot of draw calls. What is the best practice for managing multiple (dozens) of shaders in a scene with many materials?

I read that it's best to combine them together and you have a shader variable "mode" that you switch upon inside the shader, to end up with the specific branch of code in the shader that is to handle the fragment.

Since shaders do have alot of parameters populated from the model material/mesh and sent down to the fragment shader, I fear that unifying the shaders and passing too many parameters in the VBO each frame/vertex/fragment, would be really bad for performance. I target mobile/tablet devices with my product so this is important, I understand that this is less of a concern with desktop GPUs. I see modern engines like unity and unreal allowing full flexibility on adding shaders & materials at-will and their scenes render perfectly - how do they do that without increasing their shader switching and draw calls?

So how many parameters are too much? And what is the best recomendation to manage multiple shaders in a drawing a scene. I understand and have allready applied grouping and rendering each material at once to minimize shader switching, but that has its limitations if there are dozens of shaders.

Is there a technique or capability I'm missing?

$\endgroup$
  • $\begingroup$ "too many parameters in the VBO each frame/vertex/fragment" If the parameter changes within a draw call, how exactly would you expect to have made such a thing use a different shader? You can't change shaders in the middle of a draw call, so a value used by the shader is the only way to have per-vertex/primitive/fragment parameters. So there's no choice in that case. $\endgroup$ – Nicol Bolas May 26 at 21:46
  • $\begingroup$ If I'm grouping shaders with a switch statement and a mode parameter, then 2 shaders of 4 parameters each become 1 shader of 9 parameters, if there is no reuse (4+4+1 for mode). I then have to pass those 9 down on each vertex and each fagment of course, at which point the question becomes how muh can I push this design. $\endgroup$ – GnoSiS May 27 at 9:31
  • $\begingroup$ with reuse I mean that you can re-apropriate parameters to have different meaning per mode. So, simple example: mode: 0 normal, param1 ignored, mode 1: hue shift, param1 is shift distance, mode 2: color burn, param 1 is burn intensity, and so on.. $\endgroup$ – GnoSiS May 27 at 9:35
2
$\begingroup$

I target mobile/tablet devices with my product this is less of a concern with desktop GPUs. unity and unreal allowing full flexibility on adding shaders & materials at-will

Well, Unity and Unreal can also be used to make games targetting mobile/tablet devices, right?
Do you think you can throw at them all the shaders & materials you want, and expect them to magically output a performant application every time?
Try to do it and you will find that they also struggle at some point.

This can also happen on desktop ; the newest smartphones are totally faster than my oldest laptop.

What is the best practice for managing multiple (dozens) of shaders in a scene with many materials? how many parameters are too much?

The best practice is to test on the real hardware that you are targetting. If you target a wide range of mobile devices, then you have to decide the lowest spec that your product will officially support.
Then, it is a matter of finding a good balance between performance (few materials/shaders) and visual quality (more variety/complexity).
If you are comfortable with the process, you can prepare a number of performance profiles based on the type of device that will run your product (lower-end, higher-end?), with better quality for the higher end.

Since shaders do have a lot of parameters populated from the model material/mesh and sent down to the fragment shader, I fear that unifying the shaders and passing too many parameters in the VBO each frame/vertex/fragment, would be really bad for performance.

You meant Uniform Buffer Object, not Vertex Buffer Object, right?
I expect that the size of the uniform data that you pass to the GPU is not that big?
However if your shaders are complex, it could take time for the GPU to execute them.

Here again it is a balance, between the amount of parameters that you pass, and the complexity of the shader, that you have to find.
You just can try different solutions on real hardware, note down the performance and aspect of each one, and somehow compare them ; to find the optimal solution for your product.

| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ Fair points on most of my concerns, that more or less I already knew. On your last point though I really meant VBOs - as the shader parameters are passed down per vertex at first and then per fragment - how else can you have different shader/rendering per polygon/pixel? $\endgroup$ – GnoSiS Jun 17 at 9:11
  • $\begingroup$ Very often Vertex Buffers contain the same kind of information for all objects in the scene (Position, Texture UV coordinates, Normal), and this content type is the same for all shaders in the scene. Most often what makes the materials look different, is the set of parameters passed by Uniform Buffer. Are you passing a lot of additional data in your VBO? It might be possible to optimize that. $\endgroup$ – wip Jun 17 at 22:07
  • $\begingroup$ No, parameters are different, even for the same shaders, but for the most part they It could be the same per object/component being drawn. Changing the Uniform buffer before each draw seens even more expensive, if each draw would just do a single mesh - that's like having 1000s of draws instead of just 2-5. $\endgroup$ – GnoSiS Jun 19 at 10:35

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.