0sc*_*car 7 macos mouse cocoa iokit
我想在OSX上以高分辨率和高帧率获得鼠标移动.
"高帧率"= 60fps或更高(优选> 120)
"高分辨率"=子像素值
问题
我有一个大约在显示器刷新率下运行的opengl视图,所以它是~60 fps.我用鼠标环顾四周,所以我隐藏了鼠标光标,我依赖鼠标delta值.
问题是鼠标事件的帧速率太低,值被捕捉到整数(整个像素).这会导致"波涛汹涌"的观看体验.以下是鼠标增量值随时间变化的可视化:
mouse delta X
^ xx
2 | x x x x xx
| x x x x xx x x x
0 |x-x-x--xx-x-x-xx--x-x----x-xx-x-----> frame
|
-2 |
v
Run Code Online (Sandbox Code Playgroud)
这是用户将鼠标稍微向右移动而创建的典型(缩短)曲线.每个x代表每个帧的deltaX值,并且由于deltaX值四舍五入为整数,因此该图实际上非常准确.我们可以看到,deltaX值将是0.000一帧,然后是1.000,然后它将再次为0.000,然后是2.000,然后再次为0.000,然后是3.000,0.000,依此类推.
这意味着视图将在一帧中旋转2.000个单位,然后在下一个旋转0.000个单位,然后旋转3.000个单位.当鼠标以或多或少的恒定速度拖动时会发生这种情况.不用说,这看起来像垃圾.
那么,我怎样才能增加鼠标的事件帧率?2)获取子像素值?
到目前为止,
我已经尝试了以下内容:
- (void)mouseMoved:(NSEvent *)theEvent {
CGFloat dx, dy;
dx = [theEvent deltaX];
dy = [theEvent deltaY];
// ...
actOnMouse(dx,dy);
}
Run Code Online (Sandbox Code Playgroud)
嗯,这个很明显.dx这里是浮点数,但值总是四舍五入(0.000,1.000等).这将创建上面的图形.
所以下一步是在进入WindowServer之前尝试点击鼠标事件,我想.所以我创建了一个CGEventTrap:
eventMask = (1 << kCGEventMouseMoved);
eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap,
0, eventMask, myCGEventCallback, NULL);
//...
myCGEventCallback(...){
double dx = CGEventGetDoubleValueField(event, kCGMouseEventDeltaX);
double dy = CGEventGetDoubleValueField(event, kCGMouseEventDeltaY);
}
Run Code Online (Sandbox Code Playgroud)
n.000虽然我认为事件发射的速度稍微高一些,但仍然是值.但它仍然不是60 fps.我仍然得到上面的图表.
我也尝试过将鼠标灵敏度设置得很高,然后将值缩小到我这边.但似乎OSX增加了某种加速度或某种东西 - 价值变得非常"不稳定",因而无法使用,而且火力仍然太低.
没有运气,我一直开始跟踪兔子洞中的鼠标事件,我已经到了IOKit.这对我来说很可怕.这是疯狂的帽子.Apple文档变得奇怪,似乎说"如果你是这样的,你真正需要的只是头文件".
所以我一直在阅读头文件.我发现了一些有趣的花絮.
在<IOKit/hidsystem/IOLLEvent.h>第377行,有这个结构:
struct { /* For mouse-down and mouse-up events */
UInt8 subx; /* sub-pixel position for x */
UInt8 suby; /* sub-pixel position for y */
// ...
} mouse;
Run Code Online (Sandbox Code Playgroud)
看,它说亚像素位置!好.然后在第73行<IOKit/hidsystem/IOLLParameter.h>
#define kIOHIDPointerResolutionKey "HIDPointerResolution"
Run Code Online (Sandbox Code Playgroud)
嗯.
总而言之,我感觉OSX深入了解亚像素鼠标坐标,并且必须有一种方法来读取每帧的原始鼠标移动,但我不知道如何获得这些值.
问题
呃,那么,我要求的是什么?
(抱歉很长的帖子)
(这是一个非常晚的答案,但我认为对于偶然发现这个问题的其他人来说,这个答案仍然有用.)
您是否尝试过滤鼠标输入?这可能很棘手,因为过滤往往是滞后和精确度之间的权衡.然而,几年前我写了一篇文章,解释了我如何过滤鼠标移动并为游戏开发网站写了一篇文章.链接是http://www.flipcode.com/archives/Smooth_Mouse_Filtering.shtml.
由于该网站不再处于积极开发状态(可能会消失),因此相关摘录如下:
在几乎所有情况下,过滤意味着平均.但是,如果我们简单地平均鼠标随时间的移动,我们将引入滞后.那么,我们如何在不引入任何副作用的情况下进行过滤?好吧,我们仍然会使用平均值,但我们会用一些智能来做.同时,我们将为用户提供对过滤的精细控制,以便他们自己进行调整.
我们将使用平均鼠标输入的非线性滤波器,其中较旧的值对滤波结果的影响较小.
这个怎么运作
每个帧,无论您是否移动鼠标,我们将当前鼠标移动放入历史记录缓冲区并删除最旧的历史值.因此,我们的历史记录始终包含X个样本,其中X是"历史缓冲区大小",表示随时间变化的最近采样鼠标移动.
如果我们使用10的历史缓冲区大小和整个缓冲区的标准平均值,那么过滤器会引入很多延迟.在60FPS机器上,快速鼠标移动将落后1/6秒.在快速动作游戏中,这将非常流畅,但几乎无法使用.在同一场景中,历史缓冲区大小为2会给我们带来很小的延迟,但过滤效果非常差(玩家反应粗糙和生涩).
非线性过滤器旨在对抗这种相互排斥的场景.这个想法非常简单.我们不是仅仅平均地平均历史缓冲区中的所有值,而是将它们与权重进行平均.我们从1.0开始.因此历史缓冲区中的第一个值(当前帧的鼠标输入)具有完全权重.然后我们将这个权重乘以"权重修正符"(比如说...... 0.2)并继续前进到历史缓冲区中的下一个值.我们进一步回溯(通过我们的历史缓冲区),值对最终结果的权重(影响)越来越小.
详细说明,当重量调整值为0.5时,当前帧的样本将具有100%的权重,前一个样本将具有50%的权重,下一个最旧的样本将具有25%的权重,下一个将具有12.5%的权重,依此类推.如果你绘制图形,它看起来像一条曲线.因此,权重修改器背后的想法是控制曲线随着历史中的样本变老而变得多么尖锐.
减少滞后意味着减少重量调节剂.将权重修改器减少到0将为用户提供原始的,未经过滤的反馈.将其增加到1.0将导致结果是历史缓冲区中所有值的简单平均值.
我们将为用户提供两个用于精细控制的变量:历史缓冲区大小和权重修改器.我倾向于使用10的历史缓冲区大小,并且只使用权重修饰符直到我开心.
子像素坐标的可能性是存在的,因为 Mac OS X 被设计为与分辨率无关。屏幕上的 2x2 硬件像素正方形可以代表软件中的单个虚拟像素,从而允许将光标放置在 处(x + 0.5, y + 0.5)。
在任何使用正常 1 倍缩放的实际 Mac 上,您永远不会看到子像素坐标,因为鼠标光标无法移动到屏幕上的小数像素位置 - 鼠标移动的量恰好是 1 个像素。
| 归档时间: |
|
| 查看次数: |
1912 次 |
| 最近记录: |