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

时间:2016-04-05 23:15:33

标签: c# arrays unity3d marshalling arraycopy

我有 <job id="compositeJdbcReaderJob" xmlns="http://www.springframework.org/schema/batch"> <step id="compositeJdbcReaderStep" next="compositeJdbcReaderStep2"> <tasklet> <chunk reader="itemReader1" writer="itemWriter" commit-interval="5" /> </tasklet> </step> <step id="compositeJdbcReaderStep2"> <tasklet> <chunk reader="itemReader2" writer="itemWriter2" commit-interval="5" /> </tasklet> </step> </job> 种颜色需要通过byte[]转移到Texture2D对象。 我想知道从SetPixels(Color32[])创建Color32[]的最快方法是什么。 由于每个byte[]是4个字节(每个通道1个字节),因此将有四分之一的元素。

Color32不允许使用不同的对象数组类型。

根据我的阅读,似乎托管代码无法将Array.CopyTo重新解释为byte[]并完全跳过该副本。

与此相反(Color32[] - &gt; Color32[]has a SO solution通过编组,但似乎涉及大量复制。

byte[]

就我而言,我正在使用:

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); 后,我看不到纹理更新。还在试图找出原因。

无论如何,我必须做2份内存副本才能在C#中实现这一目标吗?好像这样浪费.. 我正在寻找C#中的工会来绕过这些副本。

1 个答案:

答案 0 :(得分:2)

这是我学到的东西:

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,您可以使用Texture2D将其应用于myTexture.SetPixels(data.colors);

不涉及复制。

2)不需要上面的联合解决方案。哈哈。 正如NineBerry指出的那样,您只需将原始字节缓冲区上传到纹理通道即可 myTexture.LoadRawTextureData(bytes);

这在我的情况下是完美的,因为我的相机字节缓冲来自BGRA(从低到高)并且我需要在上传之前调整频道(昂贵)或者如果我去的那样在我的着色器中解决它(1)< / p>

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

因此,理想的解决方案是编写一个本机插件,使用D3D_USAGE_DYNAMIC创建2D纹理,map-updates-unmaps,并将该引用传递回Unity。现在有点矫枉过正,但我​​稍后可能会再次访问。