Jump to content
  • Advertisement
Sign in to follow this  
d000hg

Renderstate management

This topic is 4872 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

I need to add some form of renderstate management to my engine, but I'm confused how to implement this: The first problem is that at startup, the system should be set to mirror the default values of every single renderstate and texture stage state. How can I get this information? The other problem is to do with rendering an object. Each object should make no assumptions about any states already being set, so should specify the values for every state which is relevant, correct? But how would this be done - an array of state-value pairs for each object?

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by d000hg
The first problem is that at startup, the system should be set to mirror the default values of every single renderstate and texture stage state. How can I get this information?


Thats obviously very API specific, in direct3d you could use IDirect3DDevice9::GetRenderState(). Or, you could set *all* renderstates to their default values (as specified in the directx reference).

Quote:
The other problem is to do with rendering an object. Each object should make no assumptions about any states already being set, so should specify the values for every state which is relevant, correct? But how would this be done - an array of state-value pairs for each object?


You could simply create an extra layer between your rendering function and the API which only (un)sets the renderstate if it isn't already (un)set.

Share this post


Link to post
Share on other sites
GetRenderState would allow me to find the states yes, but how do I know the complete list of states to check? I mean I can do every D3DRS_XXX value, but then I also need to to record the texture stage states (D3DTSS_XXX ?) for however many texture units are available. Actually on reflections that's not so many things but it sounds like a fair bit of very dull typing!

As far as each object knowing a complete set of the states it needs, is this how others do it - record a full list for each object and then just set the ones which aren't as required at render-time?

Share this post


Link to post
Share on other sites
Do you actually need to deal with EVERY render state? There is no point having a long, dull list of current renderstates, taking up vital (if little) memeory, when you are never going to change the state from its default value. Personally, I only deal with the renderstates I actually use. If I come accross one I havn't included in my engine, it is very simple to add it to the current list.

I have a simple to use system, which easily keeps track of the render state states. Using Push and Pop methods, each render state I deal with has a stack associated with it, that allows the user to push on a specific state, do what they need to do, the pop the state, which makes sure the state is as it was. Obviously, their are checks in the push/pop routines to check the current state (which is stored as a variable, rather than calling GetXXX, as that would be too slow).

So for example:


CRenderStateStack::PushRenderState(ERenderState_Alpha, ERenderStateAlpha_Blend);

// Do your drawing

CRenderStateStack::PopRenderState(ERenderState_Alpha);


This allows you to assume the state of the stack is unchanged when calling sub functions, which is pretty handy to know at times, and also means each object doesnt have to check the render state state, and can cut down on render state changes (though as someone is bound to point it, this method, as with most, can lead to more render state changes if it is not used effectivly;) )

To make sure my render state stack mirrors the default, i just go through the list of possible render states, and push on what I want to be the default value. This ensures that their is an entry in the stack for later, and that my engine, and the API are in sync.

Hope that helps
Spree

Share this post


Link to post
Share on other sites
Actually, my renderer is nearly identical. It uses push/pop state functions to handle state changes. Storing states in an array (a big array), and the engine does some render state sorting, and fills some vertex/index buffers as much as possible (or until it is full or runs out of geometry for this render state), and then moves on to the next state, calling push state. Once everything is on the stack, everything is rendered and popped back off the stack. This allows me to set as few unique states as possible. The only thing is I havent gotten texture render states working fully yet, so its not 100% tested, but I believe it is a good design.

Implementation of the push/pop state functions may change, if something comes up.

Share this post


Link to post
Share on other sites
I'm finding it difficult to do this without having to specify every render state. Say I start of a 3D project and use the most common states. I end up with a load of classes representing 3D objects.

Then I add a particle system, and want to disable Zbuffer writing for particle effects. I never thought of this before so when I Pop(ZWRITE_STATE) every object needs changing to set Z writing to be on.

Share this post


Link to post
Share on other sites
I'm a bit unsure as to what you mean by "every object needs changing to set Z writing to be on". If you called Pop(ZWrite), that would set the z-write renderstate to the previous state. Objects would not need to be informed of this, as the state would automatically be changed, via the API.

If an object _needed_ to know about the state of the z-write state, it could query the state stack, which would simple return true/false depending on the state.

If I take your example of a particle system.

When you call CParticleSystem::DrawAlpha(...), your particle system doesnt need to know what the current state of the state stack is. Using the push routines, the system can push the states that it needs (Alpha on, alpha type, z-write etc.). If these states are already set, then nothing happens, it just adds another entry onto the stack.

When finished, it pops them all off.

No other object needs to know what just happened in that draw routine. The state stack is exactly the same as when it entered the routine (if you have the correct no. of push's/pop's etc.)

At no point should each object be dealing with the storage of the render states. All render state stacks are conatined with the RenderStateStack object, and all push/pop and sets (which are there for completion) should be done through this object.

Hope thats clarified a part of it for you
Spree

Share this post


Link to post
Share on other sites
If popping resets the state, then potentially a state can be set and unset many times when it doesn't need to be. Shouldn't each remderable object be ignorant of other objects, leaving this decision on whether to actually change the state be the REnderState Manager's?

Share this post


Link to post
Share on other sites
That’s why I stated earlier, if not organised correctly, then yes, render states can be set/unset many times when it’s not actually needed. That’s where proper organisation of the code comes in.

So for example, batching all your alpha draws together, followed by all blend draws, then additive draws


CRenderStateStack::PushState(ERenderState_AlphaEnable, ERenderState_Enable);

CRenderStateStack::PushState(ERenderState_AlphaMode, ERenderStateAlpha_Blend);
// Do all your normal blend draws here
CRenderStateStack::PopState(ERenderState_AlphaMode);

CRenderStateStack::PushState(ERenderState_AlphaMode, ERenderStateAlpha_Additive);
// Do all your additive blend draws here
CRenderStateStack::PopState(ERenderState_AlphaMode);

CRenderStateStack::PopState(ERenderState_AlphaEnable);


In no other draw routine would you set the alpha, as you know, when entering a draw routine what the state of the render stack will be. If an object _must_ change it, then when it pops the state, the stack is returned to its expected state.

Granted, there is one unnecessary state change in the above code (popping the alpha mode would change it, then pushing the additive state will change it), but that is one of the drawbacks of this particular system. It is more concerned with the organisation and predictability of the state stack, than raw speed (though if used correctly, the speed hit would be minimal).

Quote:
Original post by d000hg
Shouldn't each renderable object be ignorant of other objects, leaving this decision on whether to actually change the state be the RenderState Manager's?


In the above code each object is ignorant of other objects, as the stack will be predictable when it comes to render.

But how do you propose the render state manager decides to change the state? In the above system, if the state is already set (so for example, the render mode is already enabled) this will be checked, and no setting will actually take place (though the push will be saved, so a resulting pop can take place).

The render state manager needs to be told when to change the state, and does, to an extent, make that decision. If, in all the subsequent additive render routines, the programmer called


CRenderStateStack::PushState(ERenderState_AlphaMode, ERenderStateAlpha_Additive);

CRenderStateStack::PopState(ERenderState_AlphaMode);


Then the state stack knows that the current mode is additive, and wouldn’t actually call on the API to set the state, thus not setting/unsetting the state unnecessarily.

Am I on the right track here, or have I totally misunderstood what you were asking? Hope not!

Spree

Share this post


Link to post
Share on other sites
What you could do to minimize the state changes is delay the change until something actually gets drawn. This is what OpenGL also does.

So in the above example of Pop(ALPHA) and then Push(NEW_ALPHA) the state will only be synced with the API when you actually draw geometry after the Push.

To do this you'll need to store the current state of the API alongside the current set state on the stack, and compare the two when you render something.

As to the default state of the API, don't assume anything. In OpenGL the different videocard vendors have a few things in a different default position I believe. Just set a default state and sync that with the API. Or if you're using the above sheme, use a NOT_SET entry so it'll be synced on first use.

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!