3
\$\begingroup\$

one Thing I found out: Windows 7 produces a memory leak while using certain methods to access rendertargets. Why doesn't Windows 8 leak at this method?

public class Game : SharpDX.Toolkit.Game {
    GraphicsDeviceManager gdm;
    RenderTarget2D target;
    public Game() {
        gdm = new GraphicsDeviceManager(this);
    }
    protected override void LoadContent() {
        target = RenderTarget2D.New(GraphicsDevice, 1000, 1000, PixelFormat.R8G8B8A8.UNorm);
        base.LoadContent();
    }
    private Color GetColor(int x, int y) {
        using (Image image = target.GetDataAsImage()) {
            PixelBuffer buffer = image.PixelBuffer[0];
            Color color = buffer.GetPixel<Color>(x, y);
            buffer = null;
            return color;
        }
    }
    protected override void Update(GameTime gameTime) {
        GetColor(5, 5);
        base.Update(gameTime);
    }
}

The memory consumption with this on Windows 8 is around 25 MB, Windows 7 uses up full memory. Any idea why Windows 8 handles memory/graphics in another way than Windows 7 (and how to fix it)?
For clarification if it was a memory leak: Memoryleak
The drop is caused by closing the application.

\$\endgroup\$
  • \$\begingroup\$ How are you measuring the memory consumption and how do you know this is a leak (for example, the task manager is not a good memory profiler)? \$\endgroup\$ – user1430 Oct 6 '13 at 16:02
  • \$\begingroup\$ I'm using the task manager and I know it's not a good memory profiler but one can see if the behaviour is leaking (memory graph increases linearly). I commented lines out to see which line is causing the massive memory consumption. It's just a subjective method of memory measurement. \$\endgroup\$ – AliveDevil Oct 6 '13 at 16:05
  • \$\begingroup\$ are you sure that it is a leak? a leak usually makes the memory increasing up until there is no more available. just because an app uses more memory on win7 or win8 it is not a leak, as long as the memory amount stays constant. \$\endgroup\$ – clamp Oct 6 '13 at 16:28
  • \$\begingroup\$ yes, it was using up all memory available until I fixed it with supplied answer below. \$\endgroup\$ – AliveDevil Oct 6 '13 at 16:31
  • \$\begingroup\$ sorry but i am still not convinced. if this would really be such a drastic memory leak than every users of the sharpdx toolkit must have experienced this also, right? did you try removing the GetColor function? \$\endgroup\$ – clamp Oct 6 '13 at 16:45
7
\$\begingroup\$

There are 2 issues in your synthetic sample:

  • The first issue is that you are calling target.GetDataAsImage which is unoptimized in scenarios where you need to do it each frame. Internally the GetDataAsImage is creating a CPU/GPU staging resource (same size as the render target) and allocating an Image on the CPU (again same size on the render target). A correct way to do it (see samples below) is to pre-allocate yourself the staging resource and the image just once, and then get the data from the render target to the image through this allocated staging resource.
  • The second issue is that you don't make any draw calls. The flush of the graphics device is usually automatically done if you are making many consecutive draw calls (to split the GPU command buffer) or at least one draw call in a frame before a swap chain present (then the flush will be done at present time). Usually, during a flush, Direct3D takes the time to cleanup things that it can cleanup (like allocated staging resources). That's why by adding an explicit flush (and because of no draw calls), you are able to let D3D freeing some resources. I don't have Windows 7, but I would believe that It would fix your issue.

Minor issue: don't perform any GPU interaction in the update method but only in Draw (in fixed time step, this method can be called several times per frame).

The correct way to implement the micro-synthetic test is to do it like this:

// TestLeak.cs
using System.Diagnostics;
using SharpDX;
using SharpDX.Toolkit;
using SharpDX.Toolkit.Graphics;

public class TestLeak : Game
{
    private GraphicsDeviceManager graphicsDeviceManager;
    RenderTarget2D target;
    private Texture targetStaging;
    private Image image;

    public TestLeak()
    {
        graphicsDeviceManager = new GraphicsDeviceManager(this);
    }

    protected override void LoadContent()
    {
        target = RenderTarget2D.New(GraphicsDevice, 1000, 1000, PixelFormat.R8G8B8A8.UNorm);

        // Pre-Allocate an image same size as the render target
        image = Image.New(target.Description);

        // Pre-Allocate a staging resource for the render target
        targetStaging = target.ToStaging();

        base.LoadContent();
    }

    private Color GetColor(int x, int y)
    {
        // Get the data from the RenderTarget using the allocated staging resource and the
        // allocated image
        target.GetData(targetStaging, new DataPointer(image.DataPointer, image.TotalSizeInBytes));

        var buffer = image.PixelBuffer[0];
        var color = buffer.GetPixel<Color>(x, y);
        return color;
    }

    protected override void Draw(GameTime gameTime)
    {
        // Just clear the target with red
        GraphicsDevice.Clear(target, Color.Red);

        // Check the color
        var color = GetColor(5, 5);
        Debug.Assert(color == Color.Red);

        base.Draw(gameTime);
    }
}
\$\endgroup\$
1
\$\begingroup\$

Oh wow -.-
Someone on private IRC reminded me to use GraphicsDevice.Flush().
Who knows the answer? Correct, it's "working". Well .. at least there is no memory leak. Although memory consumption is twice of Windows 8. (Windows 8 ~20 MB, Windows 7 starting at 80 MB falling to 45 MB)

For everyone who has this problem with SharpDX.Toolkit I suggest overriding Game.EndDraw.

protected override void EndDraw() {
    base.EndDraw();
    GraphicsDevice.Flush();
}

That will sort of fix the issue of memory leaks.

\$\endgroup\$

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.