Advertisement

DX11 What is the best way to order rendering of a scene?

Started by August 20, 2018 05:07 PM
6 comments, last by pcmaster 6 years, 5 months ago

I am curious about this subject because I am getting to the point where I will be refactoring the rendering of my engine soon.  I have searched the internet for how to order the draw calls and this answer came: https://gamedev.stackexchange.com/questions/49779/different-shaders-for-different-objects-directx-11

However, shouldn't I be instancing all my meshes even if there is only 1 of them?  And since we are instancing the meshes, doesnt that mean we are going to overdraw the pixel shader?

 

What is the solution?  Overdraw if there is > 5 meshes of the same type on the scene, or only instance in batches while keeping render order 100%.  Seems like there might be some net positive for rendering out of order vs rendering in order, even if we suffer overdraw.

 

P.S.

Okay, profile it.  I get it.  I do.  I just want to know what is usually done.

 

Thanks!

When you're referring to overdraw, are you concerned with avoiding pixel overdraw by sorting your opaque meshes in front-to-back order? This kind of sorting is indeed somewhat incompatible with batching/instancing, and the sweet spot depends on a lot of factors. We mostly sidestep this problem by doing a depth-only prepass, which ensures that there's no overdraw when it comes time for the main forward pass that uses heavy pixel shaders, so that's certainly an option for you. It does add extra draw calls, but in terms of CPU cost they will have fewer state changes than a forward pass or G-Buffer pass (typically no textures and no pixel shader), so they end up being cheaper. It can also be quite cheap on the GPU, but it depends on the geometric complexity. If you really wanted to push the limits on triangle counts then a depth prepass may not be a great idea.

Advertisement

How would you use a depth prepass?  I understand the concept, but while using it as input what are you checking?  And do you just discard the pixel if the depth != current depth?

 

Derp.  Okay.  I can do an early z and then set the depth stencil desc to only write out when depth == the same, right?  But, cant there be some issues with this?

Presumably you are already using the depth buffer to allow drawing out of depth order (rather than sorting everything back to front perfectly)? If you render the opaque parts of the scene with the normal/default D3D11_COMPARISON_LESS without a pixel shader, you should get all the final depth values without doing all the heavy work a pixel shader would normally do.

Then if you use D3D11_COMPARISON_LESS_EQUAL and do your normal scene draw anything that would have been hidden by a later draw (overdraw) will fail the depth test.

 

For this to be beneficial, the GPU time saved by skipping the pixel shaders and output in the 2nd pass will need to be greater than the extra cost of doing all those draw calls, vertex shaders, etc. twice (e.g. if you did render your scene front to back, a depth pre-pass would reduce performance since there were was no overdraw in the first place).

Thanks!  I just figured this out, kind of like a light bulb went off over my head!

 

I think what ill do is do a z-prepass, and then do instance rendering for all objects(even if 1) and see how this works out for me.

Beware that as well as choosing a sort order for opaque objects to optimize performance, you'll also need to separate out transparent objects and render them separately. There's also post-processing, and the user interface to consider. A generic rendering order might look something like this:

1. Depth only pass to minimize overdraw (ideally you only want to render stuff in this pass that's a good occluder (i.e. it's got a relatively low poly count for it's size, and is likely to be in front of other things).

2. Opaque render pass, sorted to optimize performance.

3. Some full screen post-processing might need to happen here if it shouldn't (or can't) be applied to transparent objects. For example, SSAO.

4. Render all transparent geometry, furthest away things first (you'll need to sort by distance to the camera). The sorting is for correctness, and not performance.

5. Render any other post-processing effects (bloom, etc.)

6. Render the user interface (assuming it's 2D, and not part of the 3D scene).

Obviously most of those passes are optional. Some games will also have more rendering passes than that, especially if they use some form of deferred shading.

One common way to handle the sorting within each render pass, is to create an integer sort key for each thing you render. See http://realtimecollisiondetection.net/blog/?p=86 for an example.

Advertisement

And while we're at it, I'll throw in an optimisation you can try out immediately, while doing it, for alpha-tested objects (grass, leaves, fences, ... not alpha-blended, though).

Naive approach:

  1. In PrePass, do the discards based on e.g. alpha texture sampling
  2. In main geometry pass(es), do the discards the same way as in PrePass

Better approach:

  1. In PrePass do the discards based on e.g. alpha texture sampling
  2. In main geometry pass(es), use EQUAL comparison (not LESS EQUAL/GREATER EQUAL) and don't even bother computing alpha again

Why does this work?

Depths of fragments which you want discarded will not match the depths in the depth buffer - there's already a hole from the PrePass - and will not be shaded at all (early Z) or won't write the to colour targets.

This topic is closed to new replies.

Advertisement