WPF keyDown响应时间准确性

我正在开发一个应用程序,用户可以看到某些内容,并且必须通过单击键盘上的键来做出反应。 反应时间至关重要,越精确越好。

我只用几行代码编写了示例app inf WPF来测试默认设置:

namespace Test { ///  /// Interaction logic for MainWindow.xaml ///  public partial class MainWindow : Window { private Stopwatch sw; public MainWindow() { InitializeComponent(); sw = new Stopwatch(); sw.Start(); this.KeyDown += OnKeyDown; } private void OnKeyDown(object sender, KeyEventArgs keyEventArgs) { sw.Stop(); lbl.Content = sw.ElapsedMilliseconds; sw.Restart(); } } } 

lbl是一个简单的标签。

奇怪的是,当我按下例如空格并按住它时, lbl的值在30-33范围内变化。

所以我无法预测响应准确度是多少? 例如1毫秒精度是不可能的? 用户同时点击空间(例如1毫秒精度)我可以在事件处理程序中处理它吗?

主要问题是:

假设我有一个按键事件处理程序:

 Test_KeyDown(object sender, KeyEventArgs keyEventArgs) { time = stopwatch.elapsed(); stopwatch.Restart(); } 

什么是可能发生的“时间”的最小值? 我可以确定时间值精确到1毫秒吗? 在这个方法中,我启动秒表但是我必须等待 – 多长时间 – 刷新GUI?

你的测试肯定是无效的,它只测量其他几个贡献者所指出的键盘重复率。 但它有用的意外,你可以看到你不会遇到键盘问题。

Windows中的大多数事件的发生速率取决于时钟中断速率。 默认情况下,每秒64次,每15.625毫秒一次。 这唤醒了内核,它开始寻找是否需要完成某些事情,寻找传递给处理器核心的工作。 最典型的是无所事事,核心是通过HLT指令关闭的。 直到下一个中​​断发生。

所以一个问题是你的测试永远不会比15.625毫秒更准确。 而且你的观察结果恰巧与你看到的数字是这个数字的两倍。 但事实并非如此,您可以使用您的程序来查看。 使用控制面板+键盘并调整“重复速率”滑块。 请注意如何调整它并将数字更改为不是15.625的倍数的值。

这不完全是一个意外,键盘控制器也会产生一个中断,就像时钟一样。 您有充分的证据certificate此中断本身已足以让您的程序重新激活。 你可以看出键盘控制器本身足够快,可以扫描键盘矩阵。 键盘上的错误栏不会大于+/- 2毫秒,大约是您在显示的数字中看到的噪音。 如果你的键盘扫描速度较慢,你可以通过这个测试消除它。


你有更大的担忧是video。 video适配器通常以每秒60次更新刷新LCD监视器。 在最坏的情况下,测试对象将无法在17毫秒内物理地看到图像。 液晶显示器本身也不是那么快,便宜的显示器的响应时间为16毫秒或更差。 液体中的水晶的副作用不能足够快地翻转。

消除刷新率错误要求您编程与垂直消隐间隔同步。 你可以用DirectX做些什么。 您可以找到响应时间约为4毫秒的高档液晶显示器,深受游戏玩家欢迎。

首先,如果Stopwatch.IsHighResolutiontrue ,则Stopwatch使用QueryPerformanceCounter ,它可以测量<1 ms分辨率的时间间隔。

其次,当您按住空格键时,Windows会重复开始发送WM_KEYDOWN消息,秒表将测量这些消息之间的间隔。 此时间间隔由注册表项HKCU\Control Panel\Keyboard\KeyboardSpeed

它的默认值是31,这是最快的重复率,这意味着每秒大约30个字符。 这就是为什么你测量大约1000/30 = 33 ms的间隔。

如果将其设置为0,即最慢的重复率,即每秒约2个字符,则应测量约。 间隔500毫秒。 我用这个设置测试了你的代码,我确实得到了500毫秒。 (更改KeyboardSpeed后别忘了重启Windows!)

您需要捕获单个keydown事件,而不是重复事件,因此您不必更改KeyboardSpeed设置。 您的程序应该只向用户显示对象,启动秒表,并在发生keydown事件时停止它。 ElapsedMilliseconds将给出反应时间。 多次测量并使用平均值。

问题是,即使QueryPerformanceCounter准确测量经过的时间,键盘和Windows本身也会引起延迟,这会增加测量的反应时间。 此外,延迟不是恒定的:如果Windows在应该处理keydown事件的那一刻忙,那么延迟将会更大。 因此,如果你认真对待这个任务,你应该校准你的程序。

我的意思是,您应该购买或构建一个基于微控制器的小型电子设备,该设备打开LED并检测打开LED和用户按下按钮之间所经过的时间。 使用此设备进行10-20(越多越好)反应时间测量,并与同一测试人员一起,使用您的程序再进行10-20次测量。 两者之间的差异会给你键盘和Windows造成的延迟。 可以从程序测量的反应时间中减去这种差异。

(人们可能会问,为什么你不应该使用小而精确的电子设备而不是Windows应用程序。首先,制造和销售软件比制造和销售硬件更容易和更便宜。其次,视觉对象可能很复杂(例如,棋盘格),可以在PC上更有效地渲染复杂的对象。)

我想在此指出另一种追踪时机的工具。 由于您正在考虑测试应用程序的响应,并且正如有人提到这涉及操作系统的消息,您可以利用Spy ++来查看这些消息的时间。 我将按空格的输出复制到我一直在监听键盘消息的窗口,打开了所有输出。 我按下了一次空间,然后尽可能快地在通过扩展坞的USB键盘上释放。 你可以看到它需要〜0.05ms来处理。

