From d9838206c161b84beb08c94df2845a2c670ced21 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Fri, 15 Nov 2002 23:58:34 +0000 Subject: [PATCH] Use Java's timer to invoke our frame tick. Drop frames instead of queueing up repeats when the previous tick takes too long. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1953 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../com/threerings/media/FrameManager.java | 151 +++++++----------- 1 file changed, 54 insertions(+), 97 deletions(-) diff --git a/src/java/com/threerings/media/FrameManager.java b/src/java/com/threerings/media/FrameManager.java index 8405cf454..9631a8458 100644 --- a/src/java/com/threerings/media/FrameManager.java +++ b/src/java/com/threerings/media/FrameManager.java @@ -1,5 +1,5 @@ // -// $Id: FrameManager.java,v 1.18 2002/10/26 20:31:15 mdb Exp $ +// $Id: FrameManager.java,v 1.19 2002/11/15 23:58:34 mdb Exp $ package com.threerings.media; @@ -25,6 +25,10 @@ import javax.swing.JComponent; import javax.swing.JLayeredPane; import javax.swing.RepaintManager; +import java.util.Date; +import java.util.Timer; +import java.util.TimerTask; + import com.samskivert.util.Interval; import com.samskivert.util.IntervalManager; import com.samskivert.util.ObserverList; @@ -150,13 +154,9 @@ public class FrameManager */ public void start () { - if (_ticker == null) { - // create ticker for queueing up tick requests on AWT thread - _ticker = new Ticker(); - // and start it up - _ticker.start(); - // and kick off our first frame - _ticker.tickIn(_millisPerFrame, System.currentTimeMillis()); + if (_timer == null) { + _timer = new Timer(true); + _timer.scheduleAtFixedRate(_callTick, new Date(), _millisPerFrame); } } @@ -165,8 +165,9 @@ public class FrameManager */ public synchronized void stop () { - if (_ticker != null) { - _ticker = null; + if (_timer != null) { + _timer.cancel(); + _timer = null; } } @@ -176,43 +177,44 @@ public class FrameManager */ public synchronized boolean isRunning () { - return (_ticker != null); + return (_timer != null); + } + + /** + * Returns true if we are in the middle of a call to {@link #tick}. + */ + protected synchronized boolean isTicking () + { + return _ticking; } /** * Called to perform the frame processing and rendering. */ - protected void tick () + protected void tick (long tickStamp) { - // if our frame is not showing (or is impossibly sized), don't try - // rendering anything - if (_frame.isShowing() && - _frame.getWidth() > 0 && _frame.getHeight() > 0) { - long tickStamp = System.currentTimeMillis(); - // tick our participants - tickParticipants(tickStamp); - // repaint our participants - paintParticipants(tickStamp); - } + try { + synchronized (this) { + _ticking = true; + } - // now determine how many milliseconds we have left before we need - // to start the next frame (if any) - long end = System.currentTimeMillis(); - long duration = end - _frameStart; - long remaining = _millisPerFrame - duration; + // if our frame is not showing (or is impossibly sized), don't try + // rendering anything + if (_frame.isShowing() && + _frame.getWidth() > 0 && _frame.getHeight() > 0) { + // tick our participants + tickParticipants(tickStamp); + // repaint our participants + paintParticipants(tickStamp); + } // note that we've done a frame // PerformanceMonitor.tick(this, "frame-rate"); - // if we have no time remaining, queue up another tick immediately - if (remaining <= 0) { - // make a note that we're starting our next frame now - _frameStart = end; - EventQueue.invokeLater(_callTick); - - } else { - // otherwise queue one up in the requisite number of millis - _ticker.tickIn(remaining, end); + } finally { + synchronized (this) { + _ticking = false; + } } } @@ -366,57 +368,6 @@ public class FrameManager Log.info("Frames in last second: " + ticks); } - /** - * Used to queue up frame ticks on the AWT thread at some point in the - * future. - */ - protected class Ticker extends Thread - { - /** - * Tells the ticker to queue up a frame in the requisite number of - * milliseconds. - */ - public synchronized void tickIn (long millis, long now) - { - _sleepfor = millis; - _now = now; - this.notify(); - } - - public void run () - { - synchronized (this) { - while (_sleepfor != -1) { - try { - if (_sleepfor == 0) { - this.wait(); - } - if (_sleepfor > 0) { - Thread.sleep(_sleepfor); - - // make a note of our frame start time - _frameStart = System.currentTimeMillis(); -// long error =_frameStart - (_sleepfor + _now); -// if (Math.abs(error) > 3) { -// Log.warning("Funny business: " + error); -// } - - // queue up our ticker on the AWT thread - EventQueue.invokeLater(_callTick); - _sleepfor = 0; - } - - } catch (InterruptedException ie) { - Log.warning("Girl interrupted!"); - } - } - } - } - - protected long _sleepfor = 0l; - protected long _now = 0l; - } - /** * Creates the off-screen buffer used to perform double buffered * rendering of the animated panel. @@ -585,15 +536,15 @@ public class FrameManager /** The image used to render off-screen. */ protected VolatileImage _backimg; - /** The number of milliseconds per frame (12 by default, which gives - * an fps of 80). */ - protected long _millisPerFrame = 12; + /** The number of milliseconds per frame (14 by default, which gives + * an fps of ~71). */ + protected long _millisPerFrame = 14; - /** The time at which we started the most recent "frame". */ - protected long _frameStart; + /** The timer that dispatches our frame ticks. */ + protected Timer _timer; - /** Used to queue up a tick. */ - protected Ticker _ticker; + /** Used to detect when we need to drop frames. */ + protected boolean _ticking; /** The graphics object from our back buffer. */ protected Graphics _bgfx; @@ -609,10 +560,16 @@ public class FrameManager * "layered" components. */ protected boolean[] _clipped = new boolean[1]; - /** Used to queue up a call to {@link #tick} on the AWT thread. */ - protected Runnable _callTick = new Runnable () { + /** Used to effect periodic calls to {@link #tick}. */ + protected TimerTask _callTick = new TimerTask () { public void run () { - tick(); + if (EventQueue.isDispatchThread()) { + tick(System.currentTimeMillis()); + } else if (!isTicking()) { + EventQueue.invokeLater(this); + } else { + Log.info("Dropped frame."); + } } };