diff --git a/src/java/com/threerings/media/BackFrameManager.java b/src/java/com/threerings/media/BackFrameManager.java new file mode 100644 index 000000000..301715ed7 --- /dev/null +++ b/src/java/com/threerings/media/BackFrameManager.java @@ -0,0 +1,126 @@ +// +// $Id: BackFrameManager.java,v 1.1 2003/04/26 17:56:26 mdb Exp $ + +package com.threerings.media; + +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.Rectangle; +import java.awt.image.VolatileImage; +import javax.swing.JFrame; + +import com.samskivert.util.StringUtil; + +import com.threerings.media.timer.MediaTimer; + +/** + * A {@link FrameManager} extension that uses a volatile off-screen image + * to do its rendering. + */ +public class BackFrameManager extends FrameManager +{ + // documentation inherited + protected void paint (long tickStamp) + { + // start out assuming we can do an incremental render + boolean incremental = true; + + do { + GraphicsConfiguration gc = _frame.getGraphicsConfiguration(); + + // create our off-screen buffer if necessary + if (_backimg == null || _backimg.getWidth() != _frame.getWidth() || + _backimg.getHeight() != _frame.getHeight()) { + createBackBuffer(gc); + } + + // make sure our back buffer hasn't disappeared + int valres = _backimg.validate(gc); + + // if we've changed resolutions, recreate the buffer + if (valres == VolatileImage.IMAGE_INCOMPATIBLE) { + Log.info("Back buffer incompatible, recreating."); + createBackBuffer(gc); + } + + // if the image wasn't A-OK, we need to rerender the whole + // business rather than just the dirty parts + if (valres != VolatileImage.IMAGE_OK) { + Log.info("Lost back buffer, redrawing."); + incremental = false; + } + + // dirty everything if we're not incrementally rendering + if (!incremental) { + _frame.update(_bgfx); + } + + if (!paint(_bgfx)) { + return; + } + + // we cache our frame's graphics object so that we can avoid + // instantiating a new one on every tick + if (_fgfx == null) { + _fgfx = (Graphics2D)_frame.getGraphics(); + } + _fgfx.drawImage(_backimg, 0, 0, null); + + // if we loop through a second time, we'll need to rerender + // everything + incremental = false; + + } while (_backimg.contentsLost()); + } + + // documentation inherited + protected void restoreFromBack (Rectangle dirty) + { + if (_fgfx == null || _backimg == null) { + return; + } + Log.info("Restoring from back " + StringUtil.toString(dirty) + "."); + _fgfx.setClip(dirty); + _fgfx.drawImage(_backimg, 0, 0, null); + _fgfx.setClip(null); + } + + /** + * Creates the off-screen buffer used to perform double buffered + * rendering of the animated panel. + */ + protected void createBackBuffer (GraphicsConfiguration gc) + { + // if we have an old image, clear it out + if (_backimg != null) { + _backimg.flush(); + _bgfx.dispose(); + } + + // create the offscreen buffer + int width = _frame.getWidth(), height = _frame.getHeight(); + _backimg = gc.createCompatibleVolatileImage(width, height); + + // fill the back buffer with white + _bgfx = (Graphics2D)_backimg.getGraphics(); + _bgfx.fillRect(0, 0, width, height); + + // clear out our frame graphics in case that became invalid for + // the same reasons our back buffer became invalid + if (_fgfx != null) { + _fgfx.dispose(); + _fgfx = null; + } + +// Log.info("Created back buffer [" + width + "x" + height + "]."); + } + + /** The image used to render off-screen. */ + protected VolatileImage _backimg; + + /** The graphics object from our back buffer. */ + protected Graphics2D _bgfx; + + /** The graphics object from our frame. */ + protected Graphics2D _fgfx; +} diff --git a/src/java/com/threerings/media/FlipFrameManager.java b/src/java/com/threerings/media/FlipFrameManager.java new file mode 100644 index 000000000..0b0c0fc3d --- /dev/null +++ b/src/java/com/threerings/media/FlipFrameManager.java @@ -0,0 +1,70 @@ +// +// $Id: FlipFrameManager.java,v 1.1 2003/04/26 17:56:26 mdb Exp $ + +package com.threerings.media; + +import java.awt.BufferCapabilities; +import java.awt.Graphics2D; +import java.awt.ImageCapabilities; +import java.awt.Rectangle; +import java.awt.image.BufferStrategy; + +import javax.swing.JFrame; + +import com.threerings.media.timer.MediaTimer; + +/** + * A {@link FrameManager} extension that uses a flip-buffer (via {@link + * BufferStrategy} to do its rendering. + */ +public class FlipFrameManager extends FrameManager +{ + // documentation inherited + protected void paint (long tickStamp) + { + // create our buffer strategy if we don't already have one + if (_bufstrat == null) { + BufferCapabilities cap = new BufferCapabilities( + new ImageCapabilities(true), new ImageCapabilities(true), + BufferCapabilities.FlipContents.COPIED); + _frame.createBufferStrategy(2); + _bufstrat = _frame.getBufferStrategy(); + } + + // start out assuming we can do an incremental render + boolean incremental = true; + + do { + Graphics2D gfx = (Graphics2D)_bufstrat.getDrawGraphics(); + + // dirty everything if we're not incrementally rendering + if (!incremental) { + Log.info("Doing non-incremental render; contents lost."); + _frame.update(gfx); + } + + // request to paint our participants and components and bail + // if they paint nothing + if (!paint(gfx)) { + return; + } + + // flip our buffer to visible + _bufstrat.show(); + + // if we loop through a second time, we'll need to rerender + // everything + incremental = false; + + } while (_bufstrat.contentsLost()); + } + + // documentation inherited + protected void restoreFromBack (Rectangle dirty) + { + // nothing doing + } + + /** The buffer strategy used to do our rendering. */ + protected BufferStrategy _bufstrat; +} diff --git a/src/java/com/threerings/media/FrameManager.java b/src/java/com/threerings/media/FrameManager.java index f90cb2085..054507549 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.36 2003/04/19 01:04:29 mdb Exp $ +// $Id: FrameManager.java,v 1.37 2003/04/26 17:56:25 mdb Exp $ package com.threerings.media; @@ -11,10 +11,7 @@ import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; -import java.awt.Graphics; -import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; -import java.awt.Image; import java.awt.KeyEventDispatcher; import java.awt.KeyboardFocusManager; import java.awt.Rectangle; @@ -26,15 +23,8 @@ import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; -import java.awt.image.BufferStrategy; -import java.awt.image.VolatileImage; - import java.awt.EventQueue; -import javax.swing.JFrame; -import javax.swing.event.AncestorEvent; -import javax.swing.event.AncestorListener; - import java.util.Date; import java.util.Timer; import java.util.TimerTask; @@ -43,12 +33,13 @@ import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLayeredPane; import javax.swing.RepaintManager; +import javax.swing.event.AncestorEvent; +import javax.swing.event.AncestorListener; import com.samskivert.swing.Label; import com.samskivert.util.DebugChords; -import com.samskivert.util.Interval; -import com.samskivert.util.IntervalManager; import com.samskivert.util.ObserverList; +import com.samskivert.util.RuntimeAdjust; import com.samskivert.util.StringUtil; import com.threerings.media.timer.MediaTimer; @@ -106,7 +97,7 @@ import com.threerings.media.timer.SystemMediaTimer; * SafeScrollPane} or set your scroll panes' viewports to * SIMPLE_SCROLL_MODE. */ -public class FrameManager +public abstract class FrameManager { /** {@link FrameParticipant}s can implement this interface and be * added to the performance display. */ @@ -122,11 +113,11 @@ public class FrameManager * obtain timing information, which is available on every platform, * but returns inaccurate time stamps on many platforms. * - * @see #FrameManager(JFrame, MediaTimer) + * @see #newInstance(JFrame, MediaTimer) */ - public FrameManager (JFrame frame) + public static FrameManager newInstance (JFrame frame) { - this(frame, new SystemMediaTimer()); + return newInstance(frame, new SystemMediaTimer()); } /** @@ -138,7 +129,24 @@ public class FrameManager * * @see GraphicsDevice#setFullScreenWindow */ - public FrameManager (JFrame frame, MediaTimer timer) + public static FrameManager newInstance (JFrame frame, MediaTimer timer) + { + FrameManager fmgr; + if (_useFlip.getValue()) { + Log.info("Creating flip frame manager."); + fmgr = new FlipFrameManager(); + } else { + Log.info("Creating back frame manager."); + fmgr = new BackFrameManager(); + } + fmgr.init(frame, timer); + return fmgr; + } + + /** + * Initializes this frame manager and prepares it for operation. + */ + protected void init (JFrame frame, MediaTimer timer) { _frame = frame; if (frame instanceof ManagedJFrame) { @@ -338,8 +346,8 @@ public class FrameManager _frame.getWidth() > 0 && _frame.getHeight() > 0) { // tick our participants tickParticipants(tickStamp); - // repaint our participants - paintParticipants(tickStamp); + // repaint our participants and components + paint(tickStamp); } } @@ -378,103 +386,46 @@ public class FrameManager /** * Called once per frame to invoke {@link Component#paint} on all of - * our frame participants' components. + * our frame participants' components and all dirty components managed + * by our {@link FrameRepaintManager}. */ - protected void paintParticipants (long tickStamp) + protected abstract void paint (long tickStamp); + + /** + * Paints our frame participants and any dirty components via the + * repaint manager. + * + * @return true if anything was painted, false if not. + */ + protected boolean paint (Graphics2D gfx) { -// // create our buffer strategy if we don't already have one -// if (_bufstrat == null) { -// _frame.createBufferStrategy(2); -// _bufstrat = _frame.getBufferStrategy(); -// } + // paint our frame participants (which want to be handled + // specially) + _participantPaintOp.init(gfx); + _participants.apply(_participantPaintOp); + boolean ppart = _participantPaintOp.paintedSomething(); - // start out assuming we can do an incremental render - boolean incremental = true; - - do { - GraphicsConfiguration gc = _frame.getGraphicsConfiguration(); - - // create our off-screen buffer if necessary - if (_backimg == null || _backimg.getWidth() != _frame.getWidth() || - _backimg.getHeight() != _frame.getHeight()) { - createBackBuffer(gc); - } - - // make sure our back buffer hasn't disappeared - int valres = _backimg.validate(gc); - - // if we've changed resolutions, recreate the buffer - if (valres == VolatileImage.IMAGE_INCOMPATIBLE) { -// Log.info("Back buffer incompatible, recreating."); - createBackBuffer(gc); - } - - // if the image wasn't A-OK, we need to rerender the whole - // business rather than just the dirty parts - if (valres != VolatileImage.IMAGE_OK) { -// Log.info("Lost back buffer, redrawing."); - incremental = false; - } - -// g = _bufstrat.getDrawGraphics(); - - // dirty everything if we're not incrementally rendering - if (!incremental) { - _frame.update(_bgfx); - } - - // paint our frame participants (which want to be handled - // specially) - _participantPaintOp.init(_bgfx); - _participants.apply(_participantPaintOp); - boolean ppart = _participantPaintOp.paintedSomething(); - - // repaint any widgets that have declared they need to be - // repainted since the last tick - boolean pcomp = _remgr.paintComponents(_bgfx, this); - - // if we didn't paint anything, get the fork out of dodge - if (!(ppart || pcomp)) { - return; - } + // repaint any widgets that have declared they need to be + // repainted since the last tick + boolean pcomp = _remgr.paintComponents(gfx, this); + if (ppart || pcomp) { if (_displayPerf && _perfLabel != null) { // render the current performance status - _bgfx.setClip(null); - _perfLabel.render((Graphics2D)_bgfx, FPS_X, FPS_Y); + gfx.setClip(null); + _perfLabel.render(gfx, FPS_X, FPS_Y); } + } - // we cache our frame's graphics object so that we can avoid - // instantiating a new one on every tick - if (_fgfx == null) { - _fgfx = _frame.getGraphics(); - } - _fgfx.drawImage(_backimg, 0, 0, null); - -// _bufstrat.show(); - - // if we loop through a second time, we'll need to rerender - // everything - incremental = false; - - } while (_backimg.contentsLost()); + // let the caller know if anybody painted anything + return (ppart || pcomp); } /** * Called by the {@link ManagedJFrame} when our window was hidden and * reexposed. */ - protected void restoreFromBack (Rectangle dirty) - { - if (_fgfx == null) { - _fgfx = _frame.getGraphics(); - } -// Log.info("Restoring from back buffer " + -// StringUtil.toString(dirty) + "."); - _fgfx.setClip(dirty); - _fgfx.drawImage(_backimg, 0, 0, null); - _fgfx.setClip(null); - } + protected abstract void restoreFromBack (Rectangle dirty); /** * If frame rate display is enabled, builds beginning of performance @@ -509,17 +460,15 @@ public class FrameManager _perfLabel.setText(_perfStatus.toString()); _perfStatus = null; - if (_bgfx == null) { - return; - } - // dirty our previous bounds JComponent comp = (JComponent)_frame.getRootPane(); Dimension lsize = _perfLabel.getSize(); _remgr.addDirtyRegion(comp, FPS_X, FPS_Y, lsize.width, lsize.height); // re-layout our status label - _perfLabel.layout((Graphics2D)_bgfx); + Graphics2D gfx = (Graphics2D)_frame.getGraphics(); + _perfLabel.layout(gfx); + gfx.dispose(); // dirty our new bounds lsize = _perfLabel.getSize(); @@ -530,8 +479,8 @@ public class FrameManager * Renders all components in all {@link JLayeredPane} layers that * intersect the supplied bounds. */ - protected void renderLayers (Graphics g, Component pcomp, Rectangle bounds, - boolean[] clipped) + protected void renderLayers (Graphics2D g, Component pcomp, + Rectangle bounds, boolean[] clipped) { JLayeredPane lpane = JLayeredPane.getLayeredPaneAbove(pcomp); @@ -547,8 +496,9 @@ public class FrameManager * Renders all components in the specified layer of the supplied * layered pane that intersect the supplied bounds. */ - protected void renderLayer (Graphics g, Rectangle bounds, JLayeredPane pane, - boolean[] clipped, Integer layer) + protected void renderLayer (Graphics2D g, Rectangle bounds, + JLayeredPane pane, boolean[] clipped, + Integer layer) { // stop now if there are no components in that layer int ccount = pane.getComponentCountInLayer(layer.intValue()); @@ -586,36 +536,6 @@ public class FrameManager Log.info("Frames in last second: " + ticks); } - /** - * Creates the off-screen buffer used to perform double buffered - * rendering of the animated panel. - */ - protected void createBackBuffer (GraphicsConfiguration gc) - { - // if we have an old image, clear it out - if (_backimg != null) { - _backimg.flush(); - _bgfx.dispose(); - } - - // create the offscreen buffer - int width = _frame.getWidth(), height = _frame.getHeight(); - _backimg = gc.createCompatibleVolatileImage(width, height); - - // fill the back buffer with white - _bgfx = _backimg.getGraphics(); - _bgfx.fillRect(0, 0, width, height); - - // clear out our frame graphics in case that became invalid for - // the same reasons our back buffer became invalid - if (_fgfx != null) { - _fgfx.dispose(); - _fgfx = null; - } - -// Log.info("Created back buffer [" + width + "x" + height + "]."); - } - /** * 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 @@ -708,7 +628,7 @@ public class FrameManager * Sets the graphics context to which the frame participants * render themselves. */ - public void init (Graphics g) + public void init (Graphics2D g) { _g = g; _painted = 0; @@ -788,7 +708,7 @@ public class FrameManager } /** The graphics context to which the participants render. */ - protected Graphics _g; + protected Graphics2D _g; /** The number of participants that were actually painted. */ protected int _painted; @@ -865,12 +785,6 @@ public class FrameManager /** Our custom repaint manager. */ protected FrameRepaintManager _remgr; -// /** The buffer strategy used to do our rendering. */ -// protected BufferStrategy _bufstrat; - - /** The image used to render off-screen. */ - protected VolatileImage _backimg; - /** The number of milliseconds per frame (14 by default, which gives * an fps of ~71). */ protected long _millisPerFrame = 14; @@ -884,12 +798,6 @@ public class FrameManager /** Used to track and report frames per second. */ protected float[] _fps = new float[2]; - /** The graphics object from our back buffer. */ - protected Graphics _bgfx; - - /** The graphics object from our frame. */ - protected Graphics _fgfx; - /** Used to avoid creating rectangles when rendering layered * components. */ protected Rectangle _lbounds = new Rectangle(); @@ -938,6 +846,14 @@ public class FrameManager /** The y-coordinate at which the frames per second is rendered. */ protected static final int FPS_Y = 27; + /** A debug hook that toggles debug rendering of sprite paths. */ + protected static RuntimeAdjust.BooleanAdjust _useFlip = + new RuntimeAdjust.BooleanAdjust( + "When active a flip-buffer will be used to manage our " + + "rendering, otherwise a volatile back buffer is used " + + "[requires restart]", "narya.media.frame", + MediaPrefs.config, false); + /** A debug hook that allows toggling the frame rate display. */ protected DebugChords.Hook FPS_DISPLAY_HOOK = new DebugChords.Hook() { public void invoke () { diff --git a/src/java/com/threerings/media/FrameRepaintManager.java b/src/java/com/threerings/media/FrameRepaintManager.java index dd2b76e36..004f27031 100644 --- a/src/java/com/threerings/media/FrameRepaintManager.java +++ b/src/java/com/threerings/media/FrameRepaintManager.java @@ -1,5 +1,5 @@ // -// $Id: FrameRepaintManager.java,v 1.18 2003/04/02 03:59:17 mdb Exp $ +// $Id: FrameRepaintManager.java,v 1.19 2003/04/26 17:56:26 mdb Exp $ package com.threerings.media; @@ -8,6 +8,7 @@ import java.applet.Applet; import java.awt.Component; import java.awt.Font; import java.awt.Frame; +import java.awt.Graphics2D; import java.awt.Graphics; import java.awt.Image; import java.awt.Rectangle; @@ -384,7 +385,7 @@ public class FrameRepaintManager extends RepaintManager // we also need to repaint any components in this layer // that are above our freshly repainted component - fmgr.renderLayers(g, ocomp, _cbounds, _clipped); + fmgr.renderLayers((Graphics2D)g, ocomp, _cbounds, _clipped); } else if (root != null) { if (DEBUG) { diff --git a/tests/src/java/com/threerings/media/util/PathViz.java b/tests/src/java/com/threerings/media/util/PathViz.java index a254b2a39..91f5195d6 100644 --- a/tests/src/java/com/threerings/media/util/PathViz.java +++ b/tests/src/java/com/threerings/media/util/PathViz.java @@ -1,5 +1,5 @@ // -// $Id: PathViz.java,v 1.2 2002/09/18 20:09:55 mdb Exp $ +// $Id: PathViz.java,v 1.3 2003/04/26 17:56:26 mdb Exp $ package com.threerings.media.util; @@ -57,7 +57,7 @@ public class PathViz extends MediaPanel public static void main (String[] args) { ManagedJFrame frame = new ManagedJFrame("Path viz"); - FrameManager fmgr = new FrameManager(frame); + FrameManager fmgr = FrameManager.newInstance(frame); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setContentPane(new PathViz(fmgr)); diff --git a/tests/src/java/com/threerings/miso/client/ScrollingTestApp.java b/tests/src/java/com/threerings/miso/client/ScrollingTestApp.java index ecf174dc6..0defce360 100644 --- a/tests/src/java/com/threerings/miso/client/ScrollingTestApp.java +++ b/tests/src/java/com/threerings/miso/client/ScrollingTestApp.java @@ -1,5 +1,5 @@ // -// $Id: ScrollingTestApp.java,v 1.22 2003/04/25 15:52:25 mdb Exp $ +// $Id: ScrollingTestApp.java,v 1.23 2003/04/26 17:56:26 mdb Exp $ package com.threerings.miso.client; @@ -76,7 +76,7 @@ public class ScrollingTestApp _frame = new ScrollingFrame(gc); // set up our frame manager - _framemgr = new FrameManager(_frame); + _framemgr = FrameManager.newInstance(_frame); // we don't need to configure anything ResourceManager rmgr = new ResourceManager("rsrc"); diff --git a/tests/src/java/com/threerings/miso/viewer/ViewerApp.java b/tests/src/java/com/threerings/miso/viewer/ViewerApp.java index d7b31d7ea..ea75001bc 100644 --- a/tests/src/java/com/threerings/miso/viewer/ViewerApp.java +++ b/tests/src/java/com/threerings/miso/viewer/ViewerApp.java @@ -1,5 +1,5 @@ // -// $Id: ViewerApp.java,v 1.38 2003/04/17 19:21:17 mdb Exp $ +// $Id: ViewerApp.java,v 1.39 2003/04/26 17:56:26 mdb Exp $ package com.threerings.miso.viewer; @@ -63,7 +63,7 @@ public class ViewerApp // create the window _frame = new ViewerFrame(gc); - _framemgr = new FrameManager(_frame); + _framemgr = FrameManager.newInstance(_frame); // we don't need to configure anything ResourceManager rmgr = new ResourceManager("rsrc");