下载 PDF[PDF 635KB]
4.0 前面章节的链接
第 1 章: DirectX* 12 概述
第 2 章: DirectX 12 工具
第 3 章: 从 DirectX 11 迁移到 DirectX 12
4.1 多平面叠加的功能及用法
4.1.1 简介
多平面叠加 (MPO) 是 WDDM1.3 (DX11.2) 的一项新特性,最初在 Windows 8.1 中推出,现在扩展到 Windows 10 中的 WDDM2.0 (DX12)。MPO 支持使用原始分辨率来显示绚丽的 2D 艺术和 UI 元素,同时将 3D 场景绘制到更小且可伸展的帧缓冲。不同分辨率的平面的伸展和组合由系统自动实施,并且对应用而言,此流程是透明的。
MPO 的主要作用是允许游戏在不同情况下保持稳定和适当的帧速率,从而提升整体的游戏体验。一方面,分辨率总对现代游戏的性能有着显著的影响。随着高清 4K 屏幕的普及,游戏窗口的分辨率也随之提高。提高像素也能增加渲染目标的纹理采样次数和带宽,这就对游戏的性能带来了挑战。其次,后处理技术越来越多地被用于渲染 3D 场景,这就提高了着色器的复杂性,因此,趋势将是每像素的成本决定了游戏的性能。因此,设置适当的分辨率是保持游戏性能的关键。
此外,对于角色扮演、即时战略和多人联机等游戏,以初始窗口分辨率渲染 GUI 组件也非常重要。例如,即使在低端平台上,玩家在玩游戏过程中也希望用文字与队友聊天。GUI 组件主要是渲染开销相对较低的 2D 图形,因此以窗口分辨率进行渲染可提供最佳的视觉体验。
MPO 针对高分辨率显示屏和游戏性能提供了折衷解决方案。使用 MPO 可以缓解在高分辨率渲染情况下游戏性能出现快速下降的问题,从而允许游戏在大多数硬件平台上平稳地运行。MPO 不仅支持在不同分辨率下渲染 GUI 层和 3D 内容层,而且也支持以不同的帧速率进行渲染。例如,对于 GUI 层,只在发生变化时才出现。它有利于提升性能和能效。图 1 介绍了 MPO 的交换链组成。
由于 MPO 是驱动程序模型的一项新特性,显卡驱动程序有助于将 MPO 执行到上一级 D3D 运行时间或 DXGI 运行时间。MPO 可通过软件由 WDDM1.3 或更高版本的驱动程序或由使用针对 GPU 的专用管道的显卡硬件进行实施,这样才能减少对许多 CPU 和 GPU 资源的消耗,从而有助于进一步提高游戏的性能并降低功耗。英特尔将为第 6 代英特尔® 酷睿™ 处理器(代号为 SkyLake)中的 MPO 添加硬件支持。
图 1:多平面叠加的交换链组成
4.1.2 应用场景
在游戏中,多平面叠加通常在下列场景中使用。
性能(如帧速率)低于某一阈值的场景。
根据游戏的类型,当帧速率低于某一值时,游戏的可玩性将变差。例如,在出现大量游戏人物的战斗场景中,游戏的帧速率会显著下降。要避免性能下降到游戏无法玩的程度,游戏将自动切换到 MPO 渲染模式,这样,当它检测到帧速率低于阈值时就会保持游戏可玩时的性能。
释放出粒子效果的场景。
在 MMOG 中,释放出大量粒子效果的场景常常会导致帧速率突然下降,在严重的情况下甚至造成游戏运行不畅,这是游戏常见的性能瓶颈。渲染粒子效果涉及大量象素填充,同时在高分辨率表面进行渲染会对性能产生更显著的影响。释放粒子效果时,游戏将切换到 MPO 模式,并将粒子效果渲染到采用较小分辨率的渲染目标,从而减少像素的填充量。此外,在激烈的战斗场景中,大量粒子效果的释放会导致屏幕出现急剧变化,因而玩家将难以看到各帧的细节。因此,MPO 伸展渲染目标对最终视觉质量的影响几乎感觉不到。在这些场景中使用 MPO,您的游戏可在保持视觉体验及解决常见的性能问题的同时保持适当的帧速率。
在移动平台上渲染
由于移动平台是流行的游戏终端,游戏开发人员就必须要考虑功耗问题。当平台从 AC 电源模式切换到电池电源模式时,系统性能设置会导致 CPU 或 GPU 频率降低,并迫使游戏帧速率下降。借助 MPO,游戏将自动调整渲染目标的分辨率,而不会通过更改窗口分辨率来弥补整体性能,同时降低功耗,让游戏更长久地运行。MPO 的这一特性可用于实现游戏的省电模式。此模式作为玩家的游戏设置选项进行提供。对于玩家,在省电模式下,可以接受屏幕出现的略微差异。
- 所有上述提到的应用场景专注于动态调整分辨率来保持 FPS。在实践中,只针对游戏的持续时间选择固定比率,则更为常见。对于 3D 内容层,正常的缩放比例选择将基于设备 DPI。(例如,缩放比例 = deviceDpi/96.0)
4.1.3 API 用法的示例代码
要在游戏中应用多平面叠加 (MPO),首先您需要检测平台上的软硬件是否支持它。这种情况的代码如下:
表 4.1:检测硬件是否支持多平面叠加
BOOL supportMultiPlaneOverlay = FALSE; IDXGISwapChain* dxgiSwapChain; IDXGIOutput* dxgiOutput; dxgiSwapChain->GetContainingOutput(&dxgiOutput); if (dxgiOutput) { IDXGIOutput2* dxgiOutput2; dxgiOutput->QueryInterface(IID_PPV_ARGS(&dxgiOutput2)); SAFE_RELEASE(dxgiOutput); if (dxgiOutput2) { supportMultiPlaneOverlay = dxgiOutput2->SupportsOverlays(); SAFE_RELEASE(dxgiOutput2); } }
IDXGIOutput2 :SupportsOverlays () API 被用于检测显卡硬件是否支持此特性。如果返回 true,则硬件支持此特性;如果返回 false,则硬件不支持此特性,但驱动程序在使用软件的情况下可支持此特性。英特尔 Skylake 平台和之后的处理器核心将支持硬件级别的 MPO。
在游戏初始化阶段,需要创建一些关键的多平面叠加 API 对象:如直接合成设备、缩放变压器和前后台交换链等。台式机应用的示例代码如下:
表 4.2:多平面叠加 API 对象的初始化和创建
IDXGIFactory2* dxgiFactory; adapter->GetParent(IID_PPV_ARGS(&dxgiFactory)); if (dxgiFactory) { // Create a Direct Composition device DCompositionCreateDevice(NULL, IID_PPV_ARGS(&m_directCompositionDevice)); // The device creates a render target for the window m_directCompositionDevice->CreateTargetForHwnd( (HWND)_CORE_API->GetAppWindow(), false, &m_directCompositionTarget); // The device creates a multi-layer view m_directCompositionDevice->CreateVisual(&m_rootVisual); m_directCompositionDevice->CreateVisual(&m_mainVisual); m_directCompositionDevice->CreateVisual(&m_overlayVisual); // The device creates a scale transformer and sets it into the main view m_directCompositionDevice->CreateScaleTransform(&m_mainScaleTransform); m_mainVisual->SetTransform(m_mainScaleTransform); // When the main view is stretched, adopt the linear interpolation for filtering m_mainVisual->SetBitmapInterpolationMode(DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR); // Add the main view and interface view to the root view, set the root view onto the render target m_rootVisual->AddVisual(m_mainVisual, FALSE, NULL); m_rootVisual->AddVisual(m_overlayVisual, FALSE, NULL); m_directCompositionTarget->SetRoot(m_rootVisual); // Prepare to create SwapChain DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 }; swapChainDesc.Width = width; // Match the size of the window. swapChainDesc.Height = height; swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; 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; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; swapChainDesc.Scaling = DXGI_SCALING_STRETCH; swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; // DXGIFactory creates the foreground SwapChain hr = dxgiFactory->CreateSwapChainForComposition( m_HUDCommandQueue,&swapChainDesc, nullptr,&m_foregroundSwapChain ); // Bind the interface view to the foreground SwapChain m_overlayVisual->SetContent(m_foregroundSwapChain); // Create the background SwapChain swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; hr = dxgiFactory->CreateSwapChainForComposition( m_3DCommandQueue,&swapChainDesc, nullptr,&m_backgroundSwapChain ); // Bind the main view to the background SwapChain m_mainVisual->SetContent(m_backgroundSwapChain); m_directCompositionDevice->Commit(); SAFE_RELEASE(dxgiFactory); }
注: D3D12 和 D3D11 采用不同的对象作为 CreateSwapChainForComposition 的第一个参数: D3D12 采用 CommandQueue,而 D3D11 采用 Device。
建议将不同的队列用于不同的交换链。原因是,DXGI 将代表您进行等待以便与 DWM 同步,因此,将相同队列用于两个交换链会导致针对一种交换链的工作负载被封锁,等待其他交换链提供缓冲区。
在游戏渲染过程中,基于 MPO 渲染的代码逻辑如下:
表 4.3:基于多平面叠加的游戏渲染方法
// According to changes in the number of frames, dynamically adjust the scale ratio of the background SwapChain m_mainScaleTransform->SetScaleX(scaleRatio); m_mainScaleTransform->SetScaleY(scaleRatio); m_directCompositionDevice->Commit(); // Before rendering 3D scenes, first set the corresponding RenderTarget of the background SwapChain // Adjust the size of Viewport at the same time OMSetRenderTargets(1, &m_backgroundRTV, m_backgroundDSV); viewport.Width = foregroundSwapChain.Width / scaleRatio; viewport.Height = foregroundSwapChain.Height / scaleRatio; RSSetViewports(1, viewport); // Draw 3D Scene... // When performing full-screen post-processing, you need to change the texture sampling coordinates, control the addressing range, because the background Viewport may only partially filled the RenderTarget VSSetConstantBuffer(scaleRatio); // Draw Fullscreen PostProcess // Before rendering the UI, first set the corresponding RenderTarget of the foreground SwapChain // Adjust the size of Viewport at the same time OMSetRenderTargets(1, &m_foregroundRTV, m_foregroundDSV); viewport.Width = foregroundSwapChain.Width; viewport.Height = foregroundSwapChain.Height; RSSetViewports(1, viewport); // Draw UI.. // Finally submit SwapChain m_backgroundResource->SetTransitionBarrier(D3D12_RESOURCE_STATE_PRESENT); m_foregroundResource->SetTransitionBarrier(D3D12_RESOURCE_STATE_PRESENT); m_commandList->Close(); m_backgroundSwapChain->Present(0, 0); m_foregroundSwapChain->Present(0, 0);
4.1.4 总结
多平面叠加是 Windows 8.1 和更高版本平台的一项新的图形显示功能。基于 Win10 平台的 DirectX 12 游戏可以轻松地使用 DX12 API 来渲染多平面叠加,可解决由游戏的高分辨率渲染和高负载场景所导致的帧速率突然下降的问题,从而在大多数硬件平台上实现流畅的游戏体验。