From b3e7f12f089d7726c3c609f3441444e499e7c6e9 Mon Sep 17 00:00:00 2001 From: Walter Korman Date: Tue, 7 Aug 2001 18:29:18 +0000 Subject: [PATCH] Work on dirty-tile rendering optimization. Added placeholder panels to the viewer. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@187 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../media/sprite/AnimationManager.java | 16 ++- .../com/threerings/media/sprite/Sprite.java | 18 ++- .../threerings/miso/client/IsoSceneView.java | 131 ++++++++++++++++-- .../miso/client/IsoSceneViewModel.java | 8 +- .../com/threerings/miso/client/IsoUtil.java | 18 +-- .../com/threerings/miso/viewer/ChatPanel.java | 28 ++++ .../miso/viewer/ManagementPanel.java | 28 ++++ .../threerings/miso/viewer/StatusPanel.java | 28 ++++ .../threerings/miso/viewer/ViewerFrame.java | 21 ++- .../miso/viewer/ViewerSceneViewPanel.java | 12 +- 10 files changed, 264 insertions(+), 44 deletions(-) create mode 100644 tests/src/java/com/threerings/miso/viewer/ChatPanel.java create mode 100644 tests/src/java/com/threerings/miso/viewer/ManagementPanel.java create mode 100644 tests/src/java/com/threerings/miso/viewer/StatusPanel.java diff --git a/src/java/com/threerings/media/sprite/AnimationManager.java b/src/java/com/threerings/media/sprite/AnimationManager.java index e6726da43..906520634 100644 --- a/src/java/com/threerings/media/sprite/AnimationManager.java +++ b/src/java/com/threerings/media/sprite/AnimationManager.java @@ -1,5 +1,5 @@ // -// $Id: AnimationManager.java,v 1.7 2001/08/06 18:57:39 shaper Exp $ +// $Id: AnimationManager.java,v 1.8 2001/08/07 18:29:18 shaper Exp $ package com.threerings.miso.sprite; @@ -99,13 +99,17 @@ public class AnimationManager implements Interval, PerformanceObserver // invalidate screen-rects dirtied by sprites ArrayList rects = _spritemgr.getDirtyRects(); - _view.invalidateRects(rects); - // update frame-rate information - PerformanceMonitor.tick(AnimationManager.this, "refresh"); + if (rects.size() > 0) { + // pass the dirty-rects on to the scene view + _view.invalidateRects(rects); - // refresh the display - _target.paintImmediately(_target.getBounds()); + // refresh the display + _target.paintImmediately(_target.getBounds()); + } + + // update refresh-rate information + //PerformanceMonitor.tick(AnimationManager.this, "refresh"); if (finishedTick()) { // finishedTick returning true means there's been a diff --git a/src/java/com/threerings/media/sprite/Sprite.java b/src/java/com/threerings/media/sprite/Sprite.java index 37eed2423..efea88e48 100644 --- a/src/java/com/threerings/media/sprite/Sprite.java +++ b/src/java/com/threerings/media/sprite/Sprite.java @@ -1,5 +1,5 @@ // -// $Id: Sprite.java,v 1.7 2001/08/04 00:36:33 shaper Exp $ +// $Id: Sprite.java,v 1.8 2001/08/07 18:29:18 shaper Exp $ package com.threerings.miso.sprite; @@ -187,8 +187,10 @@ public class Sprite Rectangle dirty = new Rectangle( _drawx, _drawy, _curTile.width, _curTile.height); -// Log.info("Sprite invalidate [x=" + dirty.x + ", y=" + dirty.y + -// ", width=" + dirty.width + ", height=" + dirty.height + "]."); +// Log.info("Sprite invalidate [x=" + x + ", y=" + y + +// ", dx=" + dirty.x + ", dy=" + dirty.y + +// ", dwidth=" + dirty.width + +// ", dheight=" + dirty.height + "]."); _spritemgr.addDirtyRect(dirty); } @@ -221,6 +223,9 @@ public class Sprite */ protected void handleMove () { + // dirty our rectangle since we're going to move + invalidate(); + // move the sprite incrementally toward its goal x = (int)(_movex += _incx); y = (int)(_movey += _incy); @@ -233,14 +238,15 @@ public class Sprite x = _dest.loc.x; y = _dest.loc.y; - // dirty our rectangle since we've moved - invalidate(); - // move further along the path if necessary moveAlongPath(); } + // update the draw coordinates to reflect our new position updateDrawPosition(); + + // dirty our rectangle in the new position + invalidate(); } /** diff --git a/src/java/com/threerings/miso/client/IsoSceneView.java b/src/java/com/threerings/miso/client/IsoSceneView.java index d0ea40cd0..2ba664f25 100644 --- a/src/java/com/threerings/miso/client/IsoSceneView.java +++ b/src/java/com/threerings/miso/client/IsoSceneView.java @@ -1,5 +1,5 @@ // -// $Id: IsoSceneView.java,v 1.26 2001/08/06 18:57:39 shaper Exp $ +// $Id: IsoSceneView.java,v 1.27 2001/08/07 18:29:17 shaper Exp $ package com.threerings.miso.scene; @@ -39,8 +39,9 @@ public class IsoSceneView implements EditableSceneView // get the font used to render tile coordinates _font = new Font("Arial", Font.PLAIN, 7); - // create the list of dirty rectangles + // create the lists of dirty tiles and rectangles _dirty = new ArrayList(); + _dirtyRects = new ArrayList(); } /** @@ -63,6 +64,7 @@ public class IsoSceneView implements EditableSceneView // draw the full scene into the offscreen image buffer //renderSceneInvalid(gfx); renderScene(gfx); + drawDirtyRects(gfx); // draw an outline around the highlighted tile paintHighlightedTile(gfx, _htile.x, _htile.y); @@ -74,6 +76,25 @@ public class IsoSceneView implements EditableSceneView gfx.setClip(oldclip); } + protected void drawDirtyRects (Graphics2D gfx) + { + // draw the dirty tiles + gfx.setColor(Color.cyan); + int size = _dirty.size(); + for (int ii = 0; ii < size; ii++) { + int dinfo[] = (int[])_dirty.remove(0); + gfx.draw(getTilePolygon(dinfo[0], dinfo[1])); + } + + // draw the dirty rectangles + gfx.setColor(Color.red); + size = _dirtyRects.size(); + for (int ii = 0; ii < size; ii++) { + Rectangle rect = (Rectangle)_dirtyRects.remove(0); + gfx.draw(rect); + } + } + /** * Render the scene to the given graphics context. * @@ -208,12 +229,13 @@ public class IsoSceneView implements EditableSceneView */ protected void paintMouseLines (Graphics2D gfx) { - Point[] lx = _model.lineX, ly = _model.lineY; + Point[] lx = _model.lineX; // draw the baseline x-axis line gfx.setColor(Color.red); gfx.drawLine(lx[0].x, lx[0].y, lx[1].x, lx[1].y); + /* // draw line from last mouse pos to baseline gfx.setColor(Color.yellow); gfx.drawLine(ly[0].x, ly[0].y, ly[1].x, ly[1].y); @@ -223,6 +245,7 @@ public class IsoSceneView implements EditableSceneView gfx.fillRect(ly[0].x, ly[0].y, 2, 2); gfx.setColor(Color.red); gfx.drawRect(ly[0].x - 1, ly[0].y - 1, 3, 3); + */ } /** @@ -247,11 +270,11 @@ public class IsoSceneView implements EditableSceneView // draw x-coordinate String str = "" + x; - gfx.drawString(str, sx + cx - fm.stringWidth(str), sy + cy); + gfx.drawString(str, sx + cx - (fm.stringWidth(str)/2), sy + cy); // draw y-coordinate str = "" + y; - gfx.drawString(str, sx + cx - fm.stringWidth(str), sy + cy + fhei); + gfx.drawString(str, sx + cx - (fm.stringWidth(str)/2), sy + cy + fhei); } /** @@ -290,8 +313,13 @@ public class IsoSceneView implements EditableSceneView int size = rects.size(); for (int ii = 0; ii < size; ii++) { Rectangle r = (Rectangle)rects.get(ii); - invalidateScreenRect(r.x, r.y, r.width, r.height); - } + + // dirty the tiles impacted by this rectangle + invalidateScreenRect(r.x, r.y, r.width, r.height); + + // save the rectangle for potential display later + _dirtyRects.add(r); + } } /** @@ -305,15 +333,91 @@ public class IsoSceneView implements EditableSceneView */ public void invalidateScreenRect (int x, int y, int width, int height) { + // TODO: fix boundary conditions, store contiguous dirty tile + // row segments rather than individual tiles, use specialized + // data structure rather than allocating int[] every time. + + // note that corner tiles may be included unnecessarily, but + // checking to determine whether they're actually needed + // complicates the code with likely-insufficient benefit + + // determine the top-left tile impacted by this rect Point tpos = new Point(); IsoUtil.screenToTile(_model, x, y, tpos); -// Log.info("invalidateScreenRect: mapped rect to tile " + -// "[tx=" + tpos.x + ", ty=" + tpos.y + -// ", x=" + x + ", y=" + y + ", width=" + width + -// ", height=" + height + "]."); + // determine screen coordinates for top-left tile + Point topleft = new Point(); + IsoUtil.tileToScreen(_model, tpos.x, tpos.y, topleft); - _dirty.add(new int[] { tpos.x, tpos.y }); + // determine number of horizontal and vertical tiles for rect + int numh = (int)Math.ceil((float)width / (float)_model.tilewid); + int numv = (int)Math.ceil((float)height / (float)_model.tilehhei); + + // set up iterating variables + int tx = tpos.x, ty = tpos.y, mx = tpos.x, my = tpos.y;; + + // set the starting screen y-position + int screenY = topleft.y; + + // add top row if rect may overlap + if (y < (screenY + _model.tilehhei)) { + ty--; + for (int ii = 0; ii < numh; ii++) { + _dirty.add(new int[] { tx++, ty-- }); + } + } + + // add rows to the bottom if rect may overlap + int ypos = screenY + (numv * _model.tilehhei); + if ((y + height) > ypos) { + numv += ((y + height) > (ypos + _model.tilehhei)) ? 2 : 1; + } + + // add dirty tiles from each affected row + boolean isodd = false; + for (int ii = 0; ii < numv; ii++) { + + // set up iterating variables for this row + tx = mx; + ty = my; + int length = numh; + + // set the starting screen x-position + int screenX = topleft.x; + if (isodd) { + screenX -= _model.tilehwid; + } + + // skip leftmost tile if rect doesn't overlap + if (x > screenX + _model.tilewid) { + tx++; + ty--; + screenX += _model.tilewid; + } + + // add to the right edge if rect may overlap + if (x + width > (screenX + (length * _model.tilewid))) { + length++; + } + + // add all tiles in the row to the dirty set + for (int jj = 0; jj < length; jj++) { + _dirty.add(new int[] { tx++, ty-- }); + } + + // step along the x- or y-axis appropriately + if (isodd) { + mx++; + } else { + my++; + } + + // increment the screen y-position + screenY += _model.tilehhei; + + // toggle whether we're drawing an odd-numbered row + isodd = !isodd; + } } public void setScene (Scene scene) @@ -370,6 +474,9 @@ public class IsoSceneView implements EditableSceneView /** The dirty tile row segments that need to be re-painted. */ protected ArrayList _dirty; + /** The dirty rectangles that need to be re-painted. */ + protected ArrayList _dirtyRects; + /** The scene model data. */ protected IsoSceneModel _model; diff --git a/src/java/com/threerings/miso/client/IsoSceneViewModel.java b/src/java/com/threerings/miso/client/IsoSceneViewModel.java index aba05f662..e101554a7 100644 --- a/src/java/com/threerings/miso/client/IsoSceneViewModel.java +++ b/src/java/com/threerings/miso/client/IsoSceneViewModel.java @@ -1,5 +1,5 @@ // -// $Id: IsoSceneViewModel.java,v 1.3 2001/08/03 22:23:47 shaper Exp $ +// $Id: IsoSceneViewModel.java,v 1.4 2001/08/07 18:29:17 shaper Exp $ package com.threerings.miso.scene; @@ -44,8 +44,8 @@ public class IsoSceneModel /** The slope of the x- and y-axis lines. */ public float slopeX, slopeY; - /** The last calculated x- and y-axis mouse position tracking lines. */ - public Point lineX[], lineY[]; + /** The x-axis line. */ + public Point lineX[]; /** The length between fine coordinates in pixels. */ public float finelen; @@ -160,10 +160,8 @@ public class IsoSceneModel // create the x- and y-axis lines lineX = new Point[2]; - lineY = new Point[2]; for (int ii = 0; ii < 2; ii++) { lineX[ii] = new Point(); - lineY[ii] = new Point(); } // determine the starting point diff --git a/src/java/com/threerings/miso/client/IsoUtil.java b/src/java/com/threerings/miso/client/IsoUtil.java index f97ba06dd..0dbcf2b88 100644 --- a/src/java/com/threerings/miso/client/IsoUtil.java +++ b/src/java/com/threerings/miso/client/IsoUtil.java @@ -1,5 +1,5 @@ // -// $Id: IsoUtil.java,v 1.1 2001/08/03 22:23:47 shaper Exp $ +// $Id: IsoUtil.java,v 1.2 2001/08/07 18:29:17 shaper Exp $ package com.threerings.miso.scene; @@ -110,25 +110,21 @@ public class IsoUtil public static void screenToTile ( IsoSceneModel model, int sx, int sy, Point tpos) { - Point[] lx = model.lineX, ly = model.lineY; - // calculate line parallel to the y-axis (from mouse pos to x-axis) - ly[0].setLocation(sx, sy); int bY = (int)(sy - (model.slopeY * sx)); // determine intersection of x- and y-axis lines - ly[1].x = (int)((bY - (model.bX + model.origin.y)) / - (model.slopeX - model.slopeY)); - ly[1].y = (int)((model.slopeY * ly[1].x) + bY); + int crossx = (int)((bY - (model.bX + model.origin.y)) / + (model.slopeX - model.slopeY)); + int crossy = (int)((model.slopeY * crossx) + bY); // determine distance of mouse pos along the x axis - int xdist = (int) MathUtil.distance( - lx[0].x, lx[0].y, ly[1].x, ly[1].y); + int xdist = (int)MathUtil.distance( + model.origin.x, model.origin.y, crossx, crossy); tpos.x = (int)(xdist / model.tilelen); // determine distance of mouse pos along the y-axis - int ydist = (int) MathUtil.distance( - ly[0].x, ly[0].y, ly[1].x, ly[1].y); + int ydist = (int)MathUtil.distance(sx, sy, crossx, crossy); tpos.y = (int)(ydist / model.tilelen); } diff --git a/tests/src/java/com/threerings/miso/viewer/ChatPanel.java b/tests/src/java/com/threerings/miso/viewer/ChatPanel.java new file mode 100644 index 000000000..cedc78a7e --- /dev/null +++ b/tests/src/java/com/threerings/miso/viewer/ChatPanel.java @@ -0,0 +1,28 @@ +// +// $Id: ChatPanel.java,v 1.1 2001/08/07 18:29:18 shaper Exp $ + +package com.threerings.miso.viewer; + +import java.awt.*; +import javax.swing.*; + +import com.threerings.miso.Log; + +public class ChatPanel extends JPanel +{ + public void paintComponent (Graphics g) + { + super.paintComponent(g); + + g.setColor(Color.blue); + Rectangle bounds = getBounds(); + g.fillRect(0, 0, bounds.width, bounds.height); + + Log.info("chat panel [bounds=" + bounds + "]."); + } + + public Dimension getPreferredSize () + { + return new Dimension(200, 150); + } +} diff --git a/tests/src/java/com/threerings/miso/viewer/ManagementPanel.java b/tests/src/java/com/threerings/miso/viewer/ManagementPanel.java new file mode 100644 index 000000000..94591fbcd --- /dev/null +++ b/tests/src/java/com/threerings/miso/viewer/ManagementPanel.java @@ -0,0 +1,28 @@ +// +// $Id: ManagementPanel.java,v 1.1 2001/08/07 18:29:18 shaper Exp $ + +package com.threerings.miso.viewer; + +import java.awt.*; +import javax.swing.*; + +import com.threerings.miso.Log; + +public class ManagementPanel extends JPanel +{ + public void paintComponent (Graphics g) + { + super.paintComponent(g); + + g.setColor(Color.red); + Rectangle bounds = getBounds(); + g.fillRect(0, 0, bounds.width, bounds.height); + + Log.info("mgmt panel [bounds=" + bounds + "]."); + } + + public Dimension getPreferredSize () + { + return new Dimension(200, 300); + } +} diff --git a/tests/src/java/com/threerings/miso/viewer/StatusPanel.java b/tests/src/java/com/threerings/miso/viewer/StatusPanel.java new file mode 100644 index 000000000..e70a2646c --- /dev/null +++ b/tests/src/java/com/threerings/miso/viewer/StatusPanel.java @@ -0,0 +1,28 @@ +// +// $Id: StatusPanel.java,v 1.1 2001/08/07 18:29:18 shaper Exp $ + +package com.threerings.miso.viewer; + +import java.awt.*; +import javax.swing.*; + +import com.threerings.miso.Log; + +public class StatusPanel extends JPanel +{ + public void paintComponent (Graphics g) + { + super.paintComponent(g); + + g.setColor(Color.yellow); + Rectangle bounds = getBounds(); + g.fillRect(0, 0, bounds.width, bounds.height); + + Log.info("status panel [bounds=" + bounds + "]."); + } + + public Dimension getPreferredSize () + { + return new Dimension(200, 150); + } +} diff --git a/tests/src/java/com/threerings/miso/viewer/ViewerFrame.java b/tests/src/java/com/threerings/miso/viewer/ViewerFrame.java index f0a5a509c..3df171843 100644 --- a/tests/src/java/com/threerings/miso/viewer/ViewerFrame.java +++ b/tests/src/java/com/threerings/miso/viewer/ViewerFrame.java @@ -1,5 +1,5 @@ // -// $Id: ViewerFrame.java,v 1.8 2001/08/06 18:57:39 shaper Exp $ +// $Id: ViewerFrame.java,v 1.9 2001/08/07 18:29:18 shaper Exp $ package com.threerings.miso.viewer; @@ -55,9 +55,24 @@ class ViewerFrame extends JFrame implements WindowListener // set up the scene view panel with a default scene ViewerSceneViewPanel svpanel = new ViewerSceneViewPanel(_ctx, spritemgr, sprite); - top.add(svpanel); + top.add(svpanel, GroupLayout.FIXED); - // add the scene view panel + // create a sub-panel to hold the side panels + JPanel sub = new JPanel(); + gl = new VGroupLayout(GroupLayout.STRETCH); + gl.setOffAxisPolicy(GroupLayout.STRETCH); + gl.setJustification(GroupLayout.TOP); + sub.setLayout(gl); + + // add the various side-panels + sub.add(new StatusPanel(), GroupLayout.FIXED); + sub.add(new ManagementPanel()); + sub.add(new ChatPanel()); + + // add the sub-panel to the main panel + top.add(sub); + + // add the main panel to the frame getContentPane().add(top, BorderLayout.CENTER); } diff --git a/tests/src/java/com/threerings/miso/viewer/ViewerSceneViewPanel.java b/tests/src/java/com/threerings/miso/viewer/ViewerSceneViewPanel.java index 3d9728357..d4c4efe33 100644 --- a/tests/src/java/com/threerings/miso/viewer/ViewerSceneViewPanel.java +++ b/tests/src/java/com/threerings/miso/viewer/ViewerSceneViewPanel.java @@ -1,5 +1,5 @@ // -// $Id: ViewerSceneViewPanel.java,v 1.2 2001/08/06 18:57:39 shaper Exp $ +// $Id: ViewerSceneViewPanel.java,v 1.3 2001/08/07 18:29:18 shaper Exp $ package com.threerings.miso.viewer; @@ -39,6 +39,10 @@ public class ViewerSceneViewPanel extends SceneViewPanel // load up the initial scene prepareStartingScene(); + + ((EditableSceneView)_view).setShowCoordinates(true); + + PerformanceMonitor.register(this, "paint", 1000); } /** @@ -63,6 +67,12 @@ public class ViewerSceneViewPanel extends SceneViewPanel } } + public void paintComponent (Graphics g) + { + super.paintComponent(g); + PerformanceMonitor.tick(this, "paint"); + } + public void checkpoint (String name, int ticks) { Log.info(name + " [ticks=" + ticks + "].");