Swapkettenskalierung und Überlagerungen

Applies to Windows only

Hier erfahren Sie, wie Sie skalierte Swapketten zum schnelleren Rendern auf mobilen Geräten erstellen und Überlagerungsswapketten (falls verfügbar) verwenden, um die visuelle Qualität zu steigern.

Swapketten unter DirectX 11.2

Unter Direct3D 11.2 können Sie Windows Store-Apps mit Swapketten erstellen, die von nicht systemeigenen (reduzierten) Auflösungen skaliert werden, um höhere Füllraten zu ermöglichen. Außerdem enthält Direct3D 11.2 APIs zum Rendern mit Hardwareüberlagerungen, damit Sie eine UI in einer anderen Swapkette mit systemeigener Auflösung darstellen können. So kann das Spiel die UI bei der höchsten systemeigenen Auflösung zeichnen und gleichzeitig eine hohe Framerate erzielen. Mobile Geräte und Anzeigen mit hohem DPI-Wert (z. B. 3840 x 2160) werden auf diese Weise bestmöglich eingesetzt. In diesem Artikel wird erläutert, wie Sie sich überlappende Swapketten verwenden.

Mit Direct3D 11.2 wird auch ein neues Feature zur Erzielung einer geringeren Latenz mithilfe von Flipmodell-Swapketten eingeführt. Weitere Informationen finden Sie unter Reduzieren der Latenz mit DXGI 1.3-Swapketten.

In dieser exemplarischen Vorgehensweise wird Code aus dem Beispiel zu DirectX-Vordergrund-Swapketten verwendet.

Verwenden der Swapkettenskalierung

Wenn Ihr Spiel nicht auf der neuesten Hardware ausgeführt wird – oder auf Hardware, die für sparsamen Betrieb optimiert ist –, kann es von Vorteil sein, Echtzeitinhalte des Spiels mit einer niedrigeren Auflösung als der Auflösung zu rendern, die das Display standardmäßig leisten kann. Dazu muss die zum Rendern der Spielinhalte verwendete Swapkette kleiner als die systemeigene Auflösung sein, oder es muss ein Unterbereich der Swapkette verwendet werden.

  1. Erstellen Sie zuerst eine Swapkette mit der höchstmöglichen systemeigenen Auflösung.

    
    DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
    
    swapChainDesc.Width = static_cast<UINT>(m_d3dRenderTargetSize.Width); // Match the size of the window.
    swapChainDesc.Height = static_cast<UINT>(m_d3dRenderTargetSize.Height);
    swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain 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; // Use double-buffering to minimize latency.
    swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Windows Store apps must use this SwapEffect.
    swapChainDesc.Flags = 0;
    swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
    
    // This sequence obtains the DXGI factory that was used to create the Direct3D device above.
    ComPtr<IDXGIDevice3> dxgiDevice;
    DX::ThrowIfFailed(
        m_d3dDevice.As(&dxgiDevice)
        );
    
    ComPtr<IDXGIAdapter> dxgiAdapter;
    DX::ThrowIfFailed(
        dxgiDevice->GetAdapter(&dxgiAdapter)
        );
    
    ComPtr<IDXGIFactory2> dxgiFactory;
    DX::ThrowIfFailed(
        dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
        );
    
    ComPtr<IDXGISwapChain1> swapChain;
    DX::ThrowIfFailed(
        dxgiFactory->CreateSwapChainForCoreWindow(
            m_d3dDevice.Get(),
            reinterpret_cast<IUnknown*>(m_window.Get()),
            &swapChainDesc,
            nullptr,
            &swapChain
            )
        );
    
    DX::ThrowIfFailed(
        swapChain.As(&m_swapChain)
        );
    
    
    
  2. Wählen Sie anschließend einen Unterbereich der Swapkette für die Skalierung aus, indem Sie die Quellgröße auf eine niedrigere Auflösung festlegen.

    Im Beispiel zu DX-Vordergrund-Swapketten wird anhand eines Prozentsatzes eine reduzierte Größe berechnet:

    
    m_d3dRenderSizePercentage = percentage;
    
    UINT renderWidth = static_cast<UINT>(m_d3dRenderTargetSize.Width * percentage + 0.5f);
    UINT renderHeight = static_cast<UINT>(m_d3dRenderTargetSize.Height * percentage + 0.5f);
    
    // Change the region of the swap chain that will be presented to the screen.
    DX::ThrowIfFailed(
        m_swapChain->SetSourceSize(
            renderWidth,
            renderHeight
            )
        );
    
    
    
  3. Erstellen Sie einen Viewport für den Unterbereich der Swapkette.

    
    // In Direct3D, change the Viewport to match the region of the swap
    // chain that will now be presented from.
    m_screenViewport = CD3D11_VIEWPORT(
        0.0f,
        0.0f,
        static_cast<float>(renderWidth),
        static_cast<float>(renderHeight)
        );
    
    m_d3dContext->RSSetViewports(1, &m_screenViewport);
    
    
    
  4. Bei Verwendung von Direct2D muss die Drehungstransformation angepasst werden, um die Quellregion entsprechend zu berücksichtigen.

Erstellen einer Hardware-Überlagerungsswapkette für UI-Elemente

Mit der Verwendung der Swapkettenskalierung ist der Nachteil verbunden, dass auch die UI herunterskaliert wird und ggf. verschwimmt und schwieriger zu nutzen ist. Auf Geräten mit Hardwareunterstützung für Überlagerungsswapketten kann dieses Problem vollständig vermieden werden, indem die UI mit der systemeigenen Auflösung in einer Swapkette gerendert wird, die von den Echtzeitinhalten des Spiels getrennt ist. Beachten Sie, dass dieses Verfahren nur für CoreWindow-Swapketten gilt und in Verbindung mit der XAML-Interoperabilität nicht verwendet werden kann.

Führen Sie die folgenden Schritte aus, um eine Vordergrund-Swapkette zu erstellen, bei der eine Hardwareüberlagerungsfunktion verwendet wird. Diese Schritte werden ausgeführt, nachdem wie oben beschrieben zuerst eine Swapkette für die Echtzeitinhalte des Spiels erstellt wurde.

  1. Ermitteln Sie zuerst, ob Überlagerungen vom DXGI-Adapter unterstützt werden. Rufen Sie den DXGI-Ausgabeadapter aus der Swapkette ab:

    
    ComPtr<IDXGIAdapter> outputDxgiAdapter;
    DX::ThrowIfFailed(
        dxgiFactory->EnumAdapters(0, &outputDxgiAdapter)
        );
    
    ComPtr<IDXGIOutput> dxgiOutput;
    DX::ThrowIfFailed(
        outputDxgiAdapter->EnumOutputs(0, &dxgiOutput)
        );
    
    ComPtr<IDXGIOutput2> dxgiOutput2;
    DX::ThrowIfFailed(
        dxgiOutput.As(&dxgiOutput2)
        );
    
    
    

    Überlagerungen werden vom DXGI-Adapter unterstützt, wenn der Ausgabeadapter für SupportsOverlays "True" zurückgibt.

    
    m_overlaySupportExists = dxgiOutput2->SupportsOverlays() ? true : false;
    
    
    

    Hinweis  Fahren Sie mit dem nächsten Schritt fort, falls der DXGI-Adapter Überlagerungen unterstützt. Wenn das Gerät Überlagerungen nicht unterstützt, ist das Rendern mit mehreren Swapketten nicht effizient. Rendern Sie die UI stattdessen mit reduzierter Auflösung in derselben Swapkette wie die Echtzeitinhalte des Spiels. Im Beispiel zu DirectX-Vordergrund-Swapketten wird veranschaulicht, wie Sie ein Fallback auf eine einzelne Swapkette durchführen.

  2. Erstellen Sie die Vordergrund-Swapkette mit IDXGIFactory2::CreateSwapChainForCoreWindow. Im DXGI_SWAP_CHAIN_DESC1-Element, das für den pDesc-Parameter bereitgestellt wird, müssen die folgenden Optionen festgelegt werden:

    
     foregroundSwapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER;
     foregroundSwapChainDesc.Scaling = DXGI_SCALING_NONE;
     foregroundSwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; // Foreground swap chain alpha values must be premultiplied.
    
    
    

    Hinweis  Legen Sie das DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER-Element bei jeder Größenänderung der Swapkette neu fest.

    
    HRESULT hr = m_foregroundSwapChain->ResizeBuffers(
        2, // Double-buffered swap chain.
        static_cast<UINT>(m_d3dRenderTargetSize.Width),
        static_cast<UINT>(m_d3dRenderTargetSize.Height),
        DXGI_FORMAT_B8G8R8A8_UNORM,
        DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER // The FOREGROUND_LAYER flag cannot be removed with ResizeBuffers.
        );
    
    
    
  3. Erhöhen Sie die maximale Framelatenz bei Verwendung von zwei Swapketten auf 2, damit der DXGI-Adapter genügend Zeit hat, um beide Swapketten gleichzeitig darzustellen (innerhalb desselben VSync-Intervalls).

    
    // Create a render target view of the foreground swap chain's back buffer.
    if (m_foregroundSwapChain)
    {
        ComPtr<ID3D11Texture2D> foregroundBackBuffer;
        DX::ThrowIfFailed(
            m_foregroundSwapChain->GetBuffer(0, IID_PPV_ARGS(&foregroundBackBuffer))
            );
    
        DX::ThrowIfFailed(
            m_d3dDevice->CreateRenderTargetView(
                foregroundBackBuffer.Get(),
                nullptr,
                &m_d3dForegroundRenderTargetView
                )
            );
    }
    
    
    
  4. Für Vordergrund-Swapketten wird immer prämultipliziertes Alpha verwendet. Es wird davon ausgegangen, dass die Farbwerte der einzelnen Pixel vor der Darstellung des Frames bereits mit dem Alphawert multipliziert wurden. Ein BGRA-Pixel mit 100 % Weiß und einem Alpha von 50 % wird beispielsweise auf (0,5, 0,5, 0,5, 0,5) festgelegt.

    Der Schritt der Alphaprämultiplikation kann in der Ausgabezusammenführungsphase ausgeführt werden, indem ein App-Blend-Status (siehe ID3D11BlendState) angewendet wird, bei dem das SrcBlend-Feld der D3D11_RENDER_TARGET_BLEND_DESC-Struktur auf D3D11_SRC_ALPHA festgelegt ist. Es können auch Ressourcen mit prämultiplizierten Alphawerten verwendet werden.

    Wenn der Schritt der Alphaprämultiplikation nicht erfolgt, erscheinen Farben für die Vordergrund-Swapkette heller als erwartet.

  5. Je nachdem, ob die Vordergrund-Swapkette erstellt wurde, muss die Direct2D-Zeichenoberfläche für UI-Elemente möglicherweise der Vordergrund-Swapkette zugeordnet werden.

    Erstellen von Renderzielansichten:

    
    // Create a render target view of the foreground swap chain's back buffer.
    if (m_foregroundSwapChain)
    {
        ComPtr<ID3D11Texture2D> foregroundBackBuffer;
        DX::ThrowIfFailed(
            m_foregroundSwapChain->GetBuffer(0, IID_PPV_ARGS(&foregroundBackBuffer))
            );
    
        DX::ThrowIfFailed(
            m_d3dDevice->CreateRenderTargetView(
                foregroundBackBuffer.Get(),
                nullptr,
                &m_d3dForegroundRenderTargetView
                )
            );
    }
    
    
    
    
    // Create a render target view of the swap chain's back buffer.
    ComPtr<ID3D11Texture2D> backBuffer;
    DX::ThrowIfFailed(
        m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
        );
    
    DX::ThrowIfFailed(
        m_d3dDevice->CreateRenderTargetView(
            backBuffer.Get(),
            nullptr,
            &m_d3dRenderTargetView
            )
        );
    
    
    

    Erstellen der Direct2D-Zeichenoberfläche:

    
    if (m_foregroundSwapChain)
    {
        // Create Direct2D target bitmap for foreground swap chain.
        ComPtr<IDXGISurface2> dxgiForegroundSwapChainBackBuffer;
        DX::ThrowIfFailed(
            m_foregroundSwapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiForegroundSwapChainBackBuffer))
            );
    
        DX::ThrowIfFailed(
            m_d2dContext->CreateBitmapFromDxgiSurface(
                dxgiForegroundSwapChainBackBuffer.Get(),
                &bitmapProperties,
                &m_d2dTargetBitmap
                )
            );
    }
    else
    {
        // Create Direct2D target bitmap for swap chain.
        ComPtr<IDXGISurface2> dxgiSwapChainBackBuffer;
        DX::ThrowIfFailed(
            m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiSwapChainBackBuffer))
            );
    
        DX::ThrowIfFailed(
            m_d2dContext->CreateBitmapFromDxgiSurface(
                dxgiSwapChainBackBuffer.Get(),
                &bitmapProperties,
                &m_d2dTargetBitmap
                )
            );
    }
    
    m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());
    
    
    
  6. Stellen Sie die Vordergrund-Swapkette zusammen mit der skalierten Swapkette dar, die für die Echtzeitinhalte des Spiels verwendet wird. Da die Framelatenz für beide Swapketten auf 2 festgelegt wurde, können beide von DXGI innerhalb desselben VSync-Intervalls dargestellt werden.

    Im Beispiel zu DirectX-Vordergrund-Swapketten wird die Datei "DeviceResources.cpp" wie folgt geändert, um die Vordergrund-Swapkette darzustellen:

    
    // Present the contents of the swap chain to the screen.
    void DX::DeviceResources::Present()
    {
        // The first argument instructs DXGI to block until VSync, putting the application
        // to sleep until the next VSync. This ensures we don't waste any cycles rendering
        // frames that will never be displayed to the screen.
        HRESULT hr = m_swapChain->Present(1, 0);
    
        if (SUCCEEDED(hr) && m_foregroundSwapChain)
        {
            m_foregroundSwapChain->Present(1, 0);
        }
    
        // Discard the contents of the render targets.
        // This is a valid operation only when the existing contents will be entirely
        // overwritten. If dirty or scroll rects are used, this call should be removed.
        m_d3dContext->DiscardView(m_d3dRenderTargetView.Get());
        if (m_foregroundSwapChain)
        {
            m_d3dContext->DiscardView(m_d3dForegroundRenderTargetView.Get());
        }
    
        // Discard the contents of the depth stencil.
        m_d3dContext->DiscardView(m_d3dDepthStencilView.Get());
    
        // If the device was removed either by a disconnection or a driver upgrade, we
        // must recreate all device resources.
        if (hr == DXGI_ERROR_DEVICE_REMOVED)
        {
            HandleDeviceLost();
        }
        else
        {
            DX::ThrowIfFailed(hr);
        }
    }
    
    
    

 

 

Anzeigen:
© 2014 Microsoft