
Ixeris
缓冲原始输入和线程化事件轮询
Ixeris
概述
Ixeris 是一个通过优化事件轮询来提升客户端性能的模组。
你可能会注意到当移动鼠标时帧率明显下降。部分帧率下降是因为游戏在转动视角时需要执行额外任务,例如计算区块可见性。然而,由于原生代码中事件轮询的低效性以及 JNI 上行调用的开销,部分原本可用于渲染的 CPU 时间被不必要地浪费在了事件轮询上。这在 Windows 系统上尤为明显,尤其是在鼠标轮询率较高时。
Ixeris 主要通过以下两项措施来解决此问题:
- 线程化事件轮询:Ixeris 不在同一线程上执行渲染和事件轮询,而是在主线程上进行事件轮询,并将渲染任务分流到独立的渲染线程。
- 缓冲原始输入(仅限 Windows):将用于输入轮询的方法从低效的
GetRawInputData(每个输入事件调用一次)切换为GetRawInputBuffer(允许批量读取原始输入消息,所有事件只需调用一次)。此外,在 Java 代码中执行此操作使我们能够消除 JNI 上行调用的开销。
基准测试
这些测试在世界完全加载且帧率稳定后进行。
测试 1:游戏中
此测试比较了鼠标被捕获(即光标不可见)时的性能。测试在一个无实体的超平坦世界中进行。
此测试在 Windows 上完成,Ixeris 默认使用缓冲原始输入以提高性能。"Ixeris, 非缓冲"列显示禁用该选项后的帧率。
| 轮询率 | 无 Ixeris | Ixeris, 非缓冲 | Ixeris, 缓冲 |
|---|---|---|---|
| 8000 Hz | 12 FPS | 83 FPS (6.9x) | 121 FPS (10.1x) |
| 2000 Hz | 76 FPS | 114 FPS (1.50x) | 135 FPS (1.78x) |
| 500 Hz | 134 FPS | 145 FPS (1.08x) | 151 FPS (1.13x) |
测试 2:菜单中
此测试比较了鼠标未被捕获(即光标可见)时的性能,例如 F3+Esc 暂停画面。在这种情况下,游戏从不使用原始输入,因为它需要知道实际光标位置,而非原始相对位移。轮询率为 1000Hz。
"空闲帧率"列显示未移动鼠标时的帧率,接下来两列分别显示不安装 Ixeris 和安装 Ixeris 时快速移动鼠标划过游戏窗口的帧率。请注意,此测试在 Ixeris 初始版本发布时进行,此后已做出许多改进。
| 空闲帧率 | 无 Ixeris | 有 Ixeris | |
|---|---|---|---|
| Windows | 233 FPS | 133 FPS | 165 FPS (1.24x) |
| Linux (X11) | 358 FPS | 320 FPS | 355 FPS (1.11x) |
| Linux (Wayland) | 364 FPS | 289 FPS | 298 FPS (1.03x) |
技术细节
线程安全性
在当前状态下,Ixeris 不会破坏线程安全性。通过 glfwSet*Callback 注册的回调在渲染线程上执行。要求在主线程上调用的 GLFW 函数,如果在其他线程上调用,则会被分发到主线程。这些调用若可安全延迟则可立即返回,否则可能阻塞调用者直到调用完成。
自版本 3.1.0 起,严格遵循 GLFW 文档中的线程安全要求。
GLFW 状态缓存
大多数 GLFW 函数要求从主线程调用。然而,许多模组可能会从渲染线程调用它们。为避免线程通信带来的性能下降,Ixeris 缓存了常用 GLFW 状态,以便任何线程都能快速访问,无需将调用路由到主线程。这些缓存是安全的,不会引入额外的延迟。
增强帧率限制器
原版帧率限制器(26.1 版本之前)存在缺陷,使用 glfwWaitEventsTimeout 进行休眠。然而,此函数无法从渲染线程调用,因此无法与 Ixeris 最佳配合。
Ixeris 以混合方式重写了帧率限制器:精确休眠,并在等待时间极短时开始自旋等待。
术语表
- 主线程:游戏启动的线程。大部分 GLFW 函数要求在此线程上调用,并且它负责事件轮询。
- 渲染线程:执行游戏通常所做的所有操作,但不包括事件轮询。
这两个术语在原版 Minecraft 中是同义词。
正在加载版本记录…
正在加载评论…
评论在新手盒子客户端中发表,这里同步展示。