Factored out the flip-buffer versus volatile image back-buffer code into

start-time selectable implementations. Modified back buffer to use
BufferCapabilities.FlipContents.COPIED which should do what we need in
terms of providing us with the current frame's contents on which to paint.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2468 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2003-04-26 17:56:26 +00:00
parent b907bc02b0
commit 055bd6a642
7 changed files with 277 additions and 164 deletions
@@ -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;
}
@@ -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;
}
+72 -156
View File
@@ -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
* <code>SIMPLE_SCROLL_MODE</code>.
*/
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 () {
@@ -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) {
@@ -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));
@@ -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");
@@ -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");