Java2D: XWindows事件和帧速率之间的交互。

时间:2021-02-04 21:55:25

I'm experiencing an unexpected interaction between system events and the window refresh rate in simple Java2D applications on Linux/XWindows. It is best demonstrated with the small example below.

在Linux/XWindows上的简单Java2D应用程序中,系统事件和窗口刷新速度之间发生了意外的交互。下面的小示例最好地演示了这一点。

This program creates a small window in which a half-circle is displayed at different rotations. The graphics are updated at 60 frames per second in order to produce a flickering display. This is achieved through a BufferStrategy, namely by calling its show method.

这个程序创建了一个小窗口,在这个窗口中,半圆在不同的旋转中显示。图形以每秒60帧的速度更新以产生闪烁的显示。这是通过一个BufferStrategy实现的,即调用它的show方法。

However, I note that when I (a) move the mouse over the window so that the window receives mouse-over events or (b) hold down a key on the keyboard so that the window receives keyboard events, the flickering increases visibly.

然而,我注意到,当我(a)将鼠标移到窗口上,使窗口接收鼠标移过事件,或(b)按住键盘上的键,使窗口接收键盘事件时,闪烁明显增加。

As the rate at which BufferStrategy.show() is called is not affected by these events, as can be seen by the print-outs on the console (they should constantly be at around 60 fps). However, the faster flickering indicates that the rate at which the display is actually updated, does indeed change.

由于调用BufferStrategy.show()的速率不受这些事件的影响,这可以从控制台的打印输出中看出(它们应该始终在60帧/秒左右)。然而,闪烁的速度越快表明显示的更新速度确实会改变。

It looks to me that actual, i.e., visible 60 frames per second are not achieved unless mouse or keyboard events are generated.

在我看来,它是真实的。,除非生成鼠标或键盘事件,否则无法每秒获得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;
            }
        }
    }
}

A sample image to use with this program (the path to which must be passed as a command line argument) is this:

此程序使用的示例映像(必须作为命令行参数传递到的路径)是:

Java2D: XWindows事件和帧速率之间的交互。

So my (2-part) question is:

所以我(2部分)的问题是:

Why is this effect happening? How can I reach actual 60 fps?

为什么会发生这种效应?我怎样才能达到实际的60磅?

(Bonus question for commenters: do you experience the same effect also under other operating systems?)

(对评论者的额外问题是:在其他操作系统下,你是否也体验到了同样的效果?)

1 个解决方案

#1


6  

Q: Why is this effect happening?

问:为什么会出现这种效应?

Short answer: One must call canvas.getToolkit().sync() or Toolkit.getDefaultToolkit().sync() after BufferStrategy#show

简短的回答:必须在BufferStrategy#show之后调用canvas.getToolkit().sync()或Toolkit.getDefaultToolkit() ().sync()

Long answer: Without an explicit Toolkit#sync pixels do not make it to the screen until X11 command buffer is full or another event, such as mouse move, forces a flush.

长答案:如果没有一个明确的工具包#同步像素,直到X11命令缓冲区满或另一个事件,例如鼠标移动,强制一个刷新时,同步像素才会到达屏幕。

Quote from http://www.java-gaming.org/index.php/topic,15000.

引用http://www.java-gaming.org/index.php/topic,15000。

... even if we (Java2D) issue our rendering commands immediately the video driver may chose not to execute them right away. Classic example is X11 - try timing a loop of Graphics.fillRects - see how many you can issue in a couple of seconds. Without toolkit.sync() (which in case of X11 pipeline does XFlush()) after each call or at the end of the loop what you actually measure is how fast Java2D can call X11 lib's XFillRect method, which just batches up those calls until it's command buffer is full, only then they're sent to the X server for execution.

…即使我们(Java2D)立即发出呈现命令,视频驱动程序也可能选择不立即执行它们。典型的例子是X11 -尝试计时一个图形循环。fillRects -看看你能在几秒钟内发出多少个。没有toolkit.sync()(哪个X11的管道XFlush())在每次调用或循环的最后你实际测量的是Java2D如何快速调用X11*XFillRect的方法,这批这些调用,直到它的命令缓冲区已满,只有这样他们发送到X服务器来执行。

Q: How can I reach actual 60 fps?

问:我如何才能达到实际的60磅?

A: Flush the command buffer and consider turning on OpenGL acceleration for Java2D with System.setProperty("sun.java2d.opengl", "true"); in code or -Dsun.java2d.opengl=true command line option.

A:刷新命令缓冲区,考虑使用System.setProperty(“sun.java2d”)打开Java2D的OpenGL加速。opengl”、“真正的”);在代码或-Dsun.java2d。opengl = true命令行选项。

See also:

参见:

#1


6  

Q: Why is this effect happening?

问:为什么会出现这种效应?

Short answer: One must call canvas.getToolkit().sync() or Toolkit.getDefaultToolkit().sync() after BufferStrategy#show

简短的回答:必须在BufferStrategy#show之后调用canvas.getToolkit().sync()或Toolkit.getDefaultToolkit() ().sync()

Long answer: Without an explicit Toolkit#sync pixels do not make it to the screen until X11 command buffer is full or another event, such as mouse move, forces a flush.

长答案:如果没有一个明确的工具包#同步像素,直到X11命令缓冲区满或另一个事件,例如鼠标移动,强制一个刷新时,同步像素才会到达屏幕。

Quote from http://www.java-gaming.org/index.php/topic,15000.

引用http://www.java-gaming.org/index.php/topic,15000。

... even if we (Java2D) issue our rendering commands immediately the video driver may chose not to execute them right away. Classic example is X11 - try timing a loop of Graphics.fillRects - see how many you can issue in a couple of seconds. Without toolkit.sync() (which in case of X11 pipeline does XFlush()) after each call or at the end of the loop what you actually measure is how fast Java2D can call X11 lib's XFillRect method, which just batches up those calls until it's command buffer is full, only then they're sent to the X server for execution.

…即使我们(Java2D)立即发出呈现命令,视频驱动程序也可能选择不立即执行它们。典型的例子是X11 -尝试计时一个图形循环。fillRects -看看你能在几秒钟内发出多少个。没有toolkit.sync()(哪个X11的管道XFlush())在每次调用或循环的最后你实际测量的是Java2D如何快速调用X11*XFillRect的方法,这批这些调用,直到它的命令缓冲区已满,只有这样他们发送到X服务器来执行。

Q: How can I reach actual 60 fps?

问:我如何才能达到实际的60磅?

A: Flush the command buffer and consider turning on OpenGL acceleration for Java2D with System.setProperty("sun.java2d.opengl", "true"); in code or -Dsun.java2d.opengl=true command line option.

A:刷新命令缓冲区,考虑使用System.setProperty(“sun.java2d”)打开Java2D的OpenGL加速。opengl”、“真正的”);在代码或-Dsun.java2d。opengl = true命令行选项。

See also:

参见: