I'm building a video recorder type of app, and the method GetData()
from Texture2D is just too slow. It takes about 50% of CPU usage time, which means that the game is going to have fps drops, between 30-40, instead of 60.
Here´s the current code:
byte[] stream = new byte[textW * textH * 4];
try
{
texture.GetData(stream); // Very slow !!!
} catch (Exception e)
{
Console.WriteLine(e.Message);
}
var bmp1 = new Bitmap(textW, textH);
BitmapData data = bmp1.LockBits(new Rectangle(0, 0, textW, textH), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
IntPtr ptr = data.Scan0;
Marshal.Copy(stream, 0, ptr, textW * textH * 4);
unsafe
{
byte* ptrFirstPixel = (byte*)data.Scan0;
int bytesPerPixel = Bitmap.GetPixelFormatSize(data.PixelFormat) / 8;
int heightInPixxel = data.Height;
int widthInBytes = data.Width * bytesPerPixel;
Parallel.For(0, heightInPixxel, y =>
{
byte* currLine = ptrFirstPixel + (y * data.Stride);
for (int x = 0; x < widthInBytes; x += bytesPerPixel)
{
int b = currLine[x];
int r = currLine[x + 2];
currLine[x + 2] = (byte)b;
currLine[x] = (byte)r;
}
});
}
bmp1.UnlockBits(data);
bmp1.Save(outputPath, ImageFormat.Png);
bmp1.Dispose();
texture.Dispose();
Is there a more efficient way of creating a bitmap from a Texture2D in Monogame C#?
For reasons stated below, I do not believe there is any faster way to retrieve or save a png
any faster using MonoGame's implementation of Texture2D. However, that being said, I believe you still have options available to you.
You may want to consider accessing the framebuffer directly using OpenGL and saving it manually to avoid the high level of abstraction that MonoGame uses. This has it's own drawbacks especially regarding implementation, but it may be the fastest solution for you.
But keep in mind regardless of implementation you should consider avoiding dumping the frame buffer into ram every frame, that has inherent performance issues associated with it.
You may want to consider other methods such as using Direct3d11 instead or the previously deprecated DirectX9, but I didn't do look to far into it since its outside the scope of the question.
the method GetData() from Texture2D is just too slow
If you're interested in the nitty gritty you can find their implementation of Texture2D
over on github.
In summary their implementation is slow because forced to run on the mainthread to to dump the frame buffer, in addition to the multiple layers of abstraction over their low level† OpenGL calls.††
The path for your use case would be
public void GetData<T> (T[] data) where T : struct
Texture 2D.cs 264
GetData<T>(int level, int arraySlice, Rectangle? rect, T[] data, int startIndex, int elementCount)
ValidateParams<T>()
Texture2D.cs 393
PlatformGetData<T>(int level, int arraySlice, Rectangle rect, T[] data, int startIndex, int elementCount)
Texture2D.OpenGL.cs 225
† This is used relatively. All of these layers are very remote from traditional low level calls.
†† This is speculation, based on reviewing the source and my own experience, I am neither a creator, or contributor to Monogame or their repositories