在没有Opengl或Direct3d管道的情况下使用Java2d进行平滑绘图



当opengl和direct3d管道被禁用时(通过调用带有-Dsun.Java2d.d3d=false和-Dsun.java 2d.onlog=false的vm),我无法找到使用Java2d来平滑移动或动画化任何东西的方法

下面快速而肮脏的代码说明了我的问题。它绘制了一个在屏幕上移动的方框。盒子位置每秒更新约60次,屏幕会被尽可能多次地重新绘制。它使用BufferStrategy类来实现双缓冲;翻转是在"bs.show();"中完成的

代码(按escape退出):

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();
}
}

当我用"javaFluidMovement"正常执行这段代码时,它会像丝绸一样平滑地运行(除了偶尔的撕裂),因为jvm使用了direct3d/directdraw管道。当我用"java-Dsun.java2d.d3d=false-Dsun.jjava2d.xinkle=false-FluidMovement"执行这段代码时,它非常不稳定。

我不能假设使用了direct3d或opengl管道。在我试过的3台机器中,有2台无法使用管道;它只适用于运行Windows7的具有专用图形的机器。我有没有办法让盒子顺利移动,或者我应该使用像JOGL这样的低级别访问库?

注:

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

许多事情突然向我袭来,吓到了我…

  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);
}
}
}

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

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

如果其中一个管道工作,Java2D就很好,但除此之外,它就不太可靠了。我在SDL中使用纯软件曲面而不是java2d时运气更好。

最新更新