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
This commit is contained in:
Michael Bayne
2002-11-15 23:58:34 +00:00
parent 1d602849f0
commit d9838206c1
+54 -97
View File
@@ -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; package com.threerings.media;
@@ -25,6 +25,10 @@ import javax.swing.JComponent;
import javax.swing.JLayeredPane; import javax.swing.JLayeredPane;
import javax.swing.RepaintManager; 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.Interval;
import com.samskivert.util.IntervalManager; import com.samskivert.util.IntervalManager;
import com.samskivert.util.ObserverList; import com.samskivert.util.ObserverList;
@@ -150,13 +154,9 @@ public class FrameManager
*/ */
public void start () public void start ()
{ {
if (_ticker == null) { if (_timer == null) {
// create ticker for queueing up tick requests on AWT thread _timer = new Timer(true);
_ticker = new Ticker(); _timer.scheduleAtFixedRate(_callTick, new Date(), _millisPerFrame);
// and start it up
_ticker.start();
// and kick off our first frame
_ticker.tickIn(_millisPerFrame, System.currentTimeMillis());
} }
} }
@@ -165,8 +165,9 @@ public class FrameManager
*/ */
public synchronized void stop () public synchronized void stop ()
{ {
if (_ticker != null) { if (_timer != null) {
_ticker = null; _timer.cancel();
_timer = null;
} }
} }
@@ -176,43 +177,44 @@ public class FrameManager
*/ */
public synchronized boolean isRunning () 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. * 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 try {
// rendering anything synchronized (this) {
if (_frame.isShowing() && _ticking = true;
_frame.getWidth() > 0 && _frame.getHeight() > 0) { }
long tickStamp = System.currentTimeMillis();
// tick our participants
tickParticipants(tickStamp);
// repaint our participants
paintParticipants(tickStamp);
}
// now determine how many milliseconds we have left before we need // if our frame is not showing (or is impossibly sized), don't try
// to start the next frame (if any) // rendering anything
long end = System.currentTimeMillis(); if (_frame.isShowing() &&
long duration = end - _frameStart; _frame.getWidth() > 0 && _frame.getHeight() > 0) {
long remaining = _millisPerFrame - duration; // tick our participants
tickParticipants(tickStamp);
// repaint our participants
paintParticipants(tickStamp);
}
// note that we've done a frame // note that we've done a frame
// PerformanceMonitor.tick(this, "frame-rate"); // PerformanceMonitor.tick(this, "frame-rate");
// if we have no time remaining, queue up another tick immediately } finally {
if (remaining <= 0) { synchronized (this) {
// make a note that we're starting our next frame now _ticking = false;
_frameStart = end; }
EventQueue.invokeLater(_callTick);
} else {
// otherwise queue one up in the requisite number of millis
_ticker.tickIn(remaining, end);
} }
} }
@@ -366,57 +368,6 @@ public class FrameManager
Log.info("Frames in last second: " + ticks); 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 * Creates the off-screen buffer used to perform double buffered
* rendering of the animated panel. * rendering of the animated panel.
@@ -585,15 +536,15 @@ public class FrameManager
/** The image used to render off-screen. */ /** The image used to render off-screen. */
protected VolatileImage _backimg; protected VolatileImage _backimg;
/** The number of milliseconds per frame (12 by default, which gives /** The number of milliseconds per frame (14 by default, which gives
* an fps of 80). */ * an fps of ~71). */
protected long _millisPerFrame = 12; protected long _millisPerFrame = 14;
/** The time at which we started the most recent "frame". */ /** The timer that dispatches our frame ticks. */
protected long _frameStart; protected Timer _timer;
/** Used to queue up a tick. */ /** Used to detect when we need to drop frames. */
protected Ticker _ticker; protected boolean _ticking;
/** The graphics object from our back buffer. */ /** The graphics object from our back buffer. */
protected Graphics _bgfx; protected Graphics _bgfx;
@@ -609,10 +560,16 @@ public class FrameManager
* "layered" components. */ * "layered" components. */
protected boolean[] _clipped = new boolean[1]; protected boolean[] _clipped = new boolean[1];
/** Used to queue up a call to {@link #tick} on the AWT thread. */ /** Used to effect periodic calls to {@link #tick}. */
protected Runnable _callTick = new Runnable () { protected TimerTask _callTick = new TimerTask () {
public void run () { public void run () {
tick(); if (EventQueue.isDispatchThread()) {
tick(System.currentTimeMillis());
} else if (!isTicking()) {
EventQueue.invokeLater(this);
} else {
Log.info("Dropped frame.");
}
} }
}; };