Ixeris

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 中是同义词。