SpriteBatch and renderstates in XNA Game Studio 4.0


A repost of one of my more popular articles, upgraded for Game Studio 4.0…

If you are mixing 3D rendering with 2D objects using SpriteBatch, you may notice that your 3D graphics no longer draw correctly after you have rendered sprites. This is because the SpriteBatch changes several device states to values that may not be appropriate for drawing in 3D.

So exactly which states does SpriteBatch change? Here’s the complete list:

    GraphicsDevice.BlendState = BlendState.AlphaBlend;
    GraphicsDevice.DepthStencilState = DepthStencilState.None;
    GraphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
    GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;

SpriteBatch also modifies the vertex buffer, index buffer, and applies its own effect onto the GraphicsDevice.

Before you draw anything in 3D you will probably want to reset these states:

    GraphicsDevice.BlendState = BlendState.Opaque;
    GraphicsDevice.DepthStencilState = DepthStencilState.Default;

Depending on your 3D content, you may also want to set:

    GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;

Comments (43)

  1. Daniel says:

    Its's great to see that you are having time to put up more brilliant posts again.

  2. Eric says:

    What about the GraphicsDeclaration .. ?

  3. ShawnHargreaves says:

    > What about the GraphicsDeclaration .. ?

    What is a GraphicsDeclaration?  No such type in XNA…

  4. Eric says:

    Sorry meant to say VertexDeclaration, like you didn't know that 😉

    As side from re-setting the state of my GraphicsDevice, shouldn't I also re-set the VertexDeclaration?

    My code usually instanciates the VertexDeclarations somewhere during initialization and they are set on the GraphicsDevice right before filling the VertexBuffer when drawing something in 3d. But if figured that if the BasicEffect overrides the VertexDeclaration it might be good to point that out here too.

  5. ShawnHargreaves says:

    > As side from re-setting the state of my GraphicsDevice, shouldn't I also re-set the VertexDeclaration?

    There is no such thing as GraphicsDevice.VertexDeclaration in Game Studio 4.0, so no need to reset that.

  6. bobobobo says:

    So when, when, when?

  7. Artur Pais says:

    Hi, i'm getting this strange exception on Windows Phone 7 emulator (April refresh) when trying to render a simple texture with SpriteBatch: "XNA Framework Reach profile requires TextureAddressMode to be Clamp when using texture sizes that are not powers of two.". I googled it and the only search result was in your twitter :)

    The texture i'm using is not a power of two, but here are the strange facts:

    1. I render some geometry to a fullscreen 480×800 RenderTarget and use SpriteBatch to render it to the screen. After this, i draw the non power of 2 texture with SpriteBatch and the exception is thrown.

    2. I tried passing PointClamp and LinearClamp to the Begin() method of SpriteBatch, but the exception is thrown anyway.

    3. If i don't render the previous geometry and rendertarget, the SpriteBatch is drawn correctly with no exception (even though it's size is non power of two)!

    4. If i force a power of two size in the ContentPipeline for the texture, everything is fine, i get the geometry/rendertarget and this particular texture drawn to the screen!

    Well… do you think this is some kind of bug? Some kind of optimization that's not working correctly in SpriteBatch? It seems like PointClamp or LinearClamp are getting lost somewhere with the previous draw code. Or am i missing something with SpriteBatch behaviour….

    If you need further info, please tell me, i regularly read your wonderful blog.

    Thanks.

  8. ShawnHargreaves says:

    Artur: look in the debugger to see what textures and sampler states are bound onto the device when this exception is thrown (not just on index 0 – are you using dual texture anywhere so you maybe have some non-pow-2 and wrap states set on the second sampler index?)

  9. Artur Pais says:

    After further testing and debugging, i found the problem… well, i don't know if this is by design or if it just a minor issue. It has nothing to do with rendertargets and is very obvious how to work around.

    I have the next code:

               basicfx = new BasicEffect(graphicsDev);

               basicfx.VertexColorEnabled = false;

               basicfx.TextureEnabled = false;      // <— Texturing disabled

               basicfx.LightingEnabled = false;

               basicfx.FogEnabled = false;

               basicfx.Texture = this.texNormalHex; // <—- Non power of two texture

    After this, i was drawing a batch of triangles with this BasicEffect effectpass applied. Well, it seems even though texturing is disabled for this BasicEffect, if we don't explicitily set:

               graphicsDev.SamplerStates[0] = SamplerState.LinearClamp; // or any other *Clamp

    … then the render will fail unpredictably further down the Draw() method (in my case it was launching the exception in spriteBatch.End). I thought that SamplerState wouldn't be used since TextureEnabled is false.

    Thanks, hope this is useful for anyone with similar problems.

  10. Daniel Frandsen says:

    Gah!  I didn't know there was that article around for 3.1.  I ended up ripping open the 3.1 dll in Reflector to figure out what settings were being changed.

    Thanks for posting this.  I'll remember it if I need to fiddle with settings for 4.0.

  11. Nathaniel Troutman says:

    Thanks a ton, this fixed my bugs in a game I'm working on. I couldn't figure out for the life of me why some triangles were being drawn in front of others.

  12. Brent says:

    what about GraphincsDevice.RenderState.CullMode = CullMode.None;

    doesn't seem to wanna work in 4.0

  13. Adam Goss says:

    Am I missing something?  Because I do these resets in my draw method before I draw my 3D graphics (simple primitives) and then I draw my 2D graphics after that. However my 3D graphics still get messed up after the first iteration of Draw().  Is there something I might be missing?

  14. Tomas Voracek says:

    Adam Goss: I had problem with SpriteBatch and displaying wireframe of 3D model. When i added SpriteBatch, wireframe was gone. Tried suggested reset steps, but none of them worked. Then i tryed setting GraphicsDevice.RasterizerState to my wireframe state right AFTER spriteBatch.End(), but BEFORE calling Draw on model. Maybe your issue is same?

  15. Tomas Voracek says:

    Adam Goss: Or look at create.msdn.com/…/primitives_3d

  16. Adam Goss says:

    Cheers Tomas, the rasterizer sorted it.  You've saved me much grief! :)

  17. thrakazog says:

    Thanks for the post.  This got me going again after everything turned to black.

  18. Thundy says:

    I am having issues with this myself. At present i only have very basic stuff as i am trying to build an engine. was trying to write my picked positions on the y plane to screen whilst showing a simple square set up with 4 vertices. spritebatch is doing…something which is stopping my square being drawn to screen. my draw call looks like this:

               GraphicsDevice.Clear(Color.Black);        

               myterrain.Draw();

               spriteBatch.Begin();

               spriteBatch.DrawString(font, pickedPosition.X.ToString() + " , " + pickedPosition.Z.ToString(), Vector2.Zero, Color.Yellow);

               spriteBatch.End();

               GraphicsDevice.RasterizerState = RasterizerState.CullNone;

               GraphicsDevice.BlendState = BlendState.Opaque;

               GraphicsDevice.DepthStencilState = DepthStencilState.Default;

               GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;

    anyone see anything obvious here?

  19. Thundy says:

    Fixed it. i just needed to recall the method that assigns the vertices of my square. spritebatch wiped the vertex buffer each time so the vertices have to be re-allocated

  20. MarkT says:

    I have a shader on an object in the world which does:texture

    diffuseTexture : DIFFUSE;

    sampler2D diffuseSampler = sampler_state

    {

       Texture = <diffuseTexture>;

       MinFilter = Linear;

       MipFilter = Linear;

       MagFilter = Linear;

       AddressU = wrap;

       AddressV = wrap;

    };

    The texture I have is Power Of two.  But later on, I draw all this to a render target using bloom, and then it crashes on the sprite batch:  

    void DrawFullscreenQuad(Texture2D texture, int width, int height, Effect effect, IntermediateBuffer currentBuffer)

           {

               // If the user has selected one of the show intermediate buffer options,

               // we still draw the quad to make sure the image will end up on the screen,

               // but might need to skip applying the custom pixel shader.

               if (showBuffer < currentBuffer)

               {

                   effect = null;

               }

               effect.CurrentTechnique.Passes[0].Apply();

               GameCore.Device.SamplerStates[0] = SamplerState.LinearClamp;

               GameCore.SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.LinearClamp, null, null);

               GameCore.SpriteBatch.Draw(texture, new Rectangle(0, 0, width, height), Color.White);

               GameCore.SpriteBatch.End();

           }

    samplers 0, 1 are clamp, and 2 – 15 are wrap, but I'm not using those, and I still get

    "XNA Framework Reach profile requires TextureAddressMode to be Clamp when using texture sizes that are not powers of two."

    even if I loop through all 16 sampler states setting LinearClamp.  It just seems like no matter what I do, the renderer believes i'm still set for LinearWrap, even though I am not.

    If I Change the shader to clamp, everything is ok, but then my shader doesn't work.

    Any ideas?

  21. ShawnHargreaves says:

    MarkT: I would recommend the create.msdn.com forums for this question. Blog post comments aren't really the best place for support discussions!

  22. Jezze says:

    I don't understand why static states like SamplerState.PointWrap are already read-only, even if they're not bound to a graphics device.

    var samplerState = SamplerState.PointWrap; // isBound == true

    samplerState.MaxAnisotropy = 8; // InvalidOperationException (read-only)

    graphicsDevice.SamplerStates[0] = samplerState; // bound sampler state to graphics device

  23. ShawnHargreaves says:

    > I don't understand why static states like SamplerState.PointWrap are already read-only

    Exactly to stop you from changing them!

    These built-in state objects provide easy access to common sets of states. If you changed one of them to use some different state, any other code that used this built-in would get unpredictable results that are most likely not what it wanted.

    Changing the meaning of existing system wide objects is not a good idea. If you want to use a different combination of states, just make a new state object of your own. That way you don't risk breaking whatever other code might also be using this object.

  24. strayke says:

    It works!

    you are great!

  25. Trond says:

    Hi Shawn,

    Could you briefly explain why you reset the values of each of the three lines in XNA 4.0:

    GraphicsDevice.BlendState = BlendState.Opaque;

    GraphicsDevice.DepthStencilState = DepthStencilState.Default;

    GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;

    , and specifically; for what kind of 3d content would one want to include the last line ?

  26. Glitch0011 says:

    Thanks you, took dam ages to find the right things to change. You were simple and too the point which is great.

  27. dPhase says:

    Very helpful. If I didn't reset my SpriteBatch, my 3D objects became see through.

  28. SteelGolem says:

    Tomas Voracek thank you, i didn't realize SpriteBatch.Begin also screwed up the vertex/index buffers. once i was setting them at the start of every Draw() call along with the resetting of BasicEffects and the other states, it fixed my problem right up.

  29. Ga says:

    I was stuck and now I'm not, thank you. Specifically it was GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap; that did the trick.

  30. Soha says:

    Thanks a lot :)

  31. Radu says:

    Thank you very much!

  32. Peter Hawke says:

    Dude I can't thank you enough!  I was using the 'tank on a heightmap' sample and I was trying to add game state management but the tank was drawing weird.  This fixed it, thanks a googleplex^googleplex times;

  33. Nameless says:

    Thank you so much. I only wish I found this page sooner =)

  34. Keith Miller says:

    I just ran into this issue and solved it just as quick thanks to this post. Thanks!

  35. Chris P says:

    Oh for heaven's sake.  Thank you very much for solving my week-long problem.  I was going out of my mind.

  36. Arek says:

    You saved me a lot of hair. Thank you thank you!

  37. Arek says:

    Oh yes, I was going out of my mind too. I missed the phrase. :)

  38. pstr says:

    Thank you. It worked with my XNA 4.0 2D/3D project (text drawing).

  39. Programmer says:

    your solution saved my project …. thank you so much.

  40. GuMa2012 says:

    Very helpful, thank you so much!

  41. Imagrem says:

    Shouldn't i switch the DepthBuffer to disabled also before switch to 2D ? I probably have miss something.

  42. radon says:

    this. thanks very much, helped me a ton! I wish there was some kind of notification or warning for it tho