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

如何为Win32 RawInput获取高于毫秒精度的时间戳?

问题描述

我在VR游戏中使用RawInput作为输入API,需要获取输入按下时的时间戳,但目前用GetMessageTime()只能拿到毫秒精度,无法满足需求:我的游戏每帧运行9ms,速通玩家场景下同一帧可能收到多个输入消息,毫秒精度无法区分它们的时间顺序,还会影响输入相关的delta时间计算——VR场景里精准时间戳对预测未来帧时序至关重要。

请问能不能通过RawInput获取每条输入消息的微秒级(最好纳秒级)时间戳?还是必须改用其他Win32 API或系统来处理输入?

初始化代码

// 注册RawInput设备
RAWINPUTDEVICE Rid[2];
// 鼠标
Rid[0].usUsagePage = (USHORT)0x01; 
Rid[0].usUsage = (USHORT)0x02; 
Rid[0].dwFlags = RIDEV_INPUTSINK | RIDEV_DEVNOTIFY;
Rid[0].hwndTarget = WindowHandle;

// 键盘
Rid[1].usUsagePage = (USHORT)0x01; 
Rid[1].usUsage = (USHORT)0x06; 
Rid[1].dwFlags = RIDEV_INPUTSINK | RIDEV_DEVNOTIFY | RIDEV_NOLEGACY;   
Rid[1].hwndTarget = WindowHandle;

RegisterRawInputDevices(Rid, 2, sizeof(Rid[0]));

消息循环代码

while(bRunning)
{
    MSG Message;
    while(PeekMessage(&Message, 0, 0, 0, PM_REMOVE))
    {
        switch(Message.message)
        {
            case WM_INPUT:
            {
                LONG msTime = GetMessageTime();
                // 处理时间戳和输入
                break;
            }
            default:
            {
                TranslateMessage(&Message);
                DispatchMessage(&Message);
                break;
            }
        }
    }
}
解决方案

1. RawInput本身无法提供微秒级时间戳

RawInput的RAWINPUTHEADER.dwTime字段和GetMessageTime()一样,都是系统启动以来的毫秒数,精度只有毫秒级,无法满足你的需求。

2. 可行的高精度时间方案

方案一:结合QueryPerformanceCounter获取处理时刻的高精度时间

在收到WM_INPUT消息的瞬间,调用QueryPerformanceCounter获取高精度计数,再通过QueryPerformanceFrequency转换为微秒/纳秒级时间。这是最简单的修改方式,虽然拿到的是消息被处理的时间(而非事件发生的绝对精确时间),但已经是能获取到的最接近的高精度时间,足以区分同一帧内的输入顺序。

修改后的WM_INPUT处理代码示例:

case WM_INPUT:
{
    // 初始化高精度计时参数(建议在程序启动时调用一次)
    static LARGE_INTEGER freq;
    static bool init = false;
    if (!init)
    {
        QueryPerformanceFrequency(&freq);
        init = true;
    }

    // 获取当前高精度计数
    LARGE_INTEGER counter;
    QueryPerformanceCounter(&counter);

    // 转换为微秒级时间(系统启动以来的时间)
    double usTime = (double)counter.QuadPart * 1000000.0 / freq.QuadPart;
    // 转换为纳秒级则乘以1e9
    double nsTime = (double)counter.QuadPart * 1000000000.0 / freq.QuadPart;

    // 后续处理输入和时间戳
    break;
}

方案二:使用低级全局钩子(LowLevel Hooks)

如果需要更接近事件发生时刻的时间戳,可以使用SetWindowsHookEx注册WH_KEYBOARD_LL(键盘)和WH_MOUSE_LL(鼠标)钩子。这类钩子会在输入事件被放入消息队列之前触发,此时调用QueryPerformanceCounter能拿到更精准的事件发生时间。

不过需要注意:低级钩子属于全局钩子,会有一定的性能开销;且钩子回调函数必须在同一个线程内处理,不能阻塞太久,否则会影响系统响应。

方案三:利用VR Runtime的输入时间戳

如果你的游戏基于特定VR平台(比如OpenXR、SteamVR),这些Runtime本身会提供输入事件的高精度时间戳,直接使用这些平台的输入API会比自己处理RawInput更可靠,也能更好地和VR时序对齐。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 21:18:16