瞭解如何建立縮放交換鏈結,以在行動裝置上更快速地轉譯,並使用重疊交換鏈結(可用時)提高視覺品質。
DirectX 11.2 中的交換鏈結
Direct3D 11.2 可讓您建立通用 Windows 平台(UWP)應用程式,其交換鏈會從非原生解析度提升,以加快填充速率。 Direct3D 11.2 也包含用於硬體覆疊渲染的 API,讓您可以在原生解析度的另一個交換鏈中呈現 UI。 這可讓您的遊戲在維持高幀速率的同時,以原生解析度繪製UI,藉此充分利用行動裝置和高DPI顯示器(例如3840乘2160)。 本文說明如何使用重疊的交換鏈結。
Direct3D 11.2 也引進了一項新功能,可透過翻轉模型交換鏈結來降低延遲。 請參閱 使用 DXGI 1.3 交換鏈結減少延遲。
使用交換鏈縮放
當您的遊戲在舊版硬體上執行,或在針對省電優化的硬體上時,轉譯實時遊戲內容至低於原生解析度可能有利。 若要這樣做,用於轉譯遊戲內容的交換鏈結必須小於原生解析度,或是必須使用交換鏈的子區域。
首先,建立一個全原生解析度的交換鏈。
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) );
然後,選擇交換鏈結的子區域,藉由將來源大小設為較低解析度以進行放大。
DX 前景交換鏈範例會根據百分比計算縮小後的大小:
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 ) );
建立檢視區以符合交換鏈結的子區域。
// 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);
如果使用 Direct2D,則必須調整旋轉轉換以補償來源區域。
建立硬體覆疊的 UI 介面元素交換鏈
使用交換鏈結縮放時,UI 也會被縮小,這固有的缺點可能會導致UI變得模糊且難以使用。 在硬體支援覆蓋交換鏈的設備上,通過在與即時遊戲內容分開的交換鏈中以原始解析度呈現使用者介面,可以完全解決此問題。 請注意,這項技術只適用於 CoreWindow 交換鏈結 - 它不能與 XAML Interop 搭配使用。
使用下列步驟來建立使用硬體重疊功能的前景交換鏈結。 這些步驟會在第一次建立實時遊戲內容的交換鏈結之後執行,如上所述。
首先,判斷 DXGI 配接器是否支援重疊。 從交換鏈中獲取 DXGI 輸出適配器:
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) );
如果輸出配接器針對 SupportsOverlays傳回 True,則 DXGI 配接器支援重疊。
m_overlaySupportExists = dxgiOutput2->SupportsOverlays() ? true : false;
注意 如果 DXGI 配接器支援重疊,請繼續進行下一個步驟。 如果裝置不支援重疊,使用多個交換鏈結進行轉譯將不會有效率。 而是在與實時遊戲內容相同的交換鏈中,以較低解析度呈現 UI。
使用 IDXGIFactory2::CreateSwapChainForCoreWindow建立前景交換鏈結。 下列選項必須在提供給 pDesc 參數的 DXGI_SWAP_CHAIN_DESC1 中設定:
- 指定 DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER 交換鏈結旗標,以指出前景交換鏈結。
- 使用 DXGI_ALPHA_MODE_PREMULTIPLIED Alpha 模式旗標。 前景交換鏈結一律是預乘的。
- 設定 DXGI_SCALING_NONE 旗標。 前景交換鏈結一律以原生解析度執行。
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.
注意 每次調整交換鏈結大小時,再次設定 DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER。
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. );
使用兩個交換鏈結時,請將最大畫面延遲增加到 2,讓 DXGI 配接器有時間同時呈現這兩個交換鏈結(在相同的 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 ) ); }
前景交換鏈結一律使用預乘Alpha。 每個圖元的色彩值預期在顯示畫面之前已經乘以 Alpha 值。 例如,100% 白色 BGRA 像素在 50% alpha 時會設置為(0.5, 0.5, 0.5, 0.5)。
alpha 預乘步驟可以在輸出合併階段中完成,方法是套用應用程式混合狀態(請參閱 ID3D11BlendState),並將 D3D11_RENDER_TARGET_BLEND_DESC 結構的 SrcBlend 字段設定為 D3D11_SRC_ALPHA。 也可以使用具有預乘 alpha 值的資產。
如果未完成 Alpha 預乘步驟,前景交換鏈結上的色彩將會比預期更亮。
根據是否建立前景交換鏈結,UI 元素的 Direct2D 繪圖介面可能需要與前景交換鏈結相關聯。
建立渲染目標檢視:
// 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 ) ); }
建立 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 ) );
將前景交換鏈結與用於即時遊戲內容的縮放交換鏈結一起呈現。 由於兩個交換鏈結的畫面延遲設定為 2,DXGI 可以在相同的 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); } }