views:

84

answers:

2

hi, i have a class that draws and rotates a cube. every time i rotate the cube i reload the buffer with the new values for the cube.

    public void LoadBuffer(GraphicsDevice graphicsDevice)
    {
        buffer = new VertexBuffer(graphicsDevice, VertexPositionNormalTexture.VertexDeclaration, triangles * 3, BufferUsage.None);
        buffer.SetData<VertexPositionNormalTexture>(verts);
        graphicsDevice.SetVertexBuffer(buffer);
    }

    public void Draw(GraphicsDevice graphicsDevice)
    {
        graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, triangles);
    }

then call the Cube.Draw method in Game.Draw

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(ClearOptions.DepthBuffer | ClearOptions.Target, Color.White, 1f, 0);

        basicEffect.Parameters["WorldViewProj"].SetValue(world * view * projection);

        EffectPass pass = basicEffect.CurrentTechnique.Passes[0];
        if (pass != null)
        {
            pass.Apply();
            cube1.LoadBuffer(GraphicsDevice);
            cube1.Draw(GraphicsDevice);
            cube2.LoadBuffer(GraphicsDevice);
            cube2.Draw(GraphicsDevice);
            cube3.LoadBuffer(GraphicsDevice);
            cube3.Draw(GraphicsDevice);
        }
        base.Draw(gameTime);
    }

after a couple of minutes or so i get an OutOfMemory Exception on the line:

buffer.SetData<VertexPositionNormalTexture>(verts);

could somebody please explain why this is happening and what i can do to solve it.

A: 

It looks like you're creating a new vertex buffer every frame and not allowing the old one to fall out of scope to be garbage collected. In fact you're doing this for each of your cubes.

A better approach would be to just update the vertex values each frame, or better still update the transform on the cube each frame.

ChrisF
All references to each buffer are removed, so I would argue that if the garbage collector were any good it should know to delete the old no-longer-used buffers. It's not like the old buffers are being stored in a list or something. Am I wrong? In any case it is certainly not the way to go, as repeated allocation and deallocation is terribly inefficient, as actioned by the "new VertexBuffer" with each call to LoadBuffer.
Ricket
@Ricket - the repeated allocation was the thing that struck me, and with no evidence to the contrary lends itself to the idea that the old buffer isn't being freed.
ChrisF
@Ricket the GC doesn't know about the unmanaged resources backing the vertex buffers. That's why it thinks it has oodles of memory, when it does not.
Andrew Russell
+5  A: 

Vertex buffers are unmanaged resources. The garbage collector doesn't know that they are using a whole bunch of unmanaged memory (and GPU resources) behind the scenes. All it knows about is the tiny little bit of managed memory that each one uses.

I speak more about unmanaged resources in XNA in my answer to this question.

You could call Dispose() on each VertexBuffer before you leak it (but after drawing finishes, as it will still be in use!), to release the unmanaged resources. This will avoid the out of memory error, but will still be very slow!

What you really should be doing is creating the minimum necessary vertex buffers only once. The ideal place to do this is in your LoadContent function (and then Dispose() them in your UnloadContent function). If you have a whole bunch of cubes, all you need is a single vertex buffer that describes a cube, which you reuse every time you draw a cube.

Obviously you don't want to draw all your cubes in the same place. This is what the World matrix is for. Each time you draw a cube, set BasicEffect.World to your transformation matrix for that cube and call Apply().

(The way you are setting WorldViewProj directly is ok too. But using the nice API is, well, nicer.)

If rotation is what you want, use Matrix.CreateFromYawPitchRoll(yaw, pitch, roll) to create your transformation matrix.

For more details about this, your problem is similar to another question I have answered.

(Note that, if the vertices themselves really do change each frame, you should use DrawUserPrimitives. But note that this is still considerably slower than letting the vertex shader on the GPU handle any transformations.)

Andrew Russell