# Draw 60,000 cubic with DrawUserPrimitives on xna with only 20 fps-buffer problem?

I'm working on a minecraft style games on xna. I know it is not easy to draw a lot of cube with a good ratio of fps. I use the method "DrawUserPrimitive" with a buffer to draw a floor 250X250 cubes. But my game displays 20 fps for only 62,500 cubic drawn! I do not understand.

2,250,000 vertexPositionTexture is to lot ?

Here is my code explained on how I proceed-

Initialize Buffer:

//Initialize the vertex buffer 1 times before drawing

public void RenderToDevice()
{
int nbrVertexPositionTexture = vertexPositionStructureCube.Length; //2'250'000 ! to lot ?
//Declare and initialize a buffer
vertexBufferCube = new VertexBuffer(
Game.GraphicsDevice,
VertexPositionTexture.VertexDeclaration,
nbrVertexPositionTexture,//2'250'000
BufferUsage.WriteOnly);

vertexBufferCube.SetData<VertexPositionTexture>(
vertexPositionStructureCube);
//Set buffer to graphic card
Game.GraphicsDevice.SetVertexBuffer(vertexBufferCube);
}


Draw

// Draw all cube (10,000)
public void drawWorld(GameTime gameTime)
{
effet.LightingEnabled = false;
effet.VertexColorEnabled = false;
effet.TextureEnabled = true;
effet.FogEnabled = true;
effet.FogColor = new Vector3(1, 1, 1);
effet.Texture = textureInstancedModel;

foreach (EffectPass pass in effet.CurrentTechnique.Passes)
{
pass.Apply();
//Draw
Game.GraphicsDevice.DrawUserPrimitives<VertexPositionTexture>(PrimitiveType.TriangleList, vertexPositionStructureCube, 0, vertexBufferCube.VertexCount / 4);

}
needToDraw = true;

}

• It's because nobody draws 100×100 cubes for a whole rectangular object. Usually people draw 1 huge parallelepiped instead. The game Antichamber has a great chamber where you can see how the game engine optimizes separate voxels into a whole bigger model to save vertices. – user1306322 Mar 17 '13 at 14:45
• Are you talking about using "Octree"? I read a arcticle that say "Octree" is not a very good idea for a style of play "miencraft"..see here :0fps.wordpress.com/2012/01/14/… – Mehdi Bugnard Mar 17 '13 at 14:54
• Unfortunately, for those who have tried octrees, the results are typically not so impressive: in many situations, they turn out to be orders of magnitude slower than flat arrays. One commonly cited explanation is that access for neighborhood queries times in octrees are too slow, causing needless cache misses and that (re)allocating nodes leads to memory fragmentation. – Mehdi Bugnard Mar 17 '13 at 14:55
• I have little to no experience in making optimizations to voxel based sandbox games, but one look at how it's done in Antichamber will give you enough insight into how it can be done. Just bake together the adjacent voxels into top-left-front-most item in your 3d array of voxels and draw that one huge primitive instead of every part it actually consists of. And I'm not so sure that memory fragmentation or whatever can be a serious bottleneck, if we're talking about RAM. Otherwise, I see no reasons not to load everything into RAM. Anyway, have you tried any of it? – user1306322 Mar 17 '13 at 15:01
• Ok thank you. Do you think it better to use the RAM instead of the memory of the graphics card? – Mehdi Bugnard Mar 17 '13 at 15:06

Yes.

VertexPositionTexture is a 20 byte structure. With over 2 million of them, they take up around 43MB of memory. Which gets copied around a bit on the CPU, and then transferred to the GPU. Each frame!

There are almost certainly going to be other optimisations you can pursue. I see, in the comments, merging adjacent faces is suggested. I think Minecraft might do some occlusion culling. One that I recommend is not including always-invisible faces (between adjacent cubes) in your buffer.

But, for now, the simplest fix you can do is put your data in a VertexBuffer - which you then transfer to the GPU once at startup. Or a DynamicVertexBuffer, if you want to occasionally modify it (or sub-sections of it).

Additionally, an IndexBuffer or DynamicIndexBuffer should also drastically improve both memory usage and vertex performance by allowing you to remove all the duplicate vertices you have.

Edit: I see that you're actually creating a VertexBuffer. But you're not using it correctly. DrawUserPrimitives will transfer an array of vertices to the GPU each frame. You want DrawPrimitives to draw the vertex buffer you set with SetVertexBuffer (which you should be setting right before drawing, not at startup). Or, better yet, use DrawIndexedPrimitives with that index buffer I suggested.

• Okay thanks a lot. I'm going try to use DrawIndexedPrimitives with IndexBuffer and i'm going back talking about my results ^^ – Mehdi Bugnard Mar 17 '13 at 15:47
• You are absolutely right .. I changed all my project. And it works perfectly! 60 fps with a golf 250X1X250. However, beyond the limits of the "VertexBuffer" is reached. I will explore this in my field partitioning into several part and draw only what the camera can see. – Mehdi Bugnard Mar 18 '13 at 20:15

It's not a buffer problem - it's a "your code" problem.

First of all, user primitives are slow. They're not horribly slow, but they are slower than using pure vertex buffers. (For info: user primitives are emulated behind-the-scenes with a dynamic vertex buffer, so you're not only redundantly transmitting a large amount of data to the GPU each frame, but you're also incurring the cost of extra memory copies).

Secondly, and you don't confirm or deny this in your question, but if you're making 60,000 draw calls per-frame, then poor performance is to be expected. That's normal behaviour; a high number of draw calls each sending a small amount of data will perform much worse than a low number of draw calls each sending a large amount of data.

You code, and the thinking behind it, is also slightly messed-up. First of all you're setting a vertex buffer, then you're calling DrawUserPrimitives which won't actually use that buffer!

In order to get good performance from this kind of drawing you need to look at alternate strategies - just trying to brute-force it CPU-side is not good enough here. Instancing is a typical solution used, and will give you good performance (for reference, I can easily handle 1 million cubes at playable framerates on low-end mobile devices using instancing).

However, my knowledge is in D3D itself rather than XNA, so I can't give you any further help with this.