Can I draw a single VertexBuffer after drawing a VertexBufferBinding[...]?

I’m attempting to draw two objects - a tilemap and a player sprite. The tilemap is drawn using two vertex buffers (one static VertexPosition buffer for geometry; one dynamic Vector2 buffer for uvs). The player sprite is then drawn on top of the tilemap with a single VertexPositionTexture buffer.

Individually each object is drawn correctly. When drawn together, the first frame is rendered as expected, but any frame after the tilemap appears to be missing it’s texture data (though the geometry is drawn correctly). If I wrap the player sprite’s VertexPositionTexture buffer in a VertexBufferBinding - the tilemap works again. But if I call SetVertexBuffer(…player sprite data…) after calling SetVertexBuffers(…tilemap data…), it breaks.

Tilemap Constructor:

        this.vertexPositions = new VertexPosition[tileCount * 4];
        this.vertexUvs = new Vector2[tileCount * 4];
        this.indices = new int[tileCount * 6];

        this.vertexPositionBuffer = new VertexBuffer(
            graphicsDevice,
            VertexPosition.VertexDeclaration,
            this.vertexPositions.Length,
            BufferUsage.WriteOnly);

        vertexUvDeclaration = new VertexDeclaration(
            new VertexElement[]
            {
                new VertexElement(
                    0,
                    VertexElementFormat.Vector2,
                    VertexElementUsage.TextureCoordinate,
                    0)
            });

        this.vertexUvBuffer = new DynamicVertexBuffer(
            graphicsDevice,
            vertexUvDeclaration,
            this.vertexPositions.Length,
            BufferUsage.WriteOnly);

        this.indexBuffer = new IndexBuffer(
            graphicsDevice,
            IndexElementSize.ThirtyTwoBits,
            this.indices.Length,
            BufferUsage.WriteOnly);

        this.vertexBufferBindings = new VertexBufferBinding[]
        {
            new VertexBufferBinding(this.vertexPositionBuffer),
            new VertexBufferBinding(this.vertexUvBuffer)
        };

Tilemap Draw()

        this.vertexUvBuffer.SetData(this.vertexUvs);   
       basicEffect.GraphicsDevice.SetVertexBuffers(vertexBufferBindings);
        basicEffect.GraphicsDevice.Indices = this.indexBuffer;
        basicEffect.Texture = texture;
        basicEffect.View = camera.View;
        basicEffect.Projection = camera.Projection;
        basicEffect.World = Matrix.CreateTranslation(position);

        foreach (var pass in basicEffect.CurrentTechnique.Passes)
        {
            pass.Apply();
            basicEffect.GraphicsDevice.DrawIndexedPrimitives(
                PrimitiveType.TriangleList,
                0,
                0,
                this.indexBuffer.IndexCount / 3);
        }

Player Sprite Constructor

        this.vertexBuffer = new VertexBuffer(
            graphicsDevice,
            VertexPositionTexture.VertexDeclaration,
            this.vertices.Length,
            BufferUsage.WriteOnly);

        this.indexBuffer = new IndexBuffer(
            graphicsDevice,
            IndexElementSize.ThirtyTwoBits,
            this.indices.Length,
            BufferUsage.WriteOnly);

Player Sprite Draw

        basicEffect.GraphicsDevice.SetVertexBuffer(this.vertexBuffer);
        basicEffect.GraphicsDevice.Indices = this.indexBuffer;

        basicEffect.Texture = texture;
        basicEffect.View = camera.View;
        basicEffect.Projection = camera.Projection;
        basicEffect.World = Matrix.CreateTranslation(playerPosition);

        foreach (var pass in basicEffect.CurrentTechnique.Passes)
        {
            pass.Apply();

            basicEffect.GraphicsDevice.DrawIndexedPrimitives(
                PrimitiveType.TriangleList,
                0,
                0,
                this.indexBuffer.IndexCount / 3);
        }

The problem is that BasicEffect asserts a specific vertex attributes for vertex fetch depending on what properties are set to true or false. E.g. TextureEnabled and VertexColorEnabled.

Recommendation:

  • Write your own shader; don’t use BasicEffect; keep it simple and only use one technique and one “pass”.
  • The vertex declaration will then make more sense when you think of the input to the vertex shader when you write your own GPU code.
  • Use MGFXC tool to compile your shader. Don’t use the Content Pipeline. See MonoGame Effects Compiler (MGFXC) | MonoGame Documentation

Right now I have only textures enabled, so the vertex shader should expect nothing more than a Position and a TextCoord as input, which I am currently supplying - at least for the first rendered frame. Eventually I’m sure I’ll end up ditching BasicEffect, or at least extending it.

I’ve been poking around in the source code and I’m starting to think this is a bug. Internally monogame seems to use a VertexBuffersBindings object for everything, regardless of how many buffers you are attempting to draw with. For each draw call it simply overwrites the buffers and if there’s a length mismatch (ex trying to draw one buffer, after having just drawn two) it simply clears the excess data. There doesn’t appear to be any reason this shouldn’t work - and it does up work up until the second frame (which is running the same exact code as the first frame).

For now, I think i’m just going to implement a hack to work around it.