Freigeben über


Direct3D 12-Renderdurchläufe

Das Renderdurchläufe-Feature ist neu für Windows 10, Version 1809 (10.0; Build 17763) und führt das Konzept eines Direct3D 12-Renderdurchlaufs ein. Ein Renderdurchlauf besteht aus einer Teilmenge der Befehle, die Sie in einer Befehlsliste aufzeichnen.

Um zu deklarieren, wo jeder Renderdurchlauf beginnt und endet, verschachteln Sie die Befehle, die dazu gehören, innerhalb von Aufrufen an ID3D12GraphicsCommandList4::BeginRenderPass und EndRenderPass. Folglich enthält jede Befehlsliste null, einen oder mehrere Renderdurchläufe.

Drehbücher

Renderdurchläufe können die Leistung Ihres Renderers verbessern, wenn sie auf Tile-Based Verzögertes Rendering (TBDR) basiert. Genauer gesagt hilft die Technik Ihrem Renderer, die GPU-Effizienz zu verbessern, indem der Speicherverkehr auf/vom Off-Chip-Speicher reduziert wird, indem Ihre Anwendung die Anforderungen an die Sortierung von Ressourcen und Datenabhängigkeiten besser identifizieren kann.

Ein anzeigetreiber, der ausdrücklich geschrieben wurde, um das Renderdurchläufe-Feature zu nutzen, bietet die besten Ergebnisse. Renderdurchlauf-APIs können jedoch auch auf bereits vorhandenen Treibern ausgeführt werden (auch wenn dies nicht unbedingt mit Leistungsverbesserungen der Fall ist).

Dies sind die Szenarien, in denen Renderdurchläufe entwickelt wurden, um einen Wert bereitzustellen.

Ermöglichen Sie Ihrer Anwendung, unnötige Lasten/Speicher von Ressourcen aus/bis zum Hauptspeicher in einer Tile-Based TBDR-Architektur (Deferred Rendering) zu vermeiden.

Eines der Wertversprechen von Renderdurchläufen besteht darin, dass es Ihnen einen zentralen Ort zur Angabe der Datenabhängigkeiten Ihrer Anwendung für eine Reihe von Renderingvorgängen bietet. Diese Datenabhängigkeiten ermöglichen es dem Anzeigetreiber, diese Daten zur Bindungs-/Sperrzeit zu prüfen und Anweisungen zur Minimierung von Ressourcenlasten/Speicher von/zum Hauptspeicher auszugeben.

Ermöglichen Sie Ihrer TBDR-Architektur, opportunistisch persistente Ressourcen im On-Chip-Cache über Renderdurchläufe hinweg zu speichern (auch in separaten Befehlslisten)

Anmerkung

Insbesondere ist dieses Szenario auf die Fälle beschränkt, in denen Sie in mehrere Befehlslisten in dasselbe Renderziel schreiben.

Ein gängiges Renderingmuster dient dazu, dass Ihre Anwendung in mehreren Befehlslisten fortlaufend in denselben Renderzielen gerendert wird, auch wenn die Renderingbefehle parallel generiert werden. Ihre Verwendung von Renderdurchläufen in diesem Szenario ermöglicht es, diese Durchläufe so zu kombinieren (da die Anwendung weiß, dass das Rendern in der unmittelbar erfolgreichen Befehlsliste fortgesetzt wird), dass der Anzeigetreiber eine Leerung des Hauptspeichers in Befehlslistengrenzen vermeiden kann.

Verantwortlichkeiten Ihrer Anwendung

