Portieren von Direct3D 11 zu Direct3D 12

In diesem Abschnitt finden Sie Anleitungen zum Portieren von einer benutzerdefinierten Direct3D 11-Grafik-Engine zu Direct3D 12.

Geräteerstellung

Sowohl Direct3D 11 als auch Direct3D 12 weisen ein ähnliches Muster für die Geräteerstellung auf. Vorhandene Direct3D 12-Treiber sind alle D3D_FEATURE_LEVEL_11_0 oder besser, sodass Sie die älteren Featureebenen und die zugehörigen Einschränkungen ignorieren können.

Beachten Sie außerdem, dass Sie mit Direct3D 12 Geräteinformationen explizit über DXGI-Schnittstellen auflisten sollten. In Direct3D 11 können Sie vom Direct3D-Gerät auf das DXGI-Gerät zurückverketten , was für Direct3D 12 nicht unterstützt wird.

Das Erstellen eines WARP-Softwaregeräts in Direct3D 12 erfolgt durch Bereitstellen eines expliziten Adapters, der von IDXGIFactory4::EnumWarpAdapter abgerufen wurde. Das WARP-Gerät für Direct3D 12 ist nur auf Systemen verfügbar, auf denen das optionale Feature Grafiktools aktiviert ist.

Hinweis

Es gibt keine Entsprechung zu D3D11CreateDeviceAndSwapChain. Auch bei Direct3D 11 wird von der Verwendung dieser Funktion abgeraten, da es oft besser ist, das Gerät und die Swapchain in verschiedenen Schritten zu erstellen.

Zugesagte Ressourcen

Objekte, die mit den folgenden Schnittstellen in Direct3D 11 erstellt wurden, werden in Direct3D 12 als "commitierte Ressourcen" übersetzt. Eine zugesagte Ressource ist eine Ressource, der sowohl virtueller Adressraum als auch physische Seiten zugeordnet sind. Dies ist ein Konzept des Microsoft Windows Device Driver 2 (WDD2)-Speichermodells, auf dem Direct3D 12 basiert.

Direct3D 11-Ressourcen:

In Direct3D 12 werden diese alle durch ID3D12Resource und ID3D12Device::CreateCommittedResource dargestellt.

Reservierte Ressourcen

Reservierte Ressourcen sind Ressourcen, bei denen nur virtueller Adressraum zugewiesen wurde. Physischer Arbeitsspeicher wird erst zugewiesen, wenn ID3D12Device::CreateHeap aufgerufen wird. Dies ist im Wesentlichen das gleiche Konzept wie gekachelte Ressourcen in Direct3D 11.

Die Flags (D3D11_RESOURCE_MISC_FLAG), die in Direct3D 11 zum Einrichten gekachelter Ressourcen verwendet werden, und ordnen sie dann dem physischen Arbeitsspeicher zu.

  • D3D11_RESOURCE_MISC_TILED
  • D3D11_RESOURCE_MISC_TILE_POOL

Hochladen von Daten

