Jump to content
  • Advertisement
Sign in to follow this  
Aqua Costa

Simplifying renderer architecture

This topic is 1966 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

My renderer was designed following ideas from frosbite rendering architecture, and other topics.

I tried to remove all special cases making it as generic as possible (capable of handling multiple rendering paths/techniques).

 

Because of its "generic nature" it relies heavily on loops.

 

Each resource must contain multiple StateGroups (one for each rendering stage it can be drawn), example:

 

-Mesh structure contains 2 state groups:

1-only binds the vertex buffer containing vertices positions; (used in depth only/shadow passes)

2-bind both the vertex buffer containing vertices positions and and vertex buffer containing normal, tangent, texture coordinates (for gbuffer pass/forward rendering)

 

drawActor(Actor* pActor, u8 passID)
{
    pActor.getStateGroup(passID);
    pActor.model.mesh.getStateGroup(passID);

    for(Subset s in pActor.model.getSubsets())
    {
         s.material.getStateGroup(passID);
         s.getDrawCall();
    }
}
    

 

 

Each getStateGroup(passID) contains a loop to find the StateGroup associated with passID.

 

--

 

Each StateGroup contains commands and a bitset used to select which shader permutation to use.

 

So there's another loop used to find the correct shader permutation that must run before executing each draw call, since I cannot predict which permutation will be used (?).

 

 

Possible solution:

 

"Caching" StateGroups in the Actor class so only a loop is needed - this prevents hot-reload of resources or I need to check dependencies and update the cached stategroups.

 

 

I'm having trouble coming up with a "solution" so any "outside-the-box" ideas will be appreciated.

 

Thanks.

Edited by TiagoCosta

Share this post


Link to post
Share on other sites
Advertisement

Ok, explain the process of initializing a StateGroup for every resource with every pass. Also, what exactly does getStateGroup do? It's just switching the *active StateGroup* in the context of a particular resource, right? If I'm correct, I'd call it setStateGroup() ... haha but whatever. Anyway. Apparently your StateGroups are not indicable by pass IDs, hence why you must perform a linear search. I'm concerned about that. Tell me more. I'll begin reading into that "Frostbite rendering architecture question" topic you have shared with us. Cheers smile.png

 

Edit:

Ok, so pass ID merely refers to a state field which is stored internally? I'm beginning to find this "architecture" very unnecessary. I mean... look, this function has a pass ID argument for every time you draw an actor. Then you have 3 more calls within the function which merely shove this argument into the exact same parameter... Why? Isn't a pass supposed to be a... pass? It's like your defeating the purpose of a 'pass'

To me, a pass is like waving to all of the resources once. Another pass is coming back around and waving to them again, but in a different car. (stupid analogy, but whatever xD)

Edited by Reflexus

Share this post


Link to post
Share on other sites

Each getStateGroup(passID) contains a loop to find the StateGroup associated with passID.
Each StateGroup contains commands and a bitset used to select which shader permutation to use. 
So there's another loop used to find the correct shader permutation that must run before executing each draw call, since I cannot predict which permutation will be used (?).

You don't need to "predict" it. You have been given this information explicitly by the assets when the draw calls were built.

Shaders don't change after the assets have been loaded. At worse, the value contained in their uniform buffers do, or perhaps in the textures. But the code permutation, vertex buffer layout and such is constant. No need to "predict" anything.

 

Ok, let's see if I get you right. Drawables can be drawn in multiple passes. Each pass is a batch, with a drawcall to operate on IBO/VBO set, on certain uniform buffer values using a certain set of kernels (the shader code itself).

I don't see any reason for which you cannot store a handle to the settings to use directly, that way, no searches would be necessary, just use pointers. Or references if you are particularly scared.

 

edit: missed an 's'

Edited by Krohm

Share this post


Link to post
Share on other sites

That was also my point in the first place. smile.png


Edit:

Each pass is a batch


Yeah, also, I think they should explicitly take a "Batch" top-down approach.

Edited by Reflexus

Share this post


Link to post
Share on other sites

You don't need to "predict" it. You have been given this information explicitly by the assets when the draw calls were built.
Shaders don't change after the assets have been loaded. At worse, the value contained in their uniform buffers do, or perhaps in the textures. But the code permutation, vertex buffer layout and such is constant. No need to "predict" anything.

Shaders might change. Example: In a forward renderer I might have multiple shader permutations each used to draw an actor affected by n lights, so the shader used by each actor depends of the actor and lights positions.

Share this post


Link to post
Share on other sites

Shaders might change. Example: In a forward renderer I might have multiple shader permutations each used to draw an actor affected by n lights, so the shader used by each actor depends of the actor and lights positions.

 

How does this make your current architecture necessary? Correct me if I'm wrong, but I believe the approach you're going by will only convolute this example. Can't you go by a pass-order model, rather than object-order?

 

Nevertheless:

 

I don't see any reason for which you cannot store a handle to the settings to use directly, that way, no searches would be necessary, just use pointers. Or references if you are particularly scared.

Edited by Reflexus

Share this post


Link to post
Share on other sites

Shaders might change. Example: In a forward renderer I might have multiple shader permutations each used to draw an actor affected by n lights, so the shader used by each actor depends of the actor and lights positions

No. Well, it depends on what we refer as "shaders".

If your shader code has a float3 lightPositions[CURRENT_NUM_LIGHTS] then yes, shaders do change. Shader code, as called by D3D documentation.

Now, if we fetch this using tbuffers for example, this difference is not there. But let's say we go with a uniform array anyway.

You will see this change isn't really a "shader change" but rather a variation of the same shader to deal with more data than designed. If we want to take it really easy we might just go for float3 lightPositions[MAX_NUM_LIGHTS] and be done with it, some might even say this is the correct way of doing things and they sure have some good reasons to say that: this shader would be authored with real data in mind.

 

The underlying problem is we need terminology for the "higher level shader" materials we are working on. Those never change. A triangle authored to be drawn using "ParallaxMapped_Bricks" will generally be drawn using "ParallaxMapped_Bricks", regardless of the fact those bricks are going to be shaded by 2 or 4 lights. The fact that this material is referring to different shaders does not change much. It still is "ParallaxMapped_Bricks".

I guess I begin to understand your point. Notice however there are still no "permutations", not in the sense of combining multiple VS,GS,PS pairs. Each VS can change "inside itself" but not how it connects to other. I have the very same problem with some of my shaders. What I do is to use a bucketed approach so following your example, I might have a shader for use up to 4 lights, another up to 8, another up to 16.

We have indeed a multi-dimensional problem inside each shader, yet we don't need to build permutations, at least not a complete set. I'd suggest to not focus yourself on that concept. What we have instead is a shader variation, built every time a fitting variation cannot be found. Then, try to reuse those. Don't aim for 100% efficiency, it's not how things work.

 

Now, going back to the problem, how can we quickly select those variations?

Why should we "quickly" select it for first? My system typically converges to about 10 bucketed variations per shader and doing 10 (albeit non-trivial) operator== calls is not going to be much of a problem for me. Do you have hard data out of a profiler indicating this is a bottleneck for you?

 

edit: two typos

Edited by Krohm

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!