在 JavaFX 中获取显示器刷新率



使用AWT/Swing获取显示器的刷新率:

java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getRefreshRate()

问:使用JavaFX获取刷新率是否有类似的方法/过程?如果有,该怎么做?

JavaFX 中没有等效的公共 API

这是您当前使用的awt API的DisplayMode.getRefreshRate()javadoc:

返回显示器的刷新率(以赫兹为单位)。

没有等效的公共JavaFX API。

如果公共 API 存在于 JavaFX 中,则可以通过 Screen 类访问它,但那里没有定义此类功能的访问器。

有一个未记录的、不受支持的私有 API,它似乎提供了类似的功能:

com.sun.javafx.tk.Toolkit.getToolkit().getRefreshRate()

JavaFX 脉冲处理

有关 JavaFX工作原理的相关信息可以在 JavaFX 体系结构描述的脉冲文档中找到。

脉冲是一个事件,它向 JavaFX 场景图指示是时候将场景图上的元素的状态与 Prism 同步了。脉冲以每秒 60 帧 (fps) 的最大速度进行限制,并在场景图上运行动画时触发。即使动画未运行,当场景图中的某些内容发生更改时,也会计划脉冲。例如,如果按钮的位置发生变化,则会安排脉冲。

触发脉冲时,场景图上元素的状态将向下同步到渲染层。

屏幕硬件刷新率与JavaFX内部脉冲处理速率不同。 虽然我想从技术上讲,这些可以在内部实现中同步(在某些情况下可能会发生),但公共文档中没有任何内容提到这是否实际发生。

有关自适应刷新技术的说明

另请注意,随着 gsync 和自由同步等技术的出现,刷新率可以适应内容。

垂直同步是大多数系统中的一个选项,在这些系统中,在显示器完成其当前刷新周期之前,视频卡被阻止对显示内存执行任何可见的操作。FreeSync 和 G-Sync 等技术颠覆了这一概念,并根据来自计算机的内容调整显示器的刷新率。此类技术需要视频适配器和显示器的特定支持。

这些技术使得依赖显示器中的硬件刷新率的概念充其量有点模糊。

常见问题解答

您声明查询显示刷新率的意图是:

就像画任何东西一样快,只要它仍然可以通过显示器实现......

默认情况下,JavaFX 有意将帧速率限制为 60fps,而与显示器规格无关。

可以使用未记录的系统属性覆盖此上限,如下所述:

  • 如何忽略javafx中的60fps限制?

您可以使用未记录的功能来删除 JavaFX 中的帧速率上限,然后依靠基础视频卡驱动程序和硬件以屏幕的基础硬件和软件功能中可用的最大速率进行渲染。

这是在openjfx源代码中处理这个问题的代码。

/*
* Called to set the value of PULSE_DURATION or PULSE_DURATION_NS based on
* the refresh rate of the primary screen (unless overridden by the
* FRAMERATE_PROP Setting). If the refresh rate can not be determined the
* default of 60hz is used.
*
* @param precision - precision in (1000 for ms or 1000000000 for ns)
*
* @return pulse duration value, either in ms or ns depending on the
* parameter.
*/
protected int getPulseDuration(int precision) {
int retVal = precision / 60;
// Allow Setting to override monitor refresh
if (Settings.get(FRAMERATE_PROP) != null) {
int overrideHz = Settings.getInt(FRAMERATE_PROP, 60);
if (overrideHz > 0) {
retVal = precision / overrideHz;
}
} else if (Settings.get(PULSE_PROP) != null) {
int overrideHz = Settings.getInt(PULSE_PROP, 60);
if (overrideHz > 0) {
retVal = precision / overrideHz;
}
} else {
// If not explicitly set in Settings, try to set based on
// refresh rate of display
int rate = Toolkit.getToolkit().getRefreshRate();
if (rate > 0) {
retVal = precision / rate;
}
// if unknown, use default
}
return retVal;
}

在源中,帧率由未记录的属性javafx.animation.framerate控制,它对应于上述源中的设置FRAMERATE_PROPjavafx.animation.pulse对应于设置PULSE_PROP

内部 Toolkit 类提供了一个回退 API,用于使用显示器的刷新率。

因此,也许您可以通过将系统属性设置为javafx.animation.framerate,设置为com.sun.javafx.tk.Toolkit.getToolkit().getRefreshRate()的值或将javafx.animation.frameratejavafx.animation.pulse设置为 null 来实现您想要的。 我没有尝试过这个,所以我不知道。 需要注意确保您设置的值得到正确维护,并且不会被默认值覆盖。

您需要注意,如果您这样做,您将使用未记录的 API,因此这需要打开适当的模块导出,以便可以访问 API,并且也可能在平台的未来版本中被删除或变得不兼容,恕不另行通知(尽管这些设置目前已多年未更改)。

所以我一直在阅读您提供的链接,对我来说,JavaFX 似乎只是停留在 60fps,因为它更喜欢?

默认情况下,是的,这是一种权衡。

对于通常使用 JavaFX 开发的应用程序类型,无论设备的底层硬件功能如何,脉冲渲染机制中的 60fps 刷新最大值通常就足够了。 对于大多数 JavaFX 应用程序来说,将速率上限提高到该级别以上通常不是一个好的权衡,因为这意味着使用更高的资源(CPU 和视频处理容量以及功耗),以获得大多数用户通常无法检测到且通常不受底层硬件支持的刷新更新的收益。

也就是说,如果要覆盖此默认限制,可以使用未记录的方法执行此操作,如上所述。

为什么不使用相同的方法?

在这种情况下,使用 AWT 方法可能是一种合理的替代方法。

硬件刷新不会根据所选的 UI 工具包而更改。

目前(JavaFX 17),JavaFX系统依赖于java.awt包(javafx.base模块需要包含所有awt类的java.desktop模块)。 因此,如果JavaFX可用,那么java.awt.DisplayMode类也可用。

因此,如果目标是确定硬件刷新率,则可以使用现有的awt API执行此操作。

使用 Swing 或 JavaFX 时刷新率不会改变,对吗?

硬件刷新率不会根据所使用的 UI 工具包而变化。

JavaFX 中用于更新场景图渲染的场景图脉冲率的定义与刷新率有些相关,但这只是 JavaFX 的概念,因为 Swing 在其渲染阶段不使用场景图。

最新更新