Interoperabilität von DirectX und XAML

Hinweis

Dieses Thema bezieht sich auf Universelle Windows-Plattform Spiele und Apps (UWP) und auf Typen in den Windows.UI.Xaml.Xxx Namespaces (nicht Microsoft.UI.Xaml.Xxx).

Sie können XAML (Extensible Application Markup Language) zusammen mit Microsoft DirectX in Ihrem Universelle Windows-Plattform-Spiel oder Ihrer App (UWP) verwenden. Durch die Kombination von XAML und DirectX können Sie flexible Benutzeroberflächenframeworks erstellen, die mit Ihren von DirectX gerenderten Inhalten zusammenarbeiten. Dies ist besonders nützlich für grafikintensive Apps. In diesem Thema wird die Struktur einer UWP-App erläutert, die DirectX verwendet, und es werden die wichtigen Typen identifiziert, die beim Erstellen Ihrer UWP-App für die Arbeit mit DirectX verwendet werden.

Wenn sich Ihre App hauptsächlich auf das 2D-Rendering konzentriert, sollten Sie die Win2D-Windows-Runtime bibliothek verwenden. Diese Bibliothek wird von Microsoft verwaltet und basiert auf der Direct2D-Kerntechnologie . Win2D vereinfacht das Verwendungsmuster zum Implementieren von 2D-Grafiken erheblich und enthält hilfreiche Abstraktionen für einige der in diesem Dokument beschriebenen Techniken. Ausführlichere Informationen finden Sie auf der Projektseite. Bei diesem Dokument handelt es sich um einen Leitfaden für App-Entwickler, die sich gegen die Verwendung von Win2D entschieden haben.

Hinweis

