原文地址: https://msdn.microsoft.com/en-us/library/windows/desktop/ee417025(v=vs.85).aspx
未完待续。。。
DXGI是在Windows Vista时被引入,封装了Direct3D10,10.1,11和11.1所需的底层工作。从一个Direct3D 9图形程序员的角度来看,DXGI封装了大多数枚举、交换链的创建等以前封装在Direct3D 9 APIs 中的代码。当你移植一个应用程序到DXGI、Direct3D10.X和Direct3D 11.X时,你需要考虑一些注意事项以确保程序可以顺利的运行。
全屏相关问题:
从Direct3D 9过度到DXGI和Direct3D 10.x或Direct3D 11.x时,当从窗口模式转换到全屏模式所伴随的问题通常可能会让起开发者头痛,主要的问题是Direct3D 9应用程序不像DXGI应用程序需要更深入的接近window styles和 window states。当切换模式的代码在DXGI中运行时经常引起一些并非预期的效果。
通常,Direct3D 9应用程序通过设置前台缓存的分辨率迫使设备进入全屏独占模式,然后设置后台缓存的分辨率相匹配。使用了一个单独的途径改变窗体的尺寸,因为程序不得不被window process管理并且任何时候都将接收到一个WM_SIZE处理消息。
DXGI试图通过合并这两部分处理以简化该操作。例如,当窗口模式下窗体的边框被拖拽时,应用程序会接受到一个WM_SIZE 处理消息,DXGI捕获此消息并自动改变前台缓存的大小。应用程序只需使用WM_SIZE传递的尺寸参数并调用IDXGISwapChain::ResizeBuffers 重置后台缓冲的尺寸。相似的,当窗体在全屏和窗口模式之间切换时,应用程序可简单的调用 IDXGISwapChain::SetFullscreenState 。DXGI重置前台缓冲大小以匹配刚刚的全屏模式操作,并且发送一个WM_SIZE message到应用程序,应用程序再次调用ResizeBuffers函数,就像之前窗体的边框被拖拽一样。
SharpDX代码实例:
//全屏切换触发事件 void renderForm_KeyDown(object sender, KeyEventArgs e) { if (SwapChain.IsFullScreen) { if (e.KeyCode == Keys.Escape) { SwapChain.IsFullScreen = false; } } else { if (e.Modifiers == Keys.Alt && e.KeyCode == Keys.Enter) { SwapChain.IsFullScreen = true; } } } //WM_SIZE事件 void renderForm_ClientSizeChanged(object sender, EventArgs e) { if (renderForm.ClientSize.Width != 0 && renderForm.ClientSize.Height != 0)//最小化时尺寸为0 { backBufferWidth = renderForm.ClientSize.Width; backBufferHeight = renderForm.ClientSize.Height; Disposer.RemoveAndDispose(ref backBufferTexture); Disposer.RemoveAndDispose(ref backBufferTargetView); // SwapChain.ResizeBuffers(1, backBufferWidth, backBufferHeight, Format.R8G8B8A8_UNorm, SwapChainFlags.None); backBufferTexture = Texture2D.FromSwapChain<Texture2D>(SwapChain, 0); backBufferTargetView = new RenderTargetView(device, backBufferTexture); device.ImmediateContext.Rasterizer.SetViewport(new ViewportF(0, 0, backBufferWidth, backBufferHeight, 0.0f, 1.0f)); }
}
之前论述的方法遵循一个非常特殊方式,DXGI通过设置默认的桌面分辨率作为全屏模式。然而许多应用程序切换一个预设的分辨率到全屏模式,这种情况下,DXGI提供 IDXGISwapChain::ResizeTarget 函数。这个函数应在调用 SetFullscreenState 之前调用。虽然这些方法可以通过相反的顺序调用(先调用SetFullscreenState , 随后调用 ResizeTarget),这样做会导致一个额外的WM_SIZE处理消息在应用程序中触发。(这样做也会引起闪烁,因为DXGI可能会在两个模式之间强制执行)。在调用SetFullscreenState后,再次通过被重置的DXGI_MODE_DESC 的成员变量RefreshRate 调用ResizeTarget 是一个明智的操作。这意味着在DXGI中是一个无操作的命令,但是它可以避免接下来讨论的刷新率问题。
在全屏模式中,窗口桌面管理(DWM)是无效的,DXGI会以更轻量的方式代替窗口模式下的区域传输呈现后台缓冲区中的内容。这种性能的开销得到释放。然而,我们还没有谈到如何在确定条件下确保以轻量的方式代替区域的传输模式。这种情况下前台缓冲和后台缓冲必须尺寸相同。如果应用程序正确的处理了WM_SIZE处理消息,这显然不是问题,同样格式也必须完全相同。
对大部分应用程序来说问题是刷新率,在调用 ResizeTarget 时所指定的刷新率必须是交换链使用的 IDXGIOutput 的刷新率, DXGI_MODE_DESC 被传入ResizeTarget函数后其成员RefreshRate 的值会被DXGI自动重置计算。不要假定确切刷新率值会被支持。通常,开发者选择60HZ作为刷新率,并不了解显示器的刷新率是大约60,000 / 1,001 Hz。如果刷新率不符合期望的60HZ,DXGI会强制进行区域传输代替全屏的轻量传输。
最后一个问题是开发者经常面对如何在全屏模式下改变分辨率。调用 ResizeTarget 和SetFullscreenState 有时会成功,但是全屏分辨率是桌面的分辨率,同样,开发者可能会在全屏模式下创建指定的分辨率,我们发现DXGI会忽略我们传入的指定分辨率。除非特别指出,全屏交换链会使用默认桌面的分辨率。当创建全屏分辨率的交换链,DXGI_SWAP_CHAIN_DESC 的Flags成员结构体必须设置为DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH 覆盖默认参数。这个flag同样也需要被传入到 ResizeTarget 中动态的开启或者禁用。