Reputation: 1720
I've been trying to implement double buffering in my game library, but it is dramatically slower than without it.
This is my class that implements it. Basically, it draws onto an image, and then draws the image onto the JPanel's graphics (pg).
public abstract class DrawingPanel extends GameLoop {
private BufferedImage image;
private Graphics2D ig;
private Graphics pg;
private int backgroundRGB;
public DrawingPanel(final int fps, final int ups, final JPanel panel, final Color background) {
super(fps, ups);
initialize(panel, background);
}
/**
* Creates a panel with 60 fps and 120 ups
*
* @param panel
* @param background
*/
public DrawingPanel(final JPanel panel, final Color background) {
this(60, 120, panel, background);
}
public DrawingPanel(final JPanel panel) {
this(panel, Color.WHITE);
}
public DrawingPanel(final int ups, final JPanel panel, final Color background) {
super(ups);
initialize(panel, background);
}
private void initialize(final JPanel panel, final Color background) {
image = GraphicsUtils.createImage(panel.getWidth(), panel.getHeight(), Transparency.OPAQUE);
ig = (Graphics2D) image.getGraphics();
pg = panel.getGraphics();
GraphicsUtils.prettyGraphics(ig);
backgroundRGB = background.getRGB();
panel.addMouseListener(this);
panel.addMouseMotionListener(this);
panel.addKeyListener(this);
panel.addMouseWheelListener(this);
panel.setFocusable(true);
panel.requestFocusInWindow();
}
@Override
public void draw() {
// set background
Arrays.fill(((DataBufferInt) image.getRaster().getDataBuffer()).getData(), backgroundRGB);
// draw on the buffer
draw(ig);
// draw our buffer to the screen
pg.drawImage(image, 0, 0, null);
}
public abstract void draw(Graphics2D g);
}
The results of this: This means that 25% - 6% = 19% of the time is being spent in the draw method alone!
I thought it might be the fact that I was using Array.fill
, but it turned out not to be a useless "premature optimization;" if I replaced that line with
ig.setColor(backgroundColor);
ig.fillRect(0, 0, image.getWidth(), image.getHeight());
The time it takes is even longer: 26 - 5% = 21%. Is there any way I can speed this method up?
By the way, the GraphicsUtils.createImage
creates a compatible image from my GraphicsConfiguration
.
This entire library is on github, if you want to look at the entire code.
Upvotes: 0
Views: 218
Reputation: 347184
This is NOT how painting works in Swing -> pg = panel.getGraphics();
this is a horribly bad idea. You are fighting between the passive rendering engine of Swing and your attempt to paint to a "snap shot" of the component.
Swing components are, when you use them properly, double buffered internally, but, you need to be working with the API.
Start by having a look at Performing Custom Painting and Painting in AWT and Swing for more details about how painting works in Swing
Basically, what's happening is, you are using the results from getGraphics
to paint to the panel, the RepaintManager
is coming along and decides it needs to update your component and repaints it, which produces a blank panel, repeat really fast. This is what's causing your flickering. In Swing, you do not control the paint process, you can only ask to be notified when a paint cycle occurs (overriding paintComponent
for example) and make requests to the painting system that a repaint should occur, but it's up to the paint system to actually decide WHEN that paint cycle occurs.
Instead, start by overriding the panel's paintComponent
method and perform ALL your custom painting within it. Use repaint
to request that the component be updated.
If you "really" need a active painting process, then have a look at BufferStrategy
and BufferStrategy and BufferCapabilities, which provides you the means to directly control when the output is pushed to the screen device.
Upvotes: 1