Dimensionamento e sobreposições de cadeia de troca

Saiba como criar cadeias de troca dimensionadas para renderização mais rápida em dispositivos móveis e use cadeias de troca de sobreposição (quando disponíveis) para aumentar a qualidade visual.

Cadeias de troca no DirectX 11.2

O Direct3D 11.2 permite que você crie aplicativos UWP (Plataforma Universal do Windows) com cadeias de troca que são dimensionadas de resoluções não nativas (reduzidas), permitindo taxas de preenchimento mais rápidas. O Direct3D 11.2 também inclui APIs para renderização com sobreposições de hardware para que você possa apresentar uma interface do usuário em outra cadeia de troca em resolução nativa. Isso permite que seu jogo desenhe a interface do usuário em resolução nativa total, mantendo uma alta taxa de quadros, fazendo o melhor uso de dispositivos móveis e exibições de alto DPI (como 3840 por 2160). Este artigo explica como usar cadeias de troca sobrepostas.

O Direct3D 11.2 também apresenta um novo recurso para latência reduzida com cadeias de troca de modelo de inversão. Consulte Reduzir a latência com cadeias de troca DXGI 1.3.

Usar o dimensionamento da cadeia de troca

Quando seu jogo está sendo executado em hardware de nível inferior - ou hardware otimizado para economia de energia - pode ser benéfico renderizar o conteúdo do jogo em tempo real em uma resolução mais baixa do que a tela é nativamente capaz. Para fazer isso, a cadeia de troca usada para renderizar o conteúdo do jogo deve ser menor que a resolução nativa ou uma sub-região da cadeia de troca deve ser usada.

  1. Primeiro, crie uma cadeia de troca em resolução nativa total.

    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. Em seguida, escolha uma sub-região da cadeia de troca para escalar verticalmente definindo o tamanho da origem para uma resolução reduzida.

    O exemplo DX Foreground Swap Chains calcula um tamanho reduzido com base em uma porcentagem:

    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. Crie uma janela de visualização para corresponder à sub-região da cadeia de troca.

    // 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 Direct2D estiver sendo usado, a transformação de rotação precisará ser ajustada para compensar a região de origem.

Criar uma cadeia de troca de sobreposição de hardware para elementos da interface do usuário

Ao usar o dimensionamento da cadeia de troca, há uma desvantagem inerente em que a interface do usuário também é reduzida, tornando-a potencialmente embaçada e mais difícil de usar. Em dispositivos com suporte de hardware para cadeias de troca de sobreposição, esse problema é totalmente aliviado renderizando a interface do usuário em resolução nativa em uma cadeia de troca separada do conteúdo do jogo em tempo real. Observe que essa técnica se aplica somente a cadeias de troca CoreWindow – ela não pode ser usada com interoperabilidade XAML.

Use as etapas a seguir para criar uma cadeia de troca em primeiro plano que use o recurso de sobreposição de hardware. Essas etapas são executadas após a primeira criação de uma cadeia de troca para conteúdo de jogo em tempo real, conforme descrito acima.

  1. Primeiro, determine se o adaptador DXGI dá suporte a sobreposições. Obtenha o adaptador de saída DXGI da cadeia de troca:

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

    O adaptador DXGI dá suporte a sobreposições se o adaptador de saída retornar True para SupportsOverlays.

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

    Observação Se o adaptador DXGI der suporte a sobreposições, vá para a próxima etapa. Se o dispositivo não der suporte a sobreposições, a renderização com várias cadeias de troca não será eficiente. Em vez disso, renderize a interface do usuário em resolução reduzida na mesma cadeia de troca que o conteúdo do jogo em tempo real.

     

  2. Crie a cadeia de troca em primeiro plano com IDXGIFactory2::CreateSwapChainForCoreWindow. As seguintes opções devem ser definidas no DXGI_SWAP_CHAIN_DESC1 fornecido para o parâmetro 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.
    

    Observação Defina o DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER novamente sempre que a cadeia de troca for redimensionada.

    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 duas cadeias de troca estiverem sendo usadas, aumente a latência máxima do quadro para 2 para que o adaptador DXGI tenha tempo de apresentar ambas as cadeias de troca simultaneamente (dentro do mesmo intervalo 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. As cadeias de troca em primeiro plano sempre usam alfa pré-multiplicado. Espera-se que os valores de cor de cada pixel já estejam multiplicados pelo valor alfa antes que o quadro seja apresentado. Por exemplo, um pixel BGRA 100% branco com 50% alfa é definido como (0,5, 0,5, 0,5, 0,5).

    A etapa de pré-multiplicação alfa pode ser feita no estágio de fusão de saída aplicando um estado de combinação de aplicativo (consulte ID3D11BlendState) com o campo SrcBlend da estrutura D3D11_RENDER_TARGET_BLEND_DESC definido como D3D11_SRC_ALPHA. Ativos com valores alfa pré-multiplicados também podem ser usados.

    Se a etapa de pré-multiplicação alfa não for concluída, as cores na cadeia de troca de primeiro plano serão mais brilhantes do que o esperado.

  5. Dependendo se a cadeia de troca em primeiro plano foi criada, a superfície de desenho Direct2D para elementos de interface do usuário pode precisar ser associada à cadeia de troca em primeiro plano.

    Criando exibições de destino de renderização:

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

    Criando a superfície de desenho 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. Apresente a cadeia de troca em primeiro plano junto com a cadeia de troca em escala usada para conteúdo de jogo em tempo real. Como a latência de quadro foi definida como 2 para ambas as cadeias de troca, o DXGI pode apresentá-las dentro do mesmo intervalo 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);
        }
    }