Performance of Java 2D drawing operations (part 2: images)
February 08, 2019 [Java, Performance, Programming, Rabbit Escape]Series: operations, images, opacity
In my previous post I examined the performance of various drawing operations in Java 2D rendering. Here I look at some specifics around rendering images, with an eye to finding optimisations I can apply to my game Rabbit Escape.
You can find the code here: gitlab.com/andybalaam/java-2d-performance.
Results
- Drawing images with transparent sections is very slow
- Drawing one large image is slower than drawing many small images covering the same area(!)
- Drawing images outside the screen is slower than not drawing them at all (but faster than drawing them onto a visible area)
Advice
- Avoid transparent images where possible
- Don't bother pre-rendering your background tiles onto a single image
- Don't draw images that are off-screen
Images with transparency
All the images I used were PNG files with a transparency layer, but in most of my experiments there were no transparent pixels. When I used images with transparent pixels the frame rate was much slower, dropping from 78 to 46 FPS. So using images with transparent pixels causes a significant performance hit.
I'd be grateful if someone who knows more about it can recommend how to improve my program to reduce this impact - I suspect there may be tricks I can do around setComposite or setRenderingHint or enabling/encouraging hardware acceleration.
Composite images
I assumed that drawing a single image would be much faster than covering the same area of the screen by drawing lots of small images. In fact, the result was the opposite: drawing lots of small images was much faster than drawing a single image covering the same area.
The code for a single image is:
g2d.drawImage( singleLargeImage, 10, 10, null )
and for the small images it is:
for (y in 0 until 40) { for (x in 0 until 60) { g2d.drawImage( compositeImages[(y*20 + x) % compositeImages.size], 10 + (20 * x), 10 + (20 * y), null ) } }
The single large image was rendered at 74 FPS, whereas covering the same area using repeated copies of 100 images was rendered at 80 FPS. I ran this test several times because I found the result surprising, and it was consistent every time.
I have to assume some caching (possibly via accelerated graphics) of the small images is the explanation.
Drawing images off the side of the screen
Drawing images off the side of the screen was faster than drawing them in a visible area, but slower than not drawing them at all. I tested this by adding 10,000 to the x and y positions of the images being drawn (I also tested subtracting 10,000 with similar results). Not drawing any images ran at 93 FPS, drawing images on-screen at 80 FPS, and drawing them off-screen only 83 FPS, meaning drawing images off the side takes significant time.
Advice: check whether images are on-screen, and avoid drawing them if not.
Numbers
Transparency
Test | FPS |
---|---|
large nothing | 95 |
large images20 largeimages | 78 |
large images20 largeimages transparentimages | 46 |
Composite images
(Lots of small images covering an area, or a single larger image.)
Test | FPS |
---|---|
large nothing | 87 |
large largesingleimage | 74 |
large compositeimage | 80 |
Offscreen images
Test | FPS |
---|---|
large nothing | 93 |
large images20 largeimages | 80 |
large images20 largeimages offscreenimages | 83 |
Feedback please
Please do get back to me with tips about how to improve the performance of my experimental code.
Feel free to log issues, make merge requests or add comments to the blog post.