DirectX-APIs sind nicht als Windows-Runtime-Typen definiert, aber Sie können C++/WinRT verwenden, um XAML-UWP-Apps zu entwickeln, die mit DirectX zusammenarbeiten. Wenn Sie den Code, der DirectX aufruft, in eine eigene C++/WinRT-Windows-Runtime-Komponente (WRC) einteilen, können Sie diesen WRC in einer UWP-App (sogar in einer C#-App) verwenden, die dann XAML und DirectX kombiniert.

XAML und DirectX

DirectX bietet zwei leistungsstarke Bibliotheken für 2D- und 3D-Grafiken: Direct2D und Direct3D. Obwohl XAML grundlegende 2D-Grundtypen und Effekte unterstützt, benötigen viele Modellierungs- und Gaming-Apps komplexere Grafikunterstützung. Für diese können Sie Direct2D und Direct3D verwenden, um komplexere Grafiken zu rendern, und XAML für traditionellere Benutzeroberflächenelemente verwenden.

Wenn Sie benutzerdefinierte XAML- und DirectX-Interop implementieren, müssen Sie diese beiden Konzepte kennen.

  • Freigegebene Oberflächen sind durch XAML definierte Größenbereiche der Anzeige, die Sie mithilfe von DirectX indirekt mithilfe von Windows::UI::Xaml::Media::ImageSource-Typen zeichnen können. Bei freigegebenen Oberflächen steuern Sie nicht den genauen Zeitpunkt, wann neue Inhalte auf dem Bildschirm angezeigt werden. Stattdessen werden Updates auf der freigegebenen Oberfläche mit den Updates des XAML-Frameworks synchronisiert.
  • Eine Swapchain stellt eine Sammlung von Puffern dar, die zum Anzeigen von Grafiken mit minimaler Latenz verwendet werden. In der Regel wird eine Swapchain mit 60 Frames pro Sekunde getrennt vom UI-Thread aktualisiert. Eine Swapchain benötigt jedoch mehr Arbeitsspeicher- und CPU-Ressourcen, um schnelle Updates zu unterstützen, und ist relativ schwierig zu verwenden, da Sie mehrere Threads verwalten müssen.

Überlegen Sie, wofür Sie DirectX verwenden. Werden Sie es verwenden, um ein einzelnes Steuerelement zu kombinieren oder zu animieren, das in die Abmessungen des Anzeigefensters passt? Wird eine Ausgabe enthalten sein, die wie in einem Spiel in Echtzeit gerendert und gesteuert werden muss? Wenn dies der Fall ist, müssen Sie wahrscheinlich eine Swapchain implementieren. Andernfalls sollten Sie eine freigegebene Oberfläche verwenden.

Nachdem Sie festgelegt haben, wie Sie DirectX verwenden möchten, verwenden Sie einen der folgenden Windows-Runtime Typen, um DirectX-Rendering in Ihre UWP-App zu integrieren.

  • Wenn Sie ein statisches Bild erstellen oder ein komplexes Bild in ereignisgesteuerten Intervallen zeichnen möchten, zeichnen Sie mit Windows::UI::Xaml::Media::Imaging::SurfaceImageSource auf eine freigegebene Oberfläche. Dieser Typ behandelt eine große DirectX-Zeichnungsfläche. In der Regel wird dieser Typ beim Erstellen eines Bilds oder einer Textur als Bitmap verwendet, um in einem Dokument oder als UI-Element angezeigt zu werden. Der Typ ist in Szenarien mit Echtzeitinteraktivität wie hochleistungsfähige Spiele nicht gut geeignet. Dies liegt daran, dass Updates an einem SurfaceImageSource-Objekt mit XAML-Benutzeroberflächenupdates synchronisiert werden. Dies kann eine Latenz für das visuelle Feedback, das Sie dem Benutzer bereitstellen, wie z. B. eine schwankende Bildfrequenz oder eine wahrgenommene schlechte Reaktion auf Echtzeiteingaben, mit sich bringen. Updates sind jedoch immer noch schnell genug für dynamische Steuerelemente oder Datensimulationen.
  • Wenn das Bild größer als die bereitgestellte Bildschirmfläche ist und vom Benutzer verschoben oder vergrößert werden kann, verwenden Sie Windows::UI::Xaml::Media::Imaging::VirtualSurfaceImageSource. Dieser Typ behandelt eine große DirectX-Zeichenfläche, die größer als der Bildschirm ist. Wie SurfaceImageSource wird diese Klasse für die dynamische Erstellung eines komplexen Bilds oder Steuerelements verwendet. Und wie SurfaceImageSource ist diese Klasse nicht gut für hochleistungsfähige Spiele geeignet. Beispielhafte XAML-Elemente, welche die Klasse VirtualSurfaceImageSource verwenden können, sind Kartensteuerelemente oder Viewer für große Dokumente mit hoher Bilddichte.
  • Wenn Sie DirectX verwenden, um in Echtzeit aktualisierte Grafiken zu präsentieren, oder in einer Situation, in der die Updates in regelmäßigen Intervallen mit niedriger Latenz erfolgen müssen, verwenden Sie die SwapChainPanel-Klasse , damit Sie die Grafiken aktualisieren können, ohne mit dem AKTUALISIERUNGszeitgeber des XAML-Frameworks synchronisiert zu werden. Mit SwapChainPanel können Sie direkt auf die Swapchain des Grafikgeräts (IDXGISwapChain1) zugreifen und XAML über dem Renderziel überlagern. SwapChainPanel eignet sich hervorragend für Spiele und DirectX-Vollbild-Apps, die eine XAML-basierte Benutzeroberfläche erfordern. Sie müssen DirectX gut kennen, um diesen Ansatz verwenden zu können, einschließlich der Technologien Microsoft DirectX Graphics Infrastructure (DXGI), Direct2D und Direct3D. Weitere Informationen finden Sie unter Programmierhandbuch für Direct3D 11.

SurfaceImageSource

SurfaceImageSource bietet Ihnen eine freigegebene DirectX-Oberfläche, auf die Sie zeichnen können. Anschließend werden die Bits in App-Inhalte zusammengefasst.

Tipp

Die Beispielanwendungen Zeilenabstand (DirectWrite) und Herunterladbare Schriftarten (DirectWrite) veranschaulichen SurfaceImageSource.

Auf sehr hoher Ebene sehen Sie hier den Prozess zum Erstellen und Aktualisieren einer SurfaceImageSource.

  • Erstellen Sie ein Direct 3D-Gerät, ein Direct 2D-Gerät und einen Direct 2D-Gerätekontext.
  • Erstellen Sie eine SurfaceImageSource, und legen Sie das Direct 2D-Gerät (oder Direct 3D) darauf fest.
  • Beginnen Sie mit dem Zeichnen auf surfaceImageSource , um eine DXGI-Oberfläche zu erhalten.
  • Zeichnen Sie mit Direct2D (oder Direct3D) auf die DXGI-Oberfläche.
  • Beenden Sie das Zeichnen auf surfaceImageSource , wenn Sie fertig sind.
  • Legen Sie surfaceImageSource auf einem XAML-Image oder ImageBrush fest, um es in Ihrer XAML-Benutzeroberfläche anzuzeigen.

Im Folgenden finden Sie einen tieferen Einblick in diese Schritte mit Quellcodebeispielen.

  1. Sie können dem unten gezeigten und beschriebenen Code folgen, indem Sie ein neues Projekt in Microsoft Visual Studio erstellen. Erstellen Sie ein Projekt mit einer leeren App (C++/WinRT). Die neueste allgemein verfügbare Version von Windows SDK (d. h. keine Vorschauversion).

  2. Öffnen Sie pch.h, und fügen Sie die folgenden Includes unterhalb der bereits vorhandenen hinzu.

    // pch.h
    ...
    #include <d3d11_4.h>
    #include <d2d1_1.h>
    #include <windows.ui.xaml.media.dxinterop.h>
    #include <winrt/Windows.UI.Xaml.Media.Imaging.h>
    
  3. Fügen Sie die using unten gezeigte Direktive oben MainPage.cppin hinzu, unterhalb der bereits vorhandenen. Ersetzen Sie auch in MainPage.cppdie vorhandene Implementierung von MainPage::ClickHandler durch die unten gezeigte Auflistung. Der Code erstellt ein Direct 3D-Gerät, ein Direct 2D-Gerät und einen Direct 2D-Gerätekontext. Dazu werden D3D11CreateDevice, D2D1CreateDevice und ID2D1Device::CreateDeviceContext aufgerufen.

    // MainPage.cpp | paste this below the existing using directives
    using namespace Windows::UI::Xaml::Media::Imaging;
    
    // MainPage.cpp | paste this to replace the existing MainPage::ClickHandler
    void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
    {
        myButton().Content(box_value(L"Clicked"));
    
        uint32_t creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
    
        D3D_FEATURE_LEVEL featureLevels[] =
        {
            D3D_FEATURE_LEVEL_11_1,
            D3D_FEATURE_LEVEL_11_0,
            D3D_FEATURE_LEVEL_10_1,
            D3D_FEATURE_LEVEL_10_0,
            D3D_FEATURE_LEVEL_9_3,
            D3D_FEATURE_LEVEL_9_2,
            D3D_FEATURE_LEVEL_9_1
        };
    
        // Create the Direct3D device.
        winrt::com_ptr<::ID3D11Device> d3dDevice;
        D3D_FEATURE_LEVEL supportedFeatureLevel;
        winrt::check_hresult(::D3D11CreateDevice(
            nullptr,
            D3D_DRIVER_TYPE_HARDWARE,
            0,
            creationFlags,
            featureLevels,
            ARRAYSIZE(featureLevels),
            D3D11_SDK_VERSION,
            d3dDevice.put(),
            &supportedFeatureLevel,
            nullptr)
        );
    
        // Get the DXGI device.
        winrt::com_ptr<::IDXGIDevice> dxgiDevice{
            d3dDevice.as<::IDXGIDevice>() };
    
        // Create the Direct2D device and a corresponding context.
        winrt::com_ptr<::ID2D1Device> d2dDevice;
        ::D2D1CreateDevice(dxgiDevice.get(), nullptr, d2dDevice.put());
    
        winrt::com_ptr<::ID2D1DeviceContext> d2dDeviceContext;
        winrt::check_hresult(
            d2dDevice->CreateDeviceContext(
                D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
                d2dDeviceContext.put()
            )
        );
    }
    
  4. Fügen Sie als Nächstes Code hinzu, um eine SurfaceImageSource zu erstellen, und legen Sie das Direct 2D-Gerät (oder Direct 3D) darauf fest, indem Sie ISurfaceImageSourceNativeWithD2D::SetDevice aufrufen.

    Hinweis

    Wenn Sie aus einem Hintergrundthread auf SurfaceImageSource zeichnen, müssen Sie auch sicherstellen, dass auf dem DXGI-Gerät der Multithreadzugriff aktiviert ist (wie im folgenden Code dargestellt). Aus Leistungsgründen sollten Sie dies nur tun, wenn Sie aus einem Hintergrundthread zeichnen.

    Legen Sie die Größe der gemeinsam genutzten Fläche fest, indem Sie die Werte für die Höhe und Breite an den Konstruktor der Klasse SurfaceImageSource übergeben. Sie können ebenfalls festlegen, ob für die gemeinsam genutzte Fläche Alpha-Unterstützung (Deckkraft) erforderlich ist.

    Um das Gerät festzulegen und die Zeichnungsvorgänge auszuführen, benötigen wir einen Zeiger auf ISurfaceImageSourceNativeWithD2D. Um eine abzurufen, fragen Sie das SurfaceImageSource-Objekt nach seiner zugrunde liegenden ISurfaceImageSourceNativeWithD2D-Schnittstelle ab.

    // MainPage.cpp | paste this at the end of MainPage::ClickHandler
    SurfaceImageSource surfaceImageSource(500, 500);
    
    winrt::com_ptr<::ISurfaceImageSourceNativeWithD2D> sisNativeWithD2D{
        surfaceImageSource.as<::ISurfaceImageSourceNativeWithD2D>() };
    
    // Associate the Direct2D device with the SurfaceImageSource.
    sisNativeWithD2D->SetDevice(d2dDevice.get());
    
    // To enable multi-threaded access (optional)
    winrt::com_ptr<::ID3D11Multithread> d3dMultiThread{
        d3dDevice.as<::ID3D11Multithread>() };
    d3dMultiThread->SetMultithreadProtected(true);
    
  5. Rufen Sie ISurfaceImageSourceNativeWithD2D::BeginDraw auf, um eine DXGI-Oberfläche (eine IDXGISurface-Schnittstelle ) abzurufen. Sie können ISurfaceImageSourceNativeWithD2D::BeginDraw (und die späteren Zeichnungsbefehle) aus einem Hintergrundthread aufrufen, wenn Sie den Multithreadzugriff aktiviert haben. In diesem Schritt erstellen Sie auch eine Bitmap aus der DXGI-Oberfläche und legen diese in den Direct 2D-Gerätekontext fest.

    Im offset-Parameter gibt ISurfaceImageSourceNativeWithD2D::BeginDraw den Punktoffset (einen x,y-Wert) des aktualisierten Zielrechtecks zurück. Sie können diesen Offset verwenden, um zu bestimmen, wo Ihre aktualisierten Inhalte mit ID2D1DeviceContext gezeichnet werden sollen.

    // MainPage.cpp | paste this at the end of MainPage::ClickHandler
    winrt::com_ptr<::IDXGISurface> dxgiSurface;
    
    RECT updateRect{ 0, 0, 500, 500 };
    POINT offset{ 0, 0 };
    HRESULT beginDrawHR = sisNativeWithD2D->BeginDraw(
        updateRect,
        __uuidof(::IDXGISurface),
        dxgiSurface.put_void(),
        &offset);
    
    // Create render target.
    winrt::com_ptr<::ID2D1Bitmap1> bitmap;
    winrt::check_hresult(
        d2dDeviceContext->CreateBitmapFromDxgiSurface(
            dxgiSurface.get(),
            nullptr,
            bitmap.put()
        )
    );
    
    // Set context's render target.
    d2dDeviceContext->SetTarget(bitmap.get());
    
  6. Verwenden Sie den Direct 2D-Gerätekontext, um den Inhalt von SurfaceImageSource zu zeichnen. Es wird nur der Bereich gezeichnet, der im vorherigen Schritt im parameter updateRect für die Aktualisierung angegeben wurde.

    // MainPage.cpp | paste this at the end of MainPage::ClickHandler
    if (beginDrawHR == DXGI_ERROR_DEVICE_REMOVED ||
        beginDrawHR == DXGI_ERROR_DEVICE_RESET ||
        beginDrawHR == D2DERR_RECREATE_TARGET)
    {
        // The Direct3D and Direct2D devices were lost and need to be re-created.
        // Recovery steps are:
        // 1) Re-create the Direct3D and Direct2D devices
        // 2) Call ISurfaceImageSourceNativeWithD2D::SetDevice with the new Direct2D
        //    device
        // 3) Redraw the contents of the SurfaceImageSource
    }
    else if (beginDrawHR == E_SURFACE_CONTENTS_LOST)
    {
        // The devices were not lost but the entire contents of the surface
        // were. Recovery steps are:
        // 1) Call ISurfaceImageSourceNativeWithD2D::SetDevice with the Direct2D 
        //    device again
        // 2) Redraw the entire contents of the SurfaceImageSource
    }
    else
    {
        // Draw using Direct2D context.
        d2dDeviceContext->BeginDraw();
    
        d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Orange));
    
        winrt::com_ptr<::ID2D1SolidColorBrush> brush;
        winrt::check_hresult(d2dDeviceContext->CreateSolidColorBrush(
            D2D1::ColorF(D2D1::ColorF::Chocolate),
            D2D1::BrushProperties(0.8f),
            brush.put()));
    
        D2D1_SIZE_F const size{ 500, 500 };
        D2D1_RECT_F const rect{ 100.0f, 100.0f, size.width - 100.0f, size.height - 100.0f };
        d2dDeviceContext->DrawRectangle(rect, brush.get(), 100.0f);
    
        d2dDeviceContext->EndDraw();
    }
    
  7. Rufen Sie ISurfaceImageSourceNativeWithD2D::EndDraw auf, um die Bitmap abzuschließen (Sie müssen ISurfaceImageSourceNativeWithD2D::EndDraw nur über den UI-Thread aufrufen). Legen Sie dann surfaceImageSource auf einem XAML-Image (oder ImageBrush) fest, um es auf Der XAML-Benutzeroberfläche anzuzeigen.

    // MainPage.cpp | paste this at the end of MainPage::ClickHandler
    sisNativeWithD2D->EndDraw();
    
    // The SurfaceImageSource object's underlying 
    // ISurfaceImageSourceNativeWithD2D object will contain the completed bitmap.
    
    theImage().Source(surfaceImageSource);
    

    Hinweis

    Der Aufruf von SurfaceImageSource::SetSource (geerbt von IBitmapSource::SetSource) löst derzeit eine Ausnahme aus. Rufen Sie sie nicht aus Ihrem SurfaceImageSource-Objekt auf.

    Hinweis

    Vermeiden Sie das Zeichnen in SurfaceImageSource , während Ihr Fenster ausgeblendet oder inaktiv ist. Andernfalls schlagen ISurfaceImageSourceNativeWithD2D-APIs fehl. Behandeln Sie Ereignisse rund um die Fenstersichtbarkeit und das Anhalten von Anwendungen, um dies zu erreichen.

  8. Fügen Sie schließlich das folgende Image-Element innerhalb des vorhandenen XAML-Markups in MainPage.xamlhinzu.

    <!-- MainPage.xaml -->
    ...
    <Image x:Name="theImage" Width="500" Height="500" />
    ...
    
  9. Jetzt können Sie die App erstellen und ausführen. Klicken Sie auf die Schaltfläche, um den Inhalt der surfaceImageSource anzuzeigen, die im Bild angezeigt wird.

    Ein dicker, dunkelorangefarbener rechteckiger Umriss vor einem helleren orangefarbenen Hintergrund

