1
$\begingroup$

In order to reduce the amount of draw calls per frame I've decided to use a vertex array object to store vertex data from several different meshes. More precisely, I use the vao to the define the vertex format (attributes) and the buffers which contain the vertex and index data.

Now I could call glDrawElements on the batch's vao once and draw all the stored vertex data directly. But I also see a lot of discussions about sorting your draw data front-to-back for opaque objects (in order to reduce overdraw) and back-to-front for translucent objects (in order to blend 'correctly').

I was wondering how you would go about sorting the contents of several vertex batches (vaos) even though a batch most likely contains data for objects at different distances from the camera. Say batch A contains objects with depth { 1, 3, 5 } and batch B contains objects with depth { 2, 4 }, how could this be sorted properly so that it can be drawn in the order { 1, 2, 3, 4, 5 } without specifying 5 separate draw calls (with each a different buffer offset).

Were it just the overdraw optimization I would probably just say screw it as it would allow me to draw an entire buffer's contents in one go (instead of having a draw call with a different offset for each object), but then there's still the sorting of translucent objects.

Thanks in advance!

$\endgroup$
  • $\begingroup$ you probably can't unless you flatten all the data in a single batch and draw that at once which would work if they all have the same shader. $\endgroup$ – user18490 Feb 6 '17 at 22:24
0
$\begingroup$

Yes, but with tradeoffs.

The problem is that there are two optimizations going on here at different potential bottlenecks, the CPU and the GPU.

Reducing Overdraw

The goal of reducing overdraw primarily is to reduce the number of times the fragment shader runs via early-z discard. This is usually desirable when you have expensive fragment shaders. However, maintaining a strict depth ordering here may not be necessary. A few common techniques are to either use deferred rendering or to use a depth pre-pass (particularly if you have a forward renderer variant) to minimize the time it takes to run the fragment shaders.

The depth pre-pass technique will require double the draw calls and double the execution of the rest of the shader types (vertex, tessellation control, tessellation evaluation, and geometry) twice, but it may be worth it depending on the scene. The fragment shader is executed twice as well, but usually you would use a very simple one for the pre-pass.

Preserving Transparency

Transparency of course is a different story. Many applications avoided transparency because of two problems:

  • Objects blending in order
  • Fragments blending in order

Even if you are able to sort each object by depth at the VBO or VAO level, it's still not guaranteed to be accurate. For example, two objects could overlap each other. Even within the same object, two triangles can overlap each other, so unless you perform clipping, the transparency order may not be correct. You can sort by the fragment level. There are various order-independent transparency techniques, ranging from screen-door transparency to more accurate variants such as depth-peeling.

However, if you are concerned about draw calls and want accurate transparency, then there are techniques that are single-pass order-independent transparency that are becoming more popular. These rely on saving fragment shader output values in additional buffers (generally called A-buffers) and sorting through them later, performing blending manually. These single-pass techniques requires shader atomics though, so OpenGL 4.2 is required.

$\endgroup$
0
$\begingroup$

Drawing back-to-front is not optional with blending (unless you use an order-independent blending technique), so getting the visual effect you actually want has to trump performance.

But drawing front-to-back is purely a performance thing. Specifically, it makes it possible for early-Z to save you fragment processing time, as earlier objects are in front of later ones. So it's a GPU performance improvement.

Batching is a performance improvement for the CPU, in that you're spending less CPU time doing state change stuff.

So you need to decide which is more important: CPU performance or GPU performance. With deferred rendering, saving FS computation time also means saving memory bandwidth.

But the general construction of your scene, what you're actually drawing, can help inform which is more important.

Oftentimes what happens is that you render static terrain first, perhaps using your visibility system to order what you render coarsely front-to-back. Then you render your dynamic objects. Because dynamic objects usually require a lot of state changes, you won't necessarily draw all of them in a single batch (though there are techniques you can use to hide even texture changes).

In any case, the point is that there is a tradeoff. And the one you pick depends on exactly what you're rendering. A top-down game like StarCraft or DotA would never bother with front-to-back sorting; you just render the terrain after the characters. A tight-quarters FPS by contrast would probably really need to employ such techniques, particularly for terrain.

$\endgroup$

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.