Übersicht über die Ressourcenbindung

Die Schlüssel zum Verständnis der Ressourcenbindung in DirectX 12 sind die Konzepte von Deskriptoren, Deskriptortabellen, Deskriptorheaps und Stammsignaturen.

Ressourcen und die Grafikpipeline

Shaderressourcen (z. B. Texturen, Konstantentabellen, Bilder, Puffer usw.) sind nicht direkt an die Shaderpipeline gebunden. Stattdessen wird über einen Deskriptor auf sie verwiesen. Ein Deskriptor ist ein kleines Objekt, das Informationen zu einer Ressource enthält.

Deskriptoren werden zu Deskriptortabellen gruppiert. Jede Deskriptortabelle speichert Informationen zu einem Bereich von Ressourcentypen. Es gibt viele verschiedene Arten von Ressourcen. Die häufigsten Ressourcen sind:

  • Konstante Pufferansichten (CBVs)
  • Ungeordnete Zugriffsansichten (UAVs)
  • Shaderressourcenansichten (SRVs)
  • Sampler

SRV-, UAV- und CBVs-Deskriptoren können in derselben Deskriptortabelle kombiniert werden.

Die Grafik- und Computepipelines erhalten Zugriff auf Ressourcen, indem sie nach Index in Deskriptortabellen verweisen.

Deskriptortabellen werden in einem Deskriptorheap gespeichert. Deskriptorheaps enthalten idealerweise alle Deskriptoren (in Deskriptortabellen) für einen oder mehrere Frames, die gerendert werden sollen. Alle Ressourcen werden in Benutzermodus-Heaps gespeichert.

Ein anderes Konzept ist das einer Stammsignatur. Die Stammsignatur ist eine von der Anwendung definierte Bindungskonvention, die von Shadern verwendet wird, um nach den Ressourcen zu suchen, auf die sie Zugriff benötigen. Die Stammsignatur kann Folgendes speichern:

  • Indizes zu Deskriptortabellen in einem Deskriptorheap, in denen das Layout der Deskriptortabelle vorab definiert wurde.
  • Konstanten, damit Apps benutzerdefinierte Konstanten (als Stammkonstanten bezeichnet) direkt an Shader binden können, ohne Deskriptoren und Deskriptortabellen durchlaufen zu müssen.
  • Eine sehr kleine Anzahl von Deskriptoren direkt in der Stammsignatur, z. B. eine konstante Pufferansicht (CBV), die sich pro Zeichnung ändert, wodurch die Anwendung nicht mehr diese Deskriptoren in einen Deskriptorheap einfügen muss.

Anders ausgedrückt: Die Stammsignatur bietet Leistungsoptimierungen, die für kleine Datenmengen geeignet sind, die sich pro Zeichnung ändern.

Der Direct3D 12-Entwurf für die Bindung trennt sie von anderen Aufgaben, z. B. Speicherverwaltung, Objektlebensdauerverwaltung, Zustandsnachverfolgung und Speichersynchronisierung (siehe Unterschiede im Bindungsmodell von Direct3D 11). Die Direct3D 12-Bindung ist für geringen Mehraufwand konzipiert und für die API-Aufrufe optimiert, die am häufigsten ausgeführt werden. Es ist auch für Low-End- bis High-End-Hardware skalierbar und skalierbar von älteren (der eher linearen Direct3D 11-Pipeline) bis zu den neueren (paralleleren) Ansätzen der Grafik-Engine-Programmierung.

Ressourcentypen und -ansichten

Ressourcentypen sind identisch mit Direct3D 11, nämlich:

  • Texture1D und Texture1DArray
  • Texture2D und Texture2DArray, Texture2DMS, Texture2DMSArray
  • Texture3D
  • Puffer (typisiert, strukturiert und roh)

Ressourcenansichten sind ähnlich, unterscheiden sich aber geringfügig von Direct3D 11. Vertex- und Indexpuffersichten wurden hinzugefügt.

  • Konstantenpufferansicht (CBV)
  • Ungeordnete Zugriffsansicht (UAV)
  • Shaderressourcenansicht (SRV)
  • Sampler
  • Renderzielansicht (RTV)
  • Tiefenschablonenansicht (DSV)
  • Indexpufferansicht (IBV)
  • Vertexpufferansicht (VBV)
  • Streamausgabeansicht (SOV)

Nur die ersten vier Dieser Ansichten sind für Shader tatsächlich sichtbar, siehe Shader Visible Descriptor Heaps und Non Shader Visible Descriptor Heaps.

Steuerungsfluss der Ressourcenbindung

Wenn sie sich nur auf Stammsignaturen, Stammdeskriptoren, Stammkonstanten, Deskriptortabellen und Deskriptorheaps konzentrieren, sollte der Ablauf der Renderinglogik für eine App wie folgt aussehen:

  • Erstellen Sie ein oder mehrere Stammsignaturobjekte – eines für jede andere Bindungskonfiguration, die eine Anwendung benötigt.
  • Erstellen Sie Shader und Pipelinestatus mit den Stammsignaturobjekten, mit denen sie verwendet werden.
  • Erstellen Sie einen (oder ggf. mehrere) Deskriptor heaps, die alle SRV-, UAV- und CBV-Deskriptoren für jeden Frame des Renderings enthalten.
  • Initialisieren Sie die Deskriptorheap(en) nach Möglichkeit mit Deskriptoren für Deskriptorensätze, die in vielen Frames wiederverwendet werden.
  • Für jeden Frame, der gerendert werden soll:
    • Für jede Befehlsliste:
      • Legen Sie die aktuelle Stammsignatur fest, die verwendet werden soll (und ändern Sie bei Bedarf während des Renderings – was selten erforderlich ist).
      • Aktualisieren Sie einige Stammsignaturkonstanten und/oder Stammsignaturdeskriptoren für die neue Ansicht (z. B. Welt-/Ansichtsprojektionen).
      • Für jedes zu zeichnende Element:
        • Definieren Sie alle neuen Deskriptoren in Deskriptorheaps nach Bedarf für das Rendering pro Objekt. Für shader-sichtbare Deskriptorheaps muss die App sicherstellen, dass sie den Deskriptorheapraum verwendet, auf den nicht bereits durch Rendering verwiesen wird, das sich im Flight befinden könnte, z. B. lineares Zuweisen von Raum über den Deskriptorheap während des Renderings.
        • Aktualisieren Sie die Stammsignatur mit Zeigern auf die erforderlichen Regionen der Deskriptorheaps. Beispielsweise kann eine Deskriptortabelle auf einige statische (nicht ändernde) Deskriptoren verweisen, die zuvor initialisiert wurden, während eine andere Deskriptortabelle auf einige dynamische Deskriptoren verweist, die für das aktuelle Rendering konfiguriert sind.
        • Aktualisieren Sie einige Stammsignaturkonstanten und/oder Stammsignaturdeskriptoren für das Rendering pro Element.
        • Legen Sie den Pipelinestatus fest, zu dem das Element zeichnen soll (nur bei Bedarf), kompatibel mit der aktuell gebundenen Stammsignatur.
        • Draw
      • Wiederholen (nächstes Element)
    • Wiederholen (nächste Befehlsliste)
    • Genau wenn die GPU mit keinem Speicher fertig ist, der nicht mehr verwendet wird, kann sie freigegeben werden. Die Verweise von Deskriptoren darauf müssen nicht gelöscht werden, wenn kein zusätzliches Rendering, das diese Deskriptoren verwendet, übermittelt wird. So kann das nachfolgende Rendering auf andere Bereiche in Deskriptorheaps verweisen, oder veraltete Deskriptoren können mit gültigen Deskriptoren überschrieben werden, um den Deskriptor-Heapraum wiederzuverwenden.
  • Wiederholen (nächster Frame)

Beachten Sie, dass andere Deskriptortypen, Renderzielansichten (RTVs), Tiefenschablonenansichten (DSV), Indexpufferansichten (IBVs), Vertexpuffersichten (VBVs) und Streamausgabeansichten (SOV) unterschiedlich verwaltet werden. Der Treiber übernimmt die Versionsverwaltung des Deskriptorssatzes, der für jede Zeichnung während der Aufzeichnung der Befehlsliste gebunden ist (ähnlich wie die Stammsignaturbindungen durch die Hardware/den Treiber versioniert werden). Dies unterscheidet sich von den Inhalten von Shader-Sichtbaren Deskriptorheaps, für die die Anwendung manuell über den Heap zuordnen muss, da sie auf verschiedene Deskriptoren zwischen Ziehungen verweist. Die Versionsverwaltung von Heapinhalten, die shader-sichtbar sind, bleibt der Anwendung überlassen, da anwendungen beispielsweise Deskriptoren wiederverwenden können, die sich nicht ändern, oder große statische Mengen von Deskriptoren verwenden und Shaderindizierung (z. B. nach Material-ID) verwenden, um Deskriptoren auszuwählen, die aus dem Deskriptorheap verwendet werden sollen, oder Kombinationen von Techniken für verschiedene Deskriptorensätze zu verwenden. Die Hardware ist nicht für diese Art von Flexibilität für die anderen Deskriptortypen (RTV, DSV, IBV, VBV, SOV) ausgestattet.

Unterzuordnung

In Direct3D 12 hat die App die Steuerung der Speicherverwaltung auf niedriger Ebene. In früheren Versionen von Direct3D, einschließlich Direct3D 11, gab es eine Zuordnung pro Ressource. In Direct3D 12 kann die App die API verwenden, um einen großen Speicherblock zuzuweisen, der größer ist als jedes einzelne Objekt. Danach kann die App Deskriptoren erstellen, die auf Abschnitte dieses großen Speicherblocks verweisen. Dieser Prozess der Entscheidung, was wo platziert werden soll (kleinere Blöcke innerhalb des großen Blocks), wird als Unterzuordnung bezeichnet. Wenn die App dies aktiviert, kann dies zu Gewinnen bei der effizienten Nutzung der Berechnung und des Arbeitsspeichers führen. Beispielsweise wird die Ressourcenumbenennung veraltet. Stattdessen können Apps Zäune verwenden, um zu bestimmen, wann eine bestimmte Ressource verwendet wird und wann sie nicht verwendet wird, indem sie befehlslistenausführungen, bei denen die Befehlsliste die Verwendung dieser bestimmten Ressource erfordert.

Freigeben von Ressourcen

Bevor Speicher, der an die Pipeline gebunden wurde, freigegeben werden kann, muss die GPU damit fertig sein.

Das Warten auf das Framerendering ist wahrscheinlich der gröbere Weg, um sicher zu sein, dass die GPU fertig ist. Bei einer genaueren Körnung können Sie wieder Zäune verwenden. Wenn ein Befehl in einer Befehlsliste aufgezeichnet wird, deren Abschluss Sie nachverfolgen möchten, fügen Sie direkt danach einen Zaun ein. Anschließend können Sie verschiedene Synchronisierungsvorgänge mit dem Zaun ausführen. Sie übermitteln neue Arbeit (Befehlslisten), die wartet, bis ein angegebener Zaun für die GPU übergeben wurde, was angibt, dass alles abgeschlossen ist, oder Sie können anfordern, dass ein CPU-Ereignis ausgelöst wird, wenn der Zaun bestanden ist (auf das die App mit einem schlafenden Thread warten kann). In Direct3D 11 war EnqueueSetEventdies ().