使用Texture2D.SaveAsPng连续保存许多屏幕截图会导致内存不足异常

时间:2013-01-14 01:00:10

标签: c# xna out-of-memory

当我在XNA游戏中制作屏幕截图时,每个texture.SaveAsPng消耗一些内存并且它似乎不会返回游戏。所以最终我的内存耗尽了。我尝试将纹理数据保存到FileStreamMemoryStream,希望我可以将其保存为Bitmap,但结果是相同的。有没有办法强行释放这个内存或一些解决方法,让我获取图像数据并以其他方式保存,而不会遇到内存不足异常?

sw = GraphicsDevice.Viewport.Width;
sh = GraphicsDevice.Viewport.Height;

int[] backBuffer = new int[sw * sh];
GraphicsDevice.GetBackBufferData(backBuffer);
using(Texture2D texture = new Texture2D(GraphicsDevice, sw, sh, false,
  GraphicsDevice.PresentationParameters.BackBufferFormat))
{
    texture.SetData(backBuffer);
    using(var fs = new FileStream("screenshot.png", FileMode.Create))        
        texture.SaveAsPng(fs, sw, sh);  // ← this line causes memory leak      
}

1 个答案:

答案 0 :(得分:2)

您可以直接从纹理字节创建位图,并绕过内部方法以检查SaveAsPng是否泄漏或其他内容。

尝试这种扩展方法(不幸的是我无法测试(没有xna在工作)但是应该工作。)

public static class TextureExtensions
{
    public static void TextureToPng(this Texture2D texture, int width, int height, ImageFormat imageFormat, string filename)
    {
        using (Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
        {
            byte blue;
            IntPtr safePtr;
            BitmapData bitmapData;
            Rectangle rect = new Rectangle(0, 0, width, height);
            byte[] textureData = new byte[4 * width * height];

            texture.GetData<byte>(textureData);
            for (int i = 0; i < textureData.Length; i += 4)
            {
                blue = textureData[i];
                textureData[i] = textureData[i + 2];
                textureData[i + 2] = blue;
            }
            bitmapData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
            safePtr = bitmapData.Scan0;
            Marshal.Copy(textureData, 0, safePtr, textureData.Length);
            bitmap.UnlockBits(bitmapData);
            bitmap.Save(filename, imageFormat);
        }
    }
}

它有点粗糙,但你可以清理它(如果它甚至可以工作)。

最后但并非最不重要(如果所有其他尝试都失败了)您可以自己致电GarbageCollection,但不推荐这是非常糟糕的做法。

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

上述代码应该是最后的代码。

祝你好运。