能否在纯 Java 中以官方、可移植的方式访问 VSync 信号?
结论
不能。Java SE 没有提供同时满足纯 Java、有官方文档支持、可跨平台访问 VSync 信号这些条件的公共 API。在 Java SE 27 中,标准桌面 API 仍没有等待下一次垂直消隐、接收 VSync 回调或显式设置缓冲交换同步间隔的接口。
如果不增加依赖,可以使用 AWT 的缓冲绘制机制,但它无法保证跨平台的 VSync。即便画面在某个系统上恰好与刷新同步,也不能据此认定程序访问到了 VSync 信号。
为什么现有办法不满足要求
ExtendedBufferCapabilities 属于内部 API
sun.java2d.pipe.hw.ExtendedBufferCapabilities 是 JDK 实现内部的类,不属于 Java SE 公共 API。某个 JDK 能调用 VSyncType.VSYNC_ON,不代表其他 JDK 实现或操作系统也必须支持它。
通过额外参数开放内部包,只能解除特定环境中的访问限制,无法为接口提供可移植性或兼容性保证。
图形管线参数不等于 VSync 开关
-Dsun.java2d.opengl=true 和 -Dsun.java2d.d3d=true 影响 Java 2D 使用的渲染管线。选择 OpenGL 或 Direct3D,并不意味着应用就能控制垂直同步或接收刷新通知。
官方排障文档按平台说明了其中一些参数的用途。例如,OpenGL 参数用于在相应平台上尝试启用 OpenGL 管线,这并不构成标准 VSync API。Java 2D Properties
Toolkit.sync() 不保证等待 VSync
Toolkit.sync() 是正式的公共 API,调用方式如下:
Toolkit.getDefaultToolkit().sync();
文档规定它用于同步工具包的图形状态,并说明某些窗口系统会缓冲图形事件,但没有承诺它会等待垂直消隐,或在某个显示刷新边界返回。因此,不能将它当作可移植的 waitForVSync()。Toolkit.sync()
不增加依赖时,可以怎样实现绘制
如果需要在后台缓冲区中绘制完整的一帧,可以使用 Canvas 或 Window 的 BufferStrategy:
- 创建并显示窗口,使绘图组件处于可显示状态。
- 调用
createBufferStrategy(2)创建双缓冲策略。 - 获取绘图对象,绘制完整画面,然后释放绘图对象。
- 调用
show()提交缓冲区,并处理缓冲区丢失或恢复的情况。
BufferStrategy 自 Java 1.4 起提供。它的 show() 可以通过复制或页面翻转显示缓冲区,但公共 API 的约定没有要求这一操作必须与垂直刷新同步。BufferStrategy
假设 canvas 已加入窗口并显示,可以这样初始化:
canvas.createBufferStrategy(2);
BufferStrategy strategy = canvas.getBufferStrategy();
单帧绘制方法可以写成:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
public final class FrameRenderer {
public static void drawFrame(
BufferStrategy strategy, int width, int height) {
do {
do {
Graphics g = strategy.getDrawGraphics();
try {
g.setColor(Color.BLACK);
g.fillRect(0, 0, width, height);
g.setColor(Color.WHITE);
g.fillOval(20, 20, 40, 40);
} finally {
g.dispose();
}
} while (strategy.contentsRestored());
strategy.show();
} while (strategy.contentsLost());
}
}
这段代码采用标准缓冲绘制流程,没有启用或访问 VSync。恢复缓冲区时可能需要重复绘制同一帧,因此动画状态更新应放在重绘重试循环之外。
实现时需要注意什么
BufferCapabilities.isPageFlipping() 只能说明缓冲策略是否使用页面翻转,不能用来判断 VSync 是否开启。FlipContents 描述的是翻转后后台缓冲区的内容。BufferCapabilities
使用 Thread.sleep() 或定时任务限制帧率,也无法代替 VSync。即使目标帧率与显示器刷新率相同,绘制时刻仍可能与扫描时刻错开。
如果必须控制缓冲交换与刷新的关系,就需要放宽限制,使用提供相应能力的图形库或平台原生接口,并确认其支持哪些平台。如果还需要直接接收刷新通知,则应单独确认所选接口是否提供这种通知;仅支持开启 VSync 并不足以满足这一要求。
备注:内容仅供参考。
