Modified rendering to use a hand managed volatile image as a back buffer

instead of using a buffer strategy. This will shortly be extended to
support more efficient scrolling as well.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1021 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2002-02-19 01:23:56 +00:00
parent 8d56ec853a
commit 63a32a6332
3 changed files with 112 additions and 107 deletions
@@ -1,5 +1,5 @@
// //
// $Id: AnimatedPanel.java,v 1.8 2002/02/18 06:05:58 mdb Exp $ // $Id: AnimatedPanel.java,v 1.9 2002/02/19 01:23:56 mdb Exp $
package com.threerings.media.animation; package com.threerings.media.animation;
@@ -7,15 +7,20 @@ import java.awt.AWTException;
import java.awt.BufferCapabilities; import java.awt.BufferCapabilities;
import java.awt.Canvas; import java.awt.Canvas;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.ImageCapabilities; import java.awt.ImageCapabilities;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.image.BufferStrategy; import java.awt.image.BufferStrategy;
import java.awt.image.VolatileImage;
import java.util.List; import java.util.List;
import com.samskivert.util.Histogram;
import com.samskivert.util.StringUtil;
import com.threerings.media.Log; import com.threerings.media.Log;
import com.threerings.media.sprite.SpriteManager; import com.threerings.media.sprite.SpriteManager;
@@ -33,9 +38,7 @@ import com.threerings.media.sprite.SpriteManager;
* deactivate and reactivate the underlying animation manager. * deactivate and reactivate the underlying animation manager.
* *
* <p> Sub-classes should override {@link #render} to draw their * <p> Sub-classes should override {@link #render} to draw their
* panel-specific contents, and may choose to override {@link * panel-specific contents.
* #invalidateRects} and {@link #invalidateRect} to optimize their
* internal rendering.
*/ */
public class AnimatedPanel extends Canvas implements AnimatedView public class AnimatedPanel extends Canvas implements AnimatedView
{ {
@@ -133,30 +136,8 @@ public class AnimatedPanel extends Canvas implements AnimatedView
paint(g); paint(g);
} }
/**
* Renders the panel to the given graphics object. Sub-classes
* should override this method to paint their panel-specific
* contents.
*/
protected void render (Graphics2D gfx)
{
// nothing for now
}
// documentation inherited // documentation inherited
public void invalidateRects (List rects) public void paintImmediately (List invalidRects)
{
// nothing for now
}
// documentation inherited
public void invalidateRect (Rectangle rect)
{
// nothing for now
}
// documentation inherited
public void paintImmediately (int invalidRectCount)
{ {
// no use in painting if we're not showing or if we've not yet // no use in painting if we're not showing or if we've not yet
// been validated // been validated
@@ -164,12 +145,19 @@ public class AnimatedPanel extends Canvas implements AnimatedView
return; return;
} }
if (_strategy == null) { // track how long it was since we were last painted
// create and obtain a reference to the buffer strategy long now = System.currentTimeMillis();
createBufferStrategy(BUFFER_COUNT); // if (_last != 0) {
_strategy = getBufferStrategy(); // int delta = (int)(now-_last);
Log.info("Created buffer strategy [strategy=" + _strategy + "]."); // _histo.addValue(delta);
}
// // dump the histogram every ten seconds
// if (_last % (10*1000) < 50) {
// Log.info("Render histogram.");
// Log.info(StringUtil.toMatrixString(_histo.getBuckets(), 10, 3));
// }
// }
// _last = now;
// if scrolling is enabled, determine the scrolling delta to be // if scrolling is enabled, determine the scrolling delta to be
// used and do the business // used and do the business
@@ -177,7 +165,7 @@ public class AnimatedPanel extends Canvas implements AnimatedView
if (_stime != 0) { if (_stime != 0) {
// compute the total distance scrolled since we started (to // compute the total distance scrolled since we started (to
// avoid rounding errors) // avoid rounding errors)
long now = System.currentTimeMillis(); // long now = System.currentTimeMillis();
// determine how many pixels further along we've moved and // determine how many pixels further along we've moved and
// make a note of our latest position // make a note of our latest position
@@ -199,30 +187,72 @@ public class AnimatedPanel extends Canvas implements AnimatedView
// if we didn't scroll and have no invalid rects, there's no need // if we didn't scroll and have no invalid rects, there's no need
// to repaint anything // to repaint anything
if (invalidRectCount == 0 && dx == 0 && dy == 0) { if (invalidRects.size() == 0 && dx == 0 && dy == 0) {
return; return;
} }
// render the panel // create our off-screen buffer if necessary
Graphics g = null; GraphicsConfiguration gc = getGraphicsConfiguration();
try { if (_backimg == null) {
g = _strategy.getDrawGraphics(); createBackBuffer(gc);
}
// if we're scrolling, do the deed // render into our back buffer
if (_stime != 0) { do {
Dimension size = getSize(); // make sure our back buffer hasn't disappeared
g.copyArea(0, 0, size.width, size.height, -dx, -dy); 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);
} }
// now do our actual rendering Graphics g = null;
render((Graphics2D)g); try {
g = _backimg.getGraphics();
} finally { // if the image wasn't A-OK, we need to rerender the whole
if (g != null) { // business rather than just the dirty parts
if (valres != VolatileImage.IMAGE_OK) {
invalidRects.clear();
invalidRects.add(new Rectangle(
0, 0, getWidth(), getHeight()));
Log.info("Lost back buffer, redrawing.");
} else if (_stime != 0) {
// if it was OK, we may need to do some scrolling
Dimension size = getSize();
g.copyArea(0, 0, size.width, size.height, -dx, -dy);
}
// now do our actual rendering
render((Graphics2D)g, invalidRects);
} finally {
g.dispose(); g.dispose();
} }
}
_strategy.show(); // draw the back buffer to the screen
try {
g = getGraphics();
// iterate through the invalid rectangles, copying those
// areas from the back buffer to the display
int isize = invalidRects.size();
for (int i = 0; i < isize; i++) {
Rectangle rect = (Rectangle)invalidRects.get(i);
g.setClip(rect);
g.drawImage(_backimg, 0, 0, null);
}
} finally {
if (g != null) {
g.dispose();
}
}
} while (_backimg.contentsLost());
} }
/** /**
@@ -237,40 +267,30 @@ public class AnimatedPanel extends Canvas implements AnimatedView
// nothing to do here // nothing to do here
} }
// documentation inherited /**
public void createBufferStrategy (int numBuffers) * Requests that the supplied list of invalid rectangles be redrawn in
* the supplied graphics context. Sub-classes should override this
* method to do the actual rendering for their display.
*/
protected void render (Graphics2D gfx, List invalidRects)
{ {
// explicitly avoid trying to create a page-flipping strategy, as // nothing for now
// page-flipping seems to result in artifacts in certain }
// conditions, and the buffer strategy's volatile images are
// irretrievably lost when the panel is hidden.
// try an accelerated blitting strategy /**
BufferCapabilities bufferCaps = new BufferCapabilities( * Creates the off-screen buffer used to perform double buffered
new ImageCapabilities(true), new ImageCapabilities(true), null); * rendering of the animated panel.
try { */
createBufferStrategy(numBuffers, bufferCaps); protected void createBackBuffer (GraphicsConfiguration gc)
Log.info("Created accelerated blitting strategy."); {
return; _backimg = gc.createCompatibleVolatileImage(getWidth(), getHeight());
} catch (AWTException e) {
// failed, fall through to the next potential strategy
}
// try an un-accelerated blitting strategy
bufferCaps = new BufferCapabilities(
new ImageCapabilities(false), new ImageCapabilities(false), null);
try {
createBufferStrategy(numBuffers, bufferCaps);
} catch (AWTException e) {
throw new InternalError("Could not create a buffer strategy");
}
} }
/** The animation manager we use in this panel. */ /** The animation manager we use in this panel. */
protected AnimationManager _animmgr; protected AnimationManager _animmgr;
/** The buffer strategy used for optimal animation rendering. */ /** The image used to render off-screen. */
protected BufferStrategy _strategy; protected VolatileImage _backimg;
/** The scrolling velocity in milliseconds per pixel. */ /** The scrolling velocity in milliseconds per pixel. */
protected int _msppx, _msppy; protected int _msppx, _msppy;
@@ -282,6 +302,12 @@ public class AnimatedPanel extends Canvas implements AnimatedView
* scrolling velocity was last set. */ * scrolling velocity was last set. */
protected int _lastx, _lasty; protected int _lastx, _lasty;
/** The last time we were rendered. */
protected long _last;
/** A histogram for tracking how frequently we're rendered. */
protected Histogram _histo = new Histogram(0, 1, 100);
/** The number of buffers to use when rendering. */ /** The number of buffers to use when rendering. */
protected static final int BUFFER_COUNT = 2; protected static final int BUFFER_COUNT = 2;
} }
@@ -1,5 +1,5 @@
// //
// $Id: AnimatedView.java,v 1.2 2002/02/17 23:39:32 mdb Exp $ // $Id: AnimatedView.java,v 1.3 2002/02/19 01:23:56 mdb Exp $
package com.threerings.media.animation; package com.threerings.media.animation;
@@ -14,32 +14,14 @@ import java.util.List;
*/ */
public interface AnimatedView public interface AnimatedView
{ {
/**
* Invalidate a list of rectangles in screen pixel coordinates in the
* scene view for later repainting.
*
* @param rects the list of {@link java.awt.Rectangle} objects.
*/
public void invalidateRects (List rects);
/**
* Invalidates a rectangle in screen pixel coordinates in the scene
* view for later repainting.
*
* @param rect the {@link java.awt.Rectangle} to dirty.
*/
public void invalidateRect (Rectangle rect);
/** /**
* Requests that the animated view paint itself immediately (that it * Requests that the animated view paint itself immediately (that it
* complete the painting process before returning from this function). * complete the painting process before returning from this function).
* This will only be called on the AWT thread and when it is safe to * This will only be called on the AWT thread and when it is safe to
* paint. * paint.
* *
* @param invalidRectCount the number of invalide regions that * @param invalidRects the list of rectangles that have been
* motivated the animation manager to repaint the animated view (which * invalidated since the last call to this method.
* could be zero if the view is scrolling and otherwise has no dirty
* regions)
*/ */
public void paintImmediately (int invalidRectCount); public void paintImmediately (List invalidRects);
} }
@@ -1,5 +1,5 @@
// //
// $Id: AnimationManager.java,v 1.6 2002/02/18 06:05:59 mdb Exp $ // $Id: AnimationManager.java,v 1.7 2002/02/19 01:23:56 mdb Exp $
package com.threerings.media.animation; package com.threerings.media.animation;
@@ -294,15 +294,11 @@ public class AnimationManager
// that this will also clear out the contents of our internal // that this will also clear out the contents of our internal
// dirty rectangle list. // dirty rectangle list.
List rects = mergeDirtyRects(_dirty); List rects = mergeDirtyRects(_dirty);
int rcount = rects.size();
// invalidate screen-rects dirtied by sprites and/or animations // invalidate screen-rects dirtied by sprites and/or animations
if (rcount > 0 || _scrollvel > 0) { if (rects.size() > 0 || _scrollvel > 0) {
// pass the dirty-rects on to the scene view // pass the dirty-rects on to the animated view and repaint
_view.invalidateRects(rects); _view.paintImmediately(rects);
// refresh the display
_view.paintImmediately(rcount);
} }
// remove any finished animations // remove any finished animations
@@ -316,6 +312,7 @@ public class AnimationManager
// request for at least one more tick since we started // request for at least one more tick since we started
// this tick, so we want to queue up another tick // this tick, so we want to queue up another tick
// immediately // immediately
// Log.info("Queueing immediate tick.");
queueTick(); queueTick();
} }
} }