diff --git a/src/java/com/threerings/cast/ActionFrames.java b/src/java/com/threerings/cast/ActionFrames.java index 5402d1553..b2d18bf0e 100644 --- a/src/java/com/threerings/cast/ActionFrames.java +++ b/src/java/com/threerings/cast/ActionFrames.java @@ -1,5 +1,5 @@ // -// $Id: ActionFrames.java,v 1.8 2003/01/08 04:09:02 mdb Exp $ +// $Id: ActionFrames.java,v 1.9 2003/01/20 19:37:58 mdb Exp $ package com.threerings.cast; @@ -49,10 +49,4 @@ public interface ActionFrames * colorizations applied to the frame images. */ public ActionFrames cloneColorized (Colorization[] zations); - - /** - * Returns the estimated memory usage in bytes for all images cached - * internally for use by the action frames. - */ - public long getEstimatedMemoryUsage (); } diff --git a/src/java/com/threerings/cast/CharacterManager.java b/src/java/com/threerings/cast/CharacterManager.java index 723df9f42..086a12693 100644 --- a/src/java/com/threerings/cast/CharacterManager.java +++ b/src/java/com/threerings/cast/CharacterManager.java @@ -1,5 +1,5 @@ // -// $Id: CharacterManager.java,v 1.32 2003/01/17 02:28:32 mdb Exp $ +// $Id: CharacterManager.java,v 1.33 2003/01/20 19:37:58 mdb Exp $ package com.threerings.cast; @@ -43,15 +43,16 @@ public class CharacterManager _actions.put(action.name, action); } - // create our in-memory action cache + // create a cache for our composited action frames int acsize = _cacheSize.getValue(); Log.debug("Creating action cache [size=" + acsize + "k]."); - _frames = new LRUHashMap(acsize*1024, new LRUHashMap.ItemSizer() { + _frameCache = new LRUHashMap(acsize*1024, new LRUHashMap.ItemSizer() { public int computeSize (Object value) { - return (int)((ActionFrames)value).getEstimatedMemoryUsage(); + return (int)((CompositedMultiFrameImage) + value).getEstimatedMemoryUsage(); } }); - _frames.setTracking(true); // TODO + _frameCache.setTracking(true); // TODO } /** @@ -169,36 +170,22 @@ public class CharacterManager CharacterDescriptor descrip, String action) throws NoSuchComponentException { - // first check the in-memory cache; which is keyed on both values Tuple key = new Tuple(descrip, action); - ActionFrames frames = (ActionFrames)_frames.get(key); - - // next check the disk cache - if (frames == null && _acache != null) { - frames = _acache.getActionFrames(descrip, action); - // cache the result in memory - _frames.put(key, frames); - } - - // if that failed, we'll just have to generate the danged things + ActionFrames frames = (ActionFrames)_actionFrames.get(key); if (frames == null) { - // do the compositing + // this doesn't actually composite the images, but prepares an + // object to be able to do so frames = createCompositeFrames(descrip, action); - // cache the result on disk if we've got such a cache - if (_acache != null) { - _acache.cacheActionFrames(descrip, action, frames); - } - // cache the result in memory as well - _frames.put(key, frames); + _actionFrames.put(key, frames); } - // periodically report our action cache performance + // periodically report our frame image cache performance if (!_cacheStatThrottle.throttleOp()) { long size = getEstimatedCacheMemoryUsage(); - int[] eff = _frames.getTrackedEffectiveness(); - Log.debug("CharacterManager LRU " + - "[mem=" + (size / 1024) + "k, size=" + _frames.size() + - ", hits=" + eff[0] + ", misses=" + eff[1] + "]."); + int[] eff = _frameCache.getTrackedEffectiveness(); + Log.debug("CharacterManager LRU [mem=" + (size / 1024) + "k" + + ", size=" + _frameCache.size() + ", hits=" + eff[0] + + ", misses=" + eff[1] + "]."); } return frames; @@ -211,10 +198,10 @@ public class CharacterManager protected long getEstimatedCacheMemoryUsage () { long size = 0; - Iterator iter = _frames.values().iterator(); + Iterator iter = _frameCache.values().iterator(); while (iter.hasNext()) { - ActionFrames frame = (ActionFrames)iter.next(); - size += frame.getEstimatedMemoryUsage(); + size += ((CompositedMultiFrameImage) + iter.next()).getEstimatedMemoryUsage(); } return size; } @@ -257,7 +244,7 @@ public class CharacterManager // use those to create an entity that will lazily composite things // together as they are needed - return new CompositedActionFrames(_imgr, action, sources); + return new CompositedActionFrames(_imgr, _frameCache, action, sources); } /** The image manager with whom we interact. */ @@ -269,8 +256,12 @@ public class CharacterManager /** A table of our action sequences. */ protected HashMap _actions = new HashMap(); + /** A table of composited action sequences (these don't reference the + * actual image data directly and thus take up little memory). */ + protected HashMap _actionFrames = new HashMap(); + /** A cache of composited animation frames. */ - protected LRUHashMap _frames; + protected LRUHashMap _frameCache; /** The character class to be created. */ protected Class _charClass = CharacterSprite.class; @@ -287,5 +278,5 @@ public class CharacterManager new RuntimeAdjust.IntAdjust( "Size (in kb of memory used) of the character manager LRU " + "action cache [requires restart]", "narya.cast.action_cache_size", - CastPrefs.config, 2*1024); + CastPrefs.config, 1024); } diff --git a/src/java/com/threerings/cast/CompositedActionFrames.java b/src/java/com/threerings/cast/CompositedActionFrames.java index 895ac6829..8f45601cd 100644 --- a/src/java/com/threerings/cast/CompositedActionFrames.java +++ b/src/java/com/threerings/cast/CompositedActionFrames.java @@ -1,5 +1,5 @@ // -// $Id: CompositedActionFrames.java,v 1.14 2003/01/17 02:28:32 mdb Exp $ +// $Id: CompositedActionFrames.java,v 1.15 2003/01/20 19:37:58 mdb Exp $ package com.threerings.cast; @@ -11,6 +11,7 @@ import java.awt.Transparency; import java.util.Arrays; import java.util.Comparator; +import com.samskivert.util.LRUHashMap; import com.samskivert.util.StringUtil; import com.threerings.media.image.Colorization; @@ -47,8 +48,8 @@ public class CompositedActionFrames * source frames and colorization configuration. The actual component * frame images will not be composited until they are requested. */ - public CompositedActionFrames (ImageManager imgr, String action, - ComponentFrames[] sources) + public CompositedActionFrames (ImageManager imgr, LRUHashMap frameCache, + String action, ComponentFrames[] sources) { // sanity check if (sources == null || sources.length == 0) { @@ -56,16 +57,15 @@ public class CompositedActionFrames "frames! [sources=" + StringUtil.toString(sources) + "]."; throw new RuntimeException(errmsg); } + _imgr = imgr; + _frameCache = frameCache; _sources = sources; _action = action; - // the sources must all have the same orientation count, and each - // orientation must also have the same frame count, so we just use - // the counts from the first source and orientation + // the sources must all have the same orientation count, so we + // just use the first _orientCount = _sources[0].frames.getOrientationCount(); - _frameCount = _sources[0].frames.getFrames(NORTH).getFrameCount(); - _images = new CompositedMirage[_orientCount][_frameCount]; } // documentation inherited from interface @@ -75,59 +75,33 @@ public class CompositedActionFrames } // documentation inherited from interface - public TrimmedMultiFrameImage getFrames (final int orient) + public TrimmedMultiFrameImage getFrames (int orient) { - return new TrimmedMultiFrameImage() { - // documentation inherited - public int getFrameCount () { - return _frameCount; - } - - // documentation inherited from interface - public int getWidth (int index) { - // composite the frame if necessary - if (_images[orient][index] == null) { - getFrame(orient, index); - } - return _images[orient][index].getWidth(); - } - - // documentation inherited from interface - public int getHeight (int index) { - // composite the frame if necessary - if (_images[orient][index] == null) { - getFrame(orient, index); - } - return _images[orient][index].getHeight(); - } - - // documentation inherited from interface - public void paintFrame (Graphics2D g, int index, int x, int y) { - getFrame(orient, index).paint(g, x, y); - } - - // documentation inherited from interface - public boolean hitTest (int index, int x, int y) { - return getFrame(orient, index).hitTest(x, y); - } - - // documentation inherited from interface - public void getTrimmedBounds (int index, Rectangle bounds) { - bounds.setBounds(0, 0, getWidth(index), getHeight(index)); - } - }; + _key.setOrient(orient); + CompositedMultiFrameImage cmfi = + (CompositedMultiFrameImage)_frameCache.get(_key); + if (cmfi == null) { + cmfi = new CompositedMultiFrameImage( + _imgr, _sources, _action, orient); + _frameCache.put(new CompositedFramesKey(orient), cmfi); + } + return cmfi; } // documentation inherited from interface public int getXOrigin (int orient, int frameIdx) { - return _images[orient][frameIdx].getXOrigin(); + CompositedMultiFrameImage cmfi = (CompositedMultiFrameImage) + getFrames(orient); + return cmfi.getXOrigin(frameIdx); } // documentation inherited from interface public int getYOrigin (int orient, int frameIdx) { - return _images[orient][frameIdx].getYOrigin(); + CompositedMultiFrameImage cmfi = (CompositedMultiFrameImage) + getFrames(orient); + return cmfi.getYOrigin(frameIdx); } // documentation inherited from interface @@ -136,161 +110,52 @@ public class CompositedActionFrames throw new RuntimeException("What you talkin' about Willis?"); } - // documentation inherited from interface - public long getEstimatedMemoryUsage () + /** Used to cache composited frames for a particular action and + * orientation. */ + protected class CompositedFramesKey { - long size = 0; - for (int orient = 0; orient < _orientCount; orient++) { - for (int ii = 0; ii < _images[orient].length; ii++) { - Mirage mirage = _images[orient][ii]; - if (mirage == null) { - continue; - } - size += mirage.getEstimatedMemoryUsage(); - } - } - return size; - } - - // documentation inherited - protected Mirage getFrame (int orient, int index) - { - // create the frame if we don't already have it - if (_images[orient][index] == null) { -// Log.info("Compositing [action=" + _action + ", orient=" + orient + -// ", index=" + index + "]."); - _images[orient][index] = new CompositedMirage(_imgr, orient, index); - } - - return _images[orient][index]; - } - - /** - * Used to create our mirage using the source action frame images. - */ - protected class CompositedMirage extends VolatileMirage - implements Comparator - { - public CompositedMirage (ImageManager imgr, int orient, int index) - { - super(imgr, new Rectangle(0, 0, 0, 0)); - - // keep these for later + public CompositedFramesKey (int orient) { _orient = orient; - _index = index; - - // first we need to determine the bounds of the rectangle that - // will enclose all of our various components - Rectangle tbounds = new Rectangle(); - int scount = _sources.length; - for (int ii = 0; ii < scount; ii++) { - TrimmedMultiFrameImage source = - _sources[ii].frames.getFrames(orient); - source.getTrimmedBounds(index, tbounds); - - // the first one defines our initial bounds - if (_bounds.width == 0 && _bounds.height == 0) { - _bounds.setBounds(tbounds); - } else { - _bounds.add(tbounds); - } - } - - // compute our new origin - _origin.x = (_sources[0].frames.getXOrigin(orient, index) - - _bounds.x); - _origin.y = (_sources[0].frames.getYOrigin(orient, index) - - _bounds.y); -// Log.info("New origin [x=" + _origin.x + ", y=" + _origin.y + "]."); - - // render our volatile image for the first time - createVolatileImage(); } - public int getXOrigin () - { - return _origin.x; + public void setOrient (int orient) { + _orient = orient; } - public int getYOrigin () - { - return _origin.y; + public CompositedActionFrames getOwner () { + return CompositedActionFrames.this; } - // documentation inherited from interface - public int compare (Object o1, Object o2) - { - ComponentFrames cf1 = (ComponentFrames)o1, - cf2 = (ComponentFrames)o2; - return (cf1.ccomp.componentClass.getRenderPriority( - _action, _sorient) - - cf2.ccomp.componentClass.getRenderPriority( - _action, _sorient)); + public boolean equals (Object other) { + CompositedFramesKey okey = (CompositedFramesKey)other; + return ((getOwner() == okey.getOwner()) && + (_orient == okey._orient)); } - // documentation inherited - protected int getTransparency () - { - return Transparency.BITMASK; - } - - // documentation inherited - protected void refreshVolatileImage () - { -// long start = System.currentTimeMillis(); - - // sort the sources appropriately for this orientation - _sorient = _orient; - Arrays.sort(_sources, this); - - // now render each of the components into a composited frame - int scount = _sources.length; - Graphics2D g = (Graphics2D)_image.getGraphics(); - try { - for (int ii = 0; ii < scount; ii++) { - TrimmedMultiFrameImage source = - _sources[ii].frames.getFrames(_orient); - source.paintFrame(g, _index, -_bounds.x, -_bounds.y); - } - } finally { - // clean up after ourselves - if (g != null) { - g.dispose(); - } - } - -// Log.info("Composited [orient=" + _orient + ", index=" + _index + -// ", tbounds=" + StringUtil.toString(_bounds) + "]."); - -// long now = System.currentTimeMillis(); -// Log.info("Composited " + scount + " frames in " + -// (now-start) + " millis."); + public int hashCode () { + return CompositedActionFrames.this.hashCode() ^ _orient; } protected int _orient; - protected int _index; - protected Point _origin = new Point(); } /** The image manager from whom we can obtain prepared volatile images * onto which to render our composited actions. */ protected ImageManager _imgr; + /** Used to cache our composited action frame images. */ + protected LRUHashMap _frameCache; + /** The action for which we're compositing frames. */ protected String _action; - /** The orientation for which we're currently sorting. */ - protected int _sorient; - /** The number of orientations. */ protected int _orientCount; - /** The number of frames in each orientation. */ - protected int _frameCount; - /** Our source components and action frames. */ protected ComponentFrames[] _sources; - /** The frame images. */ - protected CompositedMirage[][] _images; + /** Used to avoid creating a new key object every time we do a cache + * lookup. */ + protected CompositedFramesKey _key = new CompositedFramesKey(0); } diff --git a/src/java/com/threerings/cast/CompositedMultiFrameImage.java b/src/java/com/threerings/cast/CompositedMultiFrameImage.java new file mode 100644 index 000000000..6df5231dd --- /dev/null +++ b/src/java/com/threerings/cast/CompositedMultiFrameImage.java @@ -0,0 +1,220 @@ +// +// $Id: CompositedMultiFrameImage.java,v 1.1 2003/01/20 19:37:58 mdb Exp $ + +package com.threerings.cast; + +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Transparency; + +import java.util.Arrays; +import java.util.Comparator; + +import com.threerings.media.image.ImageManager; +import com.threerings.media.image.Mirage; +import com.threerings.media.image.VolatileMirage; + +import com.threerings.cast.CompositedActionFrames.ComponentFrames; +import com.threerings.cast.TrimmedMultiFrameImage; + +/** + * Used to composite the action frames for a particular orientation of a + * {@link CompositedActionSequence}. + */ +public class CompositedMultiFrameImage + implements TrimmedMultiFrameImage +{ + public CompositedMultiFrameImage ( + ImageManager imgr, ComponentFrames[] sources, + String action, int orient) + { + _imgr = imgr; + _sources = sources; + _action = action; + _orient = orient; + + // create our frame images (which will do the compositing) + int fcount = sources[0].frames.getFrames(orient).getFrameCount(); + _images = new CompositedMirage[fcount]; + for (int ii = 0; ii < fcount; ii++) { + _images[ii] = new CompositedMirage(ii); + } + } + + // documentation inherited + public int getFrameCount () { + return _images.length; + } + + // documentation inherited from interface + public int getWidth (int index) { + return _images[index].getWidth(); + } + + // documentation inherited from interface + public int getHeight (int index) { + return _images[index].getHeight(); + } + + public int getXOrigin (int index) { + return _images[index].getXOrigin(); + } + + public int getYOrigin (int index) { + return _images[index].getYOrigin(); + } + + // documentation inherited from interface + public void paintFrame (Graphics2D g, int index, int x, int y) { + _images[index].paint(g, x, y); + } + + // documentation inherited from interface + public boolean hitTest (int index, int x, int y) { + return _images[index].hitTest(x, y); + } + + // documentation inherited from interface + public void getTrimmedBounds (int index, Rectangle bounds) { + bounds.setBounds(0, 0, getWidth(index), getHeight(index)); + } + + /** + * Returns the estimated memory usage of our composited frame images. + */ + public long getEstimatedMemoryUsage () + { + long size = 0; + for (int ii = 0; ii < _images.length; ii++) { + size += _images[ii].getEstimatedMemoryUsage(); + } + return size; + } + + // documentation inherited + protected Mirage getFrame (int orient, int index) + { + return _images[index]; + } + + /** The image manager from whom we load our images. */ + protected ImageManager _imgr; + + /** The action frames from which we obtain our source imagery. */ + protected ComponentFrames[] _sources; + + /** The action we're compositing. */ + protected String _action; + + /** The orientation we're compositing. */ + protected int _orient; + + /** Our composited action frame images. */ + protected CompositedMirage[] _images; + + /** + * Used to create our mirage using the source action frame images. + */ + protected class CompositedMirage extends VolatileMirage + implements Comparator + { + public CompositedMirage (int index) + { + super(CompositedMultiFrameImage.this._imgr, + new Rectangle(0, 0, 0, 0)); + + // keep this for later + _index = index; + + // first we need to determine the bounds of the rectangle that + // will enclose all of our various components + Rectangle tbounds = new Rectangle(); + int scount = _sources.length; + for (int ii = 0; ii < scount; ii++) { + TrimmedMultiFrameImage source = + _sources[ii].frames.getFrames(_orient); + source.getTrimmedBounds(index, tbounds); + + // the first one defines our initial bounds + if (_bounds.width == 0 && _bounds.height == 0) { + _bounds.setBounds(tbounds); + } else { + _bounds.add(tbounds); + } + } + + // compute our new origin + _origin.x = (_sources[0].frames.getXOrigin(_orient, index) - + _bounds.x); + _origin.y = (_sources[0].frames.getYOrigin(_orient, index) - + _bounds.y); +// Log.info("New origin [x=" + _origin.x + ", y=" + _origin.y + "]."); + + // render our volatile image for the first time + createVolatileImage(); + } + + public int getXOrigin () + { + return _origin.x; + } + + public int getYOrigin () + { + return _origin.y; + } + + // documentation inherited from interface + public int compare (Object o1, Object o2) + { + ComponentFrames cf1 = (ComponentFrames)o1, + cf2 = (ComponentFrames)o2; + return (cf1.ccomp.componentClass.getRenderPriority( + _action, _orient) - + cf2.ccomp.componentClass.getRenderPriority( + _action, _orient)); + } + + // documentation inherited + protected int getTransparency () + { + return Transparency.BITMASK; + } + + // documentation inherited + protected void refreshVolatileImage () + { +// long start = System.currentTimeMillis(); + + // sort the sources appropriately for this orientation + Arrays.sort(_sources, this); + + // now render each of the components into a composited frame + int scount = _sources.length; + Graphics2D g = (Graphics2D)_image.getGraphics(); + try { + for (int ii = 0; ii < scount; ii++) { + TrimmedMultiFrameImage source = + _sources[ii].frames.getFrames(_orient); + source.paintFrame(g, _index, -_bounds.x, -_bounds.y); + } + } finally { + // clean up after ourselves + if (g != null) { + g.dispose(); + } + } + +// Log.info("Composited [orient=" + _orient + ", index=" + _index + +// ", tbounds=" + StringUtil.toString(_bounds) + "]."); + +// long now = System.currentTimeMillis(); +// Log.info("Composited " + scount + " frames in " + +// (now-start) + " millis."); + } + + protected int _index; + protected Point _origin = new Point(); + } +}; diff --git a/src/java/com/threerings/cast/bundle/BundledComponentRepository.java b/src/java/com/threerings/cast/bundle/BundledComponentRepository.java index dcc68f5ca..11246bca6 100644 --- a/src/java/com/threerings/cast/bundle/BundledComponentRepository.java +++ b/src/java/com/threerings/cast/bundle/BundledComponentRepository.java @@ -1,5 +1,5 @@ // -// $Id: BundledComponentRepository.java,v 1.24 2003/01/14 00:36:14 mdb Exp $ +// $Id: BundledComponentRepository.java,v 1.25 2003/01/20 19:37:58 mdb Exp $ package com.threerings.cast.bundle; @@ -420,15 +420,6 @@ public class BundledComponentRepository return new TileSetFrameImage(_set.clone(zations), _actseq); } - // documentation inherited from interface - public long getEstimatedMemoryUsage () - { - throw new RuntimeException( - "Can't provide memory usage information since " + - "TileSetFrameImage makes use of an underlying tile set " + - "and we don't know said tileset's memory usage."); - } - /** * Fetches the requested tile. */