When it comes to pre-sorting the draw calls for a given frame, I am aware of two different methods for optimising.

The first is to batch draw calls by their material. This minimises shader changes and limits the number of times constant buffers, textures, and other data need to be switched or updated also. Furthermore, this approach leads to easy multithreaded commandlist generation (important in the coming era of DX12 and Vulkan!), each thread can build a list of draw calls for each material independently.

However, one major source of framerate slowdown is 'overdraw'- i.e. drawing partially or fully obscured geometry BEFORE drawing the geometry that obscures it. When drawing roughly front-to-back the fragment shader has to do far less work because obscured pixels can be depth culled.

However, in order to sort geometry front-to-back, it is not really possible or practical to also try and sort draw calls by material (or is it?). Therefore, I ask, which of the two approaches is the 'better' (in terms of performance or general adoption); or is there a third or hybrid approach I am unaware of?

  • 3
    \$\begingroup\$ This is a false choice if you're using deferred rendering or z-prepass, which lets you draw most or all of your opaque geometry with one material first, filling a depth buffer which gives you early-z rejection for subsequent passes no matter how they're ordered. \$\endgroup\$
    – DMGregory
    Dec 11, 2015 at 19:34
  • \$\begingroup\$ @DMGregory I'm not sure how deferred rendering = geom with only 1 material... I believe you still write out the texture/material properties in the geometry 'phase', correct? \$\endgroup\$ Dec 11, 2015 at 21:29
  • \$\begingroup\$ I oversimplified a bit (leaning toward the z-prepass part of the comment), but since you're just outputting depth/normals/etc. relatively uniformly then textures are the main impediment to combining materials (the rest of the render state being pretty consistent) and atlasses can help you combine even further. \$\endgroup\$
    – DMGregory
    Dec 11, 2015 at 21:38
  • 1
    \$\begingroup\$ @DMGregory's saying that given a minimum of one aspect (say diffuse) rendered in the g-buffer pass, you'll get a populated depth buffer which then can be used to accelerate the 2nd pass, making this question moot because there is no choice to be made: sort on distance for 1st pass, sort on material in 2nd pass, and you're done. Same deal for z-prepass as with deferred / g-buffer. It's a good point. OTOH for old school, single-pass forward rendering, my answer applies - a choice must be made. But such an old approach is of limited use. \$\endgroup\$
    – Engineer
    Dec 15, 2015 at 11:51
  • 2
    \$\begingroup\$ It's definitely not moot, since every technique still has to balance these competing factors at some stage. I just wanted to point out that it's not as all-or-nothing as it may seem, that in certain circumstances you can have your cake and eat it too, getting both early-z and good material batching for much of the frame workload. So asking which order is ideal gets a bit more complex, and there might not be a clear-cut answer that applies generally across all modern realtime rendering pipelines. \$\endgroup\$
    – DMGregory
    Dec 15, 2015 at 12:46

1 Answer 1


For a single pass approach: Obviously if you don't have the front-to-back requirement (early Z), you can primarily sort on material. But yes, you're going to have to decide which is primary and which is secondary.

For mobile, reducing overdraw is top priority. If desktop / console, I wouldn't be worrying much about that, I'd opt to sort by material primarily as overdraw has to be carelessly high to impact perf.

Bear in mind though that there is another aspect to this even if you don't worry about sorting front-to-back to reduce overdraw, and that is back-to-front sorting for realistic transparency. If you have this requirement, you're still stuck doing a secondary sort on material. If neither this nor front-to-back, you're golden and can do material as primary sort.

P.S. Have you looked into occlusion queries? They might help, depending on your GL version.

For multi-pass: see DMGregory's wisdom in comments.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .