Seemingly JPanel buffering whoas...

I have a JPanel which is being drawn on every frame by my "engine". I admit that I am pushing quite a few pixels to the screen (800*600)+ each frame, but even when I am not pushing anything the double buffering done by the JPanel seems to increadibly hinder my framerates. I can try to describe what I'm doing...

A " Game" class handles all the pixel creations and sends each Sprite or whatever object to be drawn to the "Engine" (another class)

The engine keeps a queue of all the pixels to be drawn and when the frame has been complted by the "Game" class, drains the queue and draws onto the JPanel. The JPanel is then triggered from a timer to check if the engine has finished drawing and repaints appropriately.

The mysterious thing to me is, the transistion from my "Game" class to the "Engine" and the drawing of the engine is almost instant while the double JPanel takes forever to reguarly repaint itself....

[957 byte] By [Seekely] at [2007-9-27 14:52:02]
# 1
Maybe I should rephrase that long paragraph....How many pixels should I be able to push to JPanel per frame and still maintain around 30FPS?
Seekely at 2007-7-5 22:52:05 > top of Java-index,Other Topics,Java Game Development...
# 2

> Maybe I should rephrase that long paragraph....

>

> How many pixels should I be able to push to JPanel per

> frame and still maintain around 30FPS?

on what speed computer?

with what gfx card?

with how much overlaying? (are you realy only rendering 800x600 pixels)

running on what VM?

raw figures on performance are completely dependant on your setup

and more importantly, your rendering code itself!

and your rendering architecture sounds.... how can i put it.... wrong!

u might want to post some code demonstrating exactly what happens to render a frame.

rob,

Abuse at 2007-7-5 22:52:05 > top of Java-index,Other Topics,Java Game Development...
# 3

I know I'm probably rendering slowly but I can' imagine so slowly that I am getting results like I am. Even when I don't render any pixels the frame rate does not change at all.

I am pushing just over 800*600 (probably like another 0-10,000 depending on the frame) with a 750 athlon and a voodoo5 (<- although does that really matter that much?) on 1.3.1

Thanks for any help

Ryan

Seekely at 2007-7-5 22:52:05 > top of Java-index,Other Topics,Java Game Development...
# 4
30fps on an 800x600 region should be easily achievable - as long as the logic behind your rendering is correct.
Abuse at 2007-7-5 22:52:05 > top of Java-index,Other Topics,Java Game Development...
# 5

hm...alright thanks for all your input so far...let me try and lay out the outline of the way I am doing things, its simplified to an extreme but should show what I'm doing....

public class Game{

...

public Game(Engine engine)

{

this.engine = engine;

}....

public void doFrame()

{

....

engine.addToQueue(SpriteToBeDrawn);

}

....

}//end class Game

public class GameWindow{

...

public GameWindow()

{

timer = new Timer(delay);

timer.start();

engine = new Engine();

....

}

...

public void actionPerformed()

{

if (game.isFrameFinised() )

{

repaint();

game.doFrame();

}

}

public void paintComponent(Graphics g)

{

...

engine.setPanelToDrawOn(g);

engine.drainQueue();

...

}

...

}//end class GameWindow

public class Engine()

....

public void drainQueue()

{

....

drawSprite()

....

}

public void drawSprite()

{

//creates an Image out of

a Color[][] and places it on the

screen

}

}///end class Engine

Remember, even if i choose to draw sprites or not, the frame rate is still slow (10-20FPS)

Thanks for the help, and sorry if this is just disgusting code :-)

Seekely at 2007-7-5 22:52:05 > top of Java-index,Other Topics,Java Game Development...
# 6

hmm, 3 things

1) don't use Timer, its not particularly efficient.

2) repaint() adds a repaint event to the eventqueue, however, if there is already a repaint event on the eventqueue, the paint events will be coalesced (merged) into a single paint event. Therefor, you'll never get a framerate higher than the speed at which the eventqueue is emptied.

To prevent coalescing of events, override

protected AWTEvent coalesceEvents(AWTEvent existingEvent,

AWTEvent newEvent)

(its a method inherited from Component)

make the method return null (indicates no coalesce should occur)

An alternative to this, is to use the Swing JComponent method paintImmediately(....) which makes the repaint event skip the event queue. (but you have to be using something that extends JComponent, as the draw Area, obviously :)

3) in your drawSprite() method, your comment says your code 'creates an Image out of a Color[][] and places it on the screen'

what do you mean by this?

are you creating an image at runtime, using each Color object as a pixel color?

this will be *INCREDIBLY* slow if you are!!!

rob,

Abuse at 2007-7-5 22:52:05 > top of Java-index,Other Topics,Java Game Development...
# 7

Thanks quite a bit for your help so far (I won't be able to test this till i get home)...

And actually the Image is only being created when an Object is being created or modified, so no it should not be creating the image every frame, I just put that in for references but thanks for the eye...hopefully what you recomended works and seems to make sense as my code runs rather surprisngly quickly but seems to hiccup whenever the repaint comes...

Seekely at 2007-7-5 22:52:05 > top of Java-index,Other Topics,Java Game Development...
# 8

Ok...couple of questions if you don't mind (sorry if I am bugging you)

I've learned that timers are very suave but its pretty much too late for me to change that as I am far in the project and already use Timers on multiple occasions :-(

Since the paintImmediately requires a Graphics object to be sent to it, do i place the line in the paintComponent() method?

public void paintComponent(Graphics g) {

...do engine stuff...

paintImmediately(g);

}?

Seekely at 2007-7-5 22:52:05 > top of Java-index,Other Topics,Java Game Development...
# 9
you cant draw a terabyte of pixels in one hour on screen dolt
kaze0 at 2007-7-5 22:52:05 > top of Java-index,Other Topics,Java Game Development...
# 10
no need for insults..just learning
Seekely at 2007-7-5 22:52:05 > top of Java-index,Other Topics,Java Game Development...
# 11
no insults sorry hambone
kaze0 at 2007-7-5 22:52:05 > top of Java-index,Other Topics,Java Game Development...
# 12

according to my copy of the api, paintImmediatley just takes an x,y,width,height !!

which paintImmediatley are you looking at?

I'm talking about

JComponent.paintImmediately(int x, int y, int w, int h)

simply replacing your call to

repaint()

with

paintImmediately(0,0,getWidth(),getHeight());

rob,

> Ok...couple of questions if you don't mind (sorry if I

> am bugging you)

>

>

> I've learned that timers are very suave but its pretty

> much too late for me to change that as I am far in the

> project and already use Timers on multiple occasions

> :-(

>

> Since the paintImmediately requires a Graphics object

> to be sent to it, do i place the line in the

> paintComponent() method?

> public void paintComponent(Graphics g) {

>...do engine stuff...

>paintImmediately(g);

> }?

>

>

Abuse at 2007-7-5 22:52:05 > top of Java-index,Other Topics,Java Game Development...
# 13
ah the paintimmediately helped immensly...now i can tweak my code to actually see speed enhancements THANKS SO MUCH!
Seekely at 2007-7-5 22:52:05 > top of Java-index,Other Topics,Java Game Development...
# 14
what sort of fps ru getting now?If you are after absolute performance - change your backbuffer from a BufferedImage into a VolatileImage. (I assume your doing the backbuffering yourself, not using the automatic backbuffering provided by the JPanel?)rob,
Abuse at 2007-7-5 22:52:05 > top of Java-index,Other Topics,Java Game Development...
# 15

also, if u want killer fullscreen performance, check this out

http://java.sun.com/docs/books/tutorial/extra/fullscreen/

it looks very promising ;]

now all Sun have to do, is give us the ability to poll the key states directly, and we can do away with the Event dispatcher altogether! (which would be fantastically cool from a games perspective :)

rob,

Abusea at 2007-7-18 11:40:54 > top of Java-index,Other Topics,Java Game Development...
# 16

Thank you so much so far for all your suggestions...

Actually I was letting the Jpanel do its own buffering, but have been looking into just taking it over myself for a while (just been to caught up with the rest of the game to go through with it). I'm guessing that well give quite a performance inscrease esp with the suggestion you gave. I am a little behind on all the coolness Java is capable of putting out as I am still quite the newbie, a semester of Java in college and my own learnings are all I have right now. But i'm trying :-)

Seekelya at 2007-7-18 11:40:54 > top of Java-index,Other Topics,Java Game Development...
# 17

And while the fps have improved they are still below 30, I am probably just going to have to rewrite most if not the whole engine, but thats ok its a learning experience and my first attempt worked to the extent to let me program the majority of the project.

I am a little confused on how to consistantly call a frame repaint if i don't use Timer as I saw in another thread you said multi-threading was also not very nice...any suggestions?

Seekelya at 2007-7-18 11:40:54 > top of Java-index,Other Topics,Java Game Development...
# 18

There are 2 methods

