Windows 窗口显示机制:从 GDI 到 DXGI SwapChain
Windows 的窗口显示可以理解为:程序负责绘制画面,Windows 显示系统负责将画面呈现在屏幕上。理解这条路径,关键是分清窗口、绘图资源和呈现请求各自的职责。
一、几个核心概念
HWND(窗口句柄):Windows 用于标识和管理窗口的句柄。窗口的位置、大小、层叠关系等信息由窗口系统管理。
HDC(设备上下文):GDI 绘图操作使用的上下文,可以通过
GetDC(hwnd)获取,用于绘制文字、矩形、位图等。Window Surface(窗口表面):承载窗口画面的图像资源,可以类比为 HTML Canvas 背后的画布;具体实现取决于呈现路径。
DXGI SwapChain(交换链):管理一组用于画面呈现的缓冲区,常见的窗口化用法会将它与 HWND 关联。
Back Buffer(后台缓冲区):SwapChain 管理的图像缓冲区,GPU 将渲染结果写入其中。
DWM(桌面窗口管理器):负责桌面合成,将不同窗口的内容组合成桌面画面。
二、GDI 与 D3D11 的显示流程
下面用常见的窗口化路径说明各部分如何协作。实际是否经过完整的 DWM 合成,还会受到呈现模式和系统优化的影响。
传统 GDI:由 Windows 管理窗口绘图表面
GDI 无需程序自行创建 SwapChain,也不需要主动调用 Present()。窗口表面由 Windows 图形系统管理,实际存储和加速方式取决于系统与驱动。
现代 D3D11:显式管理绘制与呈现
这里需要区分三个动作:
CPU 提交命令:CPU 主要负责调用 API、组织资源并提交 GPU 命令。
GPU 写入缓冲区:Back Buffer 通常是 GPU 图形资源,并非 CPU 可随意读写的普通内存。
调用
Present():提交呈现请求,不代表画面已经立即显示到屏幕上。
三、Flip Model 为什么更高效?
DXGI 有两种主要的窗口呈现模型:
在 Flip Model 下,SwapChain 的后台缓冲区可以直接提供给 DWM 用于合成,从而减少 GPU 内部的额外复制。
同一个 HWND,不代表同一块画布
同一个 HWND 可以通过 GetDC() 使用 GDI,也能关联 DXGI SwapChain,但这不代表两种绘图方式共享同一块画布。
当 Flip Model 开始呈现后,普通 GDI 对同一 HWND 的更新不会自动叠加到 SwapChain 画面上。即使 GDI 最后执行,也不能靠绘制顺序覆盖 GPU 画面。
这并不是 DWM 在两个缓冲区之间反复切换,而是该窗口的呈现路径由 Flip Model 决定。
四、与 FFmpeg 视频渲染的关系
使用 FFmpeg 的 D3D11VA 硬件解码时,一条常见的视频显示路径如下:
只要解码、纹理处理和渲染链路支持 GPU 资源互操作,解码后的视频帧就可以始终保留在 GPU 可访问的内存中,无须下载到 CPU 内存再上传。
UI 框架已有 SwapChain 时怎么办?
如果 UI 框架(例如 GPUI)已经管理了窗口的 SwapChain,就不能简单地再创建第二个 SwapChain,期待两个渲染器自动合成画面。
通常有两种接入方式:
将视频纹理接入原有渲染器:由同一条渲染路径完成视频与 UI 的组合。
使用独立的子 HWND 显示视频:需要额外处理窗口层叠、位置同步和裁剪问题。
总结
理解 Windows 显示机制,最关键的是区分三个对象:
HWND 决定画面属于哪个窗口;Back Buffer 保存实际绘制结果;
Present()将完成的画面提交给 Windows 显示系统。
GDI 与 D3D11 的主要区别,在于应用如何绘图、管理缓冲区,以及提交画面。