我在Linux/XWindows上简单的Java2D应用中经历了系统事件和窗口刷新率之间的意外交互。以下小例子最能说明这一点。
该程序创建一个小窗口,在其中以不同的旋转显示半圆形。图形每秒更新60帧,以产生闪烁的显示效果。这是通过BufferStrategy
实现的,即通过调用其show
方法来实现的。
然而,我注意到当我(a)将鼠标移动到窗口上,使窗口接收到鼠标事件,或者(b)按住键盘上的某个键,以便窗口接收到键盘事件时,闪烁明显增加。
由于BufferStrategy.show()
被调用的频率不受这些事件的影响,可以从控制台上的输出看出(它们应该始终保持在大约60 fps)。然而,更快的闪烁表明,实际上更新显示的速度确实发生了变化。
在我看来,只有在生成鼠标或键盘事件时,才能实现实际的,即可见的每秒60帧。
public class Test {
// pass the path to 'test.png' as command line parameter
public static void main(String[] args) throws Exception {
BufferedImage image = ImageIO.read(new File(args[0]));
// create window
JFrame frame = new JFrame();
Canvas canvas = new Canvas();
canvas.setPreferredSize(new Dimension(100, 100));
frame.getContentPane().add(canvas);
frame.pack();
frame.setVisible(true);
int fps = 0;
long nsPerFrame = 1000000000 / 60; // 60 = target fps
long showTime = System.nanoTime() + nsPerFrame;
long printTime = System.currentTimeMillis() + 1000;
for (int tick = 0; true; tick++) {
BufferStrategy bs = canvas.getBufferStrategy();
if (bs == null) {
canvas.createBufferStrategy(2);
continue;
}
// draw frame
Graphics g = bs.getDrawGraphics();
int framex = (tick % 4) * 64;
g.drawImage(image, 18, 18, 82, 82, framex, 0, framex+64, 64, null);
g.dispose();
bs.show();
// enforce frame rate
long sleepTime = showTime - System.nanoTime();
if (sleepTime > 0) {
long sleepMillis = sleepTime / 1000000;
int sleepNanos = (int) (sleepTime - (sleepMillis * 1000000));
try {
Thread.sleep(sleepMillis, sleepNanos);
} catch (InterruptedException ie) {
/* ignore */
}
}
showTime += nsPerFrame;
// print frame rate achieved
fps++;
if (System.currentTimeMillis() > printTime) {
System.out.println("fps: " + fps);
fps = 0;
printTime += 1000;
}
}
}
}
一个可以与该程序一起使用的示例图像(必须将其路径作为命令行参数传递)如下所示:
所以我有一个(由两部分组成的)问题:
为什么会出现这种效果?我如何实现 实际 的 60 fps?
(攻略者的额外问题:您是否在其他操作系统下也遇到了同样的问题?)
VolatileImage
,根据您所说的操作系统因素可能会丢失其内容,从而导致出现所述行为。因此,通过限制丢失缓冲区的循环条件可能会解决您的问题。再次强调,我不确定这是否真正是代码行为背后的原因。 - Shyam Baitmangalkarfps
值是否发生了变化吗?对我来说,我可以观察到fps
值在59到61之间变化,刷新也是按要求进行的。 - Shyam Baitmangalkar