You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows 10窗口模式下等待VBLANK提交画面的正确方式

嘿,针对Windows 10窗口模式下等待VBLANK提交画面的问题,我来给你拆解下正确的姿势,顺便解决你遇到的卡顿问题——你的推测其实已经摸到了核心原因,咱们一步步来理清楚。

卡顿原因的验证

你说的没错:DWM(桌面窗口管理器)会在VBLANK事件到来前完成所有窗口画面的合成工作,然后在VBLANK的时间间隔内,快速把合成好的帧写入前缓冲区。如果你的Present调用时机刚好落在DWM已经完成写入之后,那当前帧就只能等到下一个VBLANK周期才能显示,这就会凭空多出来一帧的延迟,表现为偶尔的卡顿掉帧。

正确的同步方式:利用DXGI与DWM协同

在窗口模式下,直接去监听硬件VBLANK是不靠谱的——因为DWM的合成节奏可能和硬件垂直同步不完全绑定,而且它会接管最终的画面输出。正确的做法是使用DXGI提供的帧延迟可等待对象,让你的渲染逻辑和DWM的合成节奏对齐:

步骤1:创建交换链时启用帧延迟可等待对象

创建DXGI交换链的时候,一定要加上DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT标志,这样交换链会生成一个可等待的同步句柄,用来和DWM的帧节奏同步:

DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {};
// 填充交换链的基础参数:比如缓冲区格式、大小、缓冲区数量等
swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.Width = windowWidth;
swapChainDesc.Height = windowHeight;
swapChainDesc.BufferCount = 2;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
swapChainDesc.SampleDesc.Count = 1;
// 关键:启用帧延迟可等待对象
swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;

// 创建交换链并获取IDXGISwapChain3接口(需要DXGI 1.3及以上)
ComPtr<IDXGISwapChain3> swapChain;
HRESULT hr = dxgiFactory->CreateSwapChainForHwnd(
    device.Get(),
    hwnd,
    &swapChainDesc,
    nullptr,
    nullptr,
    reinterpret_cast<IDXGISwapChain**>(swapChain.GetAddressOf())
);

步骤2:设置最大帧延迟为1

为了避免交换链积累过多待提交的帧(这也是卡顿的常见诱因),通过IDXGISwapChain3::SetMaximumFrameLatency把最大帧延迟设为1,确保每次只等待当前帧的同步:

swapChain->SetMaximumFrameLatency(1);

步骤3:渲染循环中等待同步对象再提交

在每次执行渲染命令、调用Present之前,先等待交换链的可等待对象,这样就能保证你的Present调用刚好卡在DWM准备好接收新帧的时机,不会错过VBLANK窗口:

// 渲染循环
while (isRunning) {
    // 1. 等待帧同步对象,对齐DWM的节奏
    HANDLE frameSyncHandle = swapChain->GetFrameLatencyWaitableObject();
    WaitForSingleObject(frameSyncHandle, INFINITE);

    // 2. 执行渲染逻辑:清屏、绘制场景、执行命令列表等
    // ...

    // 3. 提交帧到交换链
    swapChain->Present(1, 0); // 第一个参数1表示启用垂直同步(配合DXGI的同步更稳妥)
}
额外注意事项
  • 不要尝试自己用QueryPerformanceCounter或者其他硬件计时器去监听VBLANK,窗口模式下DWM的合成逻辑会让这种方法失效,反而容易导致更严重的同步问题。
  • 如果你的项目使用的是较旧的DXGI版本(低于1.3),可以尝试用DXGI_PRESENT_TEST标志来测试Present是否能立即提交,但这种方法的可靠性远不如上面的可等待对象方案,建议尽量升级到DXGI 1.3及以上。
  • 确保你的渲染线程和UI线程没有互相阻塞,比如不要在渲染线程里做UI操作,也不要在UI线程里等待渲染完成,避免额外的卡顿。

内容的提问来源于stack exchange,提问作者cairnc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:20:03