0
\$\begingroup\$

I'm building a voxel world generator with XNA where the voxels are rendered as polygonal cubes. The world is divided into 1024 chunks of 32x32x256 cubes each (as 32 chunks by 32 chunks), and each chunk has its own index and vertex buffer. Since each chunk is different, vertex buffer size is determined on runtime.

To prevent massive stalling on opening the program, only one chunk is re-built per frame until all chunks are built. Occasionally the program crashes with an "insufficient memory" related error that is caused by the line that creates the vertex buffer. Here is how I set them up:

 vb = new VertexBuffer(
      device,
      VertexPositionColorNormal.VertexDeclaration,
      nextIndex,
      BufferUsage.None
 );

 ib = new IndexBuffer(
      device, IndexElementSize.ThirtyTwoBits,
      nextIndex,
      BufferUsage.None
 );

My vertex structure is not very large, using only a Vector3 for position and Byte4 for color and normal lookup info:

public struct VertexPositionColorNormal : IVertexType
{
    public Vector3 Position;
    public uint Data;

    public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration
    (
        new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
        new VertexElement(12, VertexElementFormat.Byte4, VertexElementUsage.TextureCoordinate, 0)
    );

    VertexDeclaration IVertexType.VertexDeclaration { get { return VertexDeclaration; } }
}

NextIndex is the number of vertices and indices allocated to the buffers (always 1 index per vertex).

Am I making too many buffers? It crashes despite the GC showing only about 12 MB of usage so I guess memory fragmentation is a problem when creating lots of buffers in a short time? Should I use a smaller amount of buffers that store more data and have several chunks share them?

EDIT: I estimate roughly 0.75 to 1 GB of memory used in the vertex buffers. My graphics card has 2GB of VRAM. Though I also know sometimes not all the vertex data may be stored in VRAM at one time.

\$\endgroup\$
  • \$\begingroup\$ The GC won't show the unmanaged memory referenced by the vertex buffers. Are you sure you don't have a leak and are creating too many buffers? \$\endgroup\$ – Steven Dec 12 '14 at 3:54
  • \$\begingroup\$ That's what I want to know, if 1024 buffers is too many. \$\endgroup\$ – ChrisC Dec 12 '14 at 4:55
  • \$\begingroup\$ How big are your buffers? 1024x32x32 cubes would mean you've got around 500 Mb just in vertex buffers if you're being naive about how you're constructing these things. \$\endgroup\$ – Sean Middleditch Dec 12 '14 at 7:40
  • \$\begingroup\$ @SeanMiddleditch actually, I've done a lot of optimization for working out what cubes are actually visible from outside so I add only the visible faces to the vertex buffer. The crashes seem to happen when I exclusively use 3D noise functions to generate the meshes, but not when using 2D noise as a way to make a heightmap. \$\endgroup\$ – ChrisC Dec 12 '14 at 8:16
  • \$\begingroup\$ Is it possible that the error is referring to GPU memory? \$\endgroup\$ – Nox Dec 12 '14 at 13:13
3
\$\begingroup\$

I've never had this problem before, but I've also noticed you're using BufferUsage.None. This enum has two possible states: None and WriteOnly. In all my coding, I've always used WriteOnly.

From the documentation for BufferUsage, it states that WriteOnly does the best memory location for writing/rendering, but the downside is you can't read the data from the buffer. It might be worth while trying out WriteOnly, and seeing if it solves the problem, especially if you don't plan on reading from the buffers.

Your Vertex Declaration also looks odd, using a uint to store Color and Normal data. I dunno what your reasons behind this is, but because its not the standard way of doing it, it might be causing a problem. My VertexPositionNormalColor is usually set up like this. Don't think its likely, but I know from experience how an innocent looking line of code can be a major bug monster.

Lastly, in the creation of both vertex and index buffers, you seem to be assuming that the vertexCount = indexCount. Don't know what circumstances would cause this to be true unless you're rendering a triangle.

Another possibilty is that during the creation of your VertexBuffer, you are hitting the internal limit. I know from previous experience with terrain generated from a heightmap that VertexBuffers will only hold so many elements. There is the possibility (especially if your voxels are generated at run time dynamically) that one chunk has a few too many voxels in it.

A good way to test this cause would be to bump your game world down to 512 chunks, and then test repeatedly to see if the error occurs.

EDIT:

I've had a bit of a look around, and this link here might offer an explanation. They are reporting a near identical error (might actually be the same given the age of the thread and changes in C#/the XNA API), and have a discussion about VRAM memory allocation with multiple vertex buffers.

Chances are, you try to create a VertexBuffer in VRAM, but there isn't a free block big enough to store it, and your application errors with an 'InsufficentMemory' exception. You can work around this by creating smaller VertexBuffers by reducing the complexity of the data in them or creating fewer VertexBuffers by increasing the size of your chunks so each buffer gets allocated more fairly in VRAM.

Further explaination:

Note that memory allocation for object is random, so if it finds a large enough chunk of free memory, it'll store it in there somewhere. This means you could have 256MB of memory free in total, and have a 64MB object to store, but your largest free chunk is 32MB. Even though the total memory exceeds the size of your object, the largest free chunk that we could use is smaller than the requested object size, so we report an OutOfMemory or InsufficentMemory error (or something similar) to the application

\$\endgroup\$
  • \$\begingroup\$ I think I'm hitting an internal limit with vertex buffers, since these crashes happen more often with more chaotic-looking 3D shapes made with 3D noise functions, instead of a terrain that is made with combining 2D noise at different octaves. Also the problem goes away when rendering less chunks at a time. \$\endgroup\$ – ChrisC Dec 15 '14 at 21:21
  • \$\begingroup\$ I've added some more information to my answer, hopefully it's helpful and I've explained it well enough. From my own experience and knowledge, it seems to fit your case (Larger # of chunks + increased chunk complexity = greater chance of error). I still might be wrong though. \$\endgroup\$ – Seta Dec 15 '14 at 22:17
  • \$\begingroup\$ Thanks for the added explanation, and some good info in that XNA thread too. This I found especially telling: "a vertex buffer always needs to be stored in a single 'lump', and can't be broken up throughout RAM, filling in the gaps as it were. You can come far far closer to filling my GPU's 256MB using many 1MB VBs than you can 4 64s." It sounds like larger, but proportionally fewer VB's will be less likely to throw the exception. In that case I can either 1. reduce the amount of voxel chunks or 2. have several chunks share one VB. The latter one would be tricky to apply but it could work. \$\endgroup\$ – ChrisC Dec 16 '14 at 21:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.