Kopieren und Zugreifen auf Ressourcendaten (Direct3D 10)

Es ist nicht mehr notwendig, über Ressourcen zu denken, die entweder im Videospeicher oder im Systemspeicher erstellt werden. Oder ob die Runtime den Arbeitsspeicher verwalten soll. Dank der Architektur des neuen WDDM (Windows Display Driver Model) erstellen Anwendungen jetzt Direct3D 10-Ressourcen mit unterschiedlichen Verwendungsflags , um anzugeben, wie die Anwendung die Ressourcendaten verwenden möchte. Das neue Treibermodell virtualisiert den von Ressourcen verwendeten Arbeitsspeicher. es wird dann in die Verantwortung des Betriebssystem-/Treiber-/Speicher-Managers, Ressourcen in dem leistungsstärksten Speicherbereich zu platzieren, der bei der erwarteten Nutzung möglich ist.

Der Standardfall ist, dass Ressourcen für die GPU verfügbar sind. Natürlich gibt es Zeiten, in denen die Ressourcendaten für die CPU verfügbar sein müssen. Das Kopieren von Ressourcendaten, damit der entsprechende Prozessor darauf zugreifen kann, ohne die Leistung zu beeinträchtigen, erfordert einige Kenntnisse über die Funktionsweise der API-Methoden.

Kopieren von Ressourcendaten

Ressourcen werden im Arbeitsspeicher erstellt, wenn Direct3D einen Create-Aufruf ausführt. Sie können im Videospeicher, im Systemspeicher oder in einer beliebigen anderen Art von Arbeitsspeicher erstellt werden. Da das WDDM-Treibermodell diesen Arbeitsspeicher virtualisiert, müssen Anwendungen nicht mehr nachverfolgen, in welcher Art von Speicherressourcen erstellt wird.

Im Idealfall befinden sich alle Ressourcen im Videospeicher, sodass die GPU sofort darauf zugreifen kann. Es ist jedoch manchmal erforderlich, dass die CPU die Ressourcendaten liest oder die GPU auf Ressourcendaten zugreifen kann, in die die CPU geschrieben hat. Direct3D 10 behandelt diese verschiedenen Szenarien, indem die Anwendung eine Verwendung angeben anfordert, und bietet dann bei Bedarf mehrere Methoden zum Kopieren von Ressourcendaten.

Je nachdem, wie die Ressource erstellt wurde, ist es nicht immer möglich, direkt auf die zugrunde liegenden Daten zuzugreifen. Dies kann bedeuten, dass die Ressourcendaten aus der Quellressource in eine andere Ressource kopiert werden müssen, auf die der entsprechende Prozessor zugreifen kann. In Bezug auf Direct3D 10 kann direkt über die GPU auf Standardressourcen zugegriffen werden, auf dynamische und Stagingressourcen kann direkt von der CPU zugegriffen werden.

Nachdem eine Ressource erstellt wurde, kann ihre Verwendung nicht mehr geändert werden. Kopieren Sie stattdessen den Inhalt einer Ressource in eine andere Ressource, die mit einer anderen Verwendung erstellt wurde. Direct3D 10 bietet diese Funktionalität mit drei verschiedenen Methoden. Die ersten beiden Methoden( ID3D10Device::CopyResource und ID3D10Device::CopySubresourceRegion) sind darauf ausgelegt, Ressourcendaten von einer Ressource in eine andere zu kopieren. Die dritte Methode (ID3D10Device::UpdateSubresource) dient zum Kopieren von Daten aus dem Arbeitsspeicher in eine Ressource.

Es gibt zwei Standard Arten von Ressourcen: zuordnungsfähig und nicht zuordnungsfähig. Ressourcen, die mit dynamischen oder Stagingverwendungen erstellt wurden, können zugeordnet werden, während Ressourcen, die mit standardbasierten oder unveränderlichen Verwendungen erstellt wurden, nicht zugeordnet werden können.

Das Kopieren von Daten zwischen nicht mappierbaren Ressourcen ist sehr schnell, da dies der häufigste Fall ist und für eine gute Leistung optimiert wurde. Da die CPU nicht direkt auf diese Ressourcen zugreifen kann, werden sie optimiert, sodass die GPU sie schnell bearbeiten kann.

Das Kopieren von Daten zwischen zugeordneten Ressourcen ist problematischer, da die Leistung von der Nutzung abhängt, mit der die Ressource erstellt wurde. Beispielsweise kann die GPU eine dynamische Ressource ziemlich schnell lesen, aber nicht in sie schreiben, und die GPU kann Stagingressourcen nicht direkt lesen oder in Stagingressourcen schreiben.

Anwendungen, die Daten aus einer Ressource mit Standardverwendung in eine Ressource mit Stagingauslastung kopieren möchten (damit die CPU die Daten lesen kann, d. h. das GPU-Rückleseproblem), müssen dies mit Sorgfalt tun. Weitere Informationen zu diesem letzten Fall finden Sie unter Zugreifen auf Ressourcendaten .

