You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Windows 11下Raw Input API鼠标性能远低于Windows 10的修复咨询

Windows 11下Raw Input API鼠标性能远低于Windows 10的修复咨询

我之前也踩过Win11上Raw Input鼠标刷新率骤降的坑,你的代码逻辑本身没大问题,但Win11对Raw Input的事件调度和消息合并做了针对性优化,导致单条WM_INPUT的处理方式被系统限制了。下面结合你的代码给出亲测有效的修复方案:

问题根源

Win11为了降低消息队列的资源消耗,会把短时间内的多个Raw Input鼠标事件合并成少量WM_INPUT消息投递——1000Hz的鼠标每秒产生1000次采样,合并后就只剩约128条消息,直接导致你感知到的刷新率暴跌。另外你注册设备时用的RIDEV_CAPTUREMOUSE标志,在Win11下会进一步加剧这个事件合并的行为。

具体修复步骤

1. 调整Raw Input设备注册的标志

先修改你的_RegisterRawMouse函数,移除会触发事件合并的RIDEV_CAPTUREMOUSE标志,换用更适合全局捕获的组合:

static int _RegisterRawMouse(HWND hwnd) {
    RAWINPUTDEVICE rid[1] = {};
    rid[0].usUsagePage = 0x01;
    rid[0].usUsage = 0x02;
    // 移除RIDEV_CAPTUREMOUSE,保留RIDEV_NOLEGACY避免 legacy 消息干扰,RIDEV_INPUTSINK保证全局捕获
    rid[0].dwFlags = RIDEV_NOLEGACY | RIDEV_INPUTSINK;
    rid[0].hwndTarget = hwnd;
    if (!RegisterRawInputDevices(rid, 1, sizeof(RAWINPUTDEVICE))) {
        return 1;
    }
    return 0;
}

2. 改用缓冲读取替代单消息处理

Win11下依赖WM_INPUT逐个处理事件的方式已经不可靠,我们需要用GetRawInputBuffer批量读取设备缓冲区里的所有积压事件,绕过消息队列的投递限制。修改你的主消息循环:

首先定义一个足够容纳高频率事件的缓冲区:

#define RAW_MOUSE_BUFFER_SIZE 64  // 1000Hz下每秒1000次采样,64的缓冲区足够处理短时间积压

然后调整主循环逻辑,在处理常规消息后主动读取缓冲的鼠标事件:

MSG msg = {};
while (true) {
    // 先处理常规消息队列
    while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
        if (msg.message == WM_QUIT) {
            return (int)msg.wParam;
        }
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // 主动读取Raw Input缓冲中的所有鼠标事件
    RAWINPUT rawBuffer[RAW_MOUSE_BUFFER_SIZE];
    UINT bufferSize = sizeof(rawBuffer);
    UINT eventCount = GetRawInputBuffer(rawBuffer, &bufferSize, sizeof(RAWINPUTHEADER));

    if (eventCount > 0 && eventCount != (UINT)-1) {
        _MouseData md = mousedata.load();
        for (UINT i = 0; i < eventCount; i++) {
            RAWINPUT* raw = &rawBuffer[i];
            if (raw->header.dwType == RIM_TYPEMOUSE) {
                md.dx += raw->data.mouse.lLastX;
                md.dy += raw->data.mouse.lLastY;
                md.dw += (*(short*)&raw->data.mouse.usButtonData) / WHEEL_DELTA;
                md.tick += 1;
            }
        }
        mousedata.store(md);
        // 调试用:printf("Total Tick: %i, Cumulative X: %i\n", md.tick, md.dx);
    }

    // 这里放置你的主循环业务逻辑(如渲染、状态更新等)
}

3. 可选:优化原代码的内存分配(非核心但建议做)

你原代码在WM_INPUT里每次动态分配内存的方式,高频率下会产生内存碎片,改成预先分配的缓冲区或者栈内存会更高效,不过这不是影响刷新率的核心问题,可以后续优化。

方案原理

  • 移除RIDEV_CAPTUREMOUSE后,Win11不会再主动合并Raw Input鼠标事件,所有采样会被缓冲到设备专属队列中
  • GetRawInputBuffer直接从硬件缓冲区读取所有未处理事件,绕过了消息队列的投递限制,能完整获取1000Hz下的所有采样
  • 主动读取的方式不受Win11后台程序消息优先级的影响,即使程序在后台也能保持满刷新率

修改后你可以通过md.tick的增长速度验证:每秒应该能达到约1000次,和Win10下的表现完全一致。如果还有异常,建议检查Win11鼠标设置(关闭“提高指针精确度”),或者暂时禁用鼠标厂商的自定义驱动,用系统默认驱动测试是否是驱动层面的限制。

火山引擎 最新活动