Compartir a través de


Intercambiar escalado y superposiciones de cadenas

Obtenga información sobre cómo crear cadenas de intercambio escaladas para una representación más rápida en dispositivos móviles y usar cadenas de intercambio de superposición (cuando estén disponibles) para aumentar la calidad visual.

Cadenas de intercambio en DirectX 11.2

Direct3D 11.2 permite crear aplicaciones de Plataforma universal de Windows (UWP) con cadenas de intercambio que se escalan verticalmente desde resoluciones no nativas (reducidas), lo que permite velocidades de relleno más rápidas. Direct3D 11.2 también incluye API para la representación con superposiciones de hardware para poder presentar una interfaz de usuario en otra cadena de intercambio en resolución nativa. Esto permite que tu juego dibuje la interfaz de usuario en resolución nativa completa mientras mantienes una alta velocidad de fotogramas, lo que hace el mejor uso de dispositivos móviles y pantallas de ppp altos (como 3840 a 2160). En este artículo se explica cómo usar cadenas de intercambio superpuestas.

Direct3D 11.2 también presenta una nueva característica para reducir la latencia con cadenas de intercambio de modelos flip. Consulte Reducción de la latencia con cadenas de intercambio DXGI 1.3.

Uso del escalado de cadenas de intercambio

Cuando el juego se ejecuta en hardware de nivel inferior ( o hardware optimizado para ahorros de energía), puede ser beneficioso representar contenido del juego en tiempo real en una resolución inferior a la que la pantalla es capaz de forma nativa. Para ello, la cadena de intercambio que se usa para representar el contenido del juego debe ser menor que la resolución nativa, o se debe usar una subred de la cadena de intercambio.

  1. En primer lugar, cree una cadena de intercambio en resolución 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. A continuación, elija una subdivisión de la cadena de intercambio para escalar verticalmente estableciendo el tamaño de origen en una resolución reducida.

    El ejemplo de cadenas de intercambio en primer plano DX calcula un tamaño reducido en función de un porcentaje:

    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. Cree una ventanilla para que coincida con la subdivisión de la cadena de intercambio.

    // 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. Si se usa Direct2D, la transformación de rotación debe ajustarse para compensar la región de origen.

Creación de una cadena de intercambio de superposición de hardware para los elementos de la interfaz de usuario

Al usar el escalado de cadenas de intercambio, hay una desventaja inherente en que la interfaz de usuario también se reduce verticalmente, lo que podría hacer que sea borroso y más difícil de usar. En los dispositivos compatibles con hardware para cadenas de intercambio superpuestas, este problema se soluciona completamente mediante la representación de la interfaz de usuario en resolución nativa en una cadena de intercambio independiente del contenido del juego en tiempo real. Tenga en cuenta que esta técnica solo se aplica a las cadenas de intercambio de CoreWindow ; no se puede usar con la interoperabilidad XAML.

Siga estos pasos para crear una cadena de intercambio en primer plano que use la funcionalidad de superposición de hardware. Estos pasos se realizan después de crear primero una cadena de intercambio para el contenido del juego en tiempo real, como se ha descrito anteriormente.

  1. En primer lugar, determine si el adaptador DXGI admite superposiciones. Obtenga el adaptador de salida DXGI de la cadena de intercambio:

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

    El adaptador DXGI admite superposiciones si el adaptador de salida devuelve True para SupportsOverlays.

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

    Nota Si el adaptador DXGI admite superposiciones, continúe con el paso siguiente. Si el dispositivo no admite superposiciones, la representación con varias cadenas de intercambio no será eficaz. En su lugar, represente la interfaz de usuario en una resolución reducida en la misma cadena de intercambio que el contenido del juego en tiempo real.

     

  2. Cree la cadena de intercambio en primer plano con IDXGIFactory2::CreateSwapChainForCoreWindow. Las siguientes opciones deben establecerse en el DXGI_SWAP_CHAIN_DESC1 proporcionado al 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.
    

    Nota Establezca el DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER de nuevo cada vez que se cambie el tamaño de la cadena de intercambio.

    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. Cuando se usan dos cadenas de intercambio, aumente la latencia máxima de fotogramas a 2 para que el adaptador DXGI tenga tiempo para presentar ambas cadenas de intercambio simultáneamente (dentro del mismo intervalo de 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. Las cadenas de intercambio en primer plano siempre usan alfa premultipado. Se espera que los valores de color de cada píxel ya se multipliquen por el valor alfa antes de que se presente el marco. Por ejemplo, un píxel BGRA blanco del 100 % al 50 % alfa se establece en (0,5, 0,5, 0,5, 0,5).

    El paso de premultiplicación alfa se puede realizar en la fase de fusión de salida aplicando un estado de combinación de aplicaciones (consulte ID3D11BlendState) con el campo SrcBlend de la estructura D3D11_RENDER_TARGET_BLEND_DESC establecido en D3D11_SRC_ALPHA. También se pueden usar recursos con valores alfa multiplicados previamente.

    Si no se realiza el paso de premultiplicación alfa, los colores de la cadena de intercambio en primer plano serán más brillantes de lo esperado.

  5. Dependiendo de si se creó la cadena de intercambio en primer plano, la superficie de dibujo de Direct2D para los elementos de la interfaz de usuario podría necesitar asociarse a la cadena de intercambio en primer plano.

    Creación de vistas de destino de representación:

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

    Creación de la superficie de dibujo de 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. Presente la cadena de intercambio en primer plano junto con la cadena de intercambio escalada usada para el contenido del juego en tiempo real. Dado que la latencia de fotogramas se estableció en 2 para ambas cadenas de intercambio, DXGI puede presentarlas en el mismo intervalo de 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);
        }
    }