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. Излишне, но я мог бы вернуться к этому позже.