2013-10-09 37 views
9

我正在开发一个应用程序,用户可以在其中看到某些东西,并且必须通过单击键盘上的某个键来作出反应。反应时间至关重要,越精确越好。WPF keyDown响应时间精度

我写的示例应用程序INF WPF只有几行代码来测试默认设置:

namespace Test 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    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才能刷新?

+0

不知道你要在那里测量什么,也不知道你是如何做到的。 'KeyDown'在按键关闭时不会持续产生(我猜这会是'KeyPress'),所以按住空格键不应该多次更新标签,是吗?如果您的意思是在显示窗口之前按住空格键,则您将在构造函数之间对代码进行基准测试,直至窗口最终处于活动状态,然后泵事件。 – jods

+0

据我所知调度事件一样快,因为它可以和作品几乎相同的方式任何其他Windows应用程序做:关键是降低由操作系统,然后分派消息窗口消息队列处理,然后将其抽出,并在集中控制上提出相应的事件。与人类反应时间相比,准确性已经足够好了,无论你在UI线程上执行什么工作。 – jods

+0

按键的速度取决于按键重复速率的窗口配置。 http://windows.microsoft.com/is-is/windows-xp/help/adjust-the-character-repeat-rate – Tony

回答

5

首先,如果是Stopwatch.IsHighResolutiontrue,然后Stopwatch使用QueryPerformanceCounter,并且它可以测量与< 1毫秒的分辨率的时间间隔。

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

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

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

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

问题是,即使QueryPerformanceCounter准确测量经过时间,键盘和Windows本身也会产生延迟,这会延长测量的响应时间。而且,延迟并不是恒定的:如果Windows在处理keydown事件时处于忙碌状态,则延迟会更大。所以如果你认真对待任务,你应该校准你的程序。

我的意思是,你应该购买或建造一个小型的基于微控制器的电子设备,它打开一个LED并检测开启LED和用户按下按钮之间所经过的时间。使用此设备和同一测试人员进行10-20次(反应时间越多)反应时间测量,再对您的程序进行10-20次测量。两者之间的差异会给你由键盘和Windows造成的延迟。这个差异可以从您的程序测量的反应时间中减去。第一,制造和销售软件比制造和销售硬件要容易和便宜,第二,可视对象可以是复杂的(例如棋盘格),并且复杂的对象可以在PC上更高效地渲染)。

+0

我可能会建议更改用户注册表可能无法做到这一点的最好办法,而只是读取值和检测控股的指定键。 – SeToY

+0

@SeToY我改变了注册表,只是为了测试我的假设,即关于连续按下空间时OP测量的33毫秒间隔。时间间隔测量的精度与此无关,所以OP不应修改用户的注册表。 – kol

11

您的测试无疑是无效的,它只是衡量其他几位贡献者指出的键盘重复率。但它对于其意想不到的好处是有用的,你实际上可以看到你对键盘没有问题。

Windows中的大多数事件都以由时钟中断速率确定的速率发生。默认情况下每秒钟64次,每15.625毫秒一次。这唤醒了内核,并期待看看是否需要完成某些工作,寻找工作传递给处理器内核。最典型的是没有什么可做的,核心是用HLT指令关闭的。直到下一个中​​断发生。

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

这不完全是一个意外,键盘控制器也产生一个中断,就像时钟一样。你有确凿的证据证明这个中断本身已经足够让你的程序重新激活。你可以知道键盘控制器本身足够快地扫描键盘矩阵。键盘上的误差条不会大于+/- 2毫秒,大约是您在显示的数字中看到的噪音。如果您的键盘扫描速度较慢,您可以使用此测试将其消除。


您所关心的更大的问题是视频。视频适配器通常每秒更新60次LCD显示器。所以最坏的情况下,测试对象将无法物理上看到图像17毫秒。而液晶显示器本身也不是那么快,便宜的有16毫秒或更差的响应时间。液体中的水晶的副作用不能够快速翻转。

消除刷新率错误需要您编程与vertical blanking interval同步。你可以用DirectX做些什么。你可以找到响应时间约为4毫秒的高级液晶显示器,这是受游戏玩家欢迎的。

1

我想指出另一个工具来追踪你的时间。由于您正在考虑应用程序响应的测试,并且正如有人提到这涉及操作系统的消息,您可以使用Spy ++来查看这些消息的时间。我将压缩空间的输出复制到一个窗口,我只能听到键盘消息,已打开所有输出。我按下了一次空间,并尽可能快地释放到通过坞站的USB键盘上。你可以看到它花费了大约0.05毫秒来处理。

<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,我可以确认。

你可以做什么进一步的测试时序将有间谍++监听键盘命令和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 ++我建议去记录选项,它显示了消息选项对话框。转到消息选项卡,单击全部清除,选中'键盘',然后滚动列表框并选择WM_PAINT。这样你只有想要的信息,否则你会被他们淹没。