OK I've done some debug-printing for you to get a feeling of the behaviour. I use this debug-println: public static void println(String str) { long millis = System.currentTimeMillis() - startMillis; String threadName = Thread.currentThread().getName(); System.out.println("T="+millis+"\t"+threadName + ": " + str); }
... which give me quite a lot of information. This is the printout from a typical 5-second run: (WARNING: many lines below!) (ms) (name of thread) (message) T=0 thread applet-terraformer.TerraformerApplet.class: Setting up model. T=0 thread applet-terraformer.TerraformerApplet.class: Setting up graphics fundaments. T=0 thread applet-terraformer.TerraformerApplet.class: Loading resources. T=10 thread applet-terraformer.TerraformerApplet.class: Downloading images. T=120 thread applet-terraformer.TerraformerApplet.class: Creating backbuffer. T=130 thread applet-terraformer.TerraformerApplet.class: Creating headsup display. T=130 thread applet-terraformer.TerraformerApplet.class: Setting up rendering state. T=130 AWT-EventQueue-1: Accepting keyboard input once focused. T=130 AWT-EventQueue-1: Doing first background render. T=130 AWT-EventQueue-1: updateBackbuffer T=130 AWT-EventQueue-1: repaint T=130 AWT-EventQueue-1: paint T=140 AWT-EventQueue-1: headsup.draw T=1132 AWT-EventQueue-1: actionPerformed (by Swing-Timer) T=1132 AWT-EventQueue-1: model.step T=1132 AWT-EventQueue-1: updateBackbuffer T=1132 AWT-EventQueue-1: repaint T=1132 AWT-EventQueue-1: update T=1132 AWT-EventQueue-1: paint T=1132 AWT-EventQueue-1: headsup.draw T=2133 AWT-EventQueue-1: actionPerformed (by Swing-Timer) T=2133 AWT-EventQueue-1: model.step T=2133 AWT-EventQueue-1: updateBackbuffer T=2133 AWT-EventQueue-1: repaint T=2133 AWT-EventQueue-1: update T=2133 AWT-EventQueue-1: paint T=2133 AWT-EventQueue-1: headsup.draw T=3135 AWT-EventQueue-1: actionPerformed (by Swing-Timer) T=3145 AWT-EventQueue-1: model.step T=3145 AWT-EventQueue-1: updateBackbuffer T=3165 AWT-EventQueue-1: repaint T=3165 AWT-EventQueue-1: update T=3165 AWT-EventQueue-1: paint T=3165 AWT-EventQueue-1: headsup.draw T=3205 AWT-EventQueue-1: update T=3205 AWT-EventQueue-1: paint T=3205 AWT-EventQueue-1: headsup.draw T=3245 AWT-EventQueue-1: update T=3245 AWT-EventQueue-1: paint T=3245 AWT-EventQueue-1: headsup.draw T=3285 AWT-EventQueue-1: update T=3285 AWT-EventQueue-1: paint T=3285 AWT-EventQueue-1: headsup.draw T=3325 AWT-EventQueue-1: update T=3325 AWT-EventQueue-1: paint T=3325 AWT-EventQueue-1: headsup.draw T=3365 AWT-EventQueue-1: update T=3365 AWT-EventQueue-1: paint T=3365 AWT-EventQueue-1: headsup.draw T=3405 AWT-EventQueue-1: update T=3405 AWT-EventQueue-1: paint T=3405 AWT-EventQueue-1: headsup.draw T=3445 AWT-EventQueue-1: update T=3445 AWT-EventQueue-1: paint T=3445 AWT-EventQueue-1: headsup.draw T=3495 AWT-EventQueue-1: update T=3495 AWT-EventQueue-1: paint T=3495 AWT-EventQueue-1: headsup.draw T=3535 AWT-EventQueue-1: update T=3535 AWT-EventQueue-1: paint T=3535 AWT-EventQueue-1: headsup.draw T=3575 AWT-EventQueue-1: update T=3575 AWT-EventQueue-1: paint T=3575 AWT-EventQueue-1: headsup.draw T=3615 AWT-EventQueue-1: update T=3615 AWT-EventQueue-1: paint T=3615 AWT-EventQueue-1: headsup.draw T=3655 AWT-EventQueue-1: update T=3655 AWT-EventQueue-1: paint T=3655 AWT-EventQueue-1: headsup.draw T=3705 AWT-EventQueue-1: update T=3705 AWT-EventQueue-1: paint T=3705 AWT-EventQueue-1: headsup.draw T=3745 AWT-EventQueue-1: update T=3745 AWT-EventQueue-1: paint T=3745 AWT-EventQueue-1: headsup.draw T=3786 AWT-EventQueue-1: update T=3786 AWT-EventQueue-1: paint T=3786 AWT-EventQueue-1: headsup.draw T=3826 AWT-EventQueue-1: update T=3826 AWT-EventQueue-1: paint T=3826 AWT-EventQueue-1: headsup.draw T=3866 AWT-EventQueue-1: update T=3866 AWT-EventQueue-1: paint T=3866 AWT-EventQueue-1: headsup.draw T=3906 AWT-EventQueue-1: update T=3906 AWT-EventQueue-1: paint T=3916 AWT-EventQueue-1: headsup.draw T=3956 AWT-EventQueue-1: update T=3956 AWT-EventQueue-1: paint T=3956 AWT-EventQueue-1: headsup.draw T=3996 AWT-EventQueue-1: update T=3996 AWT-EventQueue-1: paint T=3996 AWT-EventQueue-1: headsup.draw T=4036 AWT-EventQueue-1: update T=4036 AWT-EventQueue-1: paint T=4036 AWT-EventQueue-1: headsup.draw T=4076 AWT-EventQueue-1: update T=4076 AWT-EventQueue-1: paint T=4076 AWT-EventQueue-1: headsup.draw T=4126 AWT-EventQueue-1: update T=4126 AWT-EventQueue-1: paint T=4126 AWT-EventQueue-1: headsup.draw T=4136 AWT-EventQueue-1: actionPerformed (by Swing-Timer) T=4136 AWT-EventQueue-1: model.step T=4136 AWT-EventQueue-1: updateBackbuffer T=4136 AWT-EventQueue-1: repaint T=4136 AWT-EventQueue-1: update T=4136 AWT-EventQueue-1: paint T=4136 AWT-EventQueue-1: headsup.draw T=4166 AWT-EventQueue-1: update T=4166 AWT-EventQueue-1: paint T=4166 AWT-EventQueue-1: headsup.draw T=4206 AWT-EventQueue-1: update T=4206 AWT-EventQueue-1: paint T=4206 AWT-EventQueue-1: headsup.draw T=4256 AWT-EventQueue-1: update T=4256 AWT-EventQueue-1: paint T=4256 AWT-EventQueue-1: headsup.draw T=4296 AWT-EventQueue-1: update T=4296 AWT-EventQueue-1: paint T=4296 AWT-EventQueue-1: headsup.draw T=4366 AWT-EventQueue-1: update T=4366 AWT-EventQueue-1: paint T=4366 AWT-EventQueue-1: headsup.draw T=4406 AWT-EventQueue-1: update T=4406 AWT-EventQueue-1: paint T=4406 AWT-EventQueue-1: headsup.draw T=4446 AWT-EventQueue-1: update T=4446 AWT-EventQueue-1: paint T=4446 AWT-EventQueue-1: headsup.draw T=4487 AWT-EventQueue-1: update T=4487 AWT-EventQueue-1: paint T=4487 AWT-EventQueue-1: headsup.draw T=4527 AWT-EventQueue-1: update T=4527 AWT-EventQueue-1: paint T=4527 AWT-EventQueue-1: headsup.draw T=4577 AWT-EventQueue-1: update T=4577 AWT-EventQueue-1: paint T=4577 AWT-EventQueue-1: headsup.draw T=4617 AWT-EventQueue-1: update T=4617 AWT-EventQueue-1: paint T=4617 AWT-EventQueue-1: headsup.draw T=4657 AWT-EventQueue-1: update T=4657 AWT-EventQueue-1: paint T=4657 AWT-EventQueue-1: headsup.draw T=4697 AWT-EventQueue-1: update T=4697 AWT-EventQueue-1: paint T=4697 AWT-EventQueue-1: headsup.draw As you can see, the mad-updating begins at T=3205 and continues then on. Note that the actionPerformed from the Swing-Timer is still functioning, and that the method that keeps getting called is update (which in turn calls paint, which call headsup.draw). /Olof On 8/25/05, Olof Bjarnason <[EMAIL PROTECTED]> wrote: > This is a very thourough answer. I will try to reply ... > > On 8/25/05, Jim Graham <[EMAIL PROTECTED]> wrote: > > >> From the Component javadocs: > > >> The update method of Component calls this component's paint > > >> method to redraw this component. > > >> and: > > >> Subclasses of Component that override this method should > > >> either call super.update(g), or call paint(g) directly > > >> from their update method. > > > > > > OK, guess I misread the docs. Actually, I followed this tutorial to begin > > > with: http://www.dgp.toronto.edu/~mjmcguff/learn/java/ > > > > either direction will work as long as you override both. Our docs show the > > guts in paint() and then update() redirecting to paint() since it reflects > > what would otherwise go on underneath the covers. Typically an animation > > program overrides paint() to start out with to draw a frame, because that > > is where a program should put its paint() code and that is where someone > > reading the code would go first to find the painting code. The override of > > update() is then added to stop the "erase to background color" flickering. > > When you look at it that way, it seems more logical to have the actual > > painting code in the paint() method, but both work as long as you override > > both. > I'm with you and I tried both, same result. > > > > > >> Of course, you've overridden update(), so this changes the > > >> behavior, but it's a bit confusing at the least. But there could > > >> be more wrong here: it could be that by changing the nature of > > >> update/paint interaction, you're getting in the way of the > > >> regular system of issuing and consuming repaint() events, which > > >> could cause the paint calls to keep being issued. > > >> > > >> The solution here is to simply override paint() and do your > > >> painting there. Or if you're using Swing, override paintComponent() > > >> instead. Don't override update, or at least not in the manner you > > >> are doing currently. > > > I'm using AWT I guess, no Swing. I'm trying to go for old-API in order > > > to make the game runnable on more computers. I compile for 1.4.2, but > > > I guess 1.4.2 has Swing so I could go for paintComponent, but my > > > feeling is I should use paint(), eg.the tutorial uses paint(). > > > Comments? > > > > I don't see how the way you've overridden paint/update() can lead to the > > problem in and of itself. It would be an interesting experiment, but I > > wouldn't abandon your architecture to try to fix this. > I started "porting" my Applet to JApplet but it proved horrendously > errorprone (basically had to cut the file in three --- more work to > fix this than starting from scratch) so I am back at the > AWT/paint/update stage again. > > > > > >> To improve performance in general: > > >> - use a timer to schedule regular repaints so you don't get > > >> swamped with constant repaint events (similar to what you're > > >> doing, but I don't follow the complexity of using key actions > > >> for this. Why not simply issue a repaint call?) > > > > This is a hard call. While I still need to think about the ramifications > > of what you've done with sending ActionEvents to yourself, simply using a > > Timer instead of rolling your own Thread would not change anything here if > > the action of the Timer was to send the ActionEvent. > I've tried both, same result. A potential error with my previous > "virtual-click-a-button" method was that it used the postEvent from a > custom thread. Given that the documents state that all AWT/Swing code > should execute using the invokeLater technique, this i not > recommended. Anyway, the Swing Timer didn't work either in my setting. > > > > > If you switched to periodic repaint() calls instead of sending an > > ActionEvent then it would mean that the calls to update() would happen > > directly instead of as a result of a frame update. You would either have > > to move the frame update code to the update() method, or you would have to > > have two timer events - one to update the frame, and another to call > > repaint - and the two would happen asynchronously. > > > > This points out a potential problem with the way you've written the code > > and could be the root cause of the flood of repaints. Are there ever any > > frames that take longer than 1 second to render? If so, then there is no > > inherent throttling of your ActionEvents to match the extended compute > > time. If, say, you have one frame update that takes 10 seconds to > > complete, then by the time it is done, there will be 10 ActionEvents in the > > queue waiting to be processed. If those events take less time to process > > then you will run through all 10 of them at once and see 10 updates very > > quickly in a row which could look like what is happening to you. If it is > > much more likely to encounter frame updates that take longer than 1 second > > then the backup will be constantly increasing. > Simple answer: no. My updateBackbuffer code paints at most 64 tile > images sized 32x32 each, and actually even when the > non-stopping-updates is going on, there is no performance problem more > than the CPU getting up to like 10% (I'm sorry stated 90% earlier post > but that was a misreading must have been something else..). The way I > know there is mad-updating is via the console output (println) and > flickering (the headsup display widgets are flickering). > > > > > Instead of sending an ActionEvent every second which forces a frame update, > > you might want to have some way of creating back-pressure. Either: > > > > - have something in your frame update code which requests the subsequent > > frame when it is done and don't send another ActionEvent until it is > > requested - that guarantees a delay of N milliseconds "between" frames so > > if frames take longer than your delay, the ActionEvents don't accumulate. > > This is kind of hard to implement, though. > > > > - have your frame update code check the last time a frame was updated and > > if it was less than 1 second ago, skip this ActionEvent to catch up > > (another will come along in less than a second). > > > > - you can also look at the time-stamp of the ActionEvent and if it was sent > > more than 1 second ago, skip it in favor of one that you know will be > > coming along very soon. > > > > - have your frame update code increment "number of frames handled" and have > > the event sending code increment "number of frames requested" and if the > > requested count is too far ahead of the handled count (like more than 1 > > greater), skip a beat by not sending the ActionEvent this time. > > > > - use calls to update to trigger your frame updates and use "timed repaint" > > events which coalesce: > > > > Timer thread { > > loop { > > sleep(1000); > > repaint(10); // Will coalesce with other repaint(ms) > > calls > > } > > } > > > > update(Graphics g) { > > updateframe(); > > paint(g); > > } > > > > paint(Graphics g) { > > g.drawImage(backbuffer); > > drawheadsup(g); > > } > > > > Note that calls to repaint() with no delay will be queued and result in > > calls to update() on a 1 for 1 basis so you could end up with the same > > backlog, but calls to repaint() with a delay (as above) will be coalesced > > with each other. > > > > >> - only draw the area that's changed. So if only one rectangle > > >> of the playing area has changed, draw that updated region > > >> into the back buffer, and copy that region of the back buffer > > >> into the window. > > > > That will help keep the frame update code from getting too far behind the > > event sending thread... > > > > ...jim > > > > > Thanks for your time, > > /Olof > =========================================================================== To unsubscribe, send email to [EMAIL PROTECTED] and include in the body of the message "signoff JAVA2D-INTEREST". For general help, send email to [EMAIL PROTECTED] and include in the body of the message "help".