In Direct3D 11 gibt es die Darstellung eines einzelnen Zeitleiste (Aufrufe nach einer Sequenz, z. B. daten, die mit D3D11_SUBRESOURCE_DATA initialisiert werden, dann wird ein Aufruf von ID3D11DeviceContext::UpdateSubresource und dann ein Aufruf von ID3D11DeviceContext::Map ausgeführt. Die Anzahl der erstellten Kopien der Daten ist für einen Direct3D 11-Entwickler nicht offensichtlich.

In Direct3D 12 gibt es zwei Zeitachsen: die GPU-Zeitleiste (eingerichtet durch Aufrufe von CopyTextureRegion und CopyBufferRegion aus zugeordnetem Arbeitsspeicher) und die CPU-Zeitleiste (bestimmt durch Aufrufe von Map). Hilfsfunktionen werden (in der Datei d3dx12.h) als Updatesubresources bezeichnet, die eine freigegebene Zeitleiste verwenden. Es gibt mehrere Varianten dieser Hilfsfunktion, eine, die ID3D12Device::GetCopyableFootprints verwendet, eine andere, die einen Heap-Zuweisungsmechanismus verwendet, und eine andere, die einen Stapelzuweisungsmechanismus verwendet. Diese Hilfsfunktionen kopieren Ressourcen sowohl auf die GPU als auch auf die CPU über einen zwischengeschalteten Stagingbereich des Arbeitsspeichers.

In der Regel verfügen GPU und CPU jeweils über eine eigene Kopie einer Ressource, die an ihre eigene Zeitleiste gebunden ist. Der Shared Zeitleiste-Ansatz verwaltet auf ähnliche Weise zwei Kopien.

Shader und Shaderobjekte

In Direct3D 11 werden viele Shader- und Zustandsobjekte erstellt und der Zustand dieser Objekte mithilfe der ID3D11Device-Erstellungsmethoden und der ID3D11DeviceContext-Setmethoden festgelegt. In der Regel werden diese Methoden mit einer großen Anzahl von Aufrufen ausgeführt, die dann zum Zeitpunkt des Zeichnens vom Treiber kombiniert werden, um den richtigen Pipelinezustand festzulegen.

In Direct3D 12 wurde diese Einstellung des Pipelinezustands in einem einzelnen Objekt zusammengefasst (CreateComputePipelineState für eine Compute-Engine und CreateGraphicsPipelineState für eine Grafik-Engine), das dann vor dem Draw-Aufruf mit einem Aufruf von SetPipelineState an eine Befehlsliste angefügt wird.

Diese Aufrufe ersetzen alle einzelnen Aufrufe zum Festlegen von Shadern, Eingabelayout, Mischzustand, Rasterisierungszustand, Tiefenschablonenzustand usw. in Direct3D 11

  • Geräte 11-Methoden: CreateInputLayout, CreateXShader, CreateDepthStencilStateund CreateRasterizerState.
  • Gerätekontext 11-Methoden: IASetInputLayout, xxSetShader, OMSetBlendState, OMSetDepthStencilStateund RSSetState.

Während Direct3D 12 ältere kompilierte Shaderblobs unterstützen kann, sollten Shader entweder mithilfe des Shadermodells 5.1 mit den FXC/D3DCompile-APIs oder mithilfe des Shadermodells 6 mit dem DXIL DXC-Compiler erstellt werden. Sie sollten die Unterstützung von Shader Model 6 mit CheckFeatureSupport und D3D12_FEATURE_SHADER_MODEL überprüfen.

Übermitteln von Arbeiten an die GPU

in Direct3D 11 gibt es wenig Kontrolle darüber, wie die Arbeit tatsächlich übermittelt wird, sie wird größtenteils vom Treiber verarbeitet, obwohl einige Steuerelemente über die Aufrufe ID3D11DeviceContext::Flush und IDXGISwapChain1::P resent1 aktiviert werden.

In Direct3D 12 wird die Arbeitsübermittlung sehr explizit und von der App gesteuert. Das primäre Konstrukt für die Übermittlung von Arbeiten ist die ID3D12GraphicsCommandList, die zum Aufzeichnen aller Apps-Befehle verwendet wird (und im Konzept dem verzögerten ID3D11-Kontext ziemlich ähnlich ist). Der Sicherungsspeicher für eine Befehlsliste wird vom ID3D12CommandAllocator bereitgestellt, der es der App ermöglicht, die Speicherauslastung der Befehlsliste zu verwalten, indem sie tatsächlich den Speicher verfügbar macht, den der Direct3D 12-Treiber zum Speichern der Befehlsliste verwendet.

Schließlich ist id3D12CommandQueue eine first-in-Warteschlange, in der die richtige Reihenfolge der Befehlslisten für die Übermittlung an die GPU gespeichert wird. Erst wenn eine Befehlsliste die Ausführung auf der GPU abgeschlossen hat, wird die nächste Befehlsliste aus der Warteschlange vom Treiber übermittelt.

In Direct3D 11 gibt es kein explizites Konzept einer Befehlswarteschlange. Im allgemeinen Setup für Direct3D 12 kann die derzeit geöffnete D3D12_COMMAND_LIST_TYPE_DIRECT Befehlsliste für den aktuellen Frame als analog zum direkten Direct3D 11-Kontext betrachtet werden. Dies bietet viele der gleichen Funktionen.

D3D11DeviceContext ID3D12GraphicsBefehlsliste
ClearDepthStencilView ClearDepthStencilView
ClearRenderTargetView ClearRenderTargetView
ClearUnorderedAccess* ClearUnorderedAccess*
Zeichnen, ZeichnenInstanced DrawInstanced
DrawIndexed, DrawIndexedInstanced DrawIndexedInstanced
Dispatch Dispatch
IASetInputLayout, xxSetShader usw. SetPipelineState
OMSetBlendState OMSetBlendFactor
OMSetDepthStencilState OMSetStencilRef
OMSetRenderTargets OMSetRenderTargets
RSSetViewports RSSetViewports
RSSetScissorRects RSSetScissorRects
IASetPrimitiveTopology IASetPrimitiveTopology
IASetVertexBuffers IASetVertexBuffers
IASetIndexBuffer IASetIndexBuffer
ResolveSubresource ResolveSubresource
CopySubresourceRegion CopyBufferRegion
UpdateSubresource CopyTextureRegion
CopyResource CopyResource

Hinweis

Eine mit D3D12_COMMAND_LIST_TYPE_BUNDLE erstellte Befehlsliste ist mit einem verzögerten Kontext gleichgestellt. Direct3D 12 unterstützt auch die Abiilty, um auf einige Features eines unmittelbaren Kontexts gleichzeitig mit dem Rendern über D3D12_COMMAND_LIST_TYPE_COPY und D3D12_COMMAND_LIST_TYPE_COMPUTE Befehlslistentypen zuzugreifen.

CPU/GPU-Synchronisierung

In Direct3D 11 war die CPU/GPU-Synchronisierung weitgehend automatisch, und die App musste die status des physischen Arbeitsspeichers nicht beibehalten.

In Direct3D 12 muss die App die beiden Zeitachsen (CPU und GPU) explizit verwalten. Dies erfordert, dass von der App Informationen darüber verwaltet werden müssen, welche Ressourcen von der GPU benötigt werden und wie lange. Dies bedeutet auch, dass die App dafür verantwortlich ist, dass sich der Inhalt der Ressourcen (z. B. committe Ressourcen, Heaps, Befehlszuteilungen) erst ändert, wenn die GPU sie verwendet hat.

Das Standard-Objekt zum Synchronisieren der Zeitachsen ist das ID3D12Fence-Objekt. Der Betrieb von Zäunen ist ziemlich einfach, sie ermöglichen es der GPU, zu signalisieren, wenn sie eine Aufgabe abgeschlossen hat. GPU und CPU können beide signalisieren und auf Zäunen warten.

In der Regel besteht der Ansatz darin, dass beim Übermitteln einer Befehlsliste zur Ausführung ein Zaunsignal von der GPU nach Abschluss (wenn sie das Lesen der Daten abgeschlossen hat) übertragen wird, sodass die CPU die Ressourcen wiederverwenden oder zerstören kann.

In Direct3D 11 D3D11_MAP_WRITE_DISCARD das ID3D11DeviceContext::Map-Flag im Wesentlichen jede Ressource als endlose Vorrat an Arbeitsspeicher behandelt, in den die App schreiben kann (ein Prozess, der als "Umbenennen" bezeichnet wird). In Direct3D 12 ist der Prozess erneut explizit: Es muss zusätzlicher Arbeitsspeicher zugewiesen werden, und Zäune sollten zum Synchronisieren der Vorgänge verwendet werden. Ringpuffer (bestehend aus großen Puffern) können hierfür eine gute Technik sein, siehe Ringpufferszenario unter Zaunbasierte Ressourcenverwaltung.

Verwenden eines Ringpuffers

Ressourcenbindung

Ansichten in Direct3D 11 (Shaderressourcenansichten, Renderzielansichten usw.) wurden in Direct3D 12 weitgehend durch das Konzept eines Deskriptors ersetzt. Die Erstellungsmethoden sind weiterhin in Direct3D 12 vorhanden (z. B . CreateShaderResourceView und CreateRenderTargetView), die aufgerufen werden, nachdem der Deskriptorheap erstellt wurde, um die Daten in den Heap zu schreiben. Die Bindung in Direct3D 12 wird jetzt von Deskriptorhandles verarbeitet, die in einer Stammsignatur beschrieben sind und mit der SetGraphicsRootDescriptorTable- oder SetComputeRootDescriptorTable-Methode übermittelt werden.

Stammsignaturen detailieren Zuordnungen zwischen der Stammsignaturslotnummer und den Deskriptortabellen, in denen die Deskriptortabelle Verweise auf Ressourcen enthalten kann, die für Vertex-Shader, Pixel-Shader und die anderen Shader verfügbar sind, z. B. Konstantenpuffer, Shaderressourcensichten und Sampler. Diese Flexibilität trennt den HLSL-Registerbereich vom API-Bindungsraum in Direct3D 12, im Gegensatz zu Direct3D 11, bei dem es eine 1:1-Zuordnung zwischen diesen gibt.

Eine der Auswirkungen dieses Systems ist, dass die App für die Umbenennung von Deskriptortabellen verantwortlich ist, sodass Entwickler die Leistungskosten der Änderung eines einzelnen Deskriptors pro Zeichnungsaufruf nachvollziehen können.

Ein neues Feature von Direct3D 12 ist, dass eine App steuern kann, welche Deskriptoren für welche Shaderphasen freigegeben werden. In Direct3D 11 werden Ressourcen wie UAVs von allen Shaderstufen gemeinsam genutzt. Wenn Deskriptoren für bestimmte Shaderphasen deaktiviert werden können, können die von deskriptoren verwendeten Register für Deskriptoren verwendet werden, die deaktiviert wurden, von Deskriptoren verwendet werden, die für eine bestimmte Shaderphase aktiviert sind.

Die folgende Tabelle zeigt eine Beispielstammsignatur.

Stammparameterslot Deskriptortabelleneintrag
0 VS Descriptor Range b0-b13
1 VS-Deskriptorbereich t0-t127
2 VS Descriptor Range s0-s16
3 PS Descriptor Range b0-b13
...
14 DS-Deskriptorbereich s0-16
15 Shared Descriptor Range u0-u63

 

Ressourcenzustand

In Direct3D 11 wird der Ressourcenstatus nicht von der App, sondern vom Treiber verwaltet.

In Direct3D 12 wird die Verwaltung des Ressourcenzustands in die Verantwortung der App übernommen, um die vollständige Parallelität bei der Aufzeichnung von Befehlslisten zu ermöglichen: Die App muss die Aufzeichnungszeitachsen für Befehlslisten (die parallel erfolgen können) und die Ausführungszeitachsen verarbeiten, die sequenziell sein müssen.

Ein Ressourcenzustandsübergang wird von der ResourceBarrier-Methode verarbeitet. In erster Linie muss die App den Treiber informieren, wenn sich die Ressourcennutzung ändert. Wenn beispielsweise eine Ressource als Renderziel verwendet wird und dann als Eingabe für einen Vertex-Shader beim nächsten Zeichnungsaufruf verwendet werden soll, erfordert dies möglicherweise eine kurze Verzögerung im GPU-Vorgang, um den Renderzielvorgang abzuschließen, bevor der Vertex-Shader verarbeitet wird.

Dieses System ermöglicht die feinkörnige Synchronisierung (die GPU stagniert) der Grafikpipeline sowie Cacheleerungen und möglicherweise einige Änderungen des Speicherlayouts (z. B. rendern der Zielansicht zur Tiefenschablonenansicht).

Dies wird als Übergangsbarriere bezeichnet. Es gibt andere Arten von Barrieren: In Direct3D 11 hat der ID3D11DeviceContext2::TiledResourceBarrier denselben physischen Arbeitsspeicher aktiviert, der von zwei verschiedenen gekachelten Ressourcen verwendet werden kann. In Direct3D 12 wird dies als "Aliasingbarriere" bezeichnet. Aliasbarrieren können sowohl für gekachelte als auch für platzierte Ressourcen in Direct3D 12 verwendet werden. Zusätzlich gibt es die UAV-Barriere. In Direct3D 11 müssen alle UAV-Dispatch- und Draw-Vorgänge serialisiert werden, auch wenn diese Vorgänge pipelineiert oder parallel ausgeführt werden können. Für Direct3D 12 wird diese Einschränkung durch das Hinzufügen einer UAV-Barriere entfernt. Eine UAV-Barriere stellt sicher, dass UAV-Vorgänge sequenziell sind. Wenn also ein zweiter Vorgang erfordert, dass der erste abgeschlossen ist, wird der zweite durch das Hinzufügen der Barriere zum Warten gezwungen. Der Standardvorgang für UAVs besteht einfach darin, dass die Vorgänge so schnell wie möglich fortgesetzt werden.

Es gibt eindeutig Leistungssteigerungen, wenn eine Workload parallelisiert werden kann.

Swapchains

Die DXGI-Swapkette ist die Basis für Swapchains in Direct3D 11 und 12. Es gibt einige geringfügige Unterschiede. In Direct3D 11 sind die drei Arten von Swapchain SEQUENZIELL, VERWERFEN und FLIP_SEQUENTIAL. Für Direct3D 12 gibt es nur zwei Typen: FLIP_SEQUENTIAL und FLIP_DISCARD. Wie oben erwähnt, sollten Sie Ihre Swapchain explizit über IDXGIFactory4 oder höher erstellen und dieselbe Schnittstelle für jede Adapteraufzählung verwenden.

In Direct3D 11 gibt es eine automatische Backbufferrotation: Für back buffer 0 ist nur eine Renderzielansicht erforderlich. In Direct3D 12 ist die Pufferrotation explizit. Für jeden Rückpuffer muss eine Renderzielansicht vorhanden sein. Verwenden Sie die IDXGISwapChain3::GetCurrentBackBufferIndex-Methode , um auszuwählen, in welche Methode gerendert werden soll. Auch diese zusätzliche Flexibilität ermöglicht eine größere Parallelisierung.

Hinweis

Es gibt zwar zahlreiche Möglichkeiten, Ihre Anwendung einzurichten, aber in der Regel verfügen Anwendungen über einen ID3D12CommandAllocator pro Swap-Chain-Puffer. Dadurch kann die Anwendung mit dem Erstellen einer Reihe von Befehlen für den nächsten Frame fortfahren, während die GPU den vorherigen rendert.

Behobenes Funktionsrendering

In Direct3D 11 gab es einige Methoden, die verschiedene Vorgänge auf höherer Ebene vereinfacht haben, z. B . GenerateMips (Erstellen vollständiger Mip-Ketten) und DrawAuto (verwenden der Streamausgabe als Shadereingabe ohne weitere Eingabe von der App). Diese Methoden sind in Direct3D 12 nicht verfügbar. Die App muss diese Vorgänge verarbeiten, indem Shader erstellt werden, um sie auszuführen.

Quoten und Enden

Die folgende Tabelle zeigt eine Reihe von Features, die zwischen Direct3D 11 und 12 ähnlich sind, aber nicht identisch sind.

Direct3D 11 Direct3D 12
ID3D11Query MIT ID3D12QueryHeap können Abfragen gruppiert werden, wodurch die Kosten reduziert werden.
ID3D11Prädikat Die Prädication wird jetzt aktiviert, indem Daten in einem vollständig transparenten Puffer vorhanden sind. Das Direct3D 11 ID3D11Predicate-Objekt wird durch ID3D12Resource::Map ersetzt, das einem Aufruf von ResolveQueryData und einem GPU-Synchronisierungsvorgang unter Verwendung eines Zauns folgen muss, um auf die Bereitstellung der Daten zu warten. Weitere Informationen finden Sie unter Prädication.
Ausgeblendeter UAV/SO-Zähler Die App ist für die Zuordnung und Verwaltung von SO/UAV-Zählern verantwortlich. Weitere Informationen finden Sie unter Streamausgabeindikatoren und UAV-Leistungsindikatoren.
Ressourcendynamische MinLOD (Minium-Detailebene) Dies wurde in die statische MinLOD des SRV-Deskriptors verschoben.
Draw*Indirect/DispatchIndirect Indirekte Zeichnungsmethoden werden alle mit der executeIndirect-Methode zusammengeführt.
Tiefenschablonenformate sind verschachtelt Tiefenschablonenformate sind planar. Bei einem Format von 24 Bit Tiefe würden 8 Bit Schablonen im Format 24/8/24/8 gespeichert... etc in Direct3D 11, aber als 24/24/24... gefolgt von 8/8/8... in Direct3D 12. Beachten Sie, dass jede Ebene eine eigene Unterquelle in D3D12 ist (siehe Unterressourcen).
Ändern der GrößeTilePool Reservierte Ressourcen können mehreren Heaps zugeordnet werden. Wenn ein Kachelpool in D3D11 gewachsen wäre, kann stattdessen ein zusätzlicher Heap in D3D12 zugeordnet werden.

 

DirectX-Videotutorials für erweitertes Lernen: Leitfaden für die DirectX 11-zu-DirectX 12-Portierung

Grundlegendes zu Direct3D 12

Arbeiten mit Direct3D 11, Direct3D 10 und Direct2D