VirtualSurfaceImageSource

VirtualSurfaceImageSource erweitert SurfaceImageSource und ist für Szenarien vorgesehen, in denen der Inhalt potenziell zu groß ist, um auf den Bildschirm zu passen (und/oder zu groß, um als einzelne Textur in den Videospeicher zu passen), sodass der Inhalt virtualisiert werden muss, um optimal gerendert zu werden. Beispiel: Zuordnungs-Apps oder große Dokument canvases.

Tipp

Die Beispielanwendung Für komplexe Freihandeingaben wird VirtualSurfaceImageSource veranschaulicht.

VirtualSurfaceImageSource unterscheidet sich von SurfaceImageSource darin, dass es einen Rückruf – IVirtualSurfaceImageSourceCallbacksNative::UpdatesNeeded – verwendet, den Sie implementieren, um Bereiche der Oberfläche zu aktualisieren, sobald sie auf dem Bildschirm sichtbar werden. Sie müssen ausgeblendete Bereiche nicht löschen, da das XAML-Framework dies für Sie übernimmt.

Es ist eine gute Idee, sich mit SurfaceImageSource vertraut zu machen (siehe Abschnitt SurfaceImageSource oben), bevor Sie VirtualSurfaceImageSource in Angriff nehmen. Aber auf einer sehr hohen Ebene sehen Sie hier den Prozess zum Erstellen und Aktualisieren einer VirtualSurfaceImageSource.

  • Implementieren Sie die IVirtualSurfaceImageSourceCallbackNative-Schnittstelle .
  • Erstellen Sie ein Direct 3D-Gerät, ein Direct 2D-Gerät und einen Direct 2D-Gerätekontext.
  • Erstellen Sie eine VirtualSurfaceImageSource, und legen Sie das Direct 2D-Gerät (oder Direct 3D) darauf fest.
  • Rufen Sie RegisterForUpdatesNeeded fürVirtualSurfaceImageSource auf.
  • Rufen Sie in Ihrem UpdatesNeeded-RückrufGetUpdateRectCount und GetUpdateRects auf.
  • Rendern Sie die Updaterechtecke (mit BeginDraw/EndDraw genau wie für SurfaceImageSource).
  • Legen Sie surfaceImageSource auf einem XAML-Image oder ImageBrush fest, um es in Ihrer XAML-Benutzeroberfläche anzuzeigen.

