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

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分支逻辑失效的核心错误:

  1. 错误覆盖了拖拽中的尺寸值
    WM_SIZING触发时,lParam指向的RECT存储的是用户当前拖拽出的窗口外框预期尺寸,此时窗口本身还没完成尺寸变更。你先把这个值拷贝到new_rect,紧接着就调用GetClientRect(app.wnd, &new_rect)把这个值覆盖了——而这个时间点窗口客户区尺寸还没更新,GetClientRect拿到的永远是拖拽开始前的旧尺寸,你传入resize_handler的根本不是实时拖拽的新尺寸。
  2. 拦截了窗口尺寸更新的默认流程
    你在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 10:01:52