I can't figure out a way to get smooth movement or animation of anything using Java2d when the opengl and direct3d pipelines are disabled (by invoking the vm with -Dsun.java2d.d3d=false and -Dsun.java2d.opengl=false)
The quick and dirty code below demonstrates my problem. It draws a box that moves across the screen. The box location is updated about 60 times per second and the screen is redrawn as many times as possible. It uses the BufferStrategy class to implement double buffering; the flip is done at "bs.show();"
Code(press escape to quit):
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();
}
}
When I execute this code normally with "java FluidMovement", it runs smooth as silk (besides the occasional tearing) because the jvm makes use of the direct3d/directdraw pipeline. When i execute this code with "java -Dsun.java2d.d3d=false -Dsun.java2d.opengl=false FluidMovement" it is terribly choppy.
I can't make the assumption that the direct3d or opengl pipeline is used. The pipelines don't work with 2 of the 3 machines I have tried it on; it only worked on a machine with dedicated graphics running Windows 7. Is there anyway I can make the box move smoothly or should i resort to using some kind of library with low level access like JOGL?
Notes:
A number of things jump out at me and scare me...
I tried a few solutions.
While I didn't have "significant" issues, these are really simple examples, I did generally get better performance with the default JVM options.
This is basically what you had, begin nice to the EDT and using the buffer strategy your were using
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);
}
}
}