2016-04-05 6 views
3

Ich habe eine byte[] von Farben, die ich auf Texture2D Objekt über SetPixels(Color32[]) übertragen muss. Ich möchte wissen, was der schnellste Weg ist, um die Color32[] von byte[] zu erstellen. Da jeder Color32 4 Bytes (1 Byte pro Kanal) ist, würde es ein Viertel der Anzahl der Elemente geben.C# (Unity) Schnellkopie zwischen verschiedenen verwalteten Array-Typen

Array.CopyTo lässt keine anderen Objekt-Array-Typen zu.

Von dem, was ich gelesen habe, scheint es, als ob verwalteter Code die byte[] nicht als Color32[] uminterpretieren und die Kopie insgesamt überspringen kann.

Die Rückseite dieses (Color32[] ->byte[]) has a SO solution über Marshalling, aber es scheint eine Tonne kopieren beteiligt.

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; 
} 

Für meinen Fall, ich verwende:

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); 

aber ich sehe nicht die Textur Aktualisierung nach myTexture.SetPixels(colors) tun. Ich versuche immer noch herauszufinden warum.

Unabhängig davon, muss ich 2 Mem-Kopien tun, um dies in C# zu erreichen? Scheint so eine Verschwendung. Ich sah Gewerkschaften in C# um die Kopien zu umgehen.

+0

Mögliches Duplikat von [Schnelle Kopie von Color32 \ [\] Array zu Byte \ [\] Array] (http: // stackoverflow.com/questions/21512259/fast-copy-of-color32-array-to-byte-array) – NineBerry

+0

Ich habe explizit auf diese Frage in meiner Beschreibung oben verwiesen. Der Link gibt eine mögliche Lösung (die in meinem Fall nicht funktioniert) und wichtiger ist, es ist eine Frage, ob 2 Kopien vermieden werden können. – Raja

+1

Warum können Sie LoadRawTextureData() des Texture2D-Objekts nicht verwenden? Die Dokumentation besagt, dass nach dem Ändern der Textur Apply() aufgerufen wird, damit die Änderungen tatsächlich auf die Grafikkarte hochgeladen werden. – NineBerry

Antwort

3

Hier ist, was ich gelernt habe:

1) Es gibt keine Notwendigkeit, Marshal b/w verwalteten und nicht verwalteten Speicher byte [] zu Color32 [] und umgekehrt zu konvertieren. Verwenden Sie stattdessen eine Vereinigung in C# verwenden:

[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]; 

Nun, wenn Sie die byteArray mit Daten von einer Kamera aktualisieren, können Sie es auf die Anwendung Texture2DmyTexture.SetPixels(data.colors) verwendet wird;

Kein Kopieren beteiligt.

2) Keine Notwendigkeit für die oben genannte Verbindungslösung. Haha. Wie NineBerry wies darauf hin, können Sie einfach ein rohes Bytepuffer über myTexture.LoadRawTextureData(bytes);

auf eine Textur laden Das in meinem Fall ist perfekt, da meine Kamera ByteBuffer als BGRA (aufsteigend) kommt und ich würde müssen swizzle die Kanäle vor dem Hochladen (teuer) ODER lösen Sie es in meinem Shader, wenn ich ging mit (1)

3) Die SetPixels Methode in Unity kann auf Low-End-Systeme teuer sein. Ich habe nicht tiefer gegraben, aber ich vermute, es ist, weil die unter der Haube erstellten D3D-Texturen D3D_USAGE_DEFAULT verwenden, was bedeutet, dass sie UpdateSubresource verwenden müssen, was eine Treiberkopie und einen möglichen Stall mit sich bringt (kann keine aktuell von der GPU).

So ist die ideale Lösung, ein natives Plugin zu schreiben, das eine 2D-Textur mit D3D_USAGE_DYNAMIC erstellt, maps-updates-unmap es und diese Referenz zurück an Unity übergibt. Overkill für jetzt, aber ich könnte das später noch einmal lesen.

+0

Da' byteArray' und 'colors' tatsächlich auf den gleichen Speicher verweisen, müssen Sie nur einen davon initialisieren.So können Sie eines der Arrays entfernen Konstruktoren – NineBerry

+0

Ich dachte auch, aber das Array-Objekt würde nicht initialisiert werden (dh, wie viele Color32 gibt es? v/s, wie viele Bytes sind da). Ich lief Laufzeitfehler als Ergebnis. Ich komme aus einem C++ Hintergrund, also ist das Zeug noch nicht klar :) – Raja