C# (Unity) Быстрое копирование между различными типами управляемых массивов

У меня есть byte[] цветов, которые мне нужно передать Texture2D объект через SetPixels(Color32[]), Я хочу знать, какой самый быстрый способ создать Color32[] от byte[] является. Так как каждый 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 копии копии, чтобы сделать это в C#? Похоже, такая трата... Я искал союзы в C#, чтобы обойти копии.

1 ответ

Решение

Вот что я узнал:

1) Маршалу нет необходимости в ч / б управляемой и неуправляемой памяти для преобразования байта [] в 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)

3) SetPixels метод в Unity может быть дорогим на младших системах. Я не копал глубже, но я подозреваю, что это потому, что текстуры D3D, созданные под капотом, используют D3D_USAGE_DEFAULT, что означает, что их обновление должно использовать UpdateSubresource, который влечет за собой копию драйвера и возможную остановку (не может обновить ресурс, используемый в настоящее время GPU).

Итак, идеальное решение - написать собственный плагин, который создает 2D-текстуру с D3D_USAGE_DYNAMIC, отображает-обновляет-отображает ее и передает эту ссылку обратно в Unity. Излишне, но я мог бы вернуться к этому позже.

Другие вопросы по тегам