Fux
Fux

Reputation: 171

Memory not released after disposing buffers in SharpDX, application freezes

I'm facing a memory issue in my SharpDX application when disposing of Direct3D buffers. Initially, I wasn’t using DisposeCollector, and I was manually calling Dispose() on the buffers. My application freezes when disposing a large number of buffers (around 200,000). After investigating, I found a SharpDX-based project on GitHub that uses DisposeCollector. I was able to replicate a similar memory issue there, but with one key difference.

What I observed:

Additional Observations:

My question is: Why does my application freeze when manually disposing of buffers, while in the modified GitHub project (without DisposeCollector), the memory is delayed in being freed but without freezing? Could this behavior be specific to how SharpDX handles memory management on NVIDIA GPUs, or is there something else at play?

Starting from the sample I found here, I edited D3DApp.cs by adding

switch (e.KeyCode)
{
    // ...
    case Keys.R:
        skipRenderLoop = true;
        for (var i = 0; i < quad.Length; i++)
        {
            var q = quad[i];
            //RemoveAndDispose(ref q);
            q.Dispose();
            quad[i] = null;
        }

        break;
    case Keys.T:
        skipRenderLoop = false;
        break;
}

similarly I also edited the render loop as follows:

RenderLoop.Run(Window, () =>
{
    if (skipRenderLoop) // <-- STOP DRAWING TO SIMULATE THE BEHAVIOUR OF MY APP. Without the ToDispose (see other code snippet), the issue reamins.
        return;
    
    // Start of frame:
    // Retrieve immediate context
    var context = DeviceManager.Direct3DContext;

    Matrix worldViewProjection = Matrix.Identity;
    Matrix viewProjection = Matrix.Identity;

    
    // Clear depth stencil view
    context.ClearDepthStencilView(DepthStencilView, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1.0f, 0);
    // Clear render target view
    context.ClearRenderTargetView(RenderTargetView, Color.White);
    
    // Create viewProjection matrix
    viewProjection = Matrix.Multiply(viewMatrix, projectionMatrix);

    // If Keys.CtrlKey is down, auto rotate viewProjection based on time
    if (ctrlKey)
    {
        var time = clock.ElapsedMilliseconds / 1000.0f;
        viewProjection = Matrix.RotationY(time * 1.8f) * Matrix.RotationZ(time * 1f) * Matrix.RotationZ(time * 0.6f) * viewProjection;
    }

    // Create WorldViewProjection Matrix
    worldViewProjection = worldMatrix * viewProjection;
    // HLSL uses "column major" matrices so transpose from "row major" first
    worldViewProjection.Transpose();
    // Write the worldViewProjection to the constant buffer
    context.UpdateSubresource(ref worldViewProjection, worldViewProjectionBuffer);
    

    // Render the primitives
    axisLines.Render();
    triangle.Render();

    

    for (var i = 0; i < quad.Length; i++)
    {
        var qr = quad[i];
        if (qr == null)
            continue;

        const int ITEMS_PER_ROW = 7;
        var dx = i % ITEMS_PER_ROW;
        var dy = i / ITEMS_PER_ROW;
        worldMatrix = Matrix.Translation(dx * 0.5f, dy * 0.5f, 0);

        worldViewProjection = worldMatrix * viewProjection;
        // HLSL uses "column major" matrices so transpose from "row major" first
        worldViewProjection.Transpose();
        // Write the worldViewProjection to the constant buffer
        context.UpdateSubresource(ref worldViewProjection, worldViewProjectionBuffer);
        qr.Render();
    }

    // Render FPS
    fps.Render();

    // Render instructions + position changes
    textRenderer.Render();

    // Present the frame
    Present();
}
);

In order to prevent the usage of DisposeCollector I also edited QuadRenderer.cs:

protected override void CreateDeviceDependentResources()
{
    // Ensure that if already set the device resources
    // are correctly disposed of before recreating
    RemoveAndDispose(ref quadVertices);
    RemoveAndDispose(ref quadIndices);

    // Retrieve our SharpDX.Direct3D11.Device1 instance
    var device = this.DeviceManager.Direct3DDevice;

    // Create a quad (two triangles)
    quadVertices = /*ToDispose*/(Buffer.Create(device, BindFlags.VertexBuffer, new[] { // <--- AVOID ToDispose()
    /*  Vertex Position                       Vertex Color */
        new Vector4(0.25f, 0.5f, -0.5f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f), // Top-left
        new Vector4(0.75f, 0.5f, -0.5f, 1.0f), new Vector4(1.0f, 1.0f, 0.0f, 1.0f), // Top-right
        new Vector4(0.75f, 0.0f, -0.5f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f), // Base-right
        new Vector4(0.25f, 0.0f, -0.5f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f), // Base-left
    }));
    quadBinding = new VertexBufferBinding(quadVertices, Utilities.SizeOf<Vector4>() * 2, 0);

    // v0    v1
    // |-----|
    // | \ A |
    // | B \ |
    // |-----|
    // v3    v2
    quadIndices = /*ToDispose*/(Buffer.Create(device, BindFlags.IndexBuffer, new ushort[] { // <--- AVOID ToDispose()
        0, 1, 2, // A
        2, 3, 0  // B
    }));
}

Upvotes: 4

Views: 76

Answers (0)

Related Questions