Java2D:XWindows 事件和帧率之间的交互
Posted
技术标签:
【中文标题】Java2D:XWindows 事件和帧率之间的交互【英文标题】:Java2D: interaction between XWindows events and frame rate 【发布时间】:2017-04-01 09:26:02 【问题描述】:我在 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?
(评论者的额外问题:您在其他操作系统下是否也遇到相同的效果?)
【问题讨论】:
我尝试在 Windows 10 上运行的机器上多次运行您的代码。有时我观察到闪烁变慢了。你有没有遇到过这个问题?您是否尝试过保护您的代码免受帧丢失?? @sbaitmangalkar 感谢您试用代码。帧丢失是我目前没有考虑的一个单独问题。但要明确一点:当您运行程序时,您不会在将鼠标光标移到窗口上时立即看到动画加速? 缓冲区策略中的缓冲区大多是VolatileImage
,这可能会根据您所说的操作系统因素丢失内容,从而导致被提及的行为。因此,通过限制丢失缓冲区的循环条件可能会解决您的问题。同样,我不确定这是否真的是代码行为背后的原因。
在我的测试运行中,内容从未丢失,并且所描述的行为仍然可见。
在 OSX El Capitan 中工作。可能是因为它不受 X11 命令缓冲区批处理的影响。
【参考方案1】:
问:为什么会出现这种效果?
简答:必须在BufferStrategy#show
之后调用canvas.getToolkit().sync()
或Toolkit.getDefaultToolkit().sync()
长答案:如果没有明确的 Toolkit#sync
像素,则在 X11 命令缓冲区已满或其他事件(例如鼠标移动)强制刷新之前,不会出现在屏幕上。
引用自 http://www.java-gaming.org/index.php/topic,15000.
...即使我们 (Java2D) 立即发出我们的渲染命令视频 司机可能会选择不立即执行它们。经典示例是 X11 - 尝试定时循环 Graphics.fillRects - 看看你能发出多少 在几秒钟内。没有 toolkit.sync() (在 X11 管道的情况下 XFlush()) 在每次调用之后或循环结束时,您实际测量的是 Java2D 的速度有多快 调用 X11 lib 的 XFillRect 方法,该方法只是将这些调用批处理,直到它成为命令 缓冲区已满,然后才将它们发送到 X 服务器执行。
问:如何才能达到实际的 60 fps?
A:刷新命令缓冲区并考虑在代码中使用System.setProperty("sun.java2d.opengl", "true");
或-Dsun.java2d.opengl=true
命令行选项为Java2D 开启OpenGL 加速。
另见:
Java animation stutters when not moving mouse cursor http://www.java-gaming.org/index.php/topic,15000. Drawing in Java using Canvas http://www.oracle.com/technetwork/articles/java/mixing-components-433992.html【讨论】:
以上是关于Java2D:XWindows 事件和帧率之间的交互的主要内容,如果未能解决你的问题,请参考以下文章