如何为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

