使用没有Opengl或Direct3d管道的Java2d进行平滑绘图?

时间:2012-11-23 02:07:48

标签: java java-2d flip buffered vsync

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

下面的快速和脏代码演示了我的问题。它绘制了一个在屏幕上移动的框。盒子位置每秒更新大约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个缓冲区。

2 个答案:

答案 0 :(得分:2)

有些事情在我身上跳了出来吓唬我......

  1. 你没有遵守线程/ Swing合同。 UI的所有更新必须使用Event Dispatching Thread进行。所有长时间运行和阻塞代码都应该在后台线程中执行。
  2. 你的动画循环"正在吮吸大量的CPU时间。线程应该在周期之间休眠(或者至少应该只在某些事情发生变化时进行绘制),这应该减少系统上的所有负载。
  3. 我尝试了一些解决方案。

    虽然我没有"重要的"问题,这些都是非常简单的例子,我通常使用默认的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);
            }
        }
    }
    

答案 1 :(得分:0)

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

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

如果其中一个管道工作,Java2D就很棒,但是它不是很可靠。我有幸在SDL中使用纯软件表面而不是java2d。