 <00001> 00090902 P WM_KEYDOWN nVirtKey:VK_SPACE cRepeat:1 ScanCode:39 fExtended:0 fAltDown:0 fRepeat:0 fUp:0 [wParam:00000020 lParam:00390001 time:1:07:38.116 point:(183, 290)] <00002> 00090902 P WM_CHAR chCharCode:'32' (32) cRepeat:1 ScanCode:39 fExtended:0 fAltDown:0 fRepeat:0 fUp:0 [wParam:00000020 lParam:00390001 time:1:07:38.116 point:(183, 290)] <00003> 00090902 P WM_KEYUP nVirtKey:VK_SPACE cRepeat:1 ScanCode:39 fExtended:0 fAltDown:0 fRepeat:1 fUp:1 [wParam:00000020 lParam:C0390001 time:1:07:38.163 point:(183, 290)] 

Spy ++是随Visual Studio提供的工具。 您可以在C:\Program Files\Microsoft Visual Studio XYZ\Common7\Tools\spyxx.exe中找到它,其中XYZ是8,9.0和10.0,我可以确认。

你可以做些什么来进一步测试时间,让Spy ++监听键盘命令和WM_PAINT等,看看程序响应键盘消息的UI变化有多快。

例如,下面是已经有3+3计算器后的干净日志,然后按Enter 。 您可以看到计算器能够在KeyDown和KeyUp之间的.062ms之前进行计算和显示。

 <00001> 00090902 P WM_KEYDOWN nVirtKey:VK_RETURN cRepeat:1 ScanCode:1C fExtended:1 fAltDown:0 fRepeat:0 fUp:0 [wParam:0000000D lParam:011C0001 time:1:19:12.539 point:(179, 283)] <00002> 00090902 S WM_PAINT hdc:00000000 [wParam:00000000 lParam:00000000] <00003> 00090902 R WM_PAINT lResult:00000000 <00004> 00090902 S WM_PAINT hdc:00000000 [wParam:00000000 lParam:00000000] <00005> 00090902 R WM_PAINT lResult:00000000 <00006> 00090902 S WM_PAINT hdc:00000000 [wParam:00000000 lParam:00000000] <00007> 00090902 R WM_PAINT lResult:00000000 <00008> 00090902 S WM_PAINT hdc:00000000 [wParam:00000000 lParam:00000000] <00009> 00090902 R WM_PAINT lResult:00000000 <00010> 00090902 P WM_KEYUP nVirtKey:VK_RETURN cRepeat:1 ScanCode:1C fExtended:1 fAltDown:0 fRepeat:1 fUp:1 [wParam:0000000D lParam:C11C0001 time:1:19:12.601 point:(179, 283)] 

编辑 – 在Spy ++中我建议使用Logging Options,它会显示Message Options对话框。 转到消息选项卡,单击全部清除,选中’键盘’,然后滚动列表框并选择WM_PAINT。 这样,您只有所需的消息,否则您将被它们淹没。