我正在尝试使我的窗口应用程序与显示器刷新垂直同步。此代码可在“fullscreen = true”(报告 75FPS,显示器刷新率)下正常工作,但在窗口模式下仅报告 32FPS。有人知道我在这里做错了什么吗?
import java.awt.BufferCapabilities;
import java.awt.GraphicsEnvironment;
import java.awt.ImageCapabilities;
import java.awt.image.BufferStrategy;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import sun.java2d.pipe.hw.ExtendedBufferCapabilities;
public class VSyncTest {
public static void main(String args[]) {
final boolean fullscreen = true;
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame v = new JFrame();
if (fullscreen) {
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(v);
v.createBufferStrategy(2);
} else {
v.setVisible(true);
ExtendedBufferCapabilities b = new ExtendedBufferCapabilities(new BufferCapabilities(new ImageCapabilities(true), new ImageCapabilities(true), BufferCapabilities.FlipContents.PRIOR), ExtendedBufferCapabilities.VSyncType.VSYNC_ON);
try {
v.createBufferStrategy(2, b);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
new Thread() {
@Override
public void run() {
BufferStrategy bs = v.getBufferStrategy();
long start = System.nanoTime(), frames = 0;
do {
bs.getDrawGraphics();
bs.show();
frames++;
long elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
long framesPerS = (long) (frames / (elapsed / 1000D));
if (frames % 60 == 0) {
System.out.println("Frames/s: " + framesPerS + ", Elapsed: " + elapsed / 1000);
}
} while (true);
}
}.start();
}
});
}
}
最佳答案
我会尝试一个答案,虽然问题本身似乎不是 Java 问题,但它似乎是图形问题。首先,这个答案是基于我正在测试的配置,Windows 10,配备英特尔显卡的戴尔笔记本电脑,2560x1440 显示器。
其次是对您的代码的评论,以下设置在我的配置上惨败:
ExtendedBufferCapabilities b = new ExtendedBufferCapabilities(
new BufferCapabilities(new ImageCapabilities(true), new ImageCapabilities(true), BufferCapabilities.FlipContents.PRIOR),
ExtendedBufferCapabilities.VSyncType.VSYNC_ON);
它导致闪烁并且帧计数器变得疯狂。如果您查看 createBufferStrategy(2)
使用的代码(没有功能的重载),它使用的是 FlipContents.UNDEFINED
而不是 FlipContents.PRIOR
。无论如何,我建议您使用
GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDefaultConfiguration()
.getBufferCapabilities()
为了获得适当的设备功能。这样就停止了闪烁,并将刷新率计固定为 60 fps
,这与我全屏运行时的刷新率计相同。
另一件事是我尝试为框架设置实际尺寸并删除装饰。调用 setUndecorated(true)
似乎没有任何区别,尽管建议在进入全屏之前调用它。最后,如果您调整窗口大小,计数器将再次关闭,因此看看您是否可以使用 frame.setResizes( false );
生存,如果您将框架移动到另一个显示器,则垂直同步会丢失 -您实际上可以看到帧计数器再次变得疯狂。不幸的是,我不是解释这些的图形专家,但如果我没记错的话,设备上会分配一个缓冲区,并且在调整大小时需要使其失效 - 这就是为什么全屏毕竟有意义。我不记得我在哪里读到过这篇文章。
关于Java VSync 没有全屏窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58527858/