Im Folgenden finden Sie einen tieferen Einblick in diese Schritte mit Quellcodebeispielen.

  1. Sie können dem unten gezeigten und beschriebenen Code folgen, indem Sie ein neues Projekt in Microsoft Visual Studio erstellen. Erstellen Sie ein Projekt für leere App (C++/WinRT), und nennen Sie es VSISDemo (es ist wichtig, dem Projekt diesen Namen zu geben, wenn Sie in die unten angegebenen Codeeinträge kopieren und einfügen). Die neueste allgemein verfügbare Version von Windows SDK (d. h. keine Vorschauversion).

  2. Öffnen Sie pch.h, und fügen Sie die folgenden Includes unterhalb der bereits vorhandenen hinzu.

    // pch.h
    ...
    #include <d3d11_4.h>
    #include <d2d1_1.h>
    #include <windows.ui.xaml.media.dxinterop.h>
    #include <winrt/Windows.UI.Xaml.Media.Imaging.h>
    
  3. In diesem Schritt stellen Sie eine Implementierung der IVirtualSurfaceUpdatesCallbackNative-Schnittstelle bereit . Fügen Sie dem Projekt ein neues Element der Headerdatei (.h) hinzu, und nennen Sie es CallbackImplementation.h. Ersetzen Sie den Inhalt dieser Datei durch die folgende Auflistung. Der Code wird nach der Auflistung erläutert.

    #include "pch.h"
    
    namespace winrt::VSISDemo::implementation
    {
        struct CallbackImplementation : winrt::implements<CallbackImplementation, ::IVirtualSurfaceUpdatesCallbackNative>
        {
            CallbackImplementation(
                winrt::com_ptr<::ISurfaceImageSourceNativeWithD2D> sisNativeWithD2D,
                winrt::com_ptr<::IVirtualSurfaceImageSourceNative> const& vsisNative,
                winrt::com_ptr<::ID2D1DeviceContext> const& d2dDeviceContext) :
                m_sisNativeWithD2D(sisNativeWithD2D),
                m_vsisNative(vsisNative),
                m_d2dDeviceContext(d2dDeviceContext)
            {}
    
            IFACEMETHOD(UpdatesNeeded)()
            {
                HRESULT hr = S_OK;
    
                ULONG drawingBoundsCount = 0;
                m_vsisNative->GetUpdateRectCount(&drawingBoundsCount);
    
                std::unique_ptr<RECT[]> drawingBounds(
                    new RECT[drawingBoundsCount]);
    
                m_vsisNative->GetUpdateRects(
                    drawingBounds.get(),
                    drawingBoundsCount);
    
                for (ULONG i = 0; i < drawingBoundsCount; ++i)
                {
                    winrt::com_ptr<::IDXGISurface> dxgiSurface;
    
                    POINT offset{ 0, 0 };
                    HRESULT beginDrawHR = m_sisNativeWithD2D->BeginDraw(
                        drawingBounds[i],
                        __uuidof(::IDXGISurface),
                        dxgiSurface.put_void(),
                        &offset);
    
                    // Create render target.
                    winrt::com_ptr<::ID2D1Bitmap1> bitmap;
                    winrt::check_hresult(
                        m_d2dDeviceContext->CreateBitmapFromDxgiSurface(
                            dxgiSurface.get(),
                            nullptr,
                            bitmap.put()
                        )
                    );
    
                    // Set context's render target.
                    m_d2dDeviceContext->SetTarget(bitmap.get());
    
                    if (beginDrawHR == DXGI_ERROR_DEVICE_REMOVED ||
                        beginDrawHR == DXGI_ERROR_DEVICE_RESET ||
                        beginDrawHR == D2DERR_RECREATE_TARGET)
                    {
                        // The Direct3D and Direct2D devices were lost and need to be re-created.
                        // Recovery steps are:
                        // 1) Re-create the Direct3D and Direct2D devices
                        // 2) Call ISurfaceImageSourceNativeWithD2D::SetDevice with the new Direct2D
                        //    device
                        // 3) Redraw the contents of the SurfaceImageSource
                    }
                    else if (beginDrawHR == E_SURFACE_CONTENTS_LOST)
                    {
                        // The devices were not lost but the entire contents of the surface
                        // were. Recovery steps are:
                        // 1) Call ISurfaceImageSourceNativeWithD2D::SetDevice with the Direct2D 
                        //    device again
                        // 2) Redraw the entire contents of the SurfaceImageSource
                    }
                    else
                    {
                        // Draw using Direct2D context.
                        m_d2dDeviceContext->BeginDraw();
    
                        m_d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Orange));
    
                        winrt::com_ptr<::ID2D1SolidColorBrush> brush;
                        winrt::check_hresult(m_d2dDeviceContext->CreateSolidColorBrush(
                            D2D1::ColorF(D2D1::ColorF::Chocolate),
                            D2D1::BrushProperties(0.8f),
                            brush.put()));
    
                        D2D1_SIZE_F const size{ drawingBounds[i].right - drawingBounds[i].left, drawingBounds[i].bottom - drawingBounds[i].top };
                        D2D1_RECT_F const rect{ 100.0f, 100.0f, size.width - 100.0f, size.height - 100.0f };
                        m_d2dDeviceContext->DrawRectangle(rect, brush.get(), 100.0f);
    
                        m_d2dDeviceContext->EndDraw();
                    }
    
                    m_sisNativeWithD2D->EndDraw();
                }
    
                return hr;
            }
    
        private:
            winrt::com_ptr<::ISurfaceImageSourceNativeWithD2D> m_sisNativeWithD2D{ nullptr };
            winrt::com_ptr<::IVirtualSurfaceImageSourceNative> m_vsisNative{ nullptr };
            winrt::com_ptr<::ID2D1DeviceContext> m_d2dDeviceContext{ nullptr };
        };
    }
    

    Wenn eine Region der VirtualSurfaceImageSource aktualisiert werden muss, ruft das Framework Ihre Implementierung von IVirtualSurfaceUpdatesCallbackNative::UpdatesNeeded auf (siehe oben).

    Dies kann passieren, wenn das Framework bestimmt, dass der Bereich gezeichnet werden muss (wenn der Benutzer z. B. die Ansicht der Oberfläche verschiebst oder verkleinert), oder nachdem Ihre App IVirtualSurfaceImageSourceNative::Invalidate für diesen Bereich aufgerufen hat.

    Verwenden Sie in Ihrer Implementierung von IVirtualSurfaceImageSourceNative::UpdatesNeeded die Methoden IVirtualSurfaceImageSourceNative::GetUpdateRectCount und IVirtualSurfaceImageSourceNative::GetUpdateRects , um zu bestimmen, welche Regionen der Oberfläche gezeichnet werden müssen.

    Zeichnen Sie für jede Region, die aktualisiert werden muss, den spezifischen Inhalt auf diese Region, beschränken Sie ihre Zeichnung jedoch auf die begrenzten Regionen, um die Leistung zu verbessern. Die Besonderheiten des Aufrufens der ISurfaceImageSourceNativeWithD2D-Methoden sind identisch mit denen für SurfaceImageSource (siehe Abschnitt SurfaceImageSource oben).

    Hinweis

    Vermeiden Sie das Zeichnen in VirtualSurfaceImageSource , während Ihr Fenster ausgeblendet oder inaktiv ist. Andernfalls schlagen ISurfaceImageSourceNativeWithD2D-APIs fehl. Behandeln Sie Ereignisse rund um die Fenstersichtbarkeit und das Anhalten von Anwendungen, um dies zu erreichen.

  4. In der MainPage-Klasse fügen wir einen Member vom Typ CallbackImplementation hinzu. Außerdem erstellen wir ein Direct 3D-Gerät, ein Direct 2D-Gerät und einen Direct 2D-Gerätekontext. Dazu rufen wir D3D11CreateDevice, D2D1CreateDevice und ID2D1Device::CreateDeviceContext auf.

    Ersetzen Sie den Inhalt von MainPage.idl, MainPage.hund MainPage.cpp durch den Inhalt der folgenden Auflistungen.

    // MainPage.idl
    namespace VSISDemo
    {
        [default_interface]
        runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
        {
            MainPage();
        }
    }
    
    // MainPage.h
    #pragma once
    
    #include "MainPage.g.h"
    #include "CallbackImplementation.h"
    
    namespace winrt::VSISDemo::implementation
    {
        struct MainPage : MainPageT<MainPage>
        {
            MainPage();
            void ClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args);
    
        private:
            winrt::com_ptr<::IVirtualSurfaceUpdatesCallbackNative> m_cbi{ nullptr };
        };
    }
    
    namespace winrt::VSISDemo::factory_implementation
    {
        struct MainPage : MainPageT<MainPage, implementation::MainPage>
        {
        };
    }
    
    // MainPage.cpp
    #include "pch.h"
    #include "MainPage.h"
    #include "MainPage.g.cpp"
    
    using namespace winrt;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Media::Imaging;
    
    namespace winrt::VSISDemo::implementation
    {
        MainPage::MainPage()
        {
            InitializeComponent();
        }
    
        void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
        {
            myButton().Content(box_value(L"Clicked"));
    
            uint32_t creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
    
            D3D_FEATURE_LEVEL featureLevels[] =
            {
                D3D_FEATURE_LEVEL_11_1,
                D3D_FEATURE_LEVEL_11_0,
                D3D_FEATURE_LEVEL_10_1,
                D3D_FEATURE_LEVEL_10_0,
                D3D_FEATURE_LEVEL_9_3,
                D3D_FEATURE_LEVEL_9_2,
                D3D_FEATURE_LEVEL_9_1
            };
    
            // Create the Direct3D device.
            winrt::com_ptr<::ID3D11Device> d3dDevice;
            D3D_FEATURE_LEVEL supportedFeatureLevel;
            winrt::check_hresult(::D3D11CreateDevice(
                nullptr,
                D3D_DRIVER_TYPE_HARDWARE,
                0,
                creationFlags,
                featureLevels,
                ARRAYSIZE(featureLevels),
                D3D11_SDK_VERSION,
                d3dDevice.put(),
                &supportedFeatureLevel,
                nullptr)
            );
    
            // Get the Direct3D device.
            winrt::com_ptr<::IDXGIDevice> dxgiDevice{
                d3dDevice.as<::IDXGIDevice>() };
    
            // Create the Direct2D device and a corresponding context.
            winrt::com_ptr<::ID2D1Device> d2dDevice;
            ::D2D1CreateDevice(dxgiDevice.get(), nullptr, d2dDevice.put());
    
            winrt::com_ptr<::ID2D1DeviceContext> d2dDeviceContext;
            winrt::check_hresult(
                d2dDevice->CreateDeviceContext(
                    D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
                    d2dDeviceContext.put()
                )
            );
        }
    }
    
  5. Fügen Sie als Nächstes Code hinzu, um eine VirtualSurfaceImageSource mit der gewünschten Größe zu erstellen, und legen Sie das Direct 2D-Gerät (oder Direct 3D) darauf fest, indem Sie ISurfaceImageSourceNativeWithD2D::SetDevice aufrufen.

    Hinweis

    Wenn Sie aus einem Hintergrundthread auf Ihre VirtualSurfaceImageSource zeichnen, müssen Sie auch sicherstellen, dass auf dem DXGI-Gerät der Multithreadzugriff aktiviert ist (wie im folgenden Code dargestellt). Aus Leistungsgründen sollten Sie dies nur tun, wenn Sie aus einem Hintergrundthread zeichnen.

    Um das Gerät festzulegen und die Zeichnungsvorgänge auszuführen, benötigen wir einen Zeiger auf ISurfaceImageSourceNativeWithD2D. Um eine abzurufen, fragen Sie das VirtualSurfaceImageSource-Objekt nach seiner zugrunde liegenden ISurfaceImageSourceNativeWithD2D-Schnittstelle ab.

    Fragen Sie auch IVirtualSurfaceImageSourceNative ab, und rufen Sie IVirtualSurfaceImageSourceNative::RegisterForUpdatesNeeded auf, um Ihre Implementierung von IVirtualSurfaceUpdatesCallbackNative bereitzustellen.

    Legen Sie dann surfaceImageSource auf einem XAML-Image (oder ImageBrush) fest, um es auf Der XAML-Benutzeroberfläche anzuzeigen.

    // MainPage.cpp | paste this at the end of MainPage::ClickHandler
    VirtualSurfaceImageSource virtualSurfaceImageSource(2000, 2000);
    
    winrt::com_ptr<::ISurfaceImageSourceNativeWithD2D> sisNativeWithD2D{
        virtualSurfaceImageSource.as<::ISurfaceImageSourceNativeWithD2D>() };
    
    // Associate the Direct2D device with the SurfaceImageSource.
    sisNativeWithD2D->SetDevice(d2dDevice.get());
    
    // To enable multi-threaded access (optional)
    winrt::com_ptr<::ID3D11Multithread> d3dMultiThread{
        d3dDevice.as<::ID3D11Multithread>() };
    d3dMultiThread->SetMultithreadProtected(true);
    
    winrt::com_ptr<::IVirtualSurfaceImageSourceNative> vsisNative{
        virtualSurfaceImageSource.as<::IVirtualSurfaceImageSourceNative>() };
    
    m_cbi = winrt::make<CallbackImplementation>(sisNativeWithD2D, vsisNative, d2dDeviceContext);
    vsisNative->RegisterForUpdatesNeeded(m_cbi.as<::IVirtualSurfaceUpdatesCallbackNative>().get());
    
    // The SurfaceImageSource object's underlying 
    // ISurfaceImageSourceNativeWithD2D object will contain the completed bitmap.
    
    theImage().Source(virtualSurfaceImageSource);
    
  6. Fügen Sie schließlich das folgende Image-Element innerhalb des vorhandenen XAML-Markups in MainPage.xamlhinzu.

    <!-- MainPage.xaml -->
    ...
    <Image x:Name="theImage" Width="500" Height="500" />
    ...
    
  7. Jetzt können Sie die App erstellen und ausführen. Klicken Sie auf die Schaltfläche, um den Inhalt der im Bild angezeigten VirtualSurfaceImageSource anzuzeigen.

