From f5d5171f861b652d08aa0da3de63c04927186f3e Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 14 Aug 2011 14:20:45 +0800 Subject: [PATCH] Improve scaling of memory use with large numbers of tiles, prep for suspend/resume support --- src/main/java/org/dynmap/MapManager.java | 28 ++++---- src/main/java/org/dynmap/MapTile.java | 3 + src/main/java/org/dynmap/flat/FlatMap.java | 3 + src/main/java/org/dynmap/hdmap/HDMapTile.java | 4 ++ .../java/org/dynmap/kzedmap/KzedMapTile.java | 3 +- .../org/dynmap/kzedmap/KzedZoomedMapTile.java | 2 + src/main/java/org/dynmap/utils/TileFlags.java | 66 +++++++++++++++++++ 7 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 src/main/java/org/dynmap/utils/TileFlags.java diff --git a/src/main/java/org/dynmap/MapManager.java b/src/main/java/org/dynmap/MapManager.java index 62d0bbc0..d012762b 100644 --- a/src/main/java/org/dynmap/MapManager.java +++ b/src/main/java/org/dynmap/MapManager.java @@ -29,6 +29,7 @@ import org.dynmap.utils.LegacyMapChunkCache; import org.dynmap.utils.MapChunkCache; import org.dynmap.utils.NewMapChunkCache; import org.dynmap.utils.SnapshotCache; +import org.dynmap.utils.TileFlags; public class MapManager { public AsynchronousQueue tileQueue; @@ -151,8 +152,8 @@ public class MapManager { Location loc; int map_index = -1; /* Which map are we on */ MapType map; - HashSet found = null; - HashSet rendered = null; + TileFlags found = null; + TileFlags rendered = null; LinkedList renderQueue = null; MapTile tile0 = null; MapTile tile = null; @@ -178,8 +179,8 @@ public class MapManager { FullWorldRenderState(DynmapWorld dworld, Location l, CommandSender sender, String mapname, int radius) { world = dworld; loc = l; - found = new HashSet(); - rendered = new HashSet(); + found = new TileFlags(); + rendered = new TileFlags(); renderQueue = new LinkedList(); this.sender = sender; if(radius < 0) { @@ -275,16 +276,16 @@ public class MapManager { /* Now, prime the render queue */ for (MapTile mt : map.getTiles(loc)) { - if (!found.contains(mt)) { - found.add(mt); + if (!found.getFlag(mt.tileOrdinalX(), mt.tileOrdinalY())) { + found.setFlag(mt.tileOrdinalX(), mt.tileOrdinalY(), true); renderQueue.add(mt); } } if(world.seedloc != null) { for(Location seed : world.seedloc) { for (MapTile mt : map.getTiles(seed)) { - if (!found.contains(mt)) { - found.add(mt); + if (!found.getFlag(mt.tileOrdinalX(),mt.tileOrdinalY())) { + found.setFlag(mt.tileOrdinalX(),mt.tileOrdinalY(), true); renderQueue.add(mt); } } @@ -326,16 +327,17 @@ public class MapManager { /* Switch to not checking if rendered tile is blank - breaks us on skylands, where tiles can be nominally blank - just work off chunk cache empty */ if (cache.isEmpty() == false) { tile.render(cache, mapname); - found.remove(tile); - rendered.add(tile); + found.setFlag(tile.tileOrdinalX(),tile.tileOrdinalY(),false); + rendered.setFlag(tile.tileOrdinalX(), tile.tileOrdinalY(), true); for (MapTile adjTile : map.getAdjecentTiles(tile)) { - if (!found.contains(adjTile) && !rendered.contains(adjTile)) { - found.add(adjTile); + if (!found.getFlag(adjTile.tileOrdinalX(),adjTile.tileOrdinalY()) && + !rendered.getFlag(adjTile.tileOrdinalX(),adjTile.tileOrdinalY())) { + found.setFlag(adjTile.tileOrdinalX(), adjTile.tileOrdinalY(), true); renderQueue.add(adjTile); } } } - found.remove(tile); + found.setFlag(tile.tileOrdinalX(), tile.tileOrdinalY(), false); if(!cache.isEmpty()) { rendercnt++; timeaccum += System.currentTimeMillis() - tstart; diff --git a/src/main/java/org/dynmap/MapTile.java b/src/main/java/org/dynmap/MapTile.java index 29821b2c..e88e890c 100644 --- a/src/main/java/org/dynmap/MapTile.java +++ b/src/main/java/org/dynmap/MapTile.java @@ -51,4 +51,7 @@ public abstract class MapTile { public abstract boolean isRawBiomeDataNeeded(); public abstract boolean isBlockTypeDataNeeded(); + public abstract int tileOrdinalX(); + public abstract int tileOrdinalY(); + } diff --git a/src/main/java/org/dynmap/flat/FlatMap.java b/src/main/java/org/dynmap/flat/FlatMap.java index c46161ac..9788df71 100644 --- a/src/main/java/org/dynmap/flat/FlatMap.java +++ b/src/main/java/org/dynmap/flat/FlatMap.java @@ -518,6 +518,9 @@ public class FlatMap extends MapType { public boolean isBiomeDataNeeded() { return false; } public boolean isRawBiomeDataNeeded() { return false; } public boolean isBlockTypeDataNeeded() { return true; } + public int tileOrdinalX() { return x; } + public int tileOrdinalY() { return y; } + } @Override diff --git a/src/main/java/org/dynmap/hdmap/HDMapTile.java b/src/main/java/org/dynmap/hdmap/HDMapTile.java index 15c13bfa..6d6fc42f 100644 --- a/src/main/java/org/dynmap/hdmap/HDMapTile.java +++ b/src/main/java/org/dynmap/hdmap/HDMapTile.java @@ -87,4 +87,8 @@ public class HDMapTile extends MapTile { public MapTile[] getAdjecentTiles() { return perspective.getAdjecentTiles(this); } + + public int tileOrdinalX() { return tx; } + public int tileOrdinalY() { return ty; } + } diff --git a/src/main/java/org/dynmap/kzedmap/KzedMapTile.java b/src/main/java/org/dynmap/kzedmap/KzedMapTile.java index 9207ec99..7ea2e01d 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedMapTile.java +++ b/src/main/java/org/dynmap/kzedmap/KzedMapTile.java @@ -91,5 +91,6 @@ public class KzedMapTile extends MapTile { public boolean isHightestBlockYDataNeeded() { return false; } public boolean isRawBiomeDataNeeded() { return map.isRawBiomeDataNeeded(); } public boolean isBlockTypeDataNeeded() { return true; } - + public int tileOrdinalX() { return px >> 7; } + public int tileOrdinalY() { return py >> 7; } } diff --git a/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java b/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java index 2526b5eb..d1c95111 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java +++ b/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java @@ -102,5 +102,7 @@ public class KzedZoomedMapTile extends MapTile { public boolean isHightestBlockYDataNeeded() { return false; } public boolean isRawBiomeDataNeeded() { return originalTile.isRawBiomeDataNeeded(); } public boolean isBlockTypeDataNeeded() { return true; } + public int tileOrdinalX() { return originalTile.px >> 8; } + public int tileOrdinalY() { return originalTile.py >> 8; } } diff --git a/src/main/java/org/dynmap/utils/TileFlags.java b/src/main/java/org/dynmap/utils/TileFlags.java new file mode 100644 index 00000000..2dead8e7 --- /dev/null +++ b/src/main/java/org/dynmap/utils/TileFlags.java @@ -0,0 +1,66 @@ +package org.dynmap.utils; + +import java.util.HashMap; +/** + * scalable flags primitive - used for keeping track of potentially huge number of tiles + * + * Represents a flag for each tile, with 2D coordinates based on 0,0 origin. Flags are grouped + * 64 x 64, represented by an array of 64 longs. Each set is stored in a hashmap, keyed by a long + * computed by ((x/64)<<32)+(y/64). + * + */ +public class TileFlags { + private HashMap chunkmap = new HashMap(); + private long last_key = Long.MIN_VALUE; + private long[] last_row; + + public TileFlags() { + } + + public boolean getFlag(int x, int y) { + long k = (((long)(x >> 6)) << 32) | (0xFFFFFFFFL & (long)(y >> 6)); + long[] row; + if(k == last_key) { + row = last_row; + } + else { + row = chunkmap.get(k); + last_key = k; + last_row = row; + } + if(row == null) + return false; + else + return (row[y & 0x3F] & (1L << (x & 0x3F))) != 0; + } + + public void setFlag(int x, int y, boolean f) { + long k = (((long)(x >> 6)) << 32) | (0xFFFFFFFFL & (long)(y >> 6)); + long[] row; + if(k == last_key) { + row = last_row; + } + else { + row = chunkmap.get(k); + last_key = k; + last_row = row; + } + if(f) { + if(row == null) { + row = new long[64]; + chunkmap.put(k, row); + last_row = row; + } + row[y & 0x3F] |= (1L << (x & 0x3F)); + } + else { + if(row != null) + row[y & 0x3F] &= ~(1L << (x & 0x3F)); + } + } + public void clear() { + chunkmap.clear(); + last_row = null; + last_key = Long.MIN_VALUE; + } +}