We generally want to minimise shader-program switches (glUseProgram and all associated context changes) for the sake of performance. AFAIK it is not uncommon to render by shader program, i.e. group draw calls of all mesh instances that use the same set of shaders - or vertex shader at least (i.e. that have the same set of vertex attributes), for this reason.

How then do we go about doing both depth ordering and shader program changes, quickly? Because it seems to me one either orders primarily by program (thus minimal program context changes), or primarily by depth (frequent context changes), and that they're mutually exclusive in terms of maximising performance.

PS. In my current work I'm not using the hardware Z-buffer, instead I'm using the painter's algorithm prior to making all draw calls... If this affects the answer to the question at all, I'd be happy to hear solutions for both sorts of depth-ordering.


If you're using the painter's algorithm, you have to sort back-to-front and there's no way around it if you want accurate sorting - and even that sorting isn't going to be accurate if there's any chance of objects overlapping, unless your game is 2D and you know that each object has exactly one depth value. You could only sort subsections by shader program (or texture, or any other property) if your objects shared the same depth value, or if you could guarantee that all the objects you are rendering are in the same logical depth layer and do not occlude anything else.

What was your rationale behind foregoing Z-buffer use? At first glance it looks like you could have it both ways with a Z-buffer, but I don't know your specific situation. In my experience, the performance gain of consolidating render state changes and texture loads outweighs the minimal performance impact of a Z-buffer.

  • \$\begingroup\$ I'm using billboard quads with transparent textures, as found in the old-style raycaster engines. That's why I've avoided the Z-buffer. Does this decision make sense? \$\endgroup\$ – Engineer Aug 26 '12 at 14:41
  • 2
    \$\begingroup\$ If you can guarantee that your characters will be completely opaque, you could use the alpha test to reject any totally transparent pixels that you don't want to be written into the Z-buffer. If you want your characters to be semi-transparent you'll have to fall back on the painters algorithm, as there's no reasonable alternative for sorting transparent objects in real-time applications. But as Nicol Bolas said, if your characters overlap, foregoing a Z-buffer is going to result in a LOT of overdraw, which may or may not matter based on what your game's performance bottleneck is. \$\endgroup\$ – Daniel Rowe Aug 26 '12 at 14:50
  • \$\begingroup\$ Actually, I realise that the Z-buffer will in fact be necessary here -- think of a billboarded character standing slightly behind a rise in the terrain. I was also just looking at this article, which implies I can have transparency on my billboarded characters -- using the Z-buffer. So Z-buffer it is, then. \$\endgroup\$ – Engineer Aug 26 '12 at 14:53

In general, unless it's a 2D game, people don't use the painter's algorithm. There's just no point to it; depth buffer is not only cheap, but faster than what you're trying to do. Yes, really.

With early depth tests, fragments can be culled before having the fragment shader even execute. This saves time with scenes that have heavy overdraw. With coarse front-to-back sorting, there's just no reason to not use a depth buffer.

Thus, once you commit to the painter's algoithm, you've already given up quite a bit of performance. What you might save from optimized glUseProgram calls is irrelevant next to what you lose from the massive overdraw your scenes will have.

Most important of all, until you have profiling data that suggest your number of glUseProgram calls is too great, you shouldn't much bother. Or if you are going to bother, make sure that your sorting criteria is something you can change, so that you can adjust what you sort based on.

  • \$\begingroup\$ There is a reason I'm not using the depth buffer: no far clipping plane. Under such conditions, the depth buffer will introduce artifacting. And although I could set an arbitrarily distant far clipping plane for similar effect, I simply don't wish to do so. So what you're saying here may cover the vast majority of use-cases, but it doesn't cover mine. That is not to say it doesn't offer some useful insight, however. \$\endgroup\$ – Engineer Aug 26 '12 at 14:35
  • \$\begingroup\$ @Nick: "I simply don't wish to do so" Then you simply don't wish to have the performance that goes along with a depth buffer either. There's no free lunch; if you want speed, you use a depth buffer. If you can't, then issues like how many times you call glUseProgram aren't going to change the fact that your scene will have massive overdraw. In short: any performance gained from minimized glUseProgram will be irrelevant next to how many pointless fragment shader invocations you have. \$\endgroup\$ – Nicol Bolas Aug 26 '12 at 14:38
  • \$\begingroup\$ OK. Furthermore, the reason I'm opting out from the Z-buffer is the fact that I'm using partly-transparent textured billboard quads for my characters, a la Doom. The question there is, will the Z-buffer support this transparency correctly? That is my rationale behind, "I simply don't wish to do so." \$\endgroup\$ – Engineer Aug 26 '12 at 14:39

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.