我有一个 byte[] 颜色,我需要通过 Texture2D 将其传输到 SetPixels(Color32[]) 对象。
我想知道从 Color32[] 创建 byte[] 的最快方法是什么。
由于每个 Color32 是 4 个字节(每个 channel 1 个字节),因此元素数量将是四分之一。
Array.CopyTo 不允许不同的对象数组类型。

从我读到的内容来看,托管代码似乎无法将 byte[] 重新解释为 Color32[] 并完全跳过副本。

与此相反 ( Color32[] -> byte[] ) has a SO solution 通过编码,但似乎涉及大量复制。

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) 无需对黑白托管和非托管内存进行编码(marshal)以将 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,您可以使用 Texture2D 将其应用到 myTexture.SetPixels(data.colors)

不涉及复制。

2)不需要上面的联合解决方案。哈哈。
正如 NineBerry 指出的那样,您可以通过以下方式将原始字节缓冲区上传到纹理myTexture.LoadRawTextureData(bytes);
这对我来说是完美的,因为我的相机字节缓冲区是 BGRA(从低到高),我需要在上传之前调整 channel (昂贵)或者在我的着色器中解决它(1)

3) Unity 中的 SetPixels 方法在低端系统上可能很昂贵。我没有深入挖掘,但我怀疑这是因为在引擎盖下创建的 D3D 纹理使用 D3D_USAGE_DEFAULT ,这意味着更新它们必须使用 UpdateSubresource ,这会导致驱动程序副本和可能的停顿(无法更新当前正在使用的资源) GPU)。

因此,理想的解决方案是编写一个原生插件,使用 D3D_USAGE_DYNAMIC 创建 2D 纹理,映射-更新-取消映射,然后将该引用传递回 Unity。现在矫枉过正,但我​​可能会在以后重新审视这个。

关于C# (Unity) 不同托管数组类型之间的快速复制,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36438710/

10-15 17:51