1) Use 2 Threads, and wait/notify. This solution is very elegant, but from my experience is not as fast as method 2.(atleast not in J2ME on mobile phones, i havn't tried it with a desktop PC)

The basic loop <below> that you see in most games, is very inefficient, because, while the loop is sleeping, it is doing nothing. Also, it makes no attempt to take into consideration how long the executeCycle() logic actually takes to execute.

while(running)

{

executeCycle();

sleep(period);

}

My multiThreaded version takes into account how long it takes to do the paint() code (though it doesn't take into consideration how long it takes to execute the game logic code - something im working on atm :)

2) Continually poll System.currentTimeMillis(), not so ellegant, and the minimum timer resolution of currentTimeMillis() can be a problem on some platforms (most notably win9X)

here is some code demonstrating both approaches

a brief explanation of the 2 might help...

This is pretty complex code to follow, so dont worry if u dont get exactly what is happening straight away ;]

1) a) creates 2 Threads, and starts them both up

b) 1st Thread that gets into the synchronized block will become the gameloop updater

c) 2nd Thread will become the painter Thread

d) using wait(), wait(period) and notify, the 2 Threads alternate with 1 another

e) when the game is stopped, both threads are woken, and they both exit the run method gracefully

2) a) creates 1 Thread

b) this 1 Thread performs loops continually checking System.currentTimeMillis() until 'period'ms have passed. At this point it does a gameloop update.

c) it then keeps doing gameloops until the 'game_clock' is upto date (this loop ensures the game speed is constant, and only the fps changes)

d) once the 'game_clock' is uptodate, a frame is rendered to the screen.

NOTE: neither of these classes actually performs any game logic, or render logic, they simply manage the scheduling of game updates, and screen paints. It is upto the subclass to perform the game logic, and the passed in GameCanvas to do the painting.

** Method 1 **

** Multi-Threaded rendering **

public abstract class UAL implements Runnable

{

boolean pause = false;

boolean running = false;

boolean painting = false;

Thread thread1;

Thread thread2;

protected GameCanvas target;

final int period;

public UAL(GameCanvas target, int period)

{

this.target = target;

this.period = period;

}

public void setRunning(boolean running)

{

if((this.running!=running) && (this.running=running))

{

thread1 = new Thread(this);

thread2 = new Thread(this);

thread1.start();

thread2.start();

}

}

public void toggleRunning()

{

setRunning(!running);

}

public boolean getRunning()

{

return running;

}

public void setPause(boolean pause)

{

if((this.pause!=pause) && (this.pause=pause))

{

synchronized(this)

{

notifyAll();

}

}

}

public void togglePause()

{

setPause(!pause);

}

public void run()

{

try

{

while(running)

{

synchronized(this)

{

if(pause)

{

wait();

}

else if(painting)

{

target.render();

painting = false;

wait();

}

else

{

executeCycle();

painting = true;

notify();

wait(period);

}

}

}

synchronized(this)

{

notifyAll();

}

}

catch(Exception e)

{

}

dispose();

}

/* this method will be called upon UAL termination

classes extending this should do clean here - common use

is for moving from the game canvas to the highscore page..*/

public abstract void dispose();

/* in sub-classes this method should contain environment update code*/

public abstract void executeCycle();

}

** Method 2 **

** POLLING OF System.currentTimeMillis() **

// this class loops continually either rendering a frame, or updating the environment

// this class should be subclassed and environment update code

// placed into the executeCycle() method (and dispose code put in dispose())

public abstract class UAL implements Runnable

{

// the object to render

private GameCanvas c = null;

//status of the UAL

private boolean running = false;

private Thread me = null;

//stuff taken from clock

private long environmentTime;

private long lastTime;

// number of milliseconds the environment

// waits before requiring another update

private long period;

private boolean enabled = true;

public UAL(GameCanvas r, long i)

{

c = r;

this.period = i;

}

//sets running state, if changing from stopped to running, this method will start everything 4u

public void setRunning(boolean running)

{

if(c!=null && this.running!=running)

{

if(this.running=running)

{

me = new Thread(this);

reset();

me.start();

}

}

}

public boolean getRunning()

{

return running;

}

public boolean toggleRunning()

{

if(c!=null && (running=!running))//WARNING, assignment in 'if'

{

me = new Thread(this);

reset();

me.start();

}

return running;

}

/* pauses/unpauses the environment updates

* may not pause instantly - if the environment

* is lagging behind, it will catch up first

* this method will only do something if the game is running

*/

public void setPause(boolean newEnabled)

{

if(running)

{

if(enabled==newEnabled && (enabled = !newEnabled)) reset();

else me.setPriority(Thread.MIN_PRIORITY);

}

}

/*

* as for setPause(boolean), this method only pauses the clock

* the game will continue to run until it has caught up withthe clock

* if the UAL isn't running, it will return the current pause state unchanged

*/

public boolean togglePause()

{

if(running && (enabled=!enabled)) reset();

return !enabled;

}

/* method called by start, can only be called by

* the thread owned by this UAL

* if called directly, nothing will happen

*/

public void run()

{

if(Thread.currentThread() == me)

{

while(running)

{

if(nextCycle())

{

do

{

executeCycle();

}

while(nextCycle());

c.render();

Thread.yield();

}

}

me = null;

dispose();

}

}

/* this method will be called upon UAL termination

classes extending this should do clean here - common use

is for moving from the game canvas to the highscore page..*/

public abstract void dispose();

/* in sub-classes this method should contain environment update code*/

public abstract void executeCycle();

public boolean nextCycle()

{

if(enabled) lastTime = System.currentTimeMillis();

if(lastTime - environmentTime > period)

{

environmentTime = lastTime;

return true;

}

return false;

}

}