Zugreifen auf Ressourcendaten

Für den Zugriff auf eine Ressource ist eine Zuordnung der Ressource erforderlich. Zuordnung bedeutet im Wesentlichen, dass die Anwendung versucht, der CPU Zugriff auf den Arbeitsspeicher zu gewähren. Das Zuordnen einer Ressource, damit die CPU auf den zugrunde liegenden Arbeitsspeicher zugreifen kann, kann zu Leistungsengpässen führen, und aus diesem Grund muss darauf geachtet werden, wie und wann diese Aufgabe ausgeführt werden soll.

Die Leistung kann zu einem Stillstand führen, wenn die Anwendung versucht, eine Ressource zum falschen Zeitpunkt zuzuordnen. Wenn die Anwendung versucht, auf die Ergebnisse eines Vorgangs zuzugreifen, bevor dieser Vorgang abgeschlossen ist, tritt ein Pipeline-Stillstand auf.

Wenn Sie einen Zuordnungsvorgang zum falschen Zeitpunkt ausführen, kann dies möglicherweise zu einem schwerwiegenden Leistungsverlust führen, indem die GPU und die CPU gezwungen werden, sich miteinander zu synchronisieren. Diese Synchronisierung erfolgt, wenn die Anwendung auf eine Ressource zugreifen möchte, bevor die GPU das Kopieren in eine Ressource abgeschlossen hat, die die CPU zuordnen kann.

Die CPU kann nur aus Ressourcen lesen, die mit dem flag D3D10_USAGE_STAGING erstellt wurden. Da mit diesem Flag erstellte Ressourcen nicht als Ausgaben der Pipeline festgelegt werden können, müssen die Daten in einer von der GPU generierten Ressource in eine Ressource kopiert werden, die mit dem Stagingflag erstellt wurde. Die Anwendung kann dazu die Methoden ID3D10Device::CopyResource oder ID3D10Device::CopySubresourceRegion verwenden, um den Inhalt einer Ressource in eine andere zu kopieren. Die Anwendung kann dann zugriff auf diese Ressource erhalten, indem sie die entsprechende Map-Methode aufruft. Wenn der Zugriff auf die Ressource nicht mehr benötigt wird, sollte die Anwendung dann die entsprechende Unmap-Methode aufrufen. Beispiel: ID3D10Texture2D::Map und ID3D10Texture2D::Unmap. Die verschiedenen Map-Methoden geben abhängig von den Eingabeflags bestimmte Werte zurück. Weitere Informationen finden Sie im Abschnitt Zuordnungsbemerkungen .

Hinweis

Wenn die Anwendung die Map-Methode aufruft, empfängt sie einen Zeiger auf die Ressourcendaten, auf die zugegriffen werden soll. Die Laufzeit stellt sicher, dass der Zeiger abhängig von der Featureebene eine bestimmte Ausrichtung aufweist. Für D3D_FEATURE_LEVEL_10_0 und höher wird der Zeiger auf 16 Bytes ausgerichtet. Bei niedrigeren als D3D_FEATURE_LEVEL_10_0 wird der Zeiger auf 4 Byte ausgerichtet. Die 16-Byte-Ausrichtung ermöglicht es der Anwendung, SSE-optimierte Vorgänge für die Daten nativ auszuführen, ohne neu auszurichten oder zu kopieren.

 

Überlegungen zur Leistung

Es empfiehlt sich, sich einen PC als einen Computer zu vorstellen, der als parallele Architektur mit zwei Standard Prozessortypen ausgeführt wird: eine oder mehrere CPU-Prozessoren und eine oder mehrere GPU's. Wie bei jeder parallelen Architektur wird die beste Leistung erzielt, wenn jeder Prozessor mit genügend Aufgaben geplant wird, um zu verhindern, dass er in den Leerlauf wechselt, und wenn die Arbeit eines Prozessors nicht auf die Arbeit eines anderen wartet.

Das schlechteste Szenario für GPU-/CPU-Parallelität ist die Notwendigkeit, einen Prozessor zu zwingen, auf die Ergebnisse der Arbeit eines anderen zu warten. Direct3D 10 versucht, diese Kosten zu entfernen, indem die Methoden ID3D10Device::CopyResource und ID3D10Device::CopySubresourceRegion asynchron sind. Die Kopie wurde nicht notwendigerweise bis zum Zeitpunkt der Rückgabe der Methode ausgeführt. Der Vorteil besteht darin, dass die Anwendung die Leistungskosten für das tatsächliche Kopieren der Daten erst dann bezahlt, wenn die CPU auf die Daten zugreift. Dies ist der Fall, wenn Map aufgerufen wird. Wenn die Map-Methode aufgerufen wird, nachdem die Daten tatsächlich kopiert wurden, tritt kein Leistungsverlust auf. Wenn dagegen die Map-Methode aufgerufen wird, bevor die Daten kopiert wurden, tritt ein Pipeline-Stillstand auf.

