Accelerating images through BufferStrategy

I need to draw on screen the same images many times, so I’m trying to accelerate it.

Every tutorial on acceleration I’ve seen so far uses a BufferedStrategy, inside a Canvas, inside a Frame.

But I can’t find a simple tutorial that draws an accelerated image onto that Canvas.

I’d just draw a VolatileImage on the canvas, but I guess that would defeat the purpose of the BufferedStrategy.

Also, I can’t figure out how to validate both the VolatileImage and the BufferedStrategy.
I guess they would interfere as they both call dispose() on the Graphics object.

I’ve seen this tutorials so far:

EDIT: this one seems interesting too, but I think there’s a little odd on the comment “This should not happen”
http://www.exampledepot.com/egs/java.awt.image/VolImage.html

Also, only the first tutorial use setIgnoreRepaint(true);

I’d like to edit the second tutorial making it draw a .gif image loaded from outside,
what’s the best way to do that?

BTW I’d like to allow the user to resize the window, if that’s possible

The best way is just to create compatible images (look at line 87) and avoid VolatileImage.

GIF images are not supported well in Java2D.

Resizing is always possible. :slight_smile:

Thanks, if a BufferedImage can be accelerated as well, I’ll gladly avoid using VolatileImage(s).

I’ll try to make an example and post here for review.

The problem with java2d is that it can very easily fall back to software rendering. And the software java2d rendering pipeline is really slow. So you get particularly slow software rendering without doing anything “wrong”.

Basically don’t rotate, don’t scale, only use bit alpha channel, don’t use AA, use compatible image pixel/colour formats. With all that you have good odds of getting some HW acceleration.

But i have noticed that java2d is really pretty poor for this sort of thing. Its fine for gui’s that don’t need to draw whole thing every frame, but once you are doing animations or games its a very poor match. I would recommend looking into using lwjgl or slick or something to get anything like real consistent performance.

Agree, Swing sucks, but still, it’s the standard library, so I’m just trying to learn the basics before moving on.

I finished my example, I modded the tutorial I posted, and another one too.

Here are my 2 classes. Do you know of a (quick) way to fix the flickering when resizing the window?
Because it look really awful, and I don’t want to force a fixed size.
Here’s my whole Eclipse project.

BTW I guess I also need to interrupt the thread when the frame closes, but fixing the resize takes priority now