SwapChainPanel und Gaming

SwapChainPanel ist der Windows-Runtime-Typ, der für die Unterstützung von High-End-Grafiken und -Spielen mit direkter Swapchain-Verwaltung entwickelt wurde. Sie erstellen in diesem Fall Ihre eigene DirectX-Swapchain und verwalten die Präsentation Ihrer gerenderten Inhalte selbst. Ein weiteres Feature von SwapChainPanel ist, dass Sie andere XAML-Elemente davor überlagern können.

Um eine gute Leistung sicherzustellen, gibt es bestimmte Einschränkungen für den SwapChainPanel-Typ .

  • Pro App sollten nicht mehr als 4 SwapChainPanel-Instanzen vorhanden sein.
  • Sie sollten die Höhe und Breite der DirectX-Swapchain (in DXGI_SWAP_CHAIN_DESC1) auf die aktuellen Dimensionen des Swapchainelements festlegen. Wenn dies nicht der Fall ist, wird der Anzeigeinhalt entsprechend skaliert (mit DXGI_SCALING_STRETCH).
  • Sie müssen den Skalierungsmodus der DirectX-Swapchain (in DXGI_SWAP_CHAIN_DESC1) auf DXGI_SCALING_STRETCH festlegen.
  • Sie müssen die DirectX-Swapchain erstellen, indem Sie die Methode IDXGIFactory2::CreateSwapChainForComposition aufrufen.

