Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I am refactoring some parts of the game engine I am working on. This engine is made in C# with XNA.

The part I have trouble with is shader/material and the render queue. In the new version I will give ability to indicate differents render states per pass within a technique (blend state, rasterizer state, ...). In my engine, all renderables have a material and each material is associated to one technique (thus to one or more passes).

Later, when the game will be running, I will add renderable objects to the render queue and then sort it based on the pass used by each material. What I want to achieve is to reduce states changes between draw calls by first grouping same passes together and then passes with nearly identical states to be as closed as possible to minimize changes. But this solution seems to be problematic when it comes to use technique with more than one pass with completely different states. Passes within a same technique can be completely separated in the render queue and not rendered one after the other. If I say that it's because I have always see multi-passes technique render in a loop and all passes rendered immediately like that :

foreach(EffectPass pass in technique.Passes)
{
    /* Update parameters */

    pass.apply();
    graphicsDevice.Draw(...);
}

But I don't know if I can deferred pass rendering from a same technique instead of using them one after the other as in the previous example.

Here is an example of what will probably happen with the solution I try to build:

  • Shader1
    • TechniqueA
      • Pass1 (State A)
      • Pass2 (State C)
  • Shader2
    • TechniqueB
      • Pass1 (State A)
  • Shader3
    • TechniqueC
      • Pass1 (State A)

Now I have three models to draw, Model1, Model2 and Model3, each one use a different technique from the previous list. If I add those three models in the render queue I will end up with the following render queue :

-> Set state A -> Draw Model1(Pass1) -> Draw Model2(Pass1) -> Draw Model3(Pass1) -> Set state C -> Draw Model1(Pass2)

We can see that the Model1 will be drawn two times (two passes for the Technique A) but the draw call are separated by others draw call.

I don't know how to resolve this. What will be a good solution to this problem? How can I design my render queue to reduce state changes as much as possible between draw calls.

EDIT:

After searching a little more, I have found this helpful article : http://realtimecollisiondetection.net/blog/?p=86. It seems to be an elegant way to sort object by depth and materials. And in the material id, I can had a pass id to sort same pass together that will allow to reduce state changes.

Currently my renderer draw objects directly in the backbuffer without lights nor shadows, everything is flat. So I will just loop over my render queue and call draw methods. But in a future I will probably implement deferred rendering.

I don't know a lot about it but I know that I have to render my objects in different render target to gather differents informations (Z-buffer, Normal buffer, G-Buffer, ...). I assume that for z-buffer and normal buffer I will use the same shader on every objects no matter which material they use. But for the G-buffer I will use the materials of each object so that means different shaders with different number of pass. If I use the solution in the previous link, if I have an object with a material that needs to do two or more passes, my render queue will have at least two times the object (one for each pass). But for Z-buffer or normal buffer isn't it useless to have the same object multiple times?

What I see is that I will need to have at least two different render queue : one for G-buffer in which an object can be multiple times depending on the number of pass the material require and another render queue for Z-buffer or other buffer in which each objects are only one time. Am I right?

share|improve this question

1 Answer 1

up vote 3 down vote accepted

The draw order often implied by tutorials, where you do something like this:

for each object:
    for each pass:
        apply pass state
        draw object

is actually backwards from how it makes sense to do it in a "real game" context. Rather, you'd be more likely to do something like:

for each pass:
    apply pass state
    for each object (grouped by shader/material/mesh/etc):
        draw object

Multiple per-object passes is almost never what you actually want. Instead, you want to accumulate all the objects that use a certain pass, and then draw all those objects together.

For example, some common passes you'd encounter are:

  • Depth pre-pass
  • Shadow map pass
  • Deferred shading pass
  • Decal pass
  • Lighting pass
  • Forward shading pass
  • Translucency pass
  • Postprocessing pass
  • UI pass

Not every shader will include every one of these passes - for instance, in a deferred renderer, a typical "opaque object" shader might only use the first three passes, a lighting shader would only use the lighting pass, etc.

In cases like this, naturally you want to draw all the objects in the depth pre-pass together, so you'd make a render list that includes all those objects, then apply the pass state once and draw them together. Then you apply the shadow map pass state and draw all the objects that go in the shadow map, and so on.

There are only rare special cases where you actually want per-object passes - for example, subsurface scattering using texture-space diffusion, where you draw the object's lighting into an offscreen render target, blur it, then draw the object on screen using the blurred lighting (3 passes per object). This sort of thing doesn't scale well - it gets very expensive as you add objects - so engine programmers try to avoid it as much as possible.

share|improve this answer
    
Thanks for your response, it helped me think about my problem. Meanwhile I have found an interesting article about a method to sort a render queue. I think about implementing it but I have a lot more questions, so I edited my question with more details and the link to the article. –  Takumi Apr 7 at 12:47
    
@Takumi What I call "pass" here is more like what is called "layer" in that article. It would be quite unusual for a material to have more than one deferred pass. Different objects will have different materials, but they will all be drawn once in the deferred pass. And as I mentioned in my answer, many materials will be in some passes but not others. You can still keep everything in one master render queue as long as it's sorted by pass. –  Nathan Reed Apr 7 at 20:25
    
I probably misuse the term "pass". In my question I refer to pass for a pass in a technique in a directx effect file (.fx). That is why in my question I am saying that a material in my engine can have multiple pass. A material is bind to one technique and therefore to technique's passes. Until now, the engine doesn't have complex rendering pipeline, I only draw object "as is" with their own materials. But if an object use a material with a technique containing two passes, I need to do one draw call for each effect pass, so in my render queue I will have two times the object for each pass. –  Takumi Apr 8 at 18:57
    
I think that my problem will only occurr when filling the color buffer. Because it's in this buffer that I will use the material of the object and so the technique associated to it. I don't have lot of knowledge about deferred rendering, only read some article without going in depth. So I am probably wrong on a lot of things. Since I don't have a complex rendering pipeline, I should make things easy and simple and add more functionalities and adapt it when I will implement deferred rendering. Without deferred rendering implemented in the engine I can't predict which problems I will encounter. –  Takumi Apr 8 at 18:58

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.