package sandbox;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Transparency;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class MyGame {
	private static final int FRAME_DELAY = 20; // 20ms. implies 50fps (1000/20) = 50
 
	public MyGame() {
		System.out.println(this.getClass().getClassLoader().getResource("resources/smile.gif"));
		
		JFrame frame = new JFrame();
		JPanel myPanel = new JPanel();
		Canvas myCanvas = new Canvas();
//		myCanvas.setIgnoreRepaint(true); // Doesn't seem to work at all
		
		BorderLayout myBLayout = new BorderLayout();
		myPanel.setLayout(myBLayout);
		myPanel.add(myCanvas, BorderLayout.CENTER);
		myPanel.add(new JButton("Clicky"), BorderLayout.EAST);
		
		frame.setContentPane(myPanel);
		frame.setSize(500, 500);
		frame.setVisible(true); // start AWT painting.
		
		Thread gameThread = new Thread(new GameLoop(myCanvas));
		gameThread.setPriority(Thread.MIN_PRIORITY);
		gameThread.start(); // start Game processing.
	}
	
	public static void main(String[] args) {
		MyGame myGame = new MyGame();
	}
 
	private static class GameLoop implements Runnable {
 
		boolean isRunning;
		Canvas myCanvas;
		long cycleTime;
		int moveX = 0;
		int moveY = 0;
		
		ImageLoader myImageLoader = new ImageLoader(null);
		BufferedImage myImage = myImageLoader.loadImage("resources/smile.gif", Transparency.TRANSLUCENT);
 
		public GameLoop(Canvas canvas) {
			myCanvas = canvas;
			isRunning = true;
		}
 
		public void run() {
			cycleTime = System.currentTimeMillis();
			myCanvas.createBufferStrategy(2);
			BufferStrategy strategy = myCanvas.getBufferStrategy();
 
			// Game Loop
			while (isRunning) {
 
				updateGameState();
 
				updatemyCanvas(strategy);
 
				synchFramerate();
			}
		}
 
		private void updateGameState() {
			// Nothing to do right now
		}
 
		private void updatemyCanvas(BufferStrategy strategy) {
			Graphics g = strategy.getDrawGraphics();
 
			g.setColor(Color.WHITE);
			g.fillRect(0, 0, myCanvas.getWidth(), myCanvas.getHeight());
			g.setColor(Color.BLACK);
 
			moveX = (moveX + 8)%myCanvas.getWidth();
			moveY = (moveY + 8)%myCanvas.getHeight();
			for(int i = 0; i < 5; ++i) {
				g.drawImage(myImage, (int) (i*5 + moveX), (int) (i*5 + moveY), null);
			}
			g.dispose();
			strategy.show();
		}
 
		private void synchFramerate() {
			cycleTime = cycleTime + FRAME_DELAY;
			long difference = cycleTime - System.currentTimeMillis();
			try {
				Thread.sleep(Math.max(0, difference));
			}
			catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
 
	}
}
package sandbox;

import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.util.Hashtable;

import javax.imageio.ImageIO;

public class ImageLoader {
	final GraphicsConfiguration gc;
	private Hashtable<String, BufferedImage> chache = new Hashtable<String, BufferedImage>();

	public ImageLoader(GraphicsConfiguration gc) {
		if (gc == null) {
			gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
					.getDefaultScreenDevice().getDefaultConfiguration();
		}
		this.gc = gc;
	}

	public BufferedImage loadImage(String resource, int transparencyType) {
		try {
			if (chache.get(resource) != null) {
				return chache.get(resource);
			} else {
				URL imageURL = this.getClass().getClassLoader().getResource(resource);
				BufferedImage src = ImageIO.read(imageURL);
				// Images returned from ImageIO are NOT managedImages
				// Therefore, we copy it into a ManagedImage
				BufferedImage dst = gc.createCompatibleImage(src.getWidth(),
						src.getHeight(), transparencyType);
				Graphics2D g2d = dst.createGraphics();
				g2d.setComposite(AlphaComposite.Src);
				g2d.drawImage(src, 0, 0, null);
				g2d.dispose();
				chache.put(resource, dst);
				return dst;
			}
		} catch (java.io.IOException e) {
			System.err.println("Cannot find resource " + resource);
			return null;
		}
	}
}

Swing isn’t that terrible. The only thing plaguing it is its single-threaded design, but with good coding, it shouldn’t be a problem.

I still need a fix to the flickering resize…

Should I drop Canvas completely? And what for?

LWJGL (and OpenGL in general) is single-threaded, but it’s still a lot better than Java2D, at least performance-wise.

Threads are overrated. It also has nothing to do with gui performance really. I have written snappy swing apps. There are slow C++ gui apps. But consistent performance with animation/games is not what java2d is really designed for. Regardless of what the engineers planed or claim. At the very least there would be somewhat reliable full screen modes and vsync if it was.

You say that as if it is a bad thing. A virtue of Java2D is that IF something cannot be hardware accelerated by the video hardware/drivers, it is no problem as Java2D will indeed use the software rendering techniques. It will be slower but at least you’ll see -something-. This is not a problem of Java2D, it is a defining feature. Luckily nowadays you don’t have to worry too much about that, video hardware and drivers are very mature so most of what you can do using Java2D (with the exception of some specific effects) can be hardware accelerated, even by Intel hardware.

Again not true, unless you do things which are slow in any software rendering environment. Translucent transparency is a good example. When you do it properly it is actually quite fast. Of course there is some overhead because the tech is aimed at drawing flat user interfaces, not dynamic games. Most algorithms built into the Graphics object are optimized for accuracy, not performance. The Sun way of working around that was not to optimize what was already there, but to add new ways of doing it. For example there are multiple ways of performing scaling, ranging from dead slow to wicked fast.

Most of this is not a problem though, as in game development (using Java2D) your main weapon of choice is drawImage() anyway. The main thing to focus on is that images are accelerated, meaning they can stay in videomemory. Which is the case if it is a compatible image and you keep your fingers off of the underlying raster.

All true if you would recommend using the software rendering pipeline, but it has absolutely nothing to do with “getting hardware acceleration”. These things mentioned here will not cause slowdowns IF you use the hardware rendering pipeline, as they are exactly the things that can be hardware accelerated (except for bit translucency; you should use translucent images in a hardware accelerated environment).

Try it on more than your home machine. I am not making this up. Just check the threads here for example. Non bit alpha falls back to software rendering on everything i have tested as does AA and any image scaling that is not Nearest neighbor. The -Dsun.java2d.opengl=true|True results in very buggy output on my test platforms now as it did 6 years ago. Right now on linux you don’t get any rendering at all! Rotation very quickly trips the very conservative HW acceleration in java2d easily as well, again i have seen this happen. Just asserting it didn’t is not fixing the problem. Finally the java2d SW pipe line is slow. look at the source, is a large number of classes and calls that really make it hard work for the JIT and the GC, so you don’t get any MMX/SSE acceleration.

As is Win32, if I’m not mistaken. My understanding is that virtually all (widely used) GUI toolkits are single-threaded.

Neither am I, it performs very well on all machines I have used so far (gaming oriented machines) :confused:

What we can probably both agree on is that a major downside of Java2D is that because of all the plumbing built into it since its inception, you cannot depend on it performing the same on all machines. I believe the JDK6U10 changes made it a whole lot worse. I wonder if Prism of JavaFX2 can be a better alternative…

I also have a question about image acceleration and overall java2d performance.

I draw graphics by active rendering. It’s nearly same everywhere - create JFrame with JPanel, set display mode, create buffer strategy and draw graphics. It’s the way how the fullscreen application is made. When creating in window there is no need to change display mode and work with buffer strategy. When everything is done with screen display I normally create BufferedImages via GraphicsConfiguration.createCompatibleImage() to display.

Now is the thing that’s really strange for me. I run application in window mode with frame rate counter. I do the same with fullscreen display mode and there is no change in frame rate. I always think that fullscreen mode has a really big advantage in processing images. I also add, that same thing is true for two other pc’s around me. In each case frame rate still nearly the same.

So my question is, is it something wrong with code and me or is that the way that java handles graphics? If needed I can also paste some application’s code with the way how the things are done. I highly appreciate any suggestions about this case.

When going full screen, Java2D automatically v-syncs so you can never go more than 60FPS (or whatever Hz your screen displays at).

Also, how do you create a BufferStrategy? JPanel doesn’t have an option to do so. Only Canvas and Window can. It is best to use a JFrame + Canvas and create the BufferStrategy on the Canvas.

Thanks for your quick reply.

I create a BufferStrategy from JFrame which object is my main window. Before that, I create JPanel where events and drawings are made and put it into frame. I tried to change JPanel into Canvas and create BufferStrategy from there, but drawing time didn’t change and all events on JPanel stop working. I measure drawing time by using System.nanoTime() before and after main drawing function.

Why are you worried about drawing time? As long as your screen is rendering at a reasonable framerate, you shouldn’t be worrying.

Also, you shouldn’t be using JPanel at all for events, add the listeners directly to the Canvas.

All keyboard event are binded to JFrame instance and all mouse event to JPanel.

It’s seems really odd that there is no difference in drawing time between window mode and fullscreen mode at all.

Why would you have listeners on different components?! :o
Put all listeners on the Canvas, nothing on the JFrame itself.

And why should there be an change? Your CPU/GPU doesn’t get “faster” render things when in full screen.

Key listener is added to JFrame because there were problems with focus on JPanel, but for now it doesn’t matter.

Now I even removed BufferStrategy at all and nothing changed. I read somewhere that all Swing components are default double buffered so it makes sense. In fullscreen mode there is access to many exclusive graphics card functions, so I thought that something is going to help to render faster.