在 Monogame C# 中从 Texture2D 创建位图是否有更有效的方法?
Is there a more efficient way of creating a bitmap from a Texture2D in Monogame C#?
我正在构建录像机类型的应用程序,GetData()
来自 Texture2D 的方法太慢了。它占用了 CPU 使用时间的大约 50%,这意味着游戏的 fps 将下降,在 30-40 之间,而不是 60。
这是当前代码:
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#?
出于下述原因,我认为没有任何更快的方法可以使用 MonoGame 的 Texture2D 实现来更快地检索或保存 png
。然而,话虽这么说,我相信你仍然有选择。
您可能需要考虑直接使用 OpenGL 访问帧缓冲区并手动保存它以避免 MonoGame 使用的高级抽象。这有其自身的缺点,尤其是在实施方面,但它可能是您最快的解决方案。
但请记住,无论实现方式如何,您都应考虑避免将帧缓冲区转储到 ram 中,这会带来与之相关的固有性能问题。
您可能想考虑 other methods 例如使用 Direct3d11 或以前弃用的 DirectX9,但我没有深入研究它,因为它超出了问题的范围。
the method GetData() from Texture2D is just too slow
如果您对细节感兴趣,可以在 github.
上找到他们对 Texture2D
的实现
总而言之,它们的实现速度很慢,因为除了在它们的低级别[=51=上的多层抽象外,还被迫在主线程上运行转储帧缓冲区]† OpenGL 调用。††
您的用例的路径是
public void GetData<T> (T[] data) where T : struct
纹理 2D.cs 264
GetData<T>(int level, int arraySlice, Rectangle? rect, T[] data, int startIndex, int elementCount)
ValidateParams<T>()
纹理2D.cs 393
- 最快的路径是大约 20 次布尔检查、4 次 Math.Max、2 次除法、2 次按位与和 ~10 次整数乘法。
PlatformGetData<T>(int level, int arraySlice, Rectangle rect, T[] data, int startIndex, int elementCount)
Texture2D.OpenGL.cs 225
- 传统的 OpenGL 帧缓冲区转储并复制到数组
† 这个是相对使用的。所有这些层都非常远离传统的低层调用。
†† 这是推测,基于审查来源和我自己的经验,我既不是 Monogame 或其存储库的创建者或贡献者
我对你的陈述有几个问题:
It takes about 50% of cpu usage time
什么具体占用了“cpu使用时间”的50%?是纹理转移吗? Png编码?直接内存复制的效率极低 Parallel.For
?效率更低的 GDI+ Bitmap
分配和处理每一帧?
我的猜测是全部,读取帧数据是其中最小的部分。当然这都是猜测,因为您没有显示任何分析数据,只是一些模糊的假设。但是更好地理解 C#、内存(你什么时候见过多线程 memcpy
?)和 GDI+(至少足以意识到什么时候不使用它,尤其是不喜欢那样)会帮助你更多比让帧传输更有效率。
就是说,您在 OpenGL 上使用的抽象层对您隐藏了最有效的解决方案:pixel buffer objects 用于循环格式的临时传输,根据需要不强制 pixel buffer objects =26=] 同步.
处理每个像素然后将结果编码为 png 是一个缓慢的过程。
Parallel.for
加速了初始转换,但是整个线程要等到png转换和I/O写入完成。
分配许多固定缓冲区并生成新线程将这些缓冲区写入磁盘会更快。
由于硬盘驱动器或 SSD 比 ram 慢很多,这些缓冲区将填满。您有两种选择来克服此限制:
- 每隔一帧或第三帧降低编码帧率。或者...
- 打开大型输出文件的文件句柄(使用适当的 AVI header),将原始位图数据写入文件。写入大文件比写入许多小文件快得多。
我正在构建录像机类型的应用程序,GetData()
来自 Texture2D 的方法太慢了。它占用了 CPU 使用时间的大约 50%,这意味着游戏的 fps 将下降,在 30-40 之间,而不是 60。
这是当前代码:
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#?
出于下述原因,我认为没有任何更快的方法可以使用 MonoGame 的 Texture2D 实现来更快地检索或保存 png
。然而,话虽这么说,我相信你仍然有选择。
您可能需要考虑直接使用 OpenGL 访问帧缓冲区并手动保存它以避免 MonoGame 使用的高级抽象。这有其自身的缺点,尤其是在实施方面,但它可能是您最快的解决方案。
但请记住,无论实现方式如何,您都应考虑避免将帧缓冲区转储到 ram 中,这会带来与之相关的固有性能问题。
您可能想考虑 other methods 例如使用 Direct3d11 或以前弃用的 DirectX9,但我没有深入研究它,因为它超出了问题的范围。
the method GetData() from Texture2D is just too slow
如果您对细节感兴趣,可以在 github.
上找到他们对Texture2D
的实现
总而言之,它们的实现速度很慢,因为除了在它们的低级别[=51=上的多层抽象外,还被迫在主线程上运行转储帧缓冲区]† OpenGL 调用。††
您的用例的路径是
public void GetData<T> (T[] data) where T : struct
纹理 2D.cs 264
GetData<T>(int level, int arraySlice, Rectangle? rect, T[] data, int startIndex, int elementCount)
ValidateParams<T>()
纹理2D.cs 393- 最快的路径是大约 20 次布尔检查、4 次 Math.Max、2 次除法、2 次按位与和 ~10 次整数乘法。
PlatformGetData<T>(int level, int arraySlice, Rectangle rect, T[] data, int startIndex, int elementCount)
Texture2D.OpenGL.cs 225- 传统的 OpenGL 帧缓冲区转储并复制到数组
† 这个是相对使用的。所有这些层都非常远离传统的低层调用。
†† 这是推测,基于审查来源和我自己的经验,我既不是 Monogame 或其存储库的创建者或贡献者
我对你的陈述有几个问题:
It takes about 50% of cpu usage time
什么具体占用了“cpu使用时间”的50%?是纹理转移吗? Png编码?直接内存复制的效率极低 Parallel.For
?效率更低的 GDI+ Bitmap
分配和处理每一帧?
我的猜测是全部,读取帧数据是其中最小的部分。当然这都是猜测,因为您没有显示任何分析数据,只是一些模糊的假设。但是更好地理解 C#、内存(你什么时候见过多线程 memcpy
?)和 GDI+(至少足以意识到什么时候不使用它,尤其是不喜欢那样)会帮助你更多比让帧传输更有效率。
就是说,您在 OpenGL 上使用的抽象层对您隐藏了最有效的解决方案:pixel buffer objects 用于循环格式的临时传输,根据需要不强制 pixel buffer objects =26=] 同步.
处理每个像素然后将结果编码为 png 是一个缓慢的过程。
Parallel.for
加速了初始转换,但是整个线程要等到png转换和I/O写入完成。
分配许多固定缓冲区并生成新线程将这些缓冲区写入磁盘会更快。
由于硬盘驱动器或 SSD 比 ram 慢很多,这些缓冲区将填满。您有两种选择来克服此限制:
- 每隔一帧或第三帧降低编码帧率。或者...
- 打开大型输出文件的文件句柄(使用适当的 AVI header),将原始位图数据写入文件。写入大文件比写入许多小文件快得多。