Sie aktualisieren swapChainPanel basierend auf den Anforderungen Ihrer App und synchronisieren nicht mit den Updates des XAML-Frameworks. Wenn Sie die Updates von SwapChainPanel mit denen des XAML-Frameworks synchronisieren müssen, registrieren Sie sich für das Windows::UI::Xaml::Media::CompositionTarget::Rendering-Ereignis . Andernfalls müssen Sie sämtliche threadübergreifenden Probleme berücksichtigen, wenn Sie versuchen, die XAML-Elemente über einen Thread zu aktualisieren, bei dem es sich nicht um den Thread handelt, der die Klasse SwapChainPanel aktualisiert.

Wenn Sie Zeigereingaben mit niedriger Latenz auf Ihre SwapChainPanel erhalten müssen, verwenden Sie SwapChainPanel::CreateCoreIndependentInputSource. Diese Methode gibt ein CoreIndependentInputSource-Objekt zurück, das verwendet werden kann, um Eingabeereignisse bei minimaler Latenz in einem Hintergrundthread zu empfangen. Beachten Sie, dass nach dem Aufruf dieser Methode keine normalen XAML-Zeigereingabeereignisse für swapChainPanel ausgelöst werden, da alle Eingaben an den Hintergrundthread umgeleitet werden.

Hier sehen Sie den Prozess zum Erstellen und Aktualisieren eines SwapChainPanel-Objekts .

  1. Sie können dem unten gezeigten und beschriebenen Code folgen, indem Sie ein neues Projekt in Microsoft Visual Studio erstellen. Erstellen Sie ein Leeres App-Projekt (C++/WinRT), und nennen Sie es SCPDemo (es ist wichtig, dem Projekt diesen Namen zu geben, wenn Sie in die unten angegebenen Codeeinträge kopieren und einfügen). Die neueste allgemein verfügbare Version von Windows SDK (d. h. keine Vorschauversion).

  2. Öffnen Sie pch.h, und fügen Sie die folgenden Includes unterhalb der bereits vorhandenen hinzu.

    // pch.h
    ...
    #include <d3d11_4.h>
    #include <d2d1_1.h>
    #include <windows.ui.xaml.media.dxinterop.h>
    
  3. In der MainPage-Klasse erstellen wir zunächst ein Direct 3D-Gerät, ein Direct 2D-Gerät und einen Direct 2D-Gerätekontext. Dazu rufen wir D3D11CreateDevice, D2D1CreateDevice und ID2D1Device::CreateDeviceContext auf.

    Ersetzen Sie den Inhalt von MainPage.idl, MainPage.hund MainPage.cpp durch den Inhalt der folgenden Auflistungen.

    // MainPage.idl
    namespace SCPDemo
    {
        [default_interface]
        runtimeclass MainPage : Windows.UI.Xaml.Controls.Page
        {
            MainPage();
        }
    }
    
    // MainPage.h
    #pragma once
    
    #include "MainPage.g.h"
    
    namespace winrt::SCPDemo::implementation
    {
        struct MainPage : MainPageT<MainPage>
        {
            MainPage();
            void ClickHandler(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args);
        };
    }
    
    namespace winrt::SCPDemo::factory_implementation
    {
        struct MainPage : MainPageT<MainPage, implementation::MainPage>
        {
        };
    }
    
    // MainPage.cpp
    #include "pch.h"
    #include "MainPage.h"
    #include "MainPage.g.cpp"
    
    using namespace winrt;
    using namespace Windows::UI::Xaml;
    
    namespace winrt::SCPDemo::implementation
    {
        MainPage::MainPage()
        {
            InitializeComponent();
        }
    
        void MainPage::ClickHandler(IInspectable const&, RoutedEventArgs const&)
        {
            myButton().Content(box_value(L"Clicked"));
    
            uint32_t creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
    
            D3D_FEATURE_LEVEL featureLevels[] =
            {
                D3D_FEATURE_LEVEL_11_1,
                D3D_FEATURE_LEVEL_11_0,
                D3D_FEATURE_LEVEL_10_1,
                D3D_FEATURE_LEVEL_10_0,
                D3D_FEATURE_LEVEL_9_3,
                D3D_FEATURE_LEVEL_9_2,
                D3D_FEATURE_LEVEL_9_1
            };
    
            // Create the Direct3D device.
            winrt::com_ptr<::ID3D11Device> d3dDevice;
            D3D_FEATURE_LEVEL supportedFeatureLevel;
            winrt::check_hresult(::D3D11CreateDevice(
                nullptr,
                D3D_DRIVER_TYPE_HARDWARE,
                0,
                creationFlags,
                featureLevels,
                ARRAYSIZE(featureLevels),
                D3D11_SDK_VERSION,
                d3dDevice.put(),
                &supportedFeatureLevel,
                nullptr)
            );
    
            // Get the Direct3D device.
            winrt::com_ptr<::IDXGIDevice> dxgiDevice{
                d3dDevice.as<::IDXGIDevice>() };
    
            // Create the Direct2D device and a corresponding context.
            winrt::com_ptr<::ID2D1Device> d2dDevice;
            ::D2D1CreateDevice(dxgiDevice.get(), nullptr, d2dDevice.put());
    
            winrt::com_ptr<::ID2D1DeviceContext> d2dDeviceContext;
            winrt::check_hresult(
                d2dDevice->CreateDeviceContext(
                    D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
                    d2dDeviceContext.put()
                )
            );
        }
    }
    
  4. Umschließen Sie Ihr XAML-Markup in ein SwapChainPanel-Element mit einem x:Name. Die umschlossenen XAML-Elemente werden vor swapChainPanel gerendert.

    <!-- MainPage.xaml -->
     <SwapChainPanel x:Name="swapChainPanel">
     	<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
     		<Button x:Name="myButton" Click="ClickHandler">Click Me</Button>
     	</StackPanel>
     </SwapChainPanel>
    

    Sie können dann über die Accessorfunktion mit demselben Namen auf dieses SwapChainPanel-Objekt zugreifen, wie wir sehen werden.

  5. Rufen Sie als Nächstes IDXGIFactory2::CreateSwapChainForComposition auf, um eine Swapchain zu erstellen.

    // MainPage.cpp | paste this at the end of MainPage::ClickHandler
    // Get the DXGI adapter.
    winrt::com_ptr< ::IDXGIAdapter > dxgiAdapter;
    dxgiDevice->GetAdapter(dxgiAdapter.put());
    
    // Get the DXGI factory.
    winrt::com_ptr< ::IDXGIFactory2 > dxgiFactory;
    dxgiFactory.capture(dxgiAdapter, &IDXGIAdapter::GetParent);
    
    DXGI_SWAP_CHAIN_DESC1 swapChainDesc { 0 };
    swapChainDesc.Width = 500;
    swapChainDesc.Height = 500;
    swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swapchain format.
    swapChainDesc.Stereo = false;
    swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
    swapChainDesc.SampleDesc.Quality = 0;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    swapChainDesc.BufferCount = 2;
    swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // We recommend using this swap effect for all applications.
    swapChainDesc.Flags = 0;
    
    // Create a swap chain by calling IDXGIFactory2::CreateSwapChainForComposition.
    winrt::com_ptr< ::IDXGISwapChain1 > swapChain;
    dxgiFactory->CreateSwapChainForComposition(
        d3dDevice.get(),
        &swapChainDesc,
        nullptr,
        swapChain.put());
    
  6. Rufen Sie eine ISwapChainPanelNative aus dem SwapChainPanel ab, das Sie swapChainPanel genannt haben. Der Aufruf von ISwapChainPanelNative::SetSwapChain , um die Swapchain im SwapChainPanel festzulegen.

    // MainPage.cpp | paste this at the end of MainPage::ClickHandler
    // Get native interface for SwapChainPanel
    auto panelNative{ swapChainPanel().as<ISwapChainPanelNative>() };
    
    winrt::check_hresult(
        panelNative->SetSwapChain(swapChain.get())
    );
    
  7. Zeichnen Sie schließlich in die DirectX-Swapchain, und zeigen Sie sie dann an, um den Inhalt anzuzeigen.

    // Create a Direct2D target bitmap associated with the
    // swap chain back buffer, and set it as the current target.
    D2D1_BITMAP_PROPERTIES1 bitmapProperties =
        D2D1::BitmapProperties1(
            D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
            D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
            96.f,
            96.f
        );
    
    winrt::com_ptr<::IDXGISurface> dxgiBackBuffer;
    swapChain->GetBuffer(0, __uuidof(dxgiBackBuffer), dxgiBackBuffer.put_void());
    
    winrt::com_ptr< ::ID2D1Bitmap1 > targetBitmap;
    winrt::check_hresult(
        d2dDeviceContext->CreateBitmapFromDxgiSurface(
            dxgiBackBuffer.get(),
            &bitmapProperties,
            targetBitmap.put()
        )
    );
    
    d2dDeviceContext->SetTarget(targetBitmap.get());
    
    // Draw using Direct2D context.
    d2dDeviceContext->BeginDraw();
    
    d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Orange));
    
    winrt::com_ptr<::ID2D1SolidColorBrush> brush;
    winrt::check_hresult(d2dDeviceContext->CreateSolidColorBrush(
        D2D1::ColorF(D2D1::ColorF::Chocolate),
        D2D1::BrushProperties(0.8f),
        brush.put()));
    
    D2D1_SIZE_F const size{ 500, 500 };
    D2D1_RECT_F const rect{ 100.0f, 100.0f, size.width - 100.0f, size.height - 100.0f };
    d2dDeviceContext->DrawRectangle(rect, brush.get(), 100.0f);
    
    d2dDeviceContext->EndDraw();
    
    swapChain->Present(1, 0);
    

    Die XAML-Elemente werden aktualisiert, wenn das Windows-Runtime-Layout/die Rendering-Logik ein Update signalisiert.

  8. Jetzt können Sie die App erstellen und ausführen. Klicken Sie auf die Schaltfläche, um den Inhalt des SwapChainPanel anzuzeigen, der hinter den anderen XAML-Elementen angezeigt wird.

    Ein direct2D gerendertes Rechteck hinter einem XAML-Schaltflächenelement

Hinweis

Im Allgemeinen sollte Ihre DirectX-App Swapchains im Querformat erstellen und der Anzeigefenstergröße entsprechen (die in den meisten Microsoft Store-Spielen normalerweise der nativen Bildschirmauflösung entspricht). Dadurch wird sichergestellt, dass Ihre App die optimale Swapchainimplementierung verwendet, wenn sie keine sichtbare XAML-Überlagerung hat. Wenn die App in den Hochformatmodus gedreht wird, sollte Ihre App IDXGISwapChain1::SetRotation für die vorhandene Swapchain aufrufen, bei Bedarf eine Transformation auf den Inhalt anwenden und dann SetSwapChain erneut in derselben Swapchain aufrufen. Ebenso sollte Ihre App SetSwapChain erneut in derselben Swapchain aufrufen, wenn die Größe der Swapchain durch Aufrufen von IDXGISwapChain::ResizeBuffers geändert wird.