17 Juni 2013

Inhalt der RenderTarget-Textur in ein Bitmap kopieren

datastreamIm Moment beschäftige ich mich damit, ein kleines Unittest ähnliches Tool für eine 3D-Engine zu schreiben. Funktionsweise ist relativ einfach: Es wird eine Textur angelegt, in die ganz normal gerendert werden kann. Nach Abschluss des Render-Vorgangs soll der Inhalt der Textur in den Hauptspeicher, z. B. als normale Bitmap geladen werden (ob jetzt WPF oder System.Drawing ist egal..). Dieses entstandene Bild kann man jetzt nutzen, um zu prüfen, ob diverse Render-Schritte korrekt funktioniert haben. So weit, so gut, dass soll dann gar nicht weiter Teil dieses Beitrags sein. Mit was ich mich hier beschäftigen will, ist der Weg, wie man den Inhalt einer Textur von der Grafikkarte in den Hauptspeicher bekommt.

Hört sich einfach an, oder? Es ist aber leider nicht nur ein „Texture.ToBitmap“ oder so etwas Ähnliches. Nein, Problem Nummer eins ist, dass das RenderTarget, welches ich kopieren möchte, folgendermaßen erstellt ist.

Die Aufmerksamkeit möchte ich hier auf die Zuweisungen „.Usage = D3D11.ResourceUsage.Default“ und „.CpuAccessFlags = D3D11.CpuAccessFlags.None“ legen. Laut Msdn sorgen diese Eigenschaften zwar dafür, dass die Gpu möglichst schnell auf diese Textur rendern kann, allerdings schränken sie auch den Zugriff der Cpu auf 0 Lesezugriff ein. Tja, so einfach kommt man dann wohl doch nicht ran.

Der Artikel auf Msdn sagt aber zum Glück, wie man mit der Cpu trotzdem den Inhalt auslesen kann, und zwar muss man sich dazu eine weitere Textur mit der Eigenschaft „.Usage = D3D11.ResourceUsage.Staging“ anlegen. Diese macht die Textur zum Gegenteil des RenderTargets. Ist die Eigenschaft so gesetzt, so hat die Gpu so gut wie keinen Zugriff mehr auf die Textur, die Cpu dagegen kann sie frei auslesen. Das einzige, was die Gpu noch machen kann, ist es, den Inhalt einer anderen Textur dort hinein zu kopieren. Damit schließt sich dann auch der Kreis für mein kleines Vorhaben hier. Man muss also eine Textur mit „.Usage = D3D11.ResourceUsage.Staging“, damit man den Inhalt des RenderTargets dort hinein kopieren kann um anschließend die Daten per Cpu auszulesen.

Das Anlegen einer Textur mit „.Usage = D3D11.ResourceUsage.Staging“ ist relativ einfach. Nachfolgender Code zeigt das Vorgehen kurz.

ABER: Bei solchen Texturen gibt es ein paar Einschränkungen. So kann z. B. kein Multisampling verwendet werden!

Der nächste Schritt ist nun das Kopieren an sich. Laut Msdn sind dafür Aufrufe von CopyResource oder CopySubresourceRegion zulässig. Das nächste Problem erwähnt dabei Microsoft auch beileufig: Über diesen weg kann man nicht den Inhalt eines RenderTargets mit MultiSampling auf die „Staging“ Textur kopieren. Dieser Weg direkt klappt nur, wenn man für das RenderTarget auch kein Multisampling verwendet. Naja gut, für mich jetzt nicht das große Problem, will man die Funktion aber nutzen, um von der 3D-Szene Screenshots zu machen, ist das Blöd. Einzige Lösung, die mir für dieses Problem einfallen würde, ist die Verwendung einer weiteren Textur, in welche die Texturdaten zunächst per ResolveSubresource kopiert werden können (Hier funktioniert der Weg von Multisampling-Textur auf Non-Multisampling-Textur). Aber gut, zurück zum Thema. Nachfolgend die Aufrufe, die ich verwendet, um den Inhalt des RenderTargets in die Textur zu bekommen, welche dann durch die Cpu ausgelesen werden kann.

Als nächstes folgt jetzt noch die Logik, mit der ich den Inhalt der Textur in den Hauptspeicher lade. In diesem Fall lade ich die Daten auf folgendem Weg in ein System.Drawing.Bitmap.

Der Weg ist relativ einfach. Zunächst weise ich den Treiber per MapSubresource an, den Inhalt der Textur in den Hauptspeicher zu laden. Danach lege ich eine Bitmap an, sperre diese, und kopiere dann den Inhalt der Textur aus dem Speicherbereich von DirectX in die des Bitmap. Fertig. Das NativeMethods.memcpy ist übrigens ein Plattform-Aufruf, welcher einfach nur einen Speicherblock im Hauptspeicher kopiert (die C- und C++-Programmierer kennen sich sofort aus..).

Quellen:


Schlagwörter:
Copyright 2019. All rights reserved.

Verfasst 17. Juni 2013 von Roland in category "Direct3D

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.