2D Game - Problems with Images (Memory & CPU)

Hello guys,

we are programming our first 2D Java game and are very inecperienced in the topic performance tuning. We are not sure why the performance is so bad (probably there are too many reasons), but it’s mainly about the Image Drawing. Before we used Images (png and jpg for the background) the performance was great and the game has run fast. About the game: It’s a 2d space shooter like space invaders.
Because we are quite sure that the images cause the problems the following codes are limited to the drawing methods…

Loading of the Images (example class Shot):

public class Shot extends Elements {
    private BufferedImage[] shot = new BufferedImage[3];
    String shotName;
    File shotSound;
    
    public Shot(...) {

        File shotAnimation[] = new File[3];
        
        switch (mode) {
            case 0: shotName = "defaultshot"; width = 5; height = 5; break;
            ...
        }
        
        for(int i = 0; i < shotAnimation.length; i++) {
            shotAnimation[i] = new File (shotName + i + ".png");
        }
        
        try {
            for(int i = 0; i < shot.length; i++) {
                shot[i] = ImageIO.read(shotAnimation[i]);

            }
        }        
        catch (IOException e) {}
    }
    
// ...
    
    public void paintComponent(Graphics g) {
    if(getAlive() == true && counter < 3) {
            g.drawRect(getXc(), getYc(), width, height); // for collision, nothing to do with the images / problems
            g.drawImage(shot[counter], getXc(), getYc(), null);
            delay++;
            if(delay %15 == 0)
                counter++;
            if(counter == 3)
                counter = 0;
    	}    	
    }

We thought about loading all Images in another class and use them in the classes (shot, spaceship and so on…). Maybe this would be a performance push? We are not quite sure.

The class, whrere the paintComponent() is used (Level):


public void paintComponent(Graphics g) {
    background.paintComponent(g);
        spaceship.paintComponent(g);
        boss.paintComponent(g);
        
        for(int i = 0; i < shots.size(); i++) {
            if(shots.get(i).getAlive() == true)
                shots.get(i).paintComponent(g);
        }

//...
    }

We are using Arraylists. Maybe this is slowing down the Performance? But we thought its better to use dynamic arraylists than the static Arrays.

The main Problem could be the Main class:

ActionListener Update = new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                repaint();
            }
        };
        new javax.swing.Timer(10, Update).start();

We have to repaint the whole panel after a few milliseconds, because otherwise the frame or the panel doesnt update and we aren’t able to see the differences (moving ship, enemies, generates shots and so on). Everything is still and only updates, if the frame is minimized and again on the desktop.

Maybe there are another solutions for updating the frame / panel or loading the images (faster ones of course ;D ). So it would be great, if anyone out there could help us, but thanks for taking the time reading all this anyway. Of course we would be grateful for hints on the net, but the whole possibilities in using 2D and Images some kind of overwhelmed us, that’s why we decided to post something here.

a) Use a profiler (-Xprof will be good enough usually)
b) javax.swing.Timer is a rather bad timing method (well, for games that is)

How big (dimensions - not file size) are the images and are you using translucent PNGs?

I tried it with -Xprof, but it doesn’t work properly. I get a few infos, but I couldn’t really start the game witht the start button ( like I usually do). I only get a empty window. Then I tried -Xrunhprof but there I get a huge .txt File.

Xprof gives me the following output:

[quote]Flat profile of 0.21 secs (18 total ticks): main

Interpreted + native Method
13.3% 0 + 2 sun.awt.Win32GraphicsEnvironment.initDisplay
13.3% 0 + 2 java.util.zip.Inflater.inflateBytes
6.7% 0 + 1 java.io.FileInputStream.open
6.7% 0 + 1 java.io.WinNTFileSystem.canonicalizeWithPrefix0
6.7% 0 + 1 java.io.FileInputStream.readBytes
6.7% 0 + 1 sun.awt.windows.WDesktopProperties.init
6.7% 0 + 1 sun.java2d.loops.GraphicsPrimitiveMgr.registerNativeLoops
6.7% 1 + 0 java.awt.image.DataBufferByte.
6.7% 1 + 0 java.awt.image.ColorModel.getLinearRGB16TosRGB8LUT
6.7% 1 + 0 java.util.zip.InflaterInputStream.read
6.7% 1 + 0 java.lang.String.toLowerCase
86.7% 4 + 9 Total interpreted

 Compiled + native   Method                        

6.7% 1 + 0 com.sun.imageio.plugins.png.PNGImageReader.decodePaethFilter
6.7% 1 + 0 Total compiled

     Stub + native   Method                        

6.7% 0 + 1 java.lang.StrictMath.pow
6.7% 0 + 1 Total stub

Thread-local ticks:
16.7% 3 Blocked (of total)

Flat profile of 6.45 secs (4 total ticks): Image Fetcher 0

Interpreted + native Method
100.0% 1 + 0 sun.awt.image.PNGImageDecoder.produceImage
100.0% 1 + 0 Total interpreted

Thread-local ticks:
75.0% 3 Blocked (of total)
[/quote]
I changed the timer to a java.util.Timer, but this wasn’t that successful. I would say it was better with the javax.swing.Timer…

The dimensions are totally different… a shot is about 5x10 and the background, or the level (scrolling down so it looks like the ship is moving) is really big… about 1200x600. The game is in 800x600. But we used the lowest quality for the background (jpg. photoshop 0) so it has a file size of 40 kb. At first we used a good looking background but it totally decreased the performance. The pngs (shots, enemies…) are made with photoshop. I don’t know if they are made translucent. I will ask tomorrow. The only thing I know is, that they are made without background (no black or white), otherwise we would have a white outline / contour and the enemies, ships (… and so on) wouldn’t look like they are in the level. (I hope it was understandable :D).

I changed the timer to a java.util.Timer, but this wasn’t that successful. I would say it was better with the
javax.swing.Timer…

Either use System.nanoTime (Java 1.5+) or System.currentTimeMillis.

But we used the lowest quality for the background (jpg. photoshop 0) so it has a file size of 40 kb.

File size doesn’t matter. It gets decompressed… so a 1200x600 jpg will take 2160000 bytes.

I just skimmed over the code… you’re using components for everything? Use a Canvas (with BufferStrategy) and draw the images directly into that one.

Also:

for(int i = 0; i < shots.size(); i++)

It’s faster if you do it backwards:

for(int i = shots.size()-1; i >= 0; --i)
if(shots.get(i).getAlive() == true)

“==true” can be omitted. Also at the drawing stage there should be only bullets in that list, which are “alive”. You also check if it’s alive in the drawing method itself… why?

And uhm… each time a Shot is created you load the images? I hope you aren’t doing that over and over… well, I can’t tell from those snippets.

So what images are you using now?

Could you run your application with -Dsun.java2d.trace=log and see what is
printed when your images are rendered?

There was a bug which prevented images loaded with ImageIO from
being cached in vram The workaround was to copy
those images into another image that you create yourself
(say with createCompatibleImage(w,h,Transparency) where
transparency is the same transparency as the one in
the image you loaded).

Which java version are you running this on?
If your images are translucent then they won’t be accelerated
unless you’re using one of the non-default rendering
pipelines (either opengl or d3d). They will be accelerated
if they are opaque or 1-bit transparent.

Thanks,
Dmitri
Java2D team

First of all: Thank you very much! It’s great to get so many responses in 24 hours. I really appreciate that and I’m happy about finding this forum.
I tried today to use the System.nanoTime and System.currenTimeMillis but obviously I’m too stupid to build a timer with it. I searched this forum and google, but I haven’t found a good instruction for it. If I use a Thread for it, nothing would be won, am I right? So can you please tell me how I build a timer with them?
The file size doesn’t matter? I believe you, but maybe you can explain to me why the whole game was lagging and shaking, when we used the high quality backgrounds? Then we decided to use a low quality background and it run a lot more fluid.
Then I tried using BufferStrategy, but I must have done something wrong. At first I used the tutorial I found at: http://gpwiki.org/index.php/Java:Tutorials:Double_Buffering
At first everything looked fine:

http://img155.imageshack.us/img155/6025/beforezk5.jpg

And then it destroyed the status panel I used:

http://img155.imageshack.us/img155/4991/afterxt3.jpg

The score and defeated enemies are blinking when they get updated for a really short moment they are displayed on the screen. Then I tried to change the level Jpanel into a Level Canvas and tried to add it with: frame.getContentPane().add(canvas);

But then I get a grey background. Then I tried to change the paintComponent() method in paint() methods, but then I get nullpointerexception about graphics g. :frowning: It’s crazy… maybe it’s not necessary? Or is it that good for the performance? I have read that it’s not good to use canvas with swing?
The thing with double checking… there is no reason for doing it… only stupidity. Fixed it! :smiley:
At the moment I’m loading the image each time a shot is created. We thought about that ourselves and tried to change it. Do you know a good way to do it? Maybe a ImageLibrary class? In which all images are loaded and then you give in the level paintComponent() method:
public void paintComponent(Graphics G, BufferedImage spaceship)
Is this a good way? I started to build this class, but now I’m too tired and can’t concentrate any more. I will do it like that tomorrow and maybe it runs faster with it.

At the moment we are using BufferedImages. I read that I should use VolatileImages? But then I read, that the BufferedImages are way better in the actual versions?!?
Or do you mean the photoshop output? :smiley: That are mainly pngs, just the background is a jpg. I have run the application with -Dsun.java2d.trace=log and the output was:

[quote]sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntRgb)sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOverNoEa, “Integer RGB DirectDraw”)
sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntRgb)s
un.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOverNoEa, “Integer RGB DirectDraw”)
sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntRgb)s
un.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOverNoEa, “Integer RGB DirectDraw”)
sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntRgb)
sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOverNoEa, “Integer RGB DirectDraw”)
sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntRgb)
sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOverNoEa, “Integer RGB DirectDraw”)
sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntRgb) sun.java2d.loops.DrawGlyphListAA::DrawGlyphListAA(AnyColor, SrcNoEa, IntRgb)
[/quote]
But I don’t unterstand it. :smiley: I will try the createCompatibleImage() tomorrow… if it’s a better way to handle the images. And I will look how I can make them opaque. Then I let the method run with
-Dsun.java2d.opengl=True
On my main computer the cpu usage was lower then, but on my laptop the content of the window didn’t load… :frowning:
Oh… and we are running Java on 1.6.
Again: Thank you very much. It’s really fun to make a game, although we are totally beginners. Maybe sometimes it will run as fast as it should… :smiley:

For this type of game you are probably better of not using Swing but use
“active rendering” (search for it on this forum and google, there’s a few
articles on this, including http://ping.dev.java.net).

Active rendering would typically include using BufferStrategy for
double-buffering, and having your own rendering loop. That way
you could get much more consistent frame rate.

You probably don’t need to use VolatileImages since BufferStrategy
is a better double-buffering mechanism. For the sprites BufferedImages
should be fine.

Judging by the trace output you have a bunch of translucent images.
Make sure that only images which need to be translucent
are translucent, the rest may be opaque or bitmask (1-bit transparent).
You can typically chose when exporting your artwork from
your graphics editor.

As I mentioned, translucent images aren’t hardware accelerated
by the default pipeline. The opengl pipeline works well if your
drivers are good. You can also try the D3D pipeline (in 1.6 set
-Dsun.java2d.d3d=true, in 6u10 which you can get from
jdk6.dev.java.net it is enabled by default).

Thanks,
Dmitri
Java2D Team

You should have a look around my web site, boardspace.net. All the games there use composited, partially
transparent images as their basic design element. The games are drawn on a plain canvas using drawImage.
Multiple java components, Swing, and toolkit based buffering strategies are not invoved.

In general:

the “paint” method should not paint. It should just set a flag saying some painting is required, actual painting should be
done on your clock cycle. This is a good strategy both because “paint” messages are erratic, and you don’t want your
painter running through data structures which are being mutated by responses to network or mouse events.

use 2 or three levels of “full size” bitmaps, starting with a fixed background bitmap, ending with ephemera like bullets in transit.
Draw from back bitmap to intermediate, add moving elements to the intermediate, then draw the intermediate to the completed
bitmap the real window.

According to your first post, every time you create a new Shot object, you read images from files and decom,press them into buffered-images.

Just keep the images around, reuse them. You’ll see a MASSIVE performance boost.

What Riven just said might also explain the improvement in speed when you compressed the background image smaller - less file IO is needed to read smaller files, so it might be faster to load the image, even though the time needed to draw the image on screen does not depend on the compression (since the image is stored uncompressed in the memory). You could for example build some kind of a image cache which reads the images only once, when they are first requested from the cache, and on subsequent requests it returns the same image instance. That way no time is wasted in reading the file from disk and in decompressing the image more than once.

Did you call bufferStrategy.show()? If you don’t do this, your images will sort of draw, but will be choppy and messed up. Make sure to call that.

Just something general:
Using compatible BufferedImages often can improve performance. There are createCompatibleXXX methods around somehwere in the GraphicsConfiguration or so. I dont know what type of Image you get from ImageIO.

And second, no transparency is fastest, bitmask transparency is almost as fast, but full alpha is costly. PNG supports full alpha, and maybe even without having transparent areas, they are treatet that way?

I would try to create a compatible bitmask transparency image for your sprites.

-JAW