The all too common, how do I draw a million cubes?

Hi all,

Firstly please let me say that I am a complete newbie to game programming and have loved using MonoGame as my first foray in this mysterious world. As a C# dev I’ve found it easier than both Wave and Unity to get my first project up and running. So kudos to the developers if you’re reading for making things so easy.

Now to get to business, I am trying to render a significant amount of cubes on screen, roughly 200,000 initially with the requirement to go as high as about 1,000,000.

I have followed what was the simplest approach I could find by describing the cube, pushing it into a DynamicVertexBuffer and drawing. The core of my code looks like this.

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.Transparent);
    GraphicsDevice.SetVertexBuffer(_VertexBuffer);
    GraphicsDevice.Indices = _IndexBuffer;
        
    for (int y = 0; y < 414; y++)
    {
        for (int x = 0; x < 512; x++)
        {
            Matrix world = Matrix.CreateFromYawPitchRoll(0, 0, 0) * Matrix.CreateTranslation(new Vector3(x * 2, y * 2, 0));
    
            _CubeBasicEffect.World = world;
            _CubeBasicEffect.DiffuseColor = Color.Red.ToVector3();
                
            foreach (EffectPass effectPass in _CubeBasicEffect.CurrentTechnique.Passes)
            {
                effectPass.Apply();
                    
                int primitiveCount = _Indices.Count / 3;
    
                effect.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
                    Vertices.Count, 0, primitiveCount);
            }
        }
    }
}

Running this gets me roughly 5 frames per second in release mode. I have found some resources on XNA supporting ‘geometry instancing’ which is apparently invaluable to this type of a problem but from my research it appears that MonoGame does not support this. So what are my options? Would sincerely appreciate any help…

PS: Before anyone goes “Why are you drawing millions of cubes in the first place, there are probably better solutions to your problem” I’m actually displaying a 3D point cloud and want to run some effects on it so as best I can tell this is probably(?) the approach I need to take.

You definitely don’t want to be calling DrawIndexedPrimitive 211968 times each frame (assuming one effect pass). Instancing is what you need here, and yes it is something that was part of XNA that we do not have support for just yet. Instancing is not supported on all platforms that we use (OpenGL ES 2 for starters), but we should implement it for the desktop platforms. If you wanted to try to add it yourself, you could definitely give that a go and contribute it to the source.

The other things from a performance perspective is to remove unnecessary calculations from your inner loop. For example, Matrix.CreateFromYawPitchRoll(0, 0, 0) will produce the same result each time. In this case, it will produce the identity matrix which is redundant in the calculation of the world matrix. It’s basically burning CPU for no effect. Unless you were planning to give each cube a unique rotation, remove that. Other minor points to note, calculate _Indices.Count / 3 outside the loop and store it in a local variable. Also put the return value of Color.Red.ToVector3() into a local variable outside the loop. If you know there is only one effect pass, drop the foreach loop and just reference the first effect pass directly. I realise this may have just been sample code, but if you are measuring performance, then you need to take all steps possible to make the code efficient first.

Another option is to build all the required triangles (or points) into a vertex buffer. This will be expensive to update if you need to move arbitrary bits of geometry around on the CPU, but depending on what you want to do, you might be able to move them around on the GPU. There is a standard sample XNA4 sample for GPU Particles, I think I read somewhere that people have ported it to MonoGame but I can’t find the thread right now…

In these cases I really wants the DrawInstancedPrimitive to be implemented…

In the end (while not ideal) I increased the FPS by merging some of the cloud points to reduce the size of the point-set drawn.

Would definitely be nice if MonoGame implemented instanced geometry and multi-sampling. Unfortunately I don’t know enough about these subjects to be able to implement these features myself.

Appreciate everyone’s suggestions…

+1 for instancing, it would be great to have it

1 Like