C#(Unity)不同托管数组类型之间的快速复制
本文关键字:之间 类型 复制 数组 Unity | 更新日期: 2024-06-14 04:21:54
我有一个颜色的byte[]
,需要通过SetPixels(Color32[])
传输到Texture2D
对象。我想知道从byte[]
创建Color32[]
的最快方法是什么。由于每个Color32
是4个字节(每个通道1个字节),因此将有四分之一的元素数量。
Array.CopyTo
不允许不同的对象数组类型。
从我所读到的内容来看,托管代码似乎无法将byte[]
重新解释为Color32[]
并完全跳过副本。
与此相反(Color32[]
->byte[]
)有一个通过编组的SO解决方案,但似乎需要大量的复制。
private static byte[] Color32ArrayToByteArray(Color32[] colors)
{
int length = 4 * colors.Length;
byte[] bytes = new byte[length];
IntPtr ptr = Marshal.AllocHGlobal(length); // unmanaged memory alloc
Marshal.StructureToPtr(colors, ptr, true); // memcpy 1
Marshal.Copy(ptr, bytes, 0, length); // memcpy 2
Marshal.FreeHGlobal(ptr);
return bytes;
}
对于我的情况,我使用:
int length = bytes.Length;
IntPtr ptr = Marshal.AllocHGlobal(length); // unmanaged mem alloc
Marshal.Copy(bytes, 0, ptr, length); // memcpy 1
Marshal.PtrToStructure(ptr, colors); // memcpy 2
Marshal.FreeHGlobal(ptr);
但是,在做了myTexture.SetPixels(colors)
之后,我没有看到纹理更新。还在想原因。
无论如何,我是否必须执行2个mem副本才能在C#中实现这一点?看起来太浪费了。。我一直在寻找C#中的工会来绕过副本。
以下是我学到的:
1) 不需要封送b/w托管和非托管内存来将byte[]转换为Color32[],反之亦然。相反,在C#中使用并集:
[StructLayout(LayoutKind.Explicit)]
public struct Color32Bytes
{
[FieldOffset(0)]
public byte[] byteArray;
[FieldOffset(0)]
public Color32[] colors;
}
Color32Bytes data = new Color32Bytes();
data.byteArray = new byte[width * height * bytesPerPixel];
data.colors = new Color32[width * height];
现在,如果使用相机的数据更新byteArray,则可以使用myTexture.SetPixels(data.colors)
将其应用于Texture2D
;
不涉及复制。
2) 不需要上面的联合解决方案。哈哈。正如NineBerry所指出的,你可以通过myTexture.LoadRawTextureData(bytes);
在我的情况下,这是完美的,因为我的相机字节缓冲区是BGRA(从低到高),我需要在上传之前冲洗通道(昂贵),或者如果我使用(1),在我的着色器中解决它
3) Unity中的SetPixels
方法在低端系统上可能很昂贵。我还没有深入研究,但我怀疑这是因为在引擎盖下创建的D3D纹理使用D3D_USAGE_DEFAULT,这意味着更新它们必须使用UpdateSubresource,这会导致驱动程序副本和可能的停滞(无法更新GPU当前使用的资源)。
因此,理想的解决方案是编写一个本地插件,使用D3D_USAGE_DYNAMIC创建2D纹理,映射更新取消映射,并将该引用传递回Unity。暂时过度杀戮,但我稍后可能会重新审视这一点。