Selbst bei der Renderdurchlauffunktion übernehmen weder die Direct3D 12-Laufzeit noch der Anzeigetreiber die Verantwortung für die Ableitung von Möglichkeiten zum Erneuten Anordnen/Vermeiden von Lasten und Speichern. Um das Renderdurchläufe-Feature ordnungsgemäß zu nutzen, hat Ihre Anwendung diese Zuständigkeiten.

  • Identifizieren Sie Daten-/Sortierungsabhängigkeiten für ihre Vorgänge ordnungsgemäß.
  • Ordnen Sie ihre Übermittlungen so an, dass Leerungen minimiert werden (sodass Sie die Verwendung von _PRESERVE Flags minimieren).
  • Verwenden Sie Ressourcenbarrieren richtig, und verfolgen Sie den Ressourcenstatus.
  • Vermeiden Sie nicht benötigte Kopien/Löschungen. Um diese zu identifizieren, können Sie die automatisierten Leistungswarnungen aus dem PIX unter Windows-Toolverwenden.

Verwenden des Renderdurchlauffeatures

Was ist ein Renderdurchlauf?

Ein Renderdurchlauf wird durch diese Elemente definiert.

  • Eine Reihe von Ausgabebindungen, die für die Dauer des Renderdurchlaufs festgelegt sind. Diese Bindungen sind an eine oder mehrere Renderzielansichten (RTVs) und/oder an eine Tiefenschablonenansicht (DSV) gebunden.
  • Eine Liste der GPU-Vorgänge, die auf die Ausgabebindungen abzielen.
  • Metadaten, die die Lade-/Speicherabhängigkeiten für alle Vom Renderdurchlauf gezielten Ausgabebindungen beschreiben.

Deklarieren der Ausgabebindungen

Am Anfang eines Renderdurchlaufs deklarieren Sie Bindungen an Die Renderziel(n) und/oder an den Tiefen-/Schablonenpuffer. Es ist optional, eine Bindung an renderziel(n) zu binden, und es ist optional, eine Bindung an einen Tiefen-/Schablonenpuffer zu erstellen. Sie müssen jedoch an mindestens eine der beiden Binden binden, und im folgenden Codebeispiel binden wir an beide.

Sie deklarieren diese Bindungen in einem Aufruf von ID3D12GraphicsCommandList4::BeginRenderPass.

void render_passes(::ID3D12GraphicsCommandList4 * pIGCL4,
    D3D12_CPU_DESCRIPTOR_HANDLE const& rtvCPUDescriptorHandle,
    D3D12_CPU_DESCRIPTOR_HANDLE const& dsvCPUDescriptorHandle)
{
    const float clearColor4[]{ 0.f, 0.f, 0.f, 0.f };
    CD3DX12_CLEAR_VALUE clearValue{ DXGI_FORMAT_R32G32B32_FLOAT, clearColor4 };

    D3D12_RENDER_PASS_BEGINNING_ACCESS renderPassBeginningAccessClear{ D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR, { clearValue } };
    D3D12_RENDER_PASS_ENDING_ACCESS renderPassEndingAccessPreserve{ D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE, {} };
    D3D12_RENDER_PASS_RENDER_TARGET_DESC renderPassRenderTargetDesc{ rtvCPUDescriptorHandle, renderPassBeginningAccessClear, renderPassEndingAccessPreserve };

    D3D12_RENDER_PASS_BEGINNING_ACCESS renderPassBeginningAccessNoAccess{ D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, {} };
    D3D12_RENDER_PASS_ENDING_ACCESS renderPassEndingAccessNoAccess{ D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS, {} };
    D3D12_RENDER_PASS_DEPTH_STENCIL_DESC renderPassDepthStencilDesc{ dsvCPUDescriptorHandle, renderPassBeginningAccessNoAccess, renderPassBeginningAccessNoAccess, renderPassEndingAccessNoAccess, renderPassEndingAccessNoAccess };

    pIGCL4->BeginRenderPass(1, &renderPassRenderTargetDesc, &renderPassDepthStencilDesc, D3D12_RENDER_PASS_FLAG_NONE);
    // Record command list.
    pIGCL4->EndRenderPass();
    // Begin/End further render passes and then execute the command list(s).
}

Sie legen das erste Feld der D3D12_RENDER_PASS_RENDER_TARGET_DESC Struktur auf den CPU-Deskriptorhandle fest, das einer oder mehreren Renderzielansichten (RTVs) entspricht. Ebenso enthält D3D12_RENDER_PASS_DEPTH_STENCIL_DESC den CPU-Deskriptorhandle, der einer Tiefenschablonenansicht (DSV) entspricht. Diese CPU-Deskriptorhandles sind dieselben, die Sie andernfalls an ID3D12GraphicsCommandList::OMSetRenderTargetsübergeben würden. Und genau wie bei OMSetRenderTargetswerden die CPU-Deskriptoren angedockten von ihren jeweiligen Heaps (CPU-Deskriptor) zum Zeitpunkt des Aufrufs von BeginRenderPass.

Die RTVs und DSV werden nicht an den Renderdurchlauf geerbt. Vielmehr müssen sie festgelegt werden. Außerdem werden die RTVs und DSV in BeginRenderPass an die Befehlsliste weitergegeben. Stattdessen befinden sie sich im nicht definierten Zustand nach dem Renderdurchlauf.

Renderdurchläufe und Arbeitsauslastungen

Renderdurchläufe können nicht geschachtelt werden, und Sie können keinen Renderdurchlauf über mehrere Befehlslisten verfügen (sie müssen beginnen und enden, während sie in einer einzigen Befehlsliste aufgezeichnet werden). Optimierungen, die entwickelt wurden, um eine effiziente Multithreadgenerierung von Renderdurchläufen zu ermöglichen, werden im Abschnitt Renderdurchlauf flagsunten erläutert.

Ein Schreibvorgang, den Sie in einem Renderdurchlauf ausführen, ist nicht gültigen, aus denen Sie lesen können, bis ein nachfolgendes Renderdurchlauf erfolgt. Dies schließt einige Arten von Barrieren innerhalb des Renderdurchlaufs aus , z. B. Barrieren von RENDER_TARGET bis SHADER_RESOURCE für das aktuell gebundene Renderziel. Weitere Informationen finden Sie im Abschnitt Renderdurchläufe und Ressourcenbarrierenunten.

Die einzige Ausnahme der schreibgeschützten Einschränkung umfasst die impliziten Lesevorgänge, die im Rahmen von Tiefentests und Renderzielmischungen auftreten. Daher sind diese APIs innerhalb eines Renderdurchlaufs nicht zulässig (die Kernlaufzeit entfernt die Befehlsliste, wenn eine dieser APIs während der Aufzeichnung aufgerufen wird).

Renderdurchläufe und Ressourcenbarrieren

Sie dürfen keinen Schreibvorgang aus demselben Renderdurchlauf lesen oder verwenden. Bestimmte Barrieren entsprechen dieser Einschränkung nicht, z. B. von D3D12_RESOURCE_STATE_RENDER_TARGET bis *_SHADER_RESOURCE für das aktuell gebundene Renderziel (und die Debugebene führt zu einem Fehler). Diese Barriere für ein Renderziel, das außerhalb aktuellen Renderdurchlauf geschrieben wurde, entspricht jedoch der Konformität, da die Schreibvorgänge vor dem Start des aktuellen Renderdurchlaufs abgeschlossen werden. Möglicherweise profitieren Sie von der Kenntnis bestimmter Optimierungen, die ein Displaytreiber in dieser Hinsicht machen kann. Angesichts einer konformen Arbeitsauslastung kann ein Anzeigetreiber alle Hindernisse in Ihrem Renderdurchlauf an den Anfang des Renderdurchlaufs verschieben. Dort können sie zusammengekettet werden (und keine Tiling-/Binningvorgänge stören). Dies ist eine gültige Optimierung, vorausgesetzt, alle Ihre Schreibvorgänge wurden abgeschlossen, bevor der aktuelle Renderdurchlauf gestartet wird.

Nachfolgend finden Sie ein vollständiges Beispiel für die Treiberoptimierung, das davon ausgeht, dass Sie über ein Renderingmodul verfügen, das über ein Vorabdesign für Direct3D 12-Ressourcenbindung verfügt. Dabei werden Barrieren bedarfsgesteuert basierend auf der Bindung von Ressourcen ausgeführt. Beim Schreiben in eine ungeordnete Zugriffsansicht (UAV) am Ende eines Frames (der im folgenden Frame verwendet werden soll) kann das Modul die Ressource am Ende des Frames im D3D12_RESOURCE_STATE_UNORDERED_ACCESS Zustand belassen. Im folgenden Frame wird beim Binden der Ressource durch das Modul als Shaderressourcenansicht (SRV) festgestellt, dass sich die Ressource nicht im richtigen Zustand befindet und eine Barriere von D3D12_RESOURCE_STATE_UNORDERED_ACCESS auf D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCEausgibt. Wenn diese Barriere innerhalb des Renderdurchlaufs auftritt, ist der Anzeigetreiber gerechtfertigt, wenn davon ausgegangen wird, dass alle Schreibvorgänge bereits außerhalb dieses aktuellen Renderdurchlaufs aufgetreten sind, und folglich (und hier sehen Sie, wo sich die Optimierung befindet) der Anzeigetreiber möglicherweise die Barriere bis zum Anfang des Renderdurchlaufs verschieben. Auch hier ist dies gültig, solange der Code der in diesem Abschnitt und zuletzt beschriebenen Schreibzugriffseinschränkung entspricht.

Dies sind Beispiele für konforme Barrieren.

  • D3D12_RESOURCE_STATE_UNORDERED_ACCESSD3D12_RESOURCE_STATE_INDIRECT_ARGUMENT.
  • D3D12_RESOURCE_STATE_COPY_DEST*_SHADER_RESOURCE.

Und dies sind Beispiele für nicht konforme Barrieren.

  • D3D12_RESOURCE_STATE_RENDER_TARGET zu jedem Lesestatus auf derzeit gebundenen RTVs/DSVs.
  • D3D12_RESOURCE_STATE_DEPTH_WRITE zu jedem Lesestatus auf derzeit gebundenen RTVs/DSVs.
  • Alle Aliasingbarrieren.
  • Ungeordnete Zugriffsansichtsbarrieren (UAV). 

Ressourcenzugriffsdeklaration

Bei BeginRenderPass Zeit sowie zum Deklarieren aller Ressourcen, die als RTVs und/oder DSV innerhalb dieses Durchgangs dienen, müssen Sie auch den Anfang und das Beenden Zugriffs Merkmale angeben. Wie Sie im Codebeispiel im Deklarieren Ihrer Ausgabebindungen Abschnitt oben sehen können, führen Sie dies mit den strukturen D3D12_RENDER_PASS_RENDER_TARGET_DESC und D3D12_RENDER_PASS_DEPTH_STENCIL_DESC aus.

Weitere Informationen finden Sie in den Strukturen D3D12_RENDER_PASS_BEGINNING_ACCESS und D3D12_RENDER_PASS_ENDING_ACCESS sowie in den Enumerationen D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE und D3D12_RENDER_PASS_ENDING_ACCESS_TYPE.

Renderdurchlaufkennzeichnungen

Der letzte Parameter, der an BeginRenderPass übergeben wird, ist ein Renderdurchlaufkennzeichen (ein Wert aus der D3D12_RENDER_PASS_FLAGS-Aufzählung).

enum D3D12_RENDER_PASS_FLAGS
{
    D3D12_RENDER_PASS_FLAG_NONE = 0,
    D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES = 0x1,
    D3D12_RENDER_PASS_FLAG_SUSPENDING_PASS = 0x2,
    D3D12_RENDER_PASS_FLAG_RESUMING_PASS = 0x4
};

UAV schreibt innerhalb eines Renderdurchlaufs

Ungeordnete Zugriffsansichts-Schreibvorgänge (UAV) sind innerhalb eines Renderdurchlaufs zulässig. Sie müssen jedoch ausdrücklich angeben, dass Sie UAV-Schreibvorgänge innerhalb des Renderdurchlaufs ausgeben, indem Sie D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITESangeben, damit der Anzeigetreiber bei Bedarf die Tilung deaktivieren kann.

UAV-Zugriffe müssen der oben beschriebenen Schreibzugriffseinschränkung folgen (Schreibvorgänge in einem Renderdurchlauf sind erst gültig, wenn ein späterer Renderdurchlauf gelesen werden kann). UAV-Barrieren sind innerhalb eines Renderdurchlaufs nicht zulässig.

UAV-Bindungen (über Stammtabellen oder Stammdeskriptoren) werden in Renderdurchläufe geerbt und aus Renderdurchläufen verteilt.

Anhalten und Fortsetzen von Durchläufen

Sie können einen gesamten Renderdurchlauf als Anhaltedurchlauf und/oder als Fortsetzungsdurchlauf angeben. Ein Anhalte-gefolgt-von-a-Resuming-Paar muss identische Ansichten/Zugriffsflags zwischen den Durchläufen aufweisen und darf keine dazwischen liegenden GPU-Vorgänge (z. B. Zeichnet, Verteiler, Verwerfen, Löschen, Kopien, Aktualisierungskachelzuordnungen, Schreibpuffer-Direktzeichen, Abfragen, Abfragelöser) zwischen dem anhaltenden Renderdurchlauf und dem resumierenden Renderdurchlauf aufweisen.

Der beabsichtigte Anwendungsfall ist Multithreadrendering, wobei beispielsweise vier Befehlslisten (jeweils mit eigenen Renderdurchläufen) auf dieselben Renderziele ausgerichtet werden können. Wenn Renderdurchläufe über Befehlslisten hinweg angehalten/fortgesetzt werden, müssen die Befehlslisten im gleichen Aufruf von ID3D12CommandQueue::ExecuteCommandListsausgeführt werden.

Ein Renderdurchlauf kann sowohl fortsetzen als auch anhaltend sein. Im soeben angegebenen Multithread-Beispiel würden die Befehlslisten 2 bzw. 3 von 1 bzw. 2 fortgesetzt. Und gleichzeitig würden 2 und 3 auf 3 bzw. 4 ausgesetzt.

Abfrage der Unterstützung von Renderdurchläufen

Sie können ID3D12Device::CheckFeatureSupport aufrufen, um abzufragen, inwieweit ein Gerätetreiber und/oder die Hardware Renderdurchläufe effizient unterstützt.

D3D12_RENDER_PASS_TIER get_render_passes_tier(::ID3D12Device * pIDevice)
{
    D3D12_FEATURE_DATA_D3D12_OPTIONS5 featureSupport{};
    winrt::check_hresult(
        pIDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &featureSupport, sizeof(featureSupport))
    );
    return featureSupport.RenderPassesTier;
}
...
    D3D12_RENDER_PASS_TIER renderPassesTier{ get_render_passes_tier(pIDevice) };

Aufgrund der Zuordnungslogik der Laufzeit funktionieren Renderdurchläufe immer. Je nach Featureunterstützung bieten sie jedoch nicht immer einen Vorteil. Sie können Code verwenden, der dem obigen Codebeispiel ähnelt, um zu ermitteln, ob/wann es sich lohnt, Befehle als Renderdurchläufe ausstellen zu lassen, und wenn es definitiv kein Vorteil ist (d. a., wenn die Laufzeit nur der vorhandenen API-Oberfläche zugeordnet ist). Die Durchführung dieser Überprüfung ist besonders wichtig, wenn Sie D3D11On12) verwenden.

Eine Beschreibung der drei Unterstützungsebenen finden Sie in der D3D12_RENDER_PASS_TIER Enumeration.