WOW!

thats the longest post ive ever made :]

rob,

Abusea at 2007-7-18 11:40:54 > top of Java-index,Other Topics,Java Game Development...
# 19

oops, the 2nd class

'POLLING OF System.currentTimeMillis()' should have a method

private void reset()

{

lastTime = environmentTime = System.currentTimeMillis();

me.setPriority(Thread.MAX_PRIORITY);

}

I accidentally removed it when cutting out some remmed out code ;]

rob,

Abusea at 2007-7-18 11:40:54 > top of Java-index,Other Topics,Java Game Development...
# 20

and once again it is very much appreciated :-) I'll also once again have to wait till I get home until I get to figure/try any of this stuff out...but from the intial looks all I can say is the code looks far more sophisctiacted that what I am doing right now so that can only be a good thing :-)

~Ryan

Seekelya at 2007-7-18 11:40:54 > top of Java-index,Other Topics,Java Game Development...
# 21

I wouldn't worry - its not particularly complicated once you get your head around exactly what you want to achieve.

my UAL has been evolving for the last 4 years or so!

(btw UAL stands for 'Universal Animation Loop', you can thank Fred Labrosse for the name [1 of my uni lecturers] :)

The initial structure was from a VB game i wrote back in highschool!

rob,

Abusea at 2007-7-18 11:40:54 > top of Java-index,Other Topics,Java Game Development...
# 22

I really appreciate all your help, and more than likely I won't be bothering you again :-) I have started to implement all the advice you ahve given me so far and I will be a better programmer for it. I am surprised to come by a person on forums that has been so nice to a relative newbie (as I have alos seen you help others on the board) and its a welcome change to the constant flaming of egotistical veterans.

Anyways, when i learned CS at Georgia Tech they told us to steer away from 1.4.0 because apparently it was the devil for some reason....so all this time I have been running on 1.3.1...well in order to implement some suggestions you gave I had to do the upgrade to 1.4.0 and low and behold without changing any of my code my framerates jumped from not even being close to 30 to well,well over 50. Spectacular. I do still plan however on implementing all your suggestions as I am sure it will be better coding practice and a good learning experience for future needs. Thanks so much again.

~Ryan

Seekelya at 2007-7-18 11:40:54 > top of Java-index,Other Topics,Java Game Development...
# 23

> I really appreciate all your help, and more than

> likely I won't be bothering you again :-) I have

> started to implement all the advice you ahve given me

> so far and I will be a better programmer for it. I am

> surprised to come by a person on forums that has been

> so nice to a relative newbie (as I have alos seen you

> help others on the board) and its a welcome change to

> the constant flaming of egotistical veterans.

i'm no veteran ;] but thanx for the compliment nehow.

and your not a regular newbie, you actually sound like you know what your doing, and just need some guidance onto the right track :)

>

> Anyways, when i learned CS at Georgia Tech they told

> us to steer away from 1.4.0 because apparently it was

> the devil for some reason....so all this time I have

> been running on 1.3.1...well in order to implement

> some suggestions you gave I had to do the upgrade to

> 1.4.0 and low and behold without changing any of my

> code my framerates jumped from not even being close to

> 30 to well,well over 50.

wow!

I think you've given me the motivation to go back and finish a game I started years ago - and maybe i'll get my 3d engine working again !!

> Spectacular. I do still

> plan however on implementing all your suggestions as

> I am sure it will be better coding practice and a good

> learning experience for future needs. Thanks so much

> again.

> ~Ryan

gl,

rob,

Abusea at 2007-7-18 11:40:54 > top of Java-index,Other Topics,Java Game Development...