Increasing performance of my graphics routines
Hi there,
I'm currently working on a smallish tile-based game with my friend, just a little summer project. We're both pretty square in the middle of 'intermediate' level coders, I would guess.
Our game has some fairly meaty algorithms happening, and so we're very cpu-cycle concious. However, as it happens, our algorithms aren't really wasting too much time (yay!), the current greatest offender is...
http://www.divinorum.co.nz/misc/cputimehotspots.png
... drawing graphics!
It's not maxing out a CPU yet, and it's not something that has the chance to balloon as the program grows - as it will only ever be rendering the same number of tiles. It also is responsive enough in-game, so we're not worried, per se, but we'd like to get the time spent there down so we have more room for everything else.
We're clearly not entirely solid on how best to do things in Java, so some help would be greatly appreciated.
The way it's currently implemented, is:
- Everything is eventually drawn to an object which subclasses JPanel, its paintComponent(Graphics g) is overridden, and it also implements Runnable. The run method has a loop which ensures that the object is repaint()'ed regularly.
Code :
....
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
Dimension size = getSize();
// If we're in a game, draw the game
if(worlds[currentWorld].currentGame != null){
worlds[currentWorld].currentGame.draw(g2d, size);
}
}
....
- As you can see, we're not drawing straight to the screen. A Graphics2D object is created, and then passed down into the bowels of the game object hierarchy. The Graphics2D object is passed down through many objects' draw() functions until it gets all the way down to a Sprite. Here, the actual g.drawImage(...) is called. This is the function that takes all the time:
Code :
public void draw(Graphics2D g, int x, int y){
// Convert x and y from number of tiles to pixels
x *= width;
y *= height;
g.drawImage(image, x, y, null);
}
Where image is an Image member variable of whichever Sprite is being drawn, and width and height are some power of 2 indicating the size of tiles.
This may seem like some kind of weird hodgepodge of terrible, if so - it is because I have read many tutorials on the subject of drawing in java, all of which are no newer than 2005 and all of which seem to do things a different way. Also, I am just now revisiting the graphics methods after several months where I have been busy, so things have become stale in my mind.
If anyone has any tips or would like to point out some glaring flaws, feel free to do so - it would be greatly appreciated.
Re: Increasing performance of my graphics routines
I believe the Graphics2D class uses the CPU for drawing rather than having GPU acceleration by default. There's a setting you must pass to the JDK in order to enable OpenGL for hardware acceleration. See this website for all the details:
2D graphics - new features
edit: Err, I meant JRE, not the JDK.
Re: Increasing performance of my graphics routines
A few suggestions:
a) Enable hardware acceleration as helloworld described
b) use animated GIF's if you have short animations, rather than creating an animation thread
c) Only draw things that need to be updated. You can do this several ways, one way would be to keep several flags to indicate whether drawing is necessary and evaluate those flags in the drawing hierarchy, another of which would be to use several overlayed JPanels (setOpaque to false) rather than a single JPanel to do the drawing, with each JPanel drawing things that update at different times. This way you can optimize by only drawing things that need updating.
Re: Increasing performance of my graphics routines
Have a look through your paint method and all the methods it calls to see if there are any calls to "new" which you could do without.
// Json
Re: Increasing performance of my graphics routines
Hi guys, thanks for the replies!
I had a look at that 2DGraphics - New Features links, and tried simply using
-Dsun.java2d.opengl=True
to enable OpenGL on the VM, but that just caused each frame to take 5+ seconds to render.
So I went down the miserable rabbit hole of trying to use VolatileImages again, and just like when I did this 3 or 4 months ago, I've spent 5 hours reading tutorials and pasting code and nothing I've done has had anything but a negative effect.
Cokencode has a nice tutorial from 2005, which is similar to how I've done things, so doesn't help me. There's a page on the gpwiki which looked useful, but I couldn't decipher how to actually have an accelerated image in a sprite class, and draw that to an accelerated buffer, and then draw that to the screen.
The stuff made by the nice people at Sun is all very academic and thorough, but a little abstract - I learn (slowly, it seems) by doing!
copeg: They are all good suggestions, thank you. Yes, any small/static animations are already in that format, and I'm saving your suggestion c) for when I've got a proper accelerated pipeline working.
Json: I've been very careful with my memory allocation, there are no calls to new inside the draw() functions at all. Each image is only loaded once, and a reference to this shared amongst tiles with the same image, etc.etc.
If anyone knows of a nice, straight-forward guide on how to get this working, I'd very much appreciate it!
Re: Increasing performance of my graphics routines
You have to realise though that the time spent on graphics will always be high, well the higher the better really, because that means you can squeeze out more FPS and your game updates take less time.
How many FPS do you get at the moment?
// Json
Re: Increasing performance of my graphics routines
Currently the CPU isn't saturated, so I'm getting a solid 60 FPS. However, the issue is that any graphics done by the CPU is now vying for the same resources as the rest of the game, and even though the graphics requirements are not going to grow (tiles is tiles is tiles, and I'm rendering as many as I'll ever render), the game logic requirements will grow.
So yes, I do intend on getting this working. I'm sure the high amount of time the game is spending in that draw method is just sending the tile data across the bus so many times, and I know that getting it properly working all on the graphics card memory will result in an increase... it just hasn't yet! Very frustrated, but not defeated!