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#中的工会来绕过副本。

C#(Unity)不同托管数组类型之间的快速复制

以下是我学到的:

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。暂时过度杀戮,但我稍后可能会重新审视这一点。