Possible Memory leak in SharpDX/SharpDX interface - Trivial Code Example

Hi all,

I’ve past experience of XNA, and we currently have internally our app running using Monogame + Xamarin on OSX, iOS, Android and PC. We are only having the memory issue on the PC.

I’ve been tracking a memory issue we have had just on the PC, and I can now replicate it in a few lines of code in a sample project.

Win10 + VS 2015 Enterprise + Monogame 3.4, or latest development build from the build server give me the same results.

My test case is now just this, use the VS template for a Windows Project, and pop the following code into LoadContent:

    protected override void LoadContent()
    {
        // Create a new SpriteBatch, which can be used to draw textures.
        spriteBatch = new SpriteBatch(GraphicsDevice);


        var ms =
            new MemoryStream(
                File.ReadAllBytes(
                    @"c:\temp\monogame\monogameWin10\monogameWin10\monogameWin10\bin\WindowsUAP\x86\Debug\AppX\tile.png"));

        for (int i = 0; i < 200000; i++)
        {
            ms.Seek(0, SeekOrigin.Begin);
            var tex1 = Texture2D.FromStream(graphics.GraphicsDevice, ms);
            tex1.Dispose();
        }

        // TODO: use this.Content to load your game content here
    }

The PNG is just a 260*260 pixel image.

We load maps from file - we have GB’s of data with customers, hence we do it using FromStream. It would appear its FromStream that isn’t freeing memory somewhere.

Start the above code up, memory quickly hits about 50mb, sits there for a minute or so, and then climbs very quickly into the GBs. If I run the new UAP template in the same way, the result is very similar.

If I run the same trivial code in the same way but using the Visual Studio OpenGL template, the code runs fine, with no memory issues.

Ways forward:

  1. Someone please tell me I’m a Muppet, and tell me what is wrong with the above code?
  2. I’m happy to try to track this, but I’m having an issue in compiling the code from GitHub - I’ve posted on a different thread about that.

Otherwise, I’ve worked out a way to create just a grid of textures at startup, and then use SetData:

var texturebytesNew = new byte[260 * 260 * 4];
var tex = new Texture2D(GraphicsDevice, 260, 260);
var bytes = new byte[260 * 260 * 4];

        for (int i = 0; i < 200000; i++)
        {
            ms.Seek(0, SeekOrigin.Begin);
            using (var tBitmap = new Bitmap(ms, false))
            {
                var data = tBitmap.LockBits(new System.Drawing.Rectangle(0, 0, tBitmap.Width, tBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
                Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
                tBitmap.UnlockBits(data);

                for (var oy = 0; oy < tBitmap.Height; oy++)
                    for (var ox = 0; ox < tBitmap.Width; ox++)
                    {
                        texturebytesNew[oy * (260 * 4) + ox * 4] = bytes[oy * tBitmap.Width * 4 + ox * 4 + 2];
                        texturebytesNew[oy * (260 * 4) + ox * 4 + 1] = bytes[oy * tBitmap.Width * 4 + ox * 4 + 1];
                        texturebytesNew[oy * (260 * 4) + ox * 4 + 2] = bytes[oy * tBitmap.Width * 4 + ox * 4 + 0];
                        texturebytesNew[oy * (260 * 4) + ox * 4 + 3] = bytes[oy * tBitmap.Width * 4 + ox * 4 + 3];
                    }
            }
            tex.SetData(texturebytesNew);
        }

It works, but it’s certainly slower. Again, memory usage based on SharpDX climbs but they stays fairly static. OpenGL just uses a lot less memory, and seems much more static.

I’d rather not move to OpenGL, as I would like to move on the new MS Universal Apps, and I believe they rely on DirectX. Our app is also displaying its output into a WPF window, and I’m sure there must be a solution for it from OpenGL, but it wasn’t an obvious alteration to our current code.

Many thanks for the great work here, and hoping someone can point me in the right direction/help.

Kind Regards,

Graham

Out of curiosity I took a quick look at the MonoGame source. My guess is that the BitmapDecoder (here) needs to be disposed.

I suggest you open a new issue on the GitHub page.

Hi,

Thanks for this. I think you are on the right track - looks like it needs disposing of in the calling function after use, as its an ‘out’ parameter.

However, I currently can’t get Monogame to build in VS, to prove this, and therefore make the fix.

I’ve popped my issue on another thread regarding that.
can’t build monogame

Graham

I’ve tracked this further.

I’ve got Monogame compiling now - using XS rather than VS - I’m not quite at home there, and certainly missing some of the diagnostic tools I’m used to.

However, it seems like the issue is in fact not FromStream.

I’ve now go an even more trivial set of code that just eats memory:

        for (int i = 0; i < 20000; i++)
        {
            var tex1 = new Texture2D(graphics.GraphicsDevice, 260, 260);
            tex1.Dispose();
        }

So the issue appears to be in just creating/destroying Texture2D’s.

I’m digging further into the code…

Graham

Decision made, this is now a well defined problem, so I’m going to outsource it via Guru.com, and get back to internal work. If I get a fix, I will make sure its posted back to the community.

If that approach fails, I guess I will be coming back to this in due course, but as it stands, we can get on with other work, whilst hopefully we can pay someone to get this resolved.

Just for anyone following this:

The main memory leak is in :
private static SharpDX.WIC.BitmapSource LoadBitmap(Stream stream, out SharpDX.WIC.BitmapDecoder decoder)

fconv.Initialize(
decoder.GetFrame(0),
SharpDX.WIC.PixelFormat.Format32bppPRGBA,
SharpDX.WIC.BitmapDitherType.None, null,
0.0, SharpDX.WIC.BitmapPaletteType.Custom);

decoder.GetFrame(0) is not releasing its resources.

I replaced the above with:
var tFrame = decoder.GetFrame(0);

        fconv.Initialize(
              tFrame,
              SharpDX.WIC.PixelFormat.Format32bppPRGBA,
              SharpDX.WIC.BitmapDitherType.None, null,
              0.0, SharpDX.WIC.BitmapPaletteType.Custom);

        tFrame.Dispose();
        tFrame = null; 

That seems to have done most of the trick. However, the test code is still leaking a little.
Basically, I think people have been using this code to screengrab, and hence not loading 1000’s of images, hence they haven’t noticed the leak.

In my travels, I also found a VERY useful little bit of code:
Set this
SharpDX.Configuration.EnableObjectTracking = true;
And on exit, to the output window, SharpDX will list any un-released objects.

My test code above is still leaking a little, but the above fix has resolved the main issue.

Graham