DirectX-Grafikinfrastruktur (DXGI): Bewährte Methoden

Microsoft DirectX Graphics Infrastructure (DXGI) ist ein neues Subsystem, das mit Windows Vista eingeführt wurde und einige der Aufgaben auf niedriger Ebene kapselt, die für Direct3D 10, 10.1, 11 und 11.1 erforderlich sind. Aus der Perspektive eines Direct3D 9-Programmierers umfasst DXGI den größten Teil des Codes für Enumeration, Swapchainerstellung und Präsentation, der zuvor in die Direct3D 9-APIs gepackt wurde. Wenn Sie eine App zu DXGI und Direct3D 10.x und Direct3D 11.x portieren, müssen Sie einige Überlegungen berücksichtigen, um sicherzustellen, dass der Prozess reibungslos ausgeführt wird.

In diesem Artikel werden wichtige Portierungsprobleme erläutert.

Full-Screen Probleme

Beim Portieren von Direct3D 9 zu DXGI und zu Direct3D 10.x oder Direct3D 11.x können Probleme im Zusammenhang mit dem Wechsel vom Fenstermodus zum Vollbildmodus häufig Kopfschmerzen für Entwickler verursachen. Die Standard Probleme treten auf, da Direct3D 9-Anwendungen im Gegensatz zu DXGI-Anwendungen einen praktischeren Ansatz zum Nachverfolgen von Fensterstilen und Fensterzuständen erfordern. Wenn der modusverändernde Code für die Ausführung in DXGI portiert wird, führt dies häufig zu unerwartetem Verhalten.

Häufig haben Direct3D 9-Anwendungen den Übergang in den Vollbildmodus verarbeitet, indem sie die Auflösung des Frontpuffers festlegen, das Gerät in den exklusiven Vollbildmodus erzwingen und dann die Auflösungen des Hintergrundpuffers entsprechend festlegen. Für Änderungen an der Fenstergröße wurde ein separater Pfad verwendet, da sie immer dann über den Fensterprozess verwaltet werden mussten, wenn die Anwendung eine WM_SIZE Nachricht empfangen hat.

DXGI versucht, diesen Ansatz zu vereinfachen, indem die beiden Fälle kombiniert werden. Wenn der Fensterrahmen beispielsweise im Fenstermodus gezogen wird, empfängt die Anwendung eine WM_SIZE Meldung. DXGI fängt diese Nachricht ab und ändert automatisch die Größe des Frontpuffers. Die Anwendung muss lediglich IDXGISwapChain::ResizeBuffers aufrufen, um die Größe des Hintergrundpuffers auf die Größe zu ändern, die als Parameter in WM_SIZE übergeben wurde. Wenn die Anwendung zwischen Vollbildmodus und Fenstermodus wechseln muss, kann die Anwendung auch einfach IDXGISwapChain::SetFullscreenState aufrufen. DXGI ändert die Größe des Frontpuffers an den neu ausgewählten Vollbildmodus und sendet eine WM_SIZE Nachricht an die Anwendung. Die Anwendung ruft erneut ResizeBuffers auf, genau wie beim Ziehen des Fensterrahmens.

Die Methodik der vorhergehenden Erläuterung folgt einem ganz bestimmten Pfad. DXGI legt die Vollbildauflösung standardmäßig auf die Desktopauflösung fest. Viele Anwendungen wechseln jedoch zu einer bevorzugten Vollbildauflösung. In diesem Fall stellt DXGI IDXGISwapChain::ResizeTarget bereit. Dies sollte vor dem Aufruf von SetFullscreenState aufgerufen werden. Obwohl diese Methoden in entgegengesetzter Reihenfolge aufgerufen werden können (zuerst SetFullscreenState , gefolgt von ResizeTarget), führt dies dazu, dass eine zusätzliche WM_SIZE Nachricht an die Anwendung gesendet wird. (Dies kann auch zu Flimmern führen, da DXGI gezwungen werden könnte, zwei Modusänderungen durchzuführen.) Nach dem Aufruf von SetFullscreenState empfiehlt es sich, ResizeTarget erneut aufzurufen, wobei der RefreshRate-Member von DXGI_MODE_DESC auf Null gesetzt wurde. Dies entspricht einer Anweisung ohne Operation in DXGI, kann jedoch Probleme mit der Aktualisierungsrate vermeiden, die als Nächstes erläutert werden.

Im Vollbildmodus ist der Desktopfenster-Manager (DWM) deaktiviert. DXGI kann einen Flip durchführen, um den Inhalt des Hintergrundpuffers darzustellen, anstatt einen Blit auszuführen, was im Fenstermodus der Vorgang wäre. Dieser Leistungszuwachs kann jedoch rückgängig machen, wenn bestimmte Anforderungen nicht erfüllt sind. Um sicherzustellen, dass DXGI einen Flip anstelle eines Blits ausführt, müssen der Vorder- und der Hintergrundpuffer identisch dimensionieren. Wenn die Anwendung ihre WM_SIZE Meldungen ordnungsgemäß verarbeitet, sollte dies kein Problem darstellen. Außerdem müssen die Formate identisch sein.

Das Problem für die meisten Anwendungen ist die Aktualisierungsrate. Die Aktualisierungsrate, die im Aufruf von ResizeTarget angegeben wird, muss eine Aktualisierungsrate sein, die vom IDXGIOutput-Objekt aufgezählt wird, das von der Swapchain verwendet wird. DXGI kann diesen Wert automatisch berechnen, wenn die Anwendung den RefreshRate-Member von DXGI_MODE_DESC , der an ResizeTarget übergeben wird, auf null stellt. Es ist wichtig, nicht davon auszugehen, dass bestimmte Aktualisierungsraten immer unterstützt werden, und einen Wert einfach hart zu codieren. Häufig wählen Entwickler 60 Hz als Aktualisierungsrate aus, ohne zu wissen, dass die aufgezählte Aktualisierungsrate des Monitors ungefähr 60.000 /1.001 Hz vom Monitor beträgt. Wenn die Aktualisierungsrate nicht der erwarteten Aktualisierungsrate von 60 entspricht, ist DXGI gezwungen, anstelle eines Flips einen Blit im Vollbildmodus auszuführen.

Das letzte Problem, mit dem Entwickler häufig konfrontiert werden, ist das Ändern von Vollbildauflösungen, während sie im Vollbildmodus verbleiben. Der Aufruf von ResizeTarget und SetFullscreenState ist manchmal erfolgreich, aber die Vollbildauflösung bleibt die Desktopauflösung. Außerdem können Entwickler eine Vollbild-Swapchain erstellen und eine bestimmte Auflösung festlegen, nur um festzustellen, dass DXGI standardmäßig auf die Desktopauflösung festgelegt ist, unabhängig von den übergebenen Zahlen. Sofern nicht anders angegeben, verwendet DXGI standardmäßig die Desktopauflösung für Vollbild-Swapchains. Beim Erstellen einer Vollbild-Swapchain muss das Flags-Element der DXGI_SWAP_CHAIN_DESC-Struktur auf DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH festgelegt werden, um das Standardverhalten von DXGI außer Kraft zu setzen. Dieses Flag kann auch an ResizeTarget übergeben werden, um diese Funktionalität dynamisch zu aktivieren oder zu deaktivieren.

Mehrere Monitore

Wenn Sie DXGI mit mehreren Monitoren verwenden, müssen Sie zwei Regeln befolgen.

Die erste Regel gilt für die Erstellung von zwei oder mehr Vollbild-Swapchains auf mehreren Monitoren. Beim Erstellen solcher Swapchains empfiehlt es sich, alle Swapchains als Fenster zu erstellen und sie dann auf Vollbild festzulegen. Wenn Swapchains im Vollbildmodus erstellt werden, führt die Erstellung einer zweiten Swapchain dazu, dass eine Modusänderung an die erste Swapchain gesendet wird, was zum Beenden des Vollbildmodus führen kann.

Die zweite Regel gilt für Ausgaben. Achten Sie auf Ausgaben, die beim Erstellen von Swapchains verwendet werden. Mit DXGI steuert das IDXGIOutput-Objekt , welches die Swapchain überwacht, wenn sie im Vollbildmodus angezeigt wird. Im Gegensatz zu DXGI hatte Direct3D 9 kein Konzept von Ausgaben.

Fensterstile und DXGI

Direct3D 9-Anwendungen hatten beim Wechseln zwischen Vollbild- und Fenstermodus viel Arbeit zu erledigen. Ein Großteil dieser Arbeit umfasste das Ändern von Fensterstilen zum Hinzufügen und Entfernen von Rahmen, zum Hinzufügen von Bildlaufleisten usw. Wenn Anwendungen zu DXGI und Direct3D 10.x oder Direct3D 11.x portiert werden, bleibt dieser Code häufig erhalten. Abhängig von den vorgenommenen Änderungen kann der Wechsel zwischen den Modi zu unerwartetem Verhalten führen. Wenn Sie beispielsweise in den Fenstermodus wechseln, verfügt die Anwendung möglicherweise nicht mehr über einen Fensterrahmen oder Fensterrahmen, obwohl code vorhanden ist, der diese Stile speziell festlegt. Dies liegt daran, dass DXGI jetzt einen Großteil dieses Stilwechsels selbst verarbeitet. Die manuelle Einstellung von Fensterformatvorlagen kann DXGI beeinträchtigen, was zu unerwartetem Verhalten führen kann.

Das empfohlene Verhalten besteht darin, so wenig Arbeit wie möglich zu erledigen und DXGI die meisten Interaktionen mit den Fenstern verarbeiten zu lassen. Wenn die Anwendung jedoch ein eigenes Fensterverhalten verarbeiten muss, kann IDXGIFactory::MakeWindowAssociation verwendet werden, um DXGI anzuweisen, einen Teil der automatischen Fensterbehandlung zu deaktivieren.

Multithreading und DXGI

Bei der Verwendung von DXGI in einer Multithreadanwendung ist besondere Vorsicht zu beachten, um sicherzustellen, dass keine Deadlocks auftreten. Aufgrund der engen Interaktion von DXGI mit Fenstern werden gelegentlich Fenstermeldungen an das zugeordnete Anwendungsfenster gesendet. DXGI erfordert, dass die Fensteränderungen vorgenommen werden, bevor es fortgesetzt werden kann, sodass SendMessage verwendet wird, bei dem es sich um einen synchronen Aufruf handelt. Die Anwendung muss die Fensternachricht verarbeiten, bevor SendMessage zurückgegeben wird.

In einer Anwendung, bei der DXGI-Aufrufe und die Nachrichtenpumpe sich im selben Thread (oder einer Singlethread-Anwendung) befinden, muss wenig getan werden. Wenn sich der DXGI-Aufruf im selben Thread wie die Nachrichtenpumpe befindet, ruft SendMessage die WindowProc des Fensters auf. Dadurch wird die Nachrichtenpumpe umgangen, und die Ausführung kann nach dem Aufruf von SendMessage fortgesetzt werden. Denken Sie daran, dass IDXGISwapChain-Aufrufe wie IDXGISwapChain::P resent ebenfalls als DXGI-Aufrufe gelten. DXGI kann die Arbeit von ResizeBuffers oder ResizeTarget zurückstellen, bis Present aufgerufen wird.

Wenn sich der DXGI-Aufruf und die Nachrichtenpumpe in verschiedenen Threads befinden, muss darauf geachtet werden, dass Deadlocks vermieden werden. Wenn sich die Nachrichtenpumpe und SendMessage in verschiedenen Threads befinden, fügt SendMessage der Nachrichtenwarteschlange des Fensters eine Nachricht hinzu und wartet, bis das Fenster diese Nachricht verarbeitet. Wenn die Fensterprozedur ausgelastet ist oder nicht von der Nachrichtenpumpe aufgerufen wird, wird die Nachricht möglicherweise nie verarbeitet, und DXGI wartet auf unbestimmte Zeit.

Wenn z. B. eine Anwendung, deren Nachrichten in einem Thread und ihr Rendering auf einem anderen thread vorhanden sind, kann sie den Modus ändern. Der Nachrichtenpumpthread weist den Renderingthread an, den Modus zu ändern, und wartet, bis die Modusänderung abgeschlossen ist. Der Renderingthread ruft jedoch DXGI-Funktionen auf, die wiederum SendMessage aufrufen, die blockiert, bis die Nachrichtenpumpe die Nachricht verarbeitet. Ein Deadlock tritt auf, weil beide Threads jetzt blockiert sind und aufeinander warten. Um dies zu vermeiden, blockieren Sie niemals die Nachrichtenpumpe. Wenn ein Block unvermeidlich ist, sollte die gesamte DXGI-Interaktion im selben Thread wie die Nachrichtenpumpe erfolgen.

Gamma und DXGI

Obwohl Gamma möglicherweise am besten in Direct3D 10.x oder Direct3D 11.x mithilfe von SRGB-Texturen behandelt werden kann, kann die Gammarampe dennoch für Entwickler nützlich sein, die einen anderen Gammawert als 2.2 wünschen oder ein Renderzielformat verwenden, das SRGB nicht unterstützt. Beachten Sie zwei Probleme beim Festlegen der Gammarampe über DXGI. Das erste Problem besteht darin, dass die an IDXGIOutput::SetGammaControl übergebenen Rampenwerte Gleitkommawerte und keine WORD-Werte sind. Stellen Sie außerdem sicher, dass von Direct3D 9 portierter Code nicht versucht, in WORD-Werte zu konvertieren, bevor Sie diese an SetGammaControl übergeben.

Das zweite Problem besteht darin, dass SetGammaControl nach dem Wechsel in den Vollbildmodus möglicherweise nicht funktioniert, abhängig vom verwendeten IDXGIOutput-Objekt . Beim Wechsel in den Vollbildmodus erstellt DXGI ein neues Ausgabeobjekt und verwendet das -Objekt für alle nachfolgenden Vorgänge in der Ausgabe. Wenn Sie SetGammaControl für eine Ausgabe aufrufen, die vor einem Vollbildmodusschalter aufgezählt wird, wird der Anruf nicht an die Ausgabe gerichtet, die DXGI derzeit verwendet. Um dies zu vermeiden, rufen Sie IDXGISwapChain::GetContainingOutput auf, um die aktuelle Ausgabe abzurufen, und rufen Sie dann SetGammaControl aus dieser Ausgabe auf, um das richtige Verhalten zu erhalten.

Informationen zur Verwendung der Gammakorrektur finden Sie unter Verwenden der Gammakorrektur.

DXGI 1.1

Die Direct3D 11-Runtime, die in Windows 7 enthalten und unter Windows Vista installiert ist, enthält Version 1.1 von DXGI. Mit diesem Update werden Definitionen für eine Reihe neuer Formate (insbesondere BGRA, 10-Bit-X2-Verzerrung und Direct3D 11 BC6H- und BC7-Texturkomprimierung) sowie eine neue Version der DXGI-Factory- und Adapterschnittstellen (CreateDXGIFactory1, IDXGIFactory1, IDXGIAdapter1) zum Aufzählen von Remotedesktopverbindungen hinzugefügt.

Wenn Sie Direct3D 11 verwenden, verwendet die Runtime standardmäßig DXGI 1.1, wenn D3D11CreateDevice oder D3D11CreateDeviceAndSwapChain mit einem NULL IDXGIAdapter-Zeiger aufgerufen wird. Die Verwendung von DXGI 1.0 und DXGI 1.1 im gleichen Prozess wird nicht unterstützt. Das Mischen von DXGI-Objektinstanzen aus verschiedenen Fabriken im selben Prozess wird ebenfalls nicht unterstützt. Wenn Sie DirectX 11 verwenden, verwendet jede explizite Verwendung der DXGI-Schnittstellen daher eine IDXGIFactory1 , die vom CreateDXGIFactory1-Einstiegspunkt in "DXGI.DLL" erstellt wurde, um sicherzustellen, dass die Anwendung immer DXGI 1.1 verwendet.

DXGI 1.2

Die Direct3D 11.1-Runtime, die in Windows 8 enthält auch Version 1.2 von DXGI.

DXGI 1.2 ermöglicht die folgenden Features:

  • Stereorendering

  • Formate mit 16 Bit pro Pixel

    • DXGI_FORMAT_B5G6R5_UNORM und DXGI_FORMAT_B5G5R5A1_UNORM werden jetzt vollständig unterstützt.
    • Ein neues DXGI_FORMAT_B5G5R5A1_UNORM-Format wurde hinzugefügt.
  • Videoformate

  • Neue DXGI-Schnittstellen

Weitere Informationen zu DXGI 1.2-Features finden Sie unter DXGI 1.2-Verbesserungen.