Draw order of Texture2D vs DrawUserPrimitives

I’m trying to use Texture2D objects and VertexPositionColor objects with DrawUserPrimitives at the same time in my draw calls, but my issue is that no matter in which order I call these, the Texture2D (blue square) is always on top. I can only really control the draw order between VertexPositionColor objects and Texture2D objects separately, but I can’t do it when I mix them. Why? The SpriteSortMode has also no effect. Here’s the sample code:
protected override void Draw(GameTime gameTime) { this.GraphicsDevice.Clear(Color.Transparent); GraphicsDevice.BlendState = BlendState.Opaque; GraphicsDevice.DepthStencilState = DepthStencilState.Default; GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap; _basicEffect = new BasicEffect(this.GraphicsDevice); _basicEffect.World = Matrix.CreateTranslation(-this.GraphicsDevice.Viewport.Width / 2, -this.GraphicsDevice.Viewport.Height / 2, 0); _basicEffect.View = Matrix.CreateLookAt(new Vector3(0, 0, -1), new Vector3(0, 0, 0), new Vector3(0, -1, 0)); _basicEffect.Projection = Matrix.CreateOrthographic((float)GraphicsDevice.Viewport.Width, (float)GraphicsDevice.Viewport.Height, 1.0f, 100.0f); _basicEffect.VertexColorEnabled = true; GraphicsDevice.RasterizerState = RasterizerState.CullClockwise;

    // setting up blue square
    var blueSquare = new Texture2D(this.GraphicsDevice, 100, 100);
    var blueColorData = new Color[100 * 100];
    for (int i = 0; i < 100; i++)
    {
        for (int j = 0; j < 100; j++)
        {
            blueColorData[i * 100 + j] = Color.Blue;
        }
    }

    blueSquare.SetData(blueColorData);

    // setting up red square
    VertexPositionColor[] redSquare = new VertexPositionColor[6];
    redSquare[0].Position = new Vector3(0, 0, 0f);
    redSquare[0].Color = Color.Red;
    redSquare[1].Position = new Vector3(0, 100, 0f);
    redSquare[1].Color = Color.Red;
    redSquare[2].Position = new Vector3(100f, 100f, 0f);
    redSquare[2].Color = Color.Red;
    redSquare[3].Position = new Vector3(100, 100f, 0f);
    redSquare[3].Color = Color.Red;
    redSquare[4].Position = new Vector3(100, 0f, 0f);
    redSquare[4].Color = Color.Red;
    redSquare[5].Position = new Vector3(0f, 0f, 0f);
    redSquare[5].Color = Color.Red;

    bool firstBlueThenRed = true;
    if (firstBlueThenRed)
    {
        var spriteBatch = new SpriteBatch(this.GraphicsDevice);
        spriteBatch.Begin();
        spriteBatch.Draw(blueSquare, new Vector2(50, 50), Color.White);
        foreach (EffectPass pass in _basicEffect.CurrentTechnique.Passes)
        {
            pass.Apply();
            this.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, redSquare, 0, 2);
        }

        // we put the "End" here because it flushes everything to the screen
        spriteBatch.End();
    }
    else
    {
        foreach (EffectPass pass in _basicEffect.CurrentTechnique.Passes)
        {
            pass.Apply();
            this.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, redSquare, 0, 2);
        }

        var spriteBatch = new SpriteBatch(this.GraphicsDevice);
        spriteBatch.Begin();
        spriteBatch.Draw(blueSquare, new Vector2(50, 50), Color.White);
        spriteBatch.End();
    }

    base.Draw(gameTime);
}`

So how can I achieve that the DrawUserPrimitives draws the red square on top of the blue one which is a texture in a sprite batch?

see if this works…

  if (firstBlueThenRed)
  {
    var spriteBatch = new SpriteBatch(this.GraphicsDevice);
    spriteBatch.Begin();
    spriteBatch.Draw(blueSquare, new Vector2(50, 50), Color.White);
    spriteBatch.End();

    // if this wont work begin end around this part and see if that will.
    // this wont get batched because you are manually drawing the primitives but using the basic effect shader.
    foreach (EffectPass pass in _basicEffect.CurrentTechnique.Passes)
    {
        pass.Apply();
        this.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, redSquare, 0, 2);
    }
    // ect...

if not try wrapping a new begin end around the drawuserprimitive.

Basically what you are doing with the second call is just a regular userprimitive call but you are attempting to use the basic effect shader. So spritebatch isn’t going to be batching it.

In this context end actually just draws the thing you did batch… since you call end after the userprimitive call it will draw it last.

Im not sure you can use the basic effect shader after you call end so you might have to make a new begin end call user primitives are ussually used with your own shader in the context you are calling it.
Though i think you can get it to work the way you trying to regardless.

If you were to open up the content pipeline tool and create a effect from its menu. You could actually use that default effect it creates in the file for you, to draw the redsquare instead of using the basic effect. In that way you will be using your own shader for the redsquare.

DrawUserPrimitives renders the square immediately, but the SpriteBatch.Draw call is only actually rendered when you call SpriteBatch.End. So like @willmotil said, if you call SpriteBatch.End and then call DrawUserPrimitives the order will be reversed.

If you don’t want to use the right draw order (i.e. painters model), alternatively you can enable the depth buffer when drawing with SpriteBatch and pass a depth value to the Draw call.

It is not working. If I call the SpriteBatch.End before calling the DrawUserPrimitives, then the red square won’t even be rendered at all. How can I enable the depth buffer? Also: I don’t understand how that would influence the DrawUserPrimitives in any way, because the DrawUserPrimitives doesn’t use any depth, only the SpriteBatch does. In case of DrawUserPrimitives, I can only influence draw order by directly making the calls in the order I want it. Also: what if I wanted to draw textures and primitives in an alternating draw order, a lot of them? How would that be possible?

Sorry i forget about the states.

SpriteBatch begin and end both sets and unsets some states.
I know it’s kinda weird i don’t remember why, but its what it does.

So you would have to set the states again before using that custom draw.
You need to be aware this has a price to it and you don’t want to switch states carefree.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace MatXTestPrj
{
    /// <summary>
    /// This is the main type for your game.
    /// </summary>
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// game-specific content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        ///// <summary>
        ///// This is called when the game should draw itself.
        ///// </summary>
        ///// <param name="gameTime">Provides a snapshot of timing values.</param>
        //protected override void Draw(GameTime gameTime)
        //{
        //    GraphicsDevice.Clear(Color.CornflowerBlue);

        //    // TODO: Add your drawing code here

        //    base.Draw(gameTime);
        //}

        BasicEffect _basicEffect;

        protected override void Draw(GameTime gameTime)
        {
            this.GraphicsDevice.Clear(Color.Transparent);

            SetStates();

            // setting up blue square
            var blueSquare = new Texture2D(this.GraphicsDevice, 100, 100);
            var blueColorData = new Color[100 * 100];
            for (int i = 0; i < 100; i++)
            {
                for (int j = 0; j < 100; j++)
                {
                    blueColorData[i * 100 + j] = Color.Blue;
                }
            }

            blueSquare.SetData(blueColorData);

            // setting up red square
            VertexPositionColor[] redSquare = new VertexPositionColor[6];
            redSquare[0].Position = new Vector3(0, 0, 0f);
            redSquare[0].Color = Color.Red;
            redSquare[1].Position = new Vector3(0, 100, 0f);
            redSquare[1].Color = Color.Red;
            redSquare[2].Position = new Vector3(100f, 100f, 0f);
            redSquare[2].Color = Color.Red;
            redSquare[3].Position = new Vector3(100, 100f, 0f);
            redSquare[3].Color = Color.Red;
            redSquare[4].Position = new Vector3(100, 0f, 0f);
            redSquare[4].Color = Color.Red;
            redSquare[5].Position = new Vector3(0f, 0f, 0f);
            redSquare[5].Color = Color.Red;

            bool firstBlueThenRed = true;
            if (firstBlueThenRed)
            {
                var spriteBatch = new SpriteBatch(this.GraphicsDevice);
                spriteBatch.Begin();
                spriteBatch.Draw(blueSquare, new Vector2(50, 50), Color.White);
                spriteBatch.End();

                SetStates();

                foreach (EffectPass pass in _basicEffect.CurrentTechnique.Passes)
                {
                    pass.Apply();
                    this.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, redSquare, 0, 2);
                }
            }
            else
            {
                foreach (EffectPass pass in _basicEffect.CurrentTechnique.Passes)
                {
                    pass.Apply();
                    this.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, redSquare, 0, 2);
                }

                var spriteBatch = new SpriteBatch(this.GraphicsDevice);
                spriteBatch.Begin();
                spriteBatch.Draw(blueSquare, new Vector2(50, 50), Color.White);
                spriteBatch.End();
            }

            base.Draw(gameTime);
        }

        public void SetStates()
        {
            GraphicsDevice.BlendState = BlendState.Opaque;
            GraphicsDevice.DepthStencilState = DepthStencilState.Default;
            GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
            _basicEffect = new BasicEffect(this.GraphicsDevice);
            _basicEffect.World = Matrix.CreateTranslation(-this.GraphicsDevice.Viewport.Width / 2, -this.GraphicsDevice.Viewport.Height / 2, 0);
            _basicEffect.View = Matrix.CreateLookAt(new Vector3(0, 0, -1), new Vector3(0, 0, 0), new Vector3(0, -1, 0));
            _basicEffect.Projection = Matrix.CreateOrthographic((float)GraphicsDevice.Viewport.Width, (float)GraphicsDevice.Viewport.Height, 1.0f, 100.0f);
            _basicEffect.VertexColorEnabled = true;
            GraphicsDevice.RasterizerState = RasterizerState.CullClockwise;
        }
    }
}

jjag was refering to the depth on the spritebatch.draw call the layer float at the end of it, you could just use 2 spritebatch calls and set the layer depth on each and then turn on the depth buffer.
Or alternately on the positions of the quad you created itself.
The Vector3’s that you set as position’s have a third z element (x,y,z), that z component is the depth as long as your view matrix is just looking forward. You could turn on the depth buffer and position the quad to the depth you want it at.
That might be a little more tricky for you though as you have moved the view a bit.

Anyways …

I think the only state you really needed to reset though is the cullmode to clockwise.
I didn’t test to check it and find out.

1 Like

Aha! Exactly, the CullMode had to be reset and that was it! Thanks man. I also measured how long that takes on an Android emulator with x86 CPU, 1 GB RAM: less than 100 μs. So if I don’t overdo it, it shouldn’t create too much overhead.

Ya if it becomes a performance problem just look into doing it another way or with the depth buffer ect…
there is ussually several ways to get around a problem.