I’ve been meaning to make something like this for a long time, and I know that some engines like Xen offer a viable solution already, but I wanted to make my KISS solution that interferes with the standard XNA interface as little as possible.
It’s a pretty lengthy snippet of code, so I won’t paste it all here, but basically there’s three classes : the StateStack, ManagedRenderStates and ManagedSamplerStates.
Description
StateStack is a static extension class to the GraphicsDevice. It could be just another static class, or even a global XNA service, but since it’s going to be used in combination with the GraphicsDevice most of the time, I thought it was logical to use extensions.
You can push, pop and peek the current state, which takes and returns ManagedRenderState objects. It’s very similar to how OpenGL works with the attribute stack (glPushAttrib), except you don’t choose what you push, the entire state gets pushed/popped everytime. I didn’t think isolating states in categories was useful or natural in XNA.
The ResetState and CommitState methods are shorthands to the same methods in ManagedRenderState.
ManagedRenderState is like a wrapper over a RenderState object, such that it has the same interface : a read/write property for each render state and access to the SamplerState collection.
The difference is that it tracks changes to render states as you make them, marks properties as dirty and applies only what’s needed when you decide to Commit. The Reset method refreshes the object with the current state of the GraphicsDevice; this needs to be done at the beginning of every draw call, or everytime you think the state has been tampered with.
ManagedSamplerState does the same thing but for sampler states : addressing, filtering, etc. You don’t call Reset or Commit on it directly, it gets done automatically by its host ManagedRenderState.
I added blending mode and texture filtering helper methods to both State classes, because I end up using that a lot… and with proper state management, you don’t have to worry about redundant assignments! So you can be permissive and set everything you need, every time.
Usage
So what does it look like to use it?
The Game class needs to do some initialization and per-draw-call stuff first…
protected override void Initialize() { GraphicsDevice.PushState(); // Other stuff base.Initialize(); } protected override void Draw(GameTime gameTime) { GraphicsDevice.ResetState(); SetDefaultRenderStates(); GraphicsDevice.Clear(Color.CornflowerBlue); base.Draw(gameTime); } void SetDefaultRenderStates() { var rs = GraphicsDevice.PeekState(); rs.DepthBufferEnable = true; rs.SetBlendingMode(BlendingMode.Alphablending); rs.SamplerStates[0].SetFiltering(FilteringMode.Anisotropic); rs.SamplerStates[0].AddressU = rs.SamplerStates[0].AddressV = TextureAddressMode.Wrap; GraphicsDevice.CommitState(); }
Then to use the state stack, you don’t ever touch the GraphicsDevice.RenderState object : always use the Managed equivalent.
// Push a copy of the state on the stack var rs = GraphicsDevice.PushState(); // Always-on-top, and don't disturb the depth buffer, and no culling rs.DepthBufferFunction = CompareFunction.Always; rs.DepthBufferWriteEnable = false; rs.CullMode = CullMode.None; // Commit changes GraphicsDevice.CommitState(); // Draw some stuff Draw(); // Pop back to the last state GraphicsDevice.PopState(); // We are now back to the last state
Download & Conclusion
I think it makes a lot of sense to stack states in a component system, because each component can push its own local state and work with it, and just dispose it afterwards. Add this to dirty-checking to remove almost all redundant state changes, and you’ve got a very usable system!
The next step is batching draw calls to keep calls that use the same RenderStates together… in my system, this is left to the user’s discretion. Kevin Gadd presented a way of doing this (and many other things including threaded rendering) on his blog.
Another rather important note : ManagedRenderState contains a MaxSamplers constant that you can tweak depending on the number of sampler states you know you’ll use. Leaving it at 16 will make update/reset/refresh operations kind of slow… not sure yet if it’s noticeable.
And because of the immense amount of copy-paste that’s been needed to produce these classes, I can’t promise that it doesn’t have a typo or two… I haven’t tested every single state. But up to now, it works great, and I’ll update it if needed.
StateStack.cs (2 kb – C# class)
ManagedRenderState.cs (30 kb – C# class)
ManagedSamplerState.cs (7 kb – C# class)
Bonuses in the code files : a method to unpack a packed color from uint to Color, and a simplistic object Pool implementation (to eliminate garbage when stacking up states).
Hope you find it useful!
Nice article!
I’m working on a slightly different approach. Basically, every draw-call pass will have its own render state wrapper, but I won’t track changes to the initial or main state. Instead, I’ll compare the current state to the next one in the draw-call pass for changes, and thus only change what’s necessary “from current to next”.
In order to avoid cheking all render-state fields, I’ll pack them “by category” on a single value using bit-shifting. Say, all embed (almost) all booleans in one integer field. So, if two integers -the current and the next- are equal, no need for changes; but if they are different, then I check each bit to see which boolean values need to be updated.
Since I have a “material” system that controls each pass, and the latter specifies its render state, by not allowing changing render states within the shader itself -manual process though, I’ll avoid situations that force me to mark “dirty fields” and reset.
Again, great article. Thanks for sharing.
A stack is not relevant to render states
All that matters is:
– what are the current states
– what states do we require now
to achieve this you just need a shadow cache of the device render state and then calculate the delta
then do a write-through the shadow cache to the device
I agree that a “delta” system is more efficient and probably simpler, but I wouldn’t say a state stack is entirely irrelevant.
In a hierarchical component system, it can be a useful tool. Not even render “leaf” knows or needs to know the full render state, it could only push some additions, and pop them. The upper nodes dictate the general state, the leaves work with specifics.