Mise à l’échelle et superpositions de chaînes d’échange
Découvrez comment créer des chaînes d’échange mises à l’échelle pour accélérer le rendu sur les appareils mobiles et utiliser des chaînes d’échange de superposition (le cas échéant) pour augmenter la qualité visuelle.
Chaînes d’échange dans DirectX 11.2
Direct3D 11.2 vous permet de créer des applications plateforme Windows universelle (UWP) avec des chaînes d’échange qui sont mises à l’échelle à partir de résolutions non natives (réduites), ce qui permet des taux de remplissage plus rapides. Direct3D 11.2 inclut également des API pour le rendu avec des superpositions matérielles afin de pouvoir présenter une interface utilisateur dans une autre chaîne d’échange à la résolution native. Cela permet à votre jeu de dessiner l’interface utilisateur en résolution native complète tout en conservant une vitesse d’image élevée, ce qui permet de tirer le meilleur parti des appareils mobiles et des affichages DPI élevés (par exemple, 3840 d’environ 2160). Cet article explique comment utiliser des chaînes d’échange qui se chevauchent.
Direct3D 11.2 introduit également une nouvelle fonctionnalité pour une latence réduite avec des chaînes d’échange de modèle inversées. Consultez Réduire la latence avec les chaînes d’échange DXGI 1.3.
Utiliser la mise à l’échelle de la chaîne d’échange
Lorsque votre jeu s’exécute sur du matériel de niveau inférieur ( ou un matériel optimisé pour l’économie d’énergie), il peut être utile d’afficher du contenu de jeu en temps réel à une résolution inférieure à celle de l’affichage en mode natif. Pour ce faire, la chaîne d’échange utilisée pour le rendu du contenu du jeu doit être inférieure à la résolution native, ou un sous-réseau de la chaîne d’échange doit être utilisé.
Tout d’abord, créez une chaîne d’échange à la résolution native complète.
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) );
Ensuite, choisissez une sous-région de la chaîne d’échange pour effectuer un scale-up en définissant la taille de la source à une résolution réduite.
L’exemple chaînes d’échange de premier plan DX calcule une taille réduite en fonction d’un pourcentage :
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 ) );
Créez un point de vue pour correspondre à la sous-région de la chaîne d’échange.
// 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);
Si Direct2D est utilisé, la transformation de rotation doit être ajustée pour compenser la région source.
Créer une chaîne d’échange de superposition matérielle pour les éléments d’interface utilisateur
Lors de l’utilisation de la mise à l’échelle de la chaîne d’échange, il existe un inconvénient inhérent au fait que l’interface utilisateur est également mise à l’échelle, ce qui rend potentiellement floue et plus difficile à utiliser. Sur les appareils avec prise en charge matérielle des chaînes d’échange de superposition, ce problème est entièrement atténué en rendant l’interface utilisateur à la résolution native dans une chaîne d’échange distincte du contenu du jeu en temps réel. Notez que cette technique s’applique uniquement aux chaînes d’échange CoreWindow . Elle ne peut pas être utilisée avec l’interopérabilité XAML.
Procédez comme suit pour créer une chaîne d’échange de premier plan qui utilise la fonctionnalité de superposition matérielle. Ces étapes sont effectuées après la création d’une chaîne d’échange pour le contenu de jeu en temps réel, comme décrit ci-dessus.
Tout d’abord, déterminez si l’adaptateur DXGI prend en charge les superpositions. Obtenez l’adaptateur de sortie DXGI à partir de la chaîne d’échange :
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’adaptateur DXGI prend en charge les superpositions si l’adaptateur de sortie retourne True pour SupportsOverlays.
m_overlaySupportExists = dxgiOutput2->SupportsOverlays() ? true : false;
Notez que si l’adaptateur DXGI prend en charge les superpositions, passez à l’étape suivante. Si l’appareil ne prend pas en charge les superpositions, le rendu avec plusieurs chaînes d’échange ne sera pas efficace. Au lieu de cela, affichez l’interface utilisateur à résolution réduite dans la même chaîne d’échange que le contenu de jeu en temps réel.
Créez la chaîne d’échange de premier plan avec IDXGIFactory2 ::CreateSwapChainForCoreWindow. Les options suivantes doivent être définies dans la DXGI_SWAP_CHAIN_DESC1 fournie au paramètre pDesc :
- Spécifiez l’indicateur de chaîne d’échange DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER pour indiquer une chaîne d’échange au premier plan.
- Utilisez l’indicateur de mode alpha DXGI_ALPHA_MODE_PREMULTIPLIED . Les chaînes d’échange de premier plan sont toujours prémultipliées.
- Définissez l’indicateur DXGI_SCALING_NONE. Les chaînes d’échange de premier plan s’exécutent toujours à la résolution native.
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.
Notez que définissez à nouveau la DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER chaque fois que la chaîne d’échange est redimensionnée.
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. );
Lorsque deux chaînes d’échange sont utilisées, augmentez la latence maximale d’images à 2 afin que l’adaptateur DXGI ait le temps de présenter les deux chaînes d’échange simultanément (dans le même intervalle 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 ) ); }
Les chaînes d’échange de premier plan utilisent toujours l’alpha prémultipliée. Les valeurs de couleur de chaque pixel sont censées être déjà multipliées par la valeur alpha avant la présentation du cadre. Par exemple, un pixel BGRA blanc de 100 % à 50 % alpha est défini sur (0,5, 0,5, 0,5, 0,5).
L’étape de prémultiplication alpha peut être effectuée dans l’étape de fusion de sortie en appliquant un état de fusion d’application (voir ID3D11BlendState) avec le champ SrcBlend de la structure D3D11_RENDER_TARGET_BLEND_DESC défini sur D3D11_SRC_ALPHA. Les ressources avec des valeurs alpha pré multipliées peuvent également être utilisées.
Si l’étape de prémultiplication alpha n’est pas effectuée, les couleurs de la chaîne d’échange au premier plan sont plus brillantes que prévu.
Selon que la chaîne d’échange de premier plan a été créée, l’aire de dessin Direct2D pour les éléments d’interface utilisateur peut avoir besoin d’être associée à la chaîne d’échange de premier plan.
Création de vues cibles de rendu :
// 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 ) ); }
Création de l’aire de dessin 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 ) );
Présentez la chaîne d’échange de premier plan avec la chaîne d’échange mise à l’échelle utilisée pour le contenu du jeu en temps réel. Étant donné que la latence d’image a été définie sur 2 pour les deux chaînes d’échange, DXGI peut les présenter dans le même intervalle 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); } }