3

当禁用 opengl 和 direct3d 管道时(通过使用 -Dsun.java2d.d3d=false 和 -Dsun.java2d.opengl=false 调用 vm),我无法找到使用 Java2d 获得平滑移动或动画的方法

下面快速而肮脏的代码演示了我的问题。它绘制了一个在屏幕上移动的框。方框位置每秒更新大约 60 次,屏幕重绘尽可能多的次数。它使用 BufferStrategy 类来实现双缓冲;翻转在“bs.show();”完成

代码(按退出键退出):

import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;

public class FluidMovement {
    private static volatile boolean running = true;
    private static final int WIDTH = 500;
    private static final int HEIGHT = 350;

    public static void main(String[] args) {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        GraphicsConfiguration gc = gd.getDefaultConfiguration();

        Frame frame = new Frame(gc);
        frame.setIgnoreRepaint(true);
        frame.setUndecorated(true);
        frame.addKeyListener(new KeyAdapter() {
            @Override public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                    running = false;
                }
            }
        });
        frame.setSize(WIDTH, HEIGHT);
        frame.setVisible(true);
        frame.createBufferStrategy(2);
        BufferStrategy bs = frame.getBufferStrategy();
        long nextTick = System.nanoTime();
        class Rect {
            int dx = 2, dy = 1, x = 0, y = 0;
        }
        Rect rect = new Rect();

        Graphics g;
        while (running) {

            if (System.nanoTime() > nextTick) {
                rect.x = (rect.x + rect.dx) % WIDTH;
                rect.y = (rect.y + rect.dy) % HEIGHT;
                nextTick += 1000000000 / 60;
            }

            g = bs.getDrawGraphics();
            g.setColor(Color.BLACK);
            g.fillRect(0, 0, WIDTH, HEIGHT);
            g.setColor(Color.WHITE);
            g.fillRect(rect.x, rect.y, 10, 10);
            g.dispose();
            bs.show();
        }
        bs.dispose();
        frame.dispose();
    }

}

当我使用“java FluidMovement”正常执行此代码时,它像丝绸一样顺畅运行(除了偶尔的撕裂),因为 jvm 使用了 direct3d/directdraw 管道。当我使用“java -Dsun.java2d.d3d=false -Dsun.java2d.opengl=false FluidMovement”执行此代码时,它非常不稳定。

我不能假设使用了 direct3d 或 opengl 管道。管道不适用于我尝试过的 3 台机器中的 2 台;它只能在运行 Windows 7 的具有专用图形的机器上运行。无论如何我可以使盒子顺利移动,还是我应该求助于使用某种低级别访问权限的库,如 JOGL?

笔记:

  • 帧率不是问题。在这两种情况下(启用和禁用管道),应用程序的运行速度都超过 300 fps。启用管道时,我强制关闭 vsync。
  • 我试过 Toolkit.getDefaultToolkit().sync()
  • 我尝试了许多不同类型的循环,但运动从来都不是真正流畅的。即使使用固定的帧速率,也会表现出相同的断断续续。
  • 我试过以全屏独占模式运行框架。
  • 我尝试过使用 3 个甚至 4 个缓冲区。
4

2 回答 2

3

很多东西突然出现在我身上并吓到我......

  1. 您没有遵守线程/Swing 合同。UI 的所有更新都必须通过事件调度线程进行。所有长时间运行和阻塞的代码都应该在后台线程中执行。
  2. 您的“动画循环”无所事事地占用了大量 CPU 时间。线程应该在周期之间休眠(或者至少,应该只在某些事情发生变化时才绘制),这应该会减少系统上的总体负载。

我尝试了一些解决方案。

虽然我没有遇到“重大”问题,但这些都是非常简单的示例,我通常使用默认 JVM 选项获得了更好的性能。

缓冲策略

这基本上就是你所拥有的,开始对 EDT 很好,并使用你正在使用的缓冲策略

public class SimpleAnimationTest {

    private boolean running = true;
    private Rectangle box = new Rectangle(0, 90, 10, 10);
    private int dx = 4;
    protected static final int WIDTH = 200;
    protected static final int HEIGHT = 200;

    public static void main(String[] args) {
        new SimpleAnimationTest();
    }

    public SimpleAnimationTest() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();

                frame.setIgnoreRepaint(true);

                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.setSize(WIDTH, HEIGHT);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                frame.createBufferStrategy(2);
                final BufferStrategy bs = frame.getBufferStrategy();

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        long tock = 1000 / 60;
                        while (running) {

                            box.x += dx;
                            if (box.x + box.width > WIDTH) {
                                box.x = WIDTH - box.width;
                                dx *= -1;
                            } else if (box.x < 0) {
                                box.x = 0;
                                dx *= -1;
                            }

                            Graphics2D g = (Graphics2D) bs.getDrawGraphics();
                            g.setColor(Color.BLACK);
                            g.fillRect(0, 0, WIDTH, HEIGHT);
                            g.setColor(Color.WHITE);
                            g.fill(box);
                            g.dispose();
                            bs.show();
                            try {
                                Thread.sleep(tock);
                            } catch (InterruptedException ex) {
                            }
                        }
                        bs.dispose();

                    }
                }).start();

            }
        });
    }
}

双缓冲摆动组件

public class SimpleAnimationTest {

    private Rectangle box = new Rectangle(0, 90, 10, 10);
    private int dx = 4;

    public static void main(String[] args) {
        new SimpleAnimationTest();
    }

    public SimpleAnimationTest() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new SimplePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class SimplePane extends JPanel {

        public SimplePane() {

            setDoubleBuffered(true);

            Timer timer = new Timer(1000 / 300, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    box.x += dx;
                    if (box.x + box.width > getWidth()) {
                        box.x = getWidth() - box.width;
                        dx *= -1;
                    } else if (box.x < 0) {
                        box.x = 0;
                        dx *= -1;
                    }
                    repaint();
                }
            });
            timer.setRepeats(true);
            timer.setCoalesce(true);
            timer.start();
        }

        @Override
        protected void paintComponent(Graphics g) {
            Graphics2D g2d = (Graphics2D) g.create();
            super.paintComponent(g2d);
            box.y = (getHeight() - box.height) / 2;
            g2d.setColor(Color.RED);
            g2d.fill(box);
            g2d.dispose();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }
    }
}
于 2012-11-23T03:24:49.890 回答
1

我通过切换到 JOGL 解决了我的问题。在我之前遇到困难的机器上,现在一切都很好而且很顺利。

LWJGL 看起来也很有前途,它可以让您以几乎与在 C 中相同的方式使用 opengl。

如果其中一个管道工作,Java2D 很棒,但否则它不是很可靠。我在 SDL 中通过 java2d 使用纯软件表面的运气更好。

于 2012-11-23T17:56:30.293 回答