Condividi tramite


Ridimensionamento e sovrapposizioni della swapchain

Impara come creare swapchain con scalabilità per ottenere rendering più rapido nei dispositivi mobili e usare swapchain (quando disponibile) di sovrapposizioni per aumentare la qualità visiva.

Catene di scambio in DirectX 11.2

Direct3D 11.2 consente di creare app piattaforma UWP (Universal Windows Platform) (UWP) con catene di scambio che vengono ridimensionate da risoluzioni non native (ridotte), consentendo velocità di riempimento più veloci. Direct3D 11.2 include anche API per il rendering con sovrimpressioni hardware in modo da poter presentare un'interfaccia utente in un'altra catena di scambio alla risoluzione nativa. Ciò consente al gioco di disegnare l'interfaccia utente a risoluzione nativa completa mantenendo un framerate elevato, rendendo così il miglior uso di dispositivi mobili e schermi DPI alti (ad esempio 3840 per 2160). Questo articolo illustra come usare catene di scambio sovrapposte.

Direct3D 11.2 introduce anche una nuova funzionalità per ridurre la latenza con catene di scambio di modelli flip. Vedere Ridurre la latenza con catene di scambio DXGI 1.3.

Usare il ridimensionamento della catena di scambio

Quando il gioco è in esecuzione su hardware di livello inferiore , o hardware ottimizzato per il risparmio energetico, può essere utile eseguire il rendering del contenuto del gioco in tempo reale a una risoluzione inferiore rispetto allo schermo è in grado di supportare in modo nativo. A tale scopo, la catena di scambio usata per il rendering del contenuto del gioco deve essere inferiore alla risoluzione nativa oppure è necessario usare un'area secondaria della porta di scambio.

  1. Creare prima di tutto una catena di scambio con risoluzione nativa completa.

    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 UWP 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. Scegliere quindi un'area secondaria della catena di scambio per aumentare le prestazioni impostando le dimensioni di origine su una risoluzione ridotta.

    Il campione DX Foreground Swap Chain calcola una dimensione ridotta in base a una percentuale:

    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. Creare un riquadro di visualizzazione in modo che corrisponda all'area secondaria della catena di scambio.

    // 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. Se viene usato Direct2D, la trasformazione di rotazione deve essere modificata per compensare l'area di origine.

Creare una catena di scambio di sovrimpressione hardware per gli elementi dell'interfaccia utente

Quando si usa il ridimensionamento della catena di scambio, c'è uno svantaggio intrinseco in quanto l'interfaccia utente è anche ridotta, rendendo potenzialmente sfocato e più difficile da usare. Nei dispositivi con supporto hardware per le catene di scambio di sovrapposizioni, questo problema viene risolto interamente eseguendo il rendering dell'interfaccia utente alla risoluzione nativa in una catena di scambio separata dal contenuto del gioco in tempo reale. Si noti che questa tecnica si applica solo alle catene di scambio CoreWindow . Non può essere usata con l'interoperabilità XAML.

Usare la procedura seguente per creare una catena di scambio in primo piano che usa la funzionalità di sovrapposizione hardware. Questi passaggi vengono eseguiti dopo la creazione di una catena di scambio per il contenuto del gioco in tempo reale, come descritto in precedenza.

  1. Prima di tutto, determinare se l'adattatore DXGI supporta le sovrimpressioni. Ottenere l'adattatore di output DXGI dalla catena di scambio:

    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)
        );
    

    L'adattatore DXGI supporta le sovrimpressioni se l'adattatore di output restituisce True per SupportsOverlays.

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

    Nota Se l'adattatore DXGI supporta le sovrimpressioni, continuare con il passaggio successivo. Se il dispositivo non supporta le sovrimpressioni, il rendering con più catene di scambio non sarà efficiente. Eseguire invece il rendering dell'interfaccia utente a una risoluzione ridotta nella stessa catena di scambio del contenuto del gioco in tempo reale.

     

  2. Creare la catena di scambio in primo piano con IDXGIFactory2::CreateSwapChainForCoreWindow. Le opzioni seguenti devono essere impostate nel DXGI_SWAP_CHAIN_DESC1 fornito al parametro pDesc :

     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.
    

    Nota Impostare di nuovo il DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER ogni volta che la catena di scambio viene ridimensionata.

    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. Quando vengono usate due catene di scambio, aumentare la latenza massima dei fotogrammi a 2 in modo che l'adattatore DXGI abbia tempo per presentare entrambe le catene di scambio simultaneamente (entro lo stesso intervallo VSync).

    // 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. Le catene di scambio in primo piano usano sempre alfa premoltiplicato. I valori di colore di ogni pixel devono essere già moltiplicati per il valore alfa prima che venga presentata la cornice. Ad esempio, un pixel BGRA bianco al 50% è impostato su (0,5, 0,5, 0,5, 0,5, 0,5).

    Il passaggio di premultiplicazione alfa può essere eseguito nella fase di unione dell'output applicando uno stato di fusione dell'app (vedere ID3D11BlendState) con il campo SrcBlend della struttura D3D11_RENDER_TARGET_BLEND_DESC impostato su D3D11_SRC_ALPHA. È anche possibile usare gli asset con valori alfa pre-moltiplicati.

    Se il passaggio di premultiplicazione alfa non viene eseguito, i colori nella catena di scambio in primo piano saranno più luminosi del previsto.

  5. A seconda che sia stata creata la catena di scambio in primo piano, potrebbe essere necessario associare la superficie di disegno Direct2D per gli elementi dell'interfaccia utente alla catena di scambio in primo piano.

    Creazione di viste di destinazione di rendering:

    // 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
                )
            );
    }
    

    Creazione della superficie di disegno Direct2D:

    if (m_foregroundSwapChain)
    {
        // Create a Direct2D target bitmap for the 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 a Direct2D target bitmap for the 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());
    
    // 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
            )
        );
    
  6. Presentare la catena di scambio in primo piano insieme alla catena di scambio con scalabilità usata per il contenuto del gioco in tempo reale. Poiché la latenza dei fotogrammi è stata impostata su 2 per entrambe le catene di scambio, DXGI può presentarle entrambe all'interno dello stesso intervallo VSync.

    // 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 that 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);
        }
    }