Microsoft DirectX 图形基础结构(DXGI)认识到,图形的某些部分发展速度比其他部分慢。 DXGI 的主要目标是管理独立于 DirectX 图形运行时的低级别任务。 DXGI 为未来的图形组件提供了一个通用框架;利用 DXGI 的第一个组件是 Microsoft Direct3D 10。
在 Direct3D 的早期版本中,包括硬件设备的枚举、将渲染帧呈现到输出设备、控制伽玛以及管理全屏转换等低级任务都包含在 Direct3D 运行时中。 这些任务现在在 DXGI 中实现。
DXGI 的目的是与内核模式驱动程序和系统硬件通信,如下图所示。
应用程序可以直接访问 DXGI,也可以在 D3D11_1.h、D3D11.h、D3D10_1.h 或 D3D10.h 中调用 Direct3D API,以便处理与 DXGI 的通信。 如果应用程序需要枚举设备或控制如何将数据呈现到输出,则可能需要直接使用 DXGI。
本主题包含以下各节:
若要查看 Direct3D 11 硬件支持的格式:
- Direct3D 功能级别 12.1 硬件的 DXGI 格式支持
- Direct3D 功能级别 12.0 硬件的 DXGI 格式支持
- Direct3D 功能级别 11.1 硬件的 DXGI 格式支持
- Direct3D 功能级别 11.0 硬件的 DXGI 格式支持
- Direct3D 10Level9 格式的硬件支持
- Direct3D 10.1 格式的硬件支持
- Direct3D 10 格式的硬件支持
枚举适配器
适配器是计算机的硬件和软件功能的抽象。 计算机上的适配器通常有很多。 某些设备在硬件(如视频卡)中实现,有些设备在软件(如 Direct3D 参考光栅器)中实现。 适配器实现图形应用程序使用的功能。 下图显示了一个具有一台计算机、两个适配器(视频卡)和三个输出监视器的系统。
枚举这些硬件时,DXGI 会为每个输出(或监视器)创建 IDXGIOutput1 接口,并为每个视频卡创建 IDXGIAdapter2 接口(即使它是内置于主板中的视频卡)。 枚举是通过使用 IDXGIFactory 接口调用 IDXGIFactory::EnumAdapters 来完成的,以返回一组表示视频硬件的 IDXGIAdapter 接口。
DXGI 1.1 添加了 IDXGIFactory1 接口。 IDXGIFactory1::EnumAdapters1 返回一组表示视频硬件的 IDXGIAdapter1 接口。
如果要在使用 Direct3D API 时选择特定的视频硬件功能,建议使用每个适配器句柄和可能的硬件功能级别以迭代方式调用 D3D11CreateDevice 或 D3D11CreateDeviceAndSwapChain 函数。 如果指定适配器支持功能级别,则此函数会成功。
有关枚举 Windows 8 适配器的新信息
从 Windows 8 开始,始终存在名为“Microsoft基本呈现驱动程序”的适配器。 此适配器具有 VendorId 0x1414 和 DeviceID 0x8c。 此适配器还在其 DXGI_ADAPTER_DESC2 结构的 Flags 成员中设置了 DXGI_ADAPTER_FLAG_SOFTWARE 值。 这款适配器是仅用于呈现的设备,无显示输出。 DXGI 永远不会为此适配器返回 DXGI_ERROR_DEVICE_REMOVED 。
如果计算机的显示驱动程序无法运行或被禁用,计算机的主(NULL)适配器也可能称为“Microsoft 基本显示驱动程序”。但此适配器有输出,并且未设置 DXGI_ADAPTER_FLAG_SOFTWARE 值。 作系统和应用默认使用此适配器。 如果已安装或启用显示驱动程序,应用可以对这个适配器接收到 DXGI_ERROR_DEVICE_REMOVED ,然后必须重新枚举这些适配器。
当计算机的主显示适配器是“Microsoft基本显示适配器”(WARP 适配器),该计算机也有第二个适配器。 第二个适配器是仅用于渲染的设备,不提供显示输出,且 DXGI 永远不会返回 DXGI_ERROR_DEVICE_REMOVED。
如果想使用 WARP 进行渲染、计算或其他长时间运行的任务,建议使用渲染设备。 可以通过调用IDXGIFactory1::EnumAdapters1方法获取指向仅用于渲染的设备的指针。 在 D3D11CreateDevice 的 DriverType 参数中指定D3D_DRIVER_TYPE_WARP时,还可以创建仅呈现设备,因为 WARP 设备也使用仅呈现的 WARP 适配器。
演示文稿
应用程序的工作是呈现帧,并要求 DXGI 将这些帧呈现到输出。 如果应用程序有两个可用缓冲区,则它可以呈现一个缓冲区,同时呈现另一个缓冲区。 应用程序可能需要两个以上的缓冲区,具体取决于呈现帧所需的时间或呈现所需的帧速率。 创建的缓冲区集称为交换链,如下所示。
交换链有一个前缓冲区和一个或多个后退缓冲区。 每个应用程序创建自己的交换链。 为了最大限度地提高将数据呈现到输出的速度,几乎总是在显示子系统的内存中创建交换链,如下图所示。
显示子系统(通常是视频卡,但可以在主板上实现)包含 GPU、数字到模拟转换器(DAC)和内存。 交换链在此内存中分配,以使演示速度非常快。 显示子系统向输出显示前缓冲区中的数据。
交换链设置为以全屏模式或窗口模式绘制,因此无需知道输出是开窗还是全屏。 全屏模式交换链可以通过切换显示分辨率来优化性能。
创建交换链
Direct3D 9 和 Direct3D 10 之间的差异:Direct3D 10 是第一个使用 DXGI 的图形组件。 DXGI 具有一些不同的交换链行为。
|
交换链的缓冲区以特定大小和特定格式创建。 应用程序在启动时指定这些值(或者可以从目标窗口继承大小),然后可以选择修改它们,因为窗口大小在响应用户输入或程序事件时发生更改。
创建交换链后,通常您需要将图像渲染到其中。 下面是一个代码片段,用于设置 Direct3D 上下文以呈现到交换链中。 此代码从交换链中提取缓冲区,从该缓冲区创建呈现目标视图,然后在设备上设置它:
ID3D11Resource * pBB;
ThrowFailure( pSwapChain->GetBuffer(0, __uuidof(pBB),
reinterpret_cast<void**>(&pBB)), "Couldn't get back buffer");
ID3D11RenderTargetView * pView;
ThrowFailure( pD3D11Device->CreateRenderTargetView(pBB, NULL, &pView),
"Couldn't create view" );
pD3D11DeviceContext->OMSetRenderTargets(1, &pView, 0);
应用程序将帧呈现到交换链缓冲区后,调用 IDXGISwapChain1::Present1。 然后,应用程序可以呈现下一个图像。
交换链的维护与管理
呈现图像后,调用 IDXGISwapChain1::Present1 并呈现下一个图像。 这就是你的责任范围。
如果以前调用 IDXGIFactory::MakeWindowAssociation,用户可以按 Alt-Enter 组合键,DXGI 将在窗口模式和全屏模式之间转换应用程序。 建议使用 IDXGIFactory::MakeWindowAssociation ,因为用户的标准控制机制非常需要。
虽然无需编写比描述更多的代码,但几个简单的步骤可以使应用程序响应更快速。 最重要的考虑因素是调整交换链缓冲区的大小,以响应输出窗口的大小调整。 当然,应用程序的最佳方法是响应WM_SIZE,并调用IDXGISwapChain::ResizeBuffers,传递消息参数中包含的尺寸。 此行为显然使应用程序在拖动窗口边框时对用户做出良好响应,但它也正是启用平滑全屏切换功能。 每当发生此类转换时,窗口都会收到WM_SIZE消息,调用 IDXGISwapChain::ResizeBuffers 是交换链重新分配缓冲区存储以获得最佳呈现的机会。 这就是为什么在调用 IDXGISwapChain::ResizeBuffers 之前,应用程序需要释放它在现有缓冲区上拥有的任何引用。
未能在响应切换到全屏模式(通常是响应 WM_SIZE)时调用 IDXGISwapChain::ResizeBuffers,可能会阻止翻转优化,其中 DXGI 只需交换显示哪个缓冲区,而不是复制整个屏幕的数据。
IDXGISwapChain1::Present1 将通知你,如果输出窗口完全被 DXGI_STATUS_OCCLUDED 遮挡。 发生这种情况时,我们建议应用程序进入备用模式(通过调用 IDXGISwapChain1::P resent1 和 DXGI_PRESENT_TEST),因为用于呈现帧的资源会浪费。 使用 DXGI_PRESENT_TEST 将阻止显示任何数据,同时仍执行遮挡检查。 IDXGISwapChain1::P resent1 返回S_OK后,应退出备用模式;请勿使用返回代码切换到备用模式,因为这样做会使交换链无法放弃全屏模式。
Direct3D 11.1 运行时(自 Windows 8 起提供)提供翻转模式交换链,即在 DXGI_SWAP_CHAIN_DESC 或 DXGI_SWAP_CHAIN_DESC1 的 SwapEffect 成员中设置 DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL 值的交换链。 使用翻转模型交换链将帧呈现到输出时,DXGI 会将后台缓冲区从所有管道状态位置(如输出合并呈现目标)取消绑定,这些目标会写入回缓冲区 0。 因此,建议在呈现到后台缓冲区之前立即调用 ID3D11DeviceContext::OMSetRenderTargets 。 例如,不要调用 OMSetRenderTargets ,然后执行计算着色器工作,最终不会呈现到资源。 有关翻转模型交换链及其优势的详细信息,请参阅 DXGI 翻转模型。
注释
在 Direct3D 10 和 Direct3D 11 中,无需调用 IDXGISwapChain::GetBuffer 即可在调用 IDXGISwapChain1::P resent1 后检索后台缓冲区 0,因为为方便起见,后台缓冲区的标识会更改。 这不会在 Direct3D 12 中发生,应用程序必须改为手动跟踪回退缓冲区索引。
处理窗口大小调整
可以使用 IDXGISwapChain::ResizeBuffers 方法处理窗口大小调整。 在调用 ResizeBuffers 之前,必须释放对交换链缓冲区的所有未完成引用。 通常保存对交换链缓冲区的引用的对象是呈现目标视图。
以下示例代码演示如何从 WindowProc 处理程序中为WM_SIZE消息调用 ResizeBuffers :
case WM_SIZE:
if (g_pSwapChain)
{
g_pd3dDeviceContext->OMSetRenderTargets(0, 0, 0);
// Release all outstanding references to the swap chain's buffers.
g_pRenderTargetView->Release();
HRESULT hr;
// Preserve the existing buffer count and format.
// Automatically choose the width and height to match the client rect for HWNDs.
hr = g_pSwapChain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
// Perform error handling here!
// Get buffer and create a render-target-view.
ID3D11Texture2D* pBuffer;
hr = g_pSwapChain->GetBuffer(0, __uuidof( ID3D11Texture2D),
(void**) &pBuffer );
// Perform error handling here!
hr = g_pd3dDevice->CreateRenderTargetView(pBuffer, NULL,
&g_pRenderTargetView);
// Perform error handling here!
pBuffer->Release();
g_pd3dDeviceContext->OMSetRenderTargets(1, &g_pRenderTargetView, NULL );
// Set up the viewport.
D3D11_VIEWPORT vp;
vp.Width = width;
vp.Height = height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
g_pd3dDeviceContext->RSSetViewports( 1, &vp );
}
return 1;
选择 DXGI 输出和大小
默认情况下,DXGI 选择包含窗口主要客户区的输出。 当 DXGI 自身响应 alt-enter 进入全屏时,这是唯一可用的选项。 如果应用程序选择自行进入全屏模式,则可以调用 IDXGISwapChain::SetFullscreenState 并传递显式 IDXGIOutput1 (或 NULL,如果应用程序很高兴让 DXGI 决定)。
若要在全屏或窗口化时调整输出大小,建议调用 IDXGISwapChain::ResizeTarget,因为此方法也会调整目标窗口的大小。 由于目标窗口已调整大小,因此操作系统会发送 WM_SIZE,并且你的代码会自然地调用IDXGISwapChain::ResizeBuffers。 因此,先重新调整缓冲区大小,然后再调整目标大小实际上是一种徒劳的努力。
全屏模式下调试
只有在绝对必要的情况下,DXGI 交换链才会放弃全屏模式。 这意味着,只要调试窗口不重叠交换链的目标窗口,就可以使用多个监视器调试全屏应用程序。 或者,可以通过不设置 DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH 标志来完全阻止模式切换。
如果允许模式切换,当输出窗口被其他窗口覆盖时,交换链将会放弃全屏模式。 遮挡检查是在 IDXGISwapChain1::P resent1 期间执行的,或者由一个单独的线程执行,其用途是监视应用程序是否无响应(并且不再调用 IDXGISwapChain1::P resent1)。 若要禁用单独的线程导致切换的功能,请将以下注册表项设置为任何非零值。
HKCU\Software\Microsoft\DXGI\DisableFullscreenWatchdog
销毁交换链
你不能在全屏模式下释放交换链,因为这样做可能会创建线程争用,从而导致 DXGI 引发无法继续的异常。 在释放交换链之前,请先切换到开窗模式(使用 IDXGISwapChain::SetFullscreenState(FALSE,NULL), 然后调用 IUnknown::Release。
使用旋转监视器
应用程序无需担心监视器方向,如果需要,DXGI 将在演示期间旋转交换链缓冲区。 当然,这种额外的轮换可能会影响性能。 为了获得最佳性能,请通过执行以下操作来处理应用程序中的旋转:
- 使用 DXGI_SWAP_CHAIN_FLAG_NONPREROTATED。 这会通知 DXGI 应用程序将生成旋转的图像(例如,通过更改其投影矩阵)。 需要注意的一点是,此标志仅在全屏模式下有效。
- 在其旋转大小中分配每个交换链缓冲区。 如有必要,请使用 IDXGIOutput::GetDesc 获取这些值。
在您的应用程序中执行旋转时,DXGI 只需进行一次复制,而不是复制和旋转。
Direct3D 11.1 运行时(从 Windows 8 开始提供)提供翻转模型交换链(即在 DXGI_SWAP_CHAIN_DESC1的 SwapEffect 成员中设置了DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL值的交换链)。 为了最大限度地利用翻转模型交换链提供的呈现优化,我们建议在内容完全占用输出时,使应用程序调整内容方向,以匹配该内容所驻留的特定输出。 有关翻转模型交换链及其优势的详细信息,请参阅 DXGI 翻转模型。
切换模式
当进行全屏切换时,DXGI 交换链可能会更改输出的显示模式。 若要启用自动显示模式更改,必须在交换链说明中指定 DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH 。 如果显示模式自动更改,DXGI 将选择最适度的模式(大小和分辨率不会更改,但颜色深度可能会改变)。 调整交换链缓冲区的大小不会造成模式切换。 交换链会隐式承诺:如果选择与目标输出支持的显示模式完全匹配的后退缓冲区,则在在该输出上进入全屏模式时,它将切换到该显示模式。 因此,可以通过选择后退缓冲区大小和格式来选择显示模式。
全屏性能建议
在全屏应用程序中调用 IDXGISwapChain1::Present1 时,交换链会将后台缓冲区的内容翻转(而不是直接复制)到前缓冲区。 这要求使用枚举显示模式( DXGI_SWAP_CHAIN_DESC1中指定的)创建交换链。 如果无法枚举显示模式,或在说明中错误地指定显示模式,交换链可能会改为执行位块传输(bitblt)。 bitblt 会导致额外的拉伸复制,并增加视频内存的使用量,而且难以检测。 若要避免此问题,请枚举显示模式,并在创建交换链之前正确初始化交换链说明。 这将在全屏模式下翻转时确保最佳性能,并避免额外的内存开销。
多线程注意事项
在具有多个线程的应用程序中使用 DXGI 时,需要注意避免创建死锁,其中两个不同的线程正在等待彼此完成。 有两种情况可以发生这种情况。
- 呈现线程不是消息泵线程。
- 执行 DXGI API 的线程与创建窗口的线程不同。
请注意,使用全屏交换链时,务必避免让消息泵线程等待呈现线程。 例如,调用 IDXGISwapChain1::P resent1 (来自呈现线程)可能会导致呈现线程在消息泵线程上等待。 发生模式更改时,如果 Present1 调用 ::SetWindowPos() 或 ::SetWindowStyle()和以下任一方法调用 ::SendMessage(),则可能会出现这种情况。 在此方案中,如果消息泵线程具有关键部分保护,或者如果渲染线程被阻塞,则两个线程将死锁。
有关将 DXGI 与多个线程配合使用的详细信息,请参阅 多线程和 DXGI。
DLLMain 的 DXGI 响应
由于 DllMain 函数无法保证加载和卸载 DLL 的顺序,因此我们建议应用的 DllMain 函数不调用 Direct3D 或 DXGI 函数或方法,包括创建或释放对象的函数或方法。 如果应用的 DllMain 函数调用特定组件,该组件可能会调用作系统上不存在的另一个 DLL,这会导致作系统崩溃。 Direct3D 和 DXGI 可能会加载一组 DLL(通常是一些驱动程序),这些在不同的计算机上可能会有所不同。 因此,即使应用在其 DllMain 函数调用 Direct3D 或 DXGI 函数或方法时不会在开发和测试计算机上崩溃,它也可能在另一台计算机上运行时崩溃。
为了防止创建可能导致作系统崩溃的应用,DXGI 在指定情况下提供以下响应:
DXGI 1.1 更改
我们在 DXGI 1.1 中添加了以下功能。
同步共享表面支持
Direct3D 10.1 和 Direct3D 11 的同步共享图面使多个 Direct3D 设备之间可以高效地读取和写入图面(在 Direct3D 10 和 Direct3D 11 设备之间的共享也是可能的)。 请参阅 IDXGIKeyedMutex::AcquireSync 和 IDXGIKeyedMutex::ReleaseSync。
高颜色支持
支持DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM格式。
IDXGIDevice1::SetMaximumFrameLatency 和 IDXGIDevice1::GetMaximumFrameLatency
IDXGIFactory1::EnumAdapters1 枚举本地适配器(s),不附加任何监视器或输出,以及附加了输出的适配器(s)。 返回的第一个适配器将是显示桌面主屏幕的本地适配器。
BGRA 格式支持
DXGI_FORMAT_B8G8R8A8_UNORM和DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,请参阅 IDXGISurface1::GetDC 和 IDXGISurface1::ReleaseDC。
DXGI 1.2 更改
我们在 DXGI 1.2 中添加了以下功能。
- 立体声交换链
- 翻转式交换链
- 优化的呈现(滚动、脏矩形和旋转)
- 改进了共享资源和同步
- 桌面复制
- 优化了视频内存的使用
- 支持每像素 16 位(bpp)格式(DXGI_FORMAT_B5G6R5_UNORM、DXGI_FORMAT_B5G5R5A1_UNORM、DXGI_FORMAT_B4G4R4A4_UNORM)
- 调试 API
有关 DXGI 1.2 的详细信息,请参阅 DXGI 1.2 改进。
应用程序兼容性
DXGI 可能会应用应用程序行为修改来增强应用程序兼容性。
- 兼容性设置可以存储在用户注册表下
HKCU\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\AppCompatFlags\Layers。 - 根据系统配置或其他因素,兼容性设置可能或可能未应用。
- 应用程序兼容性设置值的示例为
DXAllowModeChangeOnLaunch720。