Widened, reordered methods, removed some old debugging cruft in preparation for

adding the MediaOverlay stuff.


git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@113 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
Michael Bayne
2007-01-20 01:21:05 +00:00
parent a8de5bb789
commit 031b51419f
+155 -309
View File
@@ -56,74 +56,63 @@ import com.threerings.media.util.TrailingAverage;
import com.threerings.util.unsafe.Unsafe; import com.threerings.util.unsafe.Unsafe;
/** /**
* Provides a central point from which the computation for each "frame" or tick * Provides a central point from which the computation for each "frame" or tick can be dispatched.
* can be dispatched. This assumed that the application structures its activity * This assumed that the application structures its activity around the rendering of each frame,
* around the rendering of each frame, which is a common architecture for * which is a common architecture for games. The animation and sprite support provided by other
* games. The animation and sprite support provided by other classes in this * classes in this package are structured for use in an application that uses a frame manager to
* package are structured for use in an application that uses a frame manager * tick everything once per frame.
* to tick everything once per frame.
* *
* <p> The frame manager goes through a simple two part procedure every frame: * <p> The frame manager goes through a simple two part procedure every frame:
* *
* <ul> * <ul>
* <li> Ticking all of the frame participants: in {@link * <li> Ticking all of the frame participants: in {@link FrameParticipant#tick}, any processing
* FrameParticipant#tick}, any processing that need be performed during this * that need be performed during this frame should be performed. Care should be taken not to
* frame should be performed. Care should be taken not to execute code that * execute code that will take unduly long, instead such processing should be broken up so that it
* will take unduly long, instead such processing should be broken up so that * can be performed in small pieces every frame (or performed on a separate thread with the results
* it can be performed in small pieces every frame (or performed on a separate * safely communicated back to the frame participants for incorporation into the rendering loop).
* thread with the results safely communicated back to the frame participants
* for incorporation into the rendering loop).
* *
* <li> Painting the user interface hierarchy: the top-level component (the * <li> Painting the user interface hierarchy: the top-level component (the frame) is painted (via
* frame) is painted (via a call to {@link JRootPane#paint}) into a flip buffer * a call to {@link JRootPane#paint}) into a flip buffer (if supported, an off-screen buffer if
* (if supported, an off-screen buffer if not). Updates that were computed * not). Updates that were computed during the tick should be rendered in this call to paint. The
* during the tick should be rendered in this call to paint. The paint call * paint call will propagate down to all components in the UI hierarchy, some of which may be
* will propagate down to all components in the UI hierarchy, some of which may * {@link FrameParticipant}s and will have prepared themselves for their upcoming painting in the
* be {@link FrameParticipant}s and will have prepared themselves for their * previous call to {@link FrameParticipant#tick}. When the call to paint completes, the flip
* upcoming painting in the previous call to {@link * buffer is flipped and the process starts all over again.
* FrameParticipant#tick}. When the call to paint completes, the flip buffer is * </ul>
* flipped and the process starts all over again. </ul>
* *
* <p> The ticking and rendering takes place on the AWT thread so as to * <p> The ticking and rendering takes place on the AWT thread so as to avoid the need for
* avoid the need for complicated coordination between AWT event handler * complicated coordination between AWT event handler code and frame code. However, this means that
* code and frame code. However, this means that all AWT (and Swing) event * all AWT (and Swing) event handlers <em>must not</em> perform any complicated processing. After
* handlers <em>must not</em> perform any complicated processing. After * each frame, control of the AWT thread is given back to the AWT which processes all pending AWT
* each frame, control of the AWT thread is given back to the AWT which * events before giving the frame manager an opportunity to process the next frame. Thus the
* processes all pending AWT events before giving the frame manager an * convenience of everything running on the AWT thread comes with the price of requiring that AWT
* opportunity to process the next frame. Thus the convenience of * event handlers not block or perform any intensive processing. In general, this is a sensible
* everything running on the AWT thread comes with the price of requiring * structure for an application anyhow, so this organization tends to be preferable to an
* that AWT event handlers not block or perform any intensive processing. * organization where the AWT and frame threads are separate and must tread lightly so as not to
* In general, this is a sensible structure for an application anyhow, so
* this organization tends to be preferable to an organization where the
* AWT and frame threads are separate and must tread lightly so as not to
* collide. * collide.
* *
* <p> Note: the way that <code>JScrollPane</code> goes about improving * <p> Note: the way that <code>JScrollPane</code> goes about improving performance when scrolling
* performance when scrolling complicated contents cannot work with active * complicated contents cannot work with active rendering. If you use a <code>JScrollPane</code> in
* rendering. If you use a <code>JScrollPane</code> in an application that * an application that uses the frame manager, you should either use the provided {@link
* uses the frame manager, you should either use the provided {@link * SafeScrollPane} or set your scroll panes' viewports to <code>SIMPLE_SCROLL_MODE</code>.
* SafeScrollPane} or set your scroll panes' viewports to
* <code>SIMPLE_SCROLL_MODE</code>.
*/ */
public abstract class FrameManager public abstract class FrameManager
{ {
/** /**
* Normally, the frame manager will repaint any component in a {@link * Normally, the frame manager will repaint any component in a {@link JLayeredPane} layer
* JLayeredPane} layer (popups, overlays, etc.) that overlaps a frame * (popups, overlays, etc.) that overlaps a frame participant on every tick because the frame
* participant on every tick because the frame participant <em>could</em> * participant <em>could</em> have changed underneath the overlay which would require that the
* have changed underneath the overlay which would require that the overlay * overlay be repainted. If the application knows that the frame participant beneath the
* be repainted. If the application knows that the frame participant * overlay will never change, it can have its overlay implement this interface and avoid the
* beneath the overlay will never change, it can have its overlay implement * expense of forcibly fully repainting the overlay on every frame.
* this interface and avoid the expense of forcibly fully repainting the
* overlay on every frame.
*/ */
public static interface SafeLayerComponent public static interface SafeLayerComponent
{ {
} }
/** /**
* Provides a bridge between either {@link ManagedJFrame} or {@link * Provides a bridge between either {@link ManagedJFrame} or {@link ManagedJApplet} and the
* ManagedJApplet} and the frame manager. * frame manager.
*/ */
public static interface ManagedRoot public static interface ManagedRoot
{ {
@@ -138,16 +127,15 @@ public abstract class FrameManager
} }
/** /**
* Creates a frame manager that will use a {@link SystemMediaTimer} to * Creates a frame manager that will use a {@link SystemMediaTimer} to obtain timing
* obtain timing information, which is available on every platform, but * information, which is available on every platform, but returns inaccurate time stamps on
* returns inaccurate time stamps on many platforms. * many platforms.
* *
* @see #newInstance(ManagedRoot, MediaTimer) * @see #newInstance(ManagedRoot, MediaTimer)
*/ */
public static FrameManager newInstance (ManagedRoot root) public static FrameManager newInstance (ManagedRoot root)
{ {
// first try creating a PerfTimer which is the best if we're using // first try creating a PerfTimer which is the best if we're using JDK1.4.2
// JDK1.4.2
MediaTimer timer = null; MediaTimer timer = null;
try { try {
timer = (MediaTimer)Class.forName(PERF_TIMER).newInstance(); timer = (MediaTimer)Class.forName(PERF_TIMER).newInstance();
@@ -160,144 +148,21 @@ public abstract class FrameManager
} }
/** /**
* Constructs a frame manager that will do its rendering to the supplied * Constructs a frame manager that will do its rendering to the supplied root.
* root.
*/ */
public static FrameManager newInstance (ManagedRoot root, MediaTimer timer) public static FrameManager newInstance (ManagedRoot root, MediaTimer timer)
{ {
FrameManager fmgr; FrameManager fmgr = _useFlip.getValue() ? new FlipFrameManager() : new BackFrameManager();
if (_useFlip.getValue()) {
Log.info("Creating flip frame manager.");
fmgr = new FlipFrameManager();
} else {
Log.info("Creating back frame manager.");
fmgr = new BackFrameManager();
}
fmgr.init(root, timer); fmgr.init(root, timer);
return fmgr; return fmgr;
} }
/** /**
* Initializes this frame manager and prepares it for operation. * Instructs the frame manager to target the specified number of frames per second. If the
*/ * computation and rendering for a frame are completed with time to spare, the frame manager
protected void init (ManagedRoot root, MediaTimer timer) * will wait until the proper time to begin processing for the next frame. If a frame takes
{ * longer than its alotted time, the frame manager will immediately begin processing on the
_window = root.getWindow(); * next frame.
_root = root;
_root.init(this);
_timer = timer;
// set up our custom repaint manager
_remgr = new ActiveRepaintManager(_window);
RepaintManager.setCurrentManager(_remgr);
// turn off double buffering for the whole business because we
// handle repaints
_remgr.setDoubleBufferingEnabled(false);
if (DEBUG_EVENTS) {
addTestListeners();
}
}
/**
* Adds a variety of listeners to the frame in order to provide
* visibility into the various events received by the frame.
*/
protected void addTestListeners ()
{
// add a test window listener
_window.addWindowListener(new WindowListener() {
public void windowActivated (WindowEvent e) {
Log.info("Window activated [evt=" + e + "].");
}
public void windowClosed (WindowEvent e) {
Log.info("Window closed [evt=" + e + "].");
}
public void windowClosing (WindowEvent e) {
Log.info("Window closing [evt=" + e + "].");
}
public void windowDeactivated (WindowEvent e) {
Log.info("Window deactivated [evt=" + e + "].");
}
public void windowDeiconified (WindowEvent e) {
Log.info("Window deiconified [evt=" + e + "].");
}
public void windowIconified (WindowEvent e) {
Log.info("Window iconified [evt=" + e + "].");
}
public void windowOpened (WindowEvent e) {
Log.info("Window opened [evt=" + e + "].");
}
});
// add a component listener
_window.addComponentListener(new ComponentListener() {
public void componentHidden (ComponentEvent e) {
Log.info("Window component hidden [evt=" + e + "].");
}
public void componentShown (ComponentEvent e) {
Log.info("Window component shown [evt=" + e + "].");
}
public void componentMoved (ComponentEvent e) {
Log.info("Window component moved [evt=" + e + "].");
}
public void componentResized (ComponentEvent e) {
Log.info("Window component resized [evt=" + e + "].");
}
});
// add test ancestor focus listener
_root.getRootPane().addAncestorListener(
new AncestorListener() {
public void ancestorAdded (AncestorEvent e) {
Log.info("Root pane ancestor added [e=" + e + "].");
}
public void ancestorRemoved (AncestorEvent e) {
Log.info("Root pane ancestor removed [e=" + e + "].");
}
public void ancestorMoved (AncestorEvent e) {
Log.info("Root pane ancestor moved [e=" + e + "].");
}
});
// add test key event dispatcher
KeyboardFocusManager.getCurrentKeyboardFocusManager().
addKeyEventDispatcher(new KeyEventDispatcher() {
public boolean dispatchKeyEvent (KeyEvent e) {
// if ((e.getModifiersEx() & KeyEvent.ALT_DOWN_MASK) != 0 &&
// e.getKeyCode() == KeyEvent.VK_TAB) {
// Log.info("Detected alt-tab key event " +
// "[e=" + e + "].");
// // attempt to eat the event so that windows
// // doesn't alt-tab into unhappy land
// e.consume();
// return true;
// }
return false;
}
});
}
/**
* Instructs the frame manager to target the specified number of
* frames per second. If the computation and rendering for a frame are
* completed with time to spare, the frame manager will wait until the
* proper time to begin processing for the next frame. If a frame
* takes longer than its alotted time, the frame manager will
* immediately begin processing on the next frame.
*/ */
public void setTargetFrameRate (int fps) public void setTargetFrameRate (int fps)
{ {
@@ -306,15 +171,14 @@ public abstract class FrameManager
} }
/** /**
* Registers a frame participant. The participant will be given the * Registers a frame participant. The participant will be given the opportunity to do
* opportunity to do processing and rendering on each frame. * processing and rendering on each frame.
*/ */
public void registerFrameParticipant (FrameParticipant participant) public void registerFrameParticipant (FrameParticipant participant)
{ {
Object[] nparts = ListUtil.testAndAddRef(_participants, participant); Object[] nparts = ListUtil.testAndAddRef(_participants, participant);
if (nparts == null) { if (nparts == null) {
Log.warning("Refusing to add duplicate frame participant! " + Log.warning("Refusing to add duplicate frame participant! " + participant);
participant);
} else { } else {
_participants = nparts; _participants = nparts;
} }
@@ -337,9 +201,9 @@ public abstract class FrameManager
} }
/** /**
* Returns a millisecond granularity time stamp using the {@link * Returns a millisecond granularity time stamp using the {@link MediaTimer} with which this
* MediaTimer} with which this frame manager was configured. * frame manager was configured. <em>Note:</em> this should only be called from the AWT
* <em>Note:</em> this should only be called from the AWT thread. * thread.
*/ */
public long getTimeStamp () public long getTimeStamp ()
{ {
@@ -370,8 +234,8 @@ public abstract class FrameManager
} }
/** /**
* Returns true if the tick interval is be running (not necessarily at * Returns true if the tick interval is be running (not necessarily at that instant, but in
* that instant, but in general). * general).
*/ */
public synchronized boolean isRunning () public synchronized boolean isRunning ()
{ {
@@ -401,13 +265,51 @@ public abstract class FrameManager
{ {
if (_metrics == null) { if (_metrics == null) {
_metrics = new TrailingAverage[] { _metrics = new TrailingAverage[] {
new TrailingAverage(150), new TrailingAverage(150), new TrailingAverage(150), new TrailingAverage(150)
new TrailingAverage(150), };
new TrailingAverage(150) };
} }
return _metrics; return _metrics;
} }
/**
* Returns the root component for the supplied component or null if it is not part of a rooted
* hierarchy or if any parent along the way is found to be hidden or without a peer. Along the
* way, it adjusts the supplied component-relative rectangle to be relative to the returned
* root component.
*/
public static Component getRoot (Component comp, Rectangle rect)
{
for (Component c = comp; c != null; c = c.getParent()) {
if (!c.isVisible() || !c.isDisplayable()) {
return null;
}
if (c instanceof Window || c instanceof Applet) {
return c;
}
rect.x += c.getX();
rect.y += c.getY();
}
return null;
}
/**
* Initializes this frame manager and prepares it for operation.
*/
protected void init (ManagedRoot root, MediaTimer timer)
{
_window = root.getWindow();
_root = root;
_root.init(this);
_timer = timer;
// set up our custom repaint manager
_remgr = new ActiveRepaintManager(_window);
RepaintManager.setCurrentManager(_remgr);
// turn off double buffering for the whole business because we handle repaints
_remgr.setDoubleBufferingEnabled(false);
}
/** /**
* Called to perform the frame processing and rendering. * Called to perform the frame processing and rendering.
*/ */
@@ -417,16 +319,16 @@ public abstract class FrameManager
if (_perfDebug.getValue()) { if (_perfDebug.getValue()) {
start = paint = _timer.getElapsedMicros(); start = paint = _timer.getElapsedMicros();
} }
// if our frame is not showing (or is impossibly sized), don't try
// rendering anything // if our frame is not showing (or is impossibly sized), don't try rendering anything
if (_window.isShowing() && if (_window.isShowing() && _window.getWidth() > 0 && _window.getHeight() > 0) {
_window.getWidth() > 0 && _window.getHeight() > 0) {
// tick our participants // tick our participants
tickParticipants(tickStamp); tickParticipants(tickStamp);
paint = _timer.getElapsedMicros(); paint = _timer.getElapsedMicros();
// repaint our participants and components // repaint our participants and components
paint(tickStamp); paint(tickStamp);
} }
if (_perfDebug.getValue()) { if (_perfDebug.getValue()) {
long end = _timer.getElapsedMicros(); long end = _timer.getElapsedMicros();
getPerfMetrics()[1].record((int)(paint-start)/100); getPerfMetrics()[1].record((int)(paint-start)/100);
@@ -435,8 +337,8 @@ public abstract class FrameManager
} }
/** /**
* Called once per frame to invoke {@link FrameParticipant#tick} on * Called once per frame to invoke {@link FrameParticipant#tick} on all of our frame
* all of our frame participants. * participants.
*/ */
protected void tickParticipants (long tickStamp) protected void tickParticipants (long tickStamp)
{ {
@@ -486,9 +388,8 @@ public abstract class FrameManager
} }
/** /**
* Called once per frame to invoke {@link Component#paint} on all of * Called once per frame to invoke {@link Component#paint} on all of our frame participants'
* our frame participants' components and all dirty components managed * components and all dirty components managed by our {@link ActiveRepaintManager}.
* by our {@link ActiveRepaintManager}.
*/ */
protected abstract void paint (long tickStamp); protected abstract void paint (long tickStamp);
@@ -500,15 +401,13 @@ public abstract class FrameManager
protected abstract Graphics2D createGraphics (); protected abstract Graphics2D createGraphics ();
/** /**
* Paints our frame participants and any dirty components via the * Paints our frame participants and any dirty components via the repaint manager.
* repaint manager.
* *
* @return true if anything was painted, false if not. * @return true if anything was painted, false if not.
*/ */
protected boolean paint (Graphics2D gfx) protected boolean paint (Graphics2D gfx)
{ {
// paint our frame participants (which want to be handled // paint our frame participants (which want to be handled specially)
// specially)
int painted = 0; int painted = 0;
for (int ii = 0; ii < _participants.length; ii++) { for (int ii = 0; ii < _participants.length; ii++) {
FrameParticipant part = (FrameParticipant)_participants[ii]; FrameParticipant part = (FrameParticipant)_participants[ii];
@@ -529,24 +428,21 @@ public abstract class FrameManager
// get the bounds of this component // get the bounds of this component
pcomp.getBounds(_tbounds); pcomp.getBounds(_tbounds);
// the bounds adjustment we're about to call will add in the // the bounds adjustment we're about to call will add in the components initial bounds
// components initial bounds offsets, so we remove them here // offsets, so we remove them here
_tbounds.setLocation(0, 0); _tbounds.setLocation(0, 0);
// convert them into top-level coordinates; also note that if // convert them into top-level coordinates; also note that if this component does not
// this component does not have a valid or visible root, we // have a valid or visible root, we don't want to paint it either
// don't want to paint it either
if (getRoot(pcomp, _tbounds) == null) { if (getRoot(pcomp, _tbounds) == null) {
continue; continue;
} }
try { try {
// render this participant; we don't set the clip because // render this participant; we don't set the clip because frame participants are
// frame participants are expected to handle clipping // expected to handle clipping themselves; otherwise we might pointlessly set the
// themselves; otherwise we might pointlessly set the clip // clip here, creating a few Rectangle objects in the process, only to have the
// here, creating a few Rectangle objects in the process, // frame participant immediately set the clip to something more sensible
// only to have the frame participant immediately set the
// clip to something more sensible
gfx.translate(_tbounds.x, _tbounds.y); gfx.translate(_tbounds.x, _tbounds.y);
pcomp.paint(gfx); pcomp.paint(gfx);
gfx.translate(-_tbounds.x, -_tbounds.y); gfx.translate(-_tbounds.x, -_tbounds.y);
@@ -554,13 +450,11 @@ public abstract class FrameManager
} catch (Throwable t) { } catch (Throwable t) {
String ptos = StringUtil.safeToString(part); String ptos = StringUtil.safeToString(part);
Log.warning("Frame participant choked during paint " + Log.warning("Frame participant choked during paint [part=" + ptos + "].");
"[part=" + ptos + "].");
Log.logStackTrace(t); Log.logStackTrace(t);
} }
// render any components in our layered pane that are not in // render any components in our layered pane that are not in the default layer
// the default layer
_clipped[0] = false; _clipped[0] = false;
renderLayers(gfx, pcomp, _tbounds, _clipped); renderLayers(gfx, pcomp, _tbounds, _clipped);
@@ -573,8 +467,7 @@ public abstract class FrameManager
} }
} }
// repaint any widgets that have declared they need to be // repaint any widgets that have declared they need to be repainted since the last tick
// repainted since the last tick
boolean pcomp = _remgr.paintComponents(gfx, this); boolean pcomp = _remgr.paintComponents(gfx, this);
// let the caller know if anybody painted anything // let the caller know if anybody painted anything
@@ -582,20 +475,18 @@ public abstract class FrameManager
} }
/** /**
* Called by the {@link ManagedJFrame} when our window was hidden and * Called by the {@link ManagedJFrame} when our window was hidden and reexposed.
* reexposed.
*/ */
protected abstract void restoreFromBack (Rectangle dirty); protected abstract void restoreFromBack (Rectangle dirty);
/** /**
* Renders all components in all {@link JLayeredPane} layers that * Renders all components in all {@link JLayeredPane} layers that intersect the supplied
* intersect the supplied bounds. * bounds.
*/ */
protected void renderLayers (Graphics2D g, Component pcomp, protected void renderLayers (Graphics2D g, Component pcomp,
Rectangle bounds, boolean[] clipped) Rectangle bounds, boolean[] clipped)
{ {
JLayeredPane lpane = JLayeredPane lpane = JLayeredPane.getLayeredPaneAbove(pcomp);
JLayeredPane.getLayeredPaneAbove(pcomp);
if (lpane != null) { if (lpane != null) {
renderLayer(g, bounds, lpane, clipped, JLayeredPane.PALETTE_LAYER); renderLayer(g, bounds, lpane, clipped, JLayeredPane.PALETTE_LAYER);
renderLayer(g, bounds, lpane, clipped, JLayeredPane.MODAL_LAYER); renderLayer(g, bounds, lpane, clipped, JLayeredPane.MODAL_LAYER);
@@ -605,12 +496,11 @@ public abstract class FrameManager
} }
/** /**
* Renders all components in the specified layer of the supplied * Renders all components in the specified layer of the supplied layered pane that intersect
* layered pane that intersect the supplied bounds. * the supplied bounds.
*/ */
protected void renderLayer (Graphics2D g, Rectangle bounds, protected void renderLayer (Graphics2D g, Rectangle bounds, JLayeredPane pane,
JLayeredPane pane, boolean[] clipped, boolean[] clipped, Integer layer)
Integer layer)
{ {
// stop now if there are no components in that layer // stop now if there are no components in that layer
int ccount = pane.getComponentCountInLayer(layer.intValue()); int ccount = pane.getComponentCountInLayer(layer.intValue());
@@ -626,16 +516,16 @@ public abstract class FrameManager
continue; continue;
} }
// if this overlay does not intersect the component we just // if this overlay does not intersect the component we just rendered, we don't need to
// rendered, we don't need to repaint it // repaint it
_tbounds.setBounds(0, 0, comp.getWidth(), comp.getHeight()); _tbounds.setBounds(0, 0, comp.getWidth(), comp.getHeight());
getRoot(comp, _tbounds); getRoot(comp, _tbounds);
if (!_tbounds.intersects(bounds)) { if (!_tbounds.intersects(bounds)) {
continue; continue;
} }
// if the clipping region has not yet been set during this // if the clipping region has not yet been set during this render pass, the time has
// render pass, the time has come to do so // come to do so
if (!clipped[0]) { if (!clipped[0]) {
g.setClip(bounds); g.setClip(bounds);
clipped[0] = true; clipped[0] = true;
@@ -653,34 +543,6 @@ public abstract class FrameManager
} }
} }
// documentation inherited
public void checkpoint (String name, int ticks)
{
Log.info("Frames in last second: " + ticks);
}
/**
* Returns the root component for the supplied component or null if it
* is not part of a rooted hierarchy or if any parent along the way is
* found to be hidden or without a peer. Along the way, it adjusts the
* supplied component-relative rectangle to be relative to the
* returned root component.
*/
public static Component getRoot (Component comp, Rectangle rect)
{
for (Component c = comp; c != null; c = c.getParent()) {
if (!c.isVisible() || !c.isDisplayable()) {
return null;
}
if (c instanceof Window || c instanceof Applet) {
return c;
}
rect.x += c.getX();
rect.y += c.getY();
}
return null;
}
/** Used to effect periodic calls to {@link #tick}. */ /** Used to effect periodic calls to {@link #tick}. */
protected class Ticker extends Thread protected class Ticker extends Thread
{ {
@@ -704,12 +566,11 @@ public abstract class FrameManager
} }
} }
// work around sketchy bug on WinXP that causes the clock // work around sketchy bug on WinXP that causes the clock to leap into the past
// to leap into the past from time to time // from time to time
if (woke < _lastAttempt) { if (woke < _lastAttempt) {
Log.warning("Zoiks! We've leapt into the past, coping " + Log.warning("Zoiks! We've leapt into the past, coping as best we can " +
"as best we can [dt=" + "[dt=" + (woke - _lastAttempt) + "].");
(woke - _lastAttempt) + "].");
_lastAttempt = woke; _lastAttempt = woke;
} }
@@ -750,12 +611,9 @@ public abstract class FrameManager
_ticking = false; _ticking = false;
} }
/** Used to invoke the call to {@link #tick} on the AWT event /** Used to invoke the call to {@link #tick} on the AWT event queue thread. */
* queue thread. */ protected Runnable _awtTicker = new Runnable () {
protected Runnable _awtTicker = new Runnable () public void run () {
{
public void run ()
{
long elapsed = _timer.getElapsedMillis(); long elapsed = _timer.getElapsedMillis();
try { try {
tick(elapsed); tick(elapsed);
@@ -793,8 +651,7 @@ public abstract class FrameManager
/** Our custom repaint manager. */ /** Our custom repaint manager. */
protected ActiveRepaintManager _remgr; protected ActiveRepaintManager _remgr;
/** The number of milliseconds per frame (14 by default, which gives /** The number of milliseconds per frame (14 by default, which gives an fps of ~71). */
* an fps of ~71). */
protected long _millisPerFrame = 14; protected long _millisPerFrame = 14;
/** Used to track big delays in calls to our tick method. */ /** Used to track big delays in calls to our tick method. */
@@ -812,8 +669,7 @@ public abstract class FrameManager
/** A temporary bounds rectangle used to avoid lots of object creation. */ /** A temporary bounds rectangle used to avoid lots of object creation. */
protected Rectangle _tbounds = new Rectangle(); protected Rectangle _tbounds = new Rectangle();
/** Used to lazily set the clip when painting popups and other /** Used to lazily set the clip when painting popups and other "layered" components. */
* "layered" components. */
protected boolean[] _clipped = new boolean[1]; protected boolean[] _clipped = new boolean[1];
/** The entites that are ticked each frame. */ /** The entites that are ticked each frame. */
@@ -822,42 +678,32 @@ public abstract class FrameManager
/** If we don't get ticked for 500ms, that's worth complaining about. */ /** If we don't get ticked for 500ms, that's worth complaining about. */
protected static final long BIG_GAP = 500L; protected static final long BIG_GAP = 500L;
/** If we don't get ticked for 100ms and we're hang debugging, /** If we don't get ticked for 100ms and we're hang debugging, complain. */
* complain. */
protected static final long HANG_GAP = 100L; protected static final long HANG_GAP = 100L;
/** Enable this to log warnings when ticking or painting takes too /** Enable this to log warnings when ticking or painting takes too long. */
* long. */
protected static final boolean HANG_DEBUG = false; protected static final boolean HANG_DEBUG = false;
/** A debug hook that toggles debug rendering of sprite paths. */ /** A debug hook that toggles debug rendering of sprite paths. */
protected static RuntimeAdjust.BooleanAdjust _useFlip = protected static RuntimeAdjust.BooleanAdjust _useFlip = new RuntimeAdjust.BooleanAdjust(
new RuntimeAdjust.BooleanAdjust( "When active a flip-buffer will be used to manage our rendering, otherwise a " +
"When active a flip-buffer will be used to manage our " + "volatile back buffer is used [requires restart]", "narya.media.frame",
"rendering, otherwise a volatile back buffer is used " + // back buffer rendering doesn't work on the Mac, so we default to flip buffer on that
"[requires restart]", "narya.media.frame", // platform; we still allow it to be toggled so that we can easily test things when
// back buffer rendering doesn't work on the Mac, so we // they release new JVMs
// default to flip buffer on that platform; we still allow it MediaPrefs.config, RunAnywhere.isMacOS());
// to be toggled so that we can easily test things when they
// release new JVMs
MediaPrefs.config, RunAnywhere.isMacOS());
/** Allows us to tweak the sleep granularity. */ /** Allows us to tweak the sleep granularity. */
protected static RuntimeAdjust.IntAdjust _sleepGranularity = protected static RuntimeAdjust.IntAdjust _sleepGranularity = new RuntimeAdjust.IntAdjust(
new RuntimeAdjust.IntAdjust( "The number of milliseconds slept before checking to see if it's time to queue up a " +
"The number of milliseconds slept before checking to see if " + "new frame tick.", "narya.media.sleep_gran",
"it's time to queue up a new frame tick.", "narya.media.sleep_gran", MediaPrefs.config, RunAnywhere.isWindows() ? 10 : 7);
MediaPrefs.config, RunAnywhere.isWindows() ? 10 : 7);
/** A debug hook that toggles FPS rendering. */ /** A debug hook that toggles FPS rendering. */
protected static RuntimeAdjust.BooleanAdjust _perfDebug = new RuntimeAdjust.BooleanAdjust( protected static RuntimeAdjust.BooleanAdjust _perfDebug = new RuntimeAdjust.BooleanAdjust(
"Toggles frames per second and dirty regions per tick rendering.", "Toggles frames per second and dirty regions per tick rendering.",
"narya.media.fps_display", MediaPrefs.config, false); "narya.media.fps_display", MediaPrefs.config, false);
/** Whether to enable AWT event debugging for the frame. */
protected static final boolean DEBUG_EVENTS = false;
/** The name of the high-performance timer class we attempt to load. */ /** The name of the high-performance timer class we attempt to load. */
protected static final String PERF_TIMER = protected static final String PERF_TIMER = "com.threerings.media.timer.PerfTimer";
"com.threerings.media.timer.PerfTimer";
} }