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

当禁用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个缓冲区。

一些事情突然袭来,吓唬我……

  1. 你没有尊重线程/ Swing合同。 UI的所有更新必须使用Event Dispatching Thread进行。 所有长时间运行和阻塞代码都应该在后台线程中执行。
  2. 你的“动画循环”正在吸收大量的CPU时间。 线程应该在周期之间hibernate(或者至少应该只在某些事情发生变化时绘制),这应该减少系统上的所有负载。

我试了几个解决方案。

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

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

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

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

Interesting Posts