IDXGISwapChain::ResizeBuffers在WM_SIZING消息下未按预期工作
问题描述
我编写了如下WNDPROC窗口过程函数:
LRESULT CALLBACK window_processing(HWND window, UINT message, WPARAM wPara, LPARAM lParam) { #if WITH_DEBUG_UI if(ImGui_ImplWin32_WndProcHandler(window, message, wPara, lParam)) { return true; } #endif switch(message) { case WM_QUIT: PostQuitMessage(0); close_window(); exit(); return true; break; case WM_SIZE: { auto new_size = get_window_size(); if(app.resize_handler) { app.resize_handler(new_size.width, new_size.height); return true; } break; } case WM_SIZING: { RECT new_rect = *((LPRECT)(lParam)); GetClientRect(app.wnd, &new_rect); auto new_size = window_size{std::size_t(new_rect.right - new_rect.left), std::size_t(new_rect.bottom - new_rect.top)}; if(app.resize_handler) { app.resize_handler(new_size.width, new_size.height); return true; } } break; } return DefWindowProc(window, message, wPara, lParam); }
其中app.resize_handler绑定的实现函数如下:
void d3d11_loading_screen_renderer::window_size_changed(const std::size_t w, const std::size_t h) { std::cout << "W : " << w << " H : " << h << std::endl; back_buffer_rtv.Reset(); back_buffer.Reset(); swapchain->ResizeBuffers(0, w, h, DXGI_FORMAT_UNKNOWN, 0); swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), &back_buffer); back_buffer_rtv = create_back_buffer_rtv(device.Get(), back_buffer.Get()); back_buffer_viewport = viewport_from_window_size(window_size{w,h}); }
当前WM_SIZE事件可被正确处理:窗口大小调整操作完成后,后台缓冲区可正确完成尺寸调整与画面刷新。
但在处理WM_SIZING事件时调整逻辑未生效,后台缓冲区始终保持原有尺寸,效果参考如下动图:
从动图可见,仅当完成窗口大小调整松开鼠标时缓冲区才会正确缩放,拖拽调整过程中不会同步调整尺寸。
目前接口调用未返回任何错误,且回调中接收到的窗口尺寸数值正确,请问该问题是由什么原因导致的?
问题根因
代码里有两个直接导致WM_SIZING分支逻辑失效的核心错误:
- 错误覆盖了拖拽中的尺寸值
WM_SIZING触发时,lParam指向的RECT存储的是用户当前拖拽出的窗口外框预期尺寸,此时窗口本身还没完成尺寸变更。你先把这个值拷贝到new_rect,紧接着就调用GetClientRect(app.wnd, &new_rect)把这个值覆盖了——而这个时间点窗口客户区尺寸还没更新,GetClientRect拿到的永远是拖拽开始前的旧尺寸,你传入resize_handler的根本不是实时拖拽的新尺寸。 - 拦截了窗口尺寸更新的默认流程
你在WM_SIZING分支调用完resize_handler就直接return true,等于通知系统"这个消息我已经完全处理了,不需要执行默认操作"。而WM_SIZING的默认处理逻辑就是根据用户拖拽的矩形更新窗口实际尺寸,你直接return阻断了这个流程,窗口大小在拖拽过程中根本没有实际变化,就算你调用了ResizeBuffers,后续交换链呈现时还是会匹配当前未变更的窗口尺寸,自然看不到实时缩放效果。
额外提一句:你写在窗口过程里的WM_QUIT分支永远不会触发,WM_QUIT是线程级消息,不会进入窗口过程,退出逻辑要放到消息循环里处理。
修复建议
- 删掉
WM_SIZING分支里的return true,自定义逻辑执行完后把消息交还给DefWindowProc走默认的窗口尺寸更新流程,不要拦截系统默认处理。 - 不要在
WM_SIZING里调用GetClientRect取实时尺寸,如果要获取拖拽中的客户区大小,应该用lParam传入的外框矩形,调用ScreenToClient转换为客户区坐标后再计算宽高,不要覆盖lParam指向的原始RECT值。 - 不建议在
WM_SIZING触发时直接调用ResizeBuffers:WM_SIZING是拖拽过程中高频触发的消息,ResizeBuffers属于开销较高的资源重建操作,频繁调用会导致拖拽卡顿。常规做法是拖拽过程中只做必要的重绘标记,等WM_SIZE消息触发(即用户松开鼠标、窗口尺寸正式生效)时再执行缓冲区重设;如果一定要做拖拽实时缩放,可以搭配WM_ENTERSIZEMOVE/WM_EXITSIZEMOVE消息,在拖拽期间启动定时器按固定帧率取当前窗口尺寸更新,不要每次收到WM_SIZING就重建资源。
内容的提问来源于stack exchange,提问作者user3001150
相关产品推荐
相关产品推荐