Asynchrone Aufrufe in Direct3D 10 (dies sind die überwiegende Mehrheit der Methoden und insbesondere Renderingaufrufe) werden in einem so genannten Befehlspuffer gespeichert. Dieser Puffer ist intern für den Grafiktreiber und wird für Batchaufrufe an die zugrunde liegende Hardware verwendet, damit der kostspielige Wechsel vom Benutzermodus zum Kernelmodus in Microsoft Windows so selten wie möglich erfolgt.

Der Befehlspuffer wird geleert, was in einer von vier Situationen wie folgt zu einem Wechsel zwischen Benutzer-/Kernelmodus führt.

  1. Present wird aufgerufen.
  2. ID3D10Device::Flush wird aufgerufen.
  3. Der Befehlspuffer ist voll; seine Größe ist dynamisch und wird vom Betriebssystem und dem Grafiktreiber gesteuert.
  4. Die CPU erfordert Zugriff auf die Ergebnisse eines Befehls, der auf die Ausführung im Befehlspuffer wartet.

Von den vier oben genannten Situationen ist Nummer vier die wichtigste für die Leistung. Wenn die Anwendung einen ID3D10Device::CopyResource - oder ID3D10Device::CopySubresourceRegion-Aufruf ausgibt, wird dieser Aufruf im Befehlspuffer in die Warteschlange gestellt. Wenn die Anwendung dann versucht, die Stagingressource zuzuordnen, die das Ziel des Kopieraufrufs war, bevor der Befehlspuffer geleert wurde, tritt ein Pipeline-Stillstand auf, da nicht nur der Copy-Methodenaufruf ausgeführt werden muss, sondern auch alle anderen gepufferten Befehle im Befehlspuffer ausgeführt werden müssen. Dies führt dazu, dass GPU und CPU synchronisiert werden, da die CPU auf den Zugriff auf die Stagingressource wartet, während die GPU den Befehlspuffer leert und schließlich die von der CPU benötigten Ressourcen auffüllt. Nachdem die GPU die Kopie abgeschlossen hat, beginnt die CPU mit dem Zugriff auf die Stagingressource, aber während dieser Zeit befindet sich die GPU im Leerlauf.

Wenn Sie dies häufig zur Laufzeit tun, wird die Leistung erheblich beeinträchtigt. Aus diesem Grund sollte die Zuordnung von Ressourcen, die mit der Standardverwendung erstellt wurden, mit Sorgfalt durchgeführt werden. Die Anwendung muss lange genug warten, bis der Befehlspuffer geleert wird, sodass alle diese Befehle ausgeführt werden, bevor versucht wird, die entsprechende Stagingressource zuzuordnen. Wie lange sollte die Anwendung warten? Mindestens zwei Frames, da dies die Parallelität zwischen den CPU(n) und der GPU ermöglicht, die maximal genutzt werden kann. Die GPU funktioniert so, dass während die Anwendung Frame N verarbeitet, indem Aufrufe an den Befehlspuffer übermittelt werden, die GPU mit der Ausführung der Aufrufe aus dem vorherigen Frame N-1 beschäftigt ist.

Wenn eine Anwendung also eine Ressource zuordnen möchte, die aus dem Videospeicher stammt und ID3D10Device::CopyResource oder ID3D10Device::CopySubresourceRegion bei Frame N aufruft, beginnt dieser Aufruf tatsächlich bei Frame N+1, wenn die Anwendung Aufrufe für den nächsten Frame übermittelt. Der Kopiervorgang sollte abgeschlossen sein, wenn die Anwendung frame N+2 verarbeitet.

Frame GPU-/CPU-Status
N
  • CPU-Probleme rendern Aufrufe für den aktuellen Frame.
N+1
  • GPU führt Aufrufe aus, die während Frame N von der CPU gesendet werden.
  • CPU-Probleme rendern Aufrufe für den aktuellen Frame.
N+2
  • Die GPU hat die Ausführung von Aufrufen abgeschlossen, die während frame N von der CPU gesendet wurden. Ergebnisse bereit.
  • GPU führt Aufrufe aus, die von der CPU während frame N+1 gesendet werden.
  • CPU-Probleme rendern Aufrufe für den aktuellen Frame.
N+3
  • Die GPU hat die Ausführung von Aufrufen abgeschlossen, die von der CPU während frame N+1 gesendet wurden. Ergebnisse bereit.
  • GPU führt Aufrufe aus, die von der CPU während Frame N+2 gesendet werden.
  • CPU-Probleme rendern Aufrufe für den aktuellen Frame.
N+4 ...

 

Ressourcen (Direct3D 10)