fixing MapTile state logic

This commit is contained in:
Hannes Janetzek 2014-10-03 02:44:52 +02:00
parent 5f9a9cc909
commit 71715dccd9
8 changed files with 273 additions and 170 deletions

View File

@ -16,6 +16,7 @@
*/ */
package org.oscim.layers.tile; package org.oscim.layers.tile;
import static org.oscim.layers.tile.MapTile.State.CANCEL;
import static org.oscim.layers.tile.MapTile.State.LOADING; import static org.oscim.layers.tile.MapTile.State.LOADING;
import static org.oscim.layers.tile.MapTile.State.NONE; import static org.oscim.layers.tile.MapTile.State.NONE;
@ -37,9 +38,6 @@ public class JobQueue {
* the jobs to be added to this queue. * the jobs to be added to this queue.
*/ */
public synchronized void setJobs(MapTile[] tiles) { public synchronized void setJobs(MapTile[] tiles) {
for (MapTile t : tiles)
t.state = LOADING;
mJobs = tiles; mJobs = tiles;
mCurrentJob = 0; mCurrentJob = 0;
} }
@ -55,12 +53,12 @@ public class JobQueue {
MapTile[] tiles = mJobs; MapTile[] tiles = mJobs;
for (int i = mCurrentJob, n = mJobs.length; i < n; i++) { for (int i = mCurrentJob, n = mJobs.length; i < n; i++) {
MapTile t = tiles[i];
if (tiles[i].state == LOADING) if (t.state(LOADING | CANCEL)) {
tiles[i].state = NONE; t.setState(NONE);
else } else {
log.debug("wrong tile in queue {} {}", tiles[i], tiles[i].state); log.error("Wrong tile in queue {} {}", t, t.state());
}
tiles[i] = null; tiles[i] = null;
} }
mCurrentJob = 0; mCurrentJob = 0;

View File

@ -16,9 +16,13 @@
*/ */
package org.oscim.layers.tile; package org.oscim.layers.tile;
import static org.oscim.layers.tile.MapTile.State.CANCEL;
import static org.oscim.layers.tile.MapTile.State.DEADBEEF; import static org.oscim.layers.tile.MapTile.State.DEADBEEF;
import static org.oscim.layers.tile.MapTile.State.LOADING;
import static org.oscim.layers.tile.MapTile.State.NEW_DATA; import static org.oscim.layers.tile.MapTile.State.NEW_DATA;
import static org.oscim.layers.tile.MapTile.State.NONE;
import static org.oscim.layers.tile.MapTile.State.READY; import static org.oscim.layers.tile.MapTile.State.READY;
import static org.oscim.layers.tile.MapTile.State.TIMEOUT;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.layers.tile.vector.VectorTileLoader; import org.oscim.layers.tile.vector.VectorTileLoader;
@ -44,34 +48,38 @@ public class MapTile extends Tile {
} }
public static final class State { public static final class State {
public final static byte NONE = 0; public final static byte NONE = (1 << 0);
/** /**
* STATE_LOADING means the tile is about to be loaded / loading. * STATE_LOADING means the tile is about to be loaded / loading.
* Tile belongs to TileLoader thread. * Tile belongs to TileLoader thread.
*/ */
public final static byte LOADING = (1 << 0); public final static byte LOADING = (1 << 1);
/** /**
* STATE_NEW_DATA: tile data is prepared for rendering. * STATE_NEW_DATA: tile data is prepared for rendering.
* While 'locked' it belongs to GL Thread. * While 'locked' it belongs to GL Thread.
*/ */
public final static byte NEW_DATA = (1 << 1); public final static byte NEW_DATA = (1 << 2);
/** /**
* STATE_READY: tile data is uploaded to GL. * STATE_READY: tile data is uploaded to GL.
* While 'locked' it belongs to GL Thread. * While 'locked' it belongs to GL Thread.
*/ */
public final static byte READY = (1 << 2); public final static byte READY = (1 << 3);
/** /**
* STATE_CANCEL: tile is removed from TileManager, * STATE_CANCEL: tile is removed from TileManager,
* but may still be processed by TileLoader. * but may still be processed by TileLoader.
*/ */
public final static byte CANCEL = (1 << 3); public final static byte CANCEL = (1 << 4);
public final static byte DEADBEEF = (1 << 4); public final static byte TIMEOUT = (1 << 5);
/**
* Dont touch if you find some.
*/
public final static byte DEADBEEF = (1 << 6);
} }
public final static int PROXY_CHILD00 = (1 << 0); public final static int PROXY_CHILD00 = (1 << 0);
@ -83,7 +91,7 @@ public class MapTile extends Tile {
public final static int PROXY_HOLDER = (1 << 6); public final static int PROXY_HOLDER = (1 << 6);
/** Tile state */ /** Tile state */
byte state; byte state = State.NONE;
/** /**
* absolute tile coordinates: tileX,Y / Math.pow(2, zoomLevel) * absolute tile coordinates: tileX,Y / Math.pow(2, zoomLevel)
@ -176,6 +184,11 @@ public class MapTile extends Tile {
* used. This function should only be called through {@link TileManager} * used. This function should only be called through {@link TileManager}
*/ */
void lock() { void lock() {
if (state == DEADBEEF) {
log.debug("Locking dead tile {}", this);
return;
}
if (locked++ > 0) if (locked++ > 0)
return; return;
@ -218,6 +231,12 @@ public class MapTile extends Tile {
if (--locked > 0) if (--locked > 0)
return; return;
if (state == DEADBEEF) {
log.debug("Unlock dead tile {}", this);
clear();
return;
}
TileNode parent = node.parent; TileNode parent = node.parent;
if ((proxy & PROXY_PARENT) != 0) if ((proxy & PROXY_PARENT) != 0)
parent.item.refs--; parent.item.refs--;
@ -259,7 +278,7 @@ public class MapTile extends Tile {
data.dispose(); data.dispose();
data = data.next; data = data.next;
} }
state = DEADBEEF; state = NONE;
} }
/** /**
@ -367,4 +386,75 @@ public class MapTile extends Tile {
return p; return p;
} }
public String state() {
switch (state) {
case State.NONE:
return "None";
case State.LOADING:
return "Loading";
case State.NEW_DATA:
return "Data";
case State.READY:
return "Ready";
case State.CANCEL:
return "Cancel";
case State.TIMEOUT:
return "Timeout";
case State.DEADBEEF:
return "Dead";
}
return "";
}
void setState(byte newState) {
if (state == newState)
return;
switch (newState) {
case NONE:
if (state(LOADING | CANCEL)) {
state = newState;
return;
}
throw new IllegalStateException("None"
+ " <= " + state() + " " + this);
case LOADING:
if (state == NONE) {
state = newState;
return;
}
throw new IllegalStateException("Loading"
+ " <= " + state() + " " + this);
case NEW_DATA:
if (state == LOADING) {
state = newState;
return;
}
throw new IllegalStateException("NewData"
+ " <= " + state() + " " + this);
case READY:
if (state == NEW_DATA) {
state = newState;
return;
}
throw new IllegalStateException("Ready"
+ " <= " + state() + " " + this);
case CANCEL:
if (state == LOADING) {
state = newState;
return;
}
throw new IllegalStateException("Cancel" +
" <= " + state() + " " + this);
case TIMEOUT:
// TODO
break;
case DEADBEEF:
state = newState;
break;
}
}
} }

View File

@ -21,6 +21,7 @@ import static org.oscim.layers.tile.MapTile.State.CANCEL;
import static org.oscim.layers.tile.MapTile.State.DEADBEEF; import static org.oscim.layers.tile.MapTile.State.DEADBEEF;
import static org.oscim.layers.tile.MapTile.State.LOADING; import static org.oscim.layers.tile.MapTile.State.LOADING;
import static org.oscim.layers.tile.MapTile.State.NEW_DATA; import static org.oscim.layers.tile.MapTile.State.NEW_DATA;
import static org.oscim.layers.tile.MapTile.State.NONE;
import static org.oscim.layers.tile.MapTile.State.READY; import static org.oscim.layers.tile.MapTile.State.READY;
import java.util.ArrayList; import java.util.ArrayList;
@ -65,7 +66,6 @@ public class TileManager {
/** cache limit threshold */ /** cache limit threshold */
private static final int CACHE_THRESHOLD = 25; private static final int CACHE_THRESHOLD = 25;
private static final int CACHE_CLEAR_THRESHOLD = 10; private static final int CACHE_CLEAR_THRESHOLD = 10;
private static final int QUEUE_CLEAR_THRESHOLD = 10;
private final Map mMap; private final Map mMap;
private final Viewport mViewport; private final Viewport mViewport;
@ -77,10 +77,10 @@ public class TileManager {
private int mTilesCount; private int mTilesCount;
/** current end position in mTiles */ /** current end position in mTiles */
private int mTilesSize; private int mTilesEnd;
/** counter for tiles with new data not yet loaded to GL */ /** counter for tiles with new data not yet loaded to GL */
private int mTilesForUpload; private int mTilesToUpload;
/** new tile jobs for MapWorkers */ /** new tile jobs for MapWorkers */
private final ArrayList<MapTile> mJobs; private final ArrayList<MapTile> mJobs;
@ -112,7 +112,7 @@ public class TileManager {
@Override @Override
public void removeItem(MapTile t) { public void removeItem(MapTile t) {
if (t.node == null) if (t.node == null)
throw new IllegalStateException("already removed: " + t); throw new IllegalStateException("Already removed: " + t);
super.remove(t.node); super.remove(t.node);
t.node.item = null; t.node.item = null;
@ -149,8 +149,8 @@ public class TileManager {
mJobs = new ArrayList<MapTile>(); mJobs = new ArrayList<MapTile>();
mTiles = new MapTile[mCacheLimit]; mTiles = new MapTile[mCacheLimit];
mTilesSize = 0; mTilesEnd = 0;
mTilesForUpload = 0; mTilesToUpload = 0;
mUpdateSerial = 0; mUpdateSerial = 0;
} }
@ -165,17 +165,28 @@ public class TileManager {
} }
public void init() { public void init() {
if (mCurrentTiles != null)
mCurrentTiles.releaseTiles();
/* pass VBOs and VertexItems back to pools */ /* pass VBOs and VertexItems back to pools */
for (int i = 0; i < mTilesSize; i++) { for (int i = 0; i < mTilesEnd; i++) {
if (mTiles[i] == null) MapTile t = mTiles[i];
if (t == null)
continue; continue;
removeFromCache(mTiles[i]);
mTiles[i].state = CANCEL; if (!t.isLocked()) {
//log.debug("init clear {} {}", t, t.state());
t.clear();
}
mIndex.removeItem(t);
/* in case the tile is still loading:
* clear when returned from loader */
t.setState(DEADBEEF);
} }
/* clear references to cached MapTiles */ /* clear references to cached MapTiles */
Arrays.fill(mTiles, null); Arrays.fill(mTiles, null);
mTilesSize = 0; mTilesEnd = 0;
mTilesCount = 0; mTilesCount = 0;
/* set up TileSet large enough to hold current tiles */ /* set up TileSet large enough to hold current tiles */
@ -198,6 +209,7 @@ public class TileManager {
public boolean update(MapPosition pos) { public boolean update(MapPosition pos) {
// FIXME cant expect init to be called otherwise // FIXME cant expect init to be called otherwise
// Should use some onLayerAttached callback instead.
if (mNewTiles == null || mNewTiles.tiles.length == 0) { if (mNewTiles == null || mNewTiles.tiles.length == 0) {
mPrevZoomlevel = pos.zoomLevel; mPrevZoomlevel = pos.zoomLevel;
init(); init();
@ -319,17 +331,19 @@ public class TileManager {
mCacheReduce += 10; mCacheReduce += 10;
if (dbg) if (dbg)
log.debug("reduce cache {}", (mCacheLimit - mCacheReduce)); log.debug("reduce cache {}", (mCacheLimit - mCacheReduce));
} else } else {
mCacheReduce = 0; mCacheReduce = 0;
} }
}
/* limit cache items */ /* limit cache items */
int remove = mTilesCount - (mCacheLimit - mCacheReduce); int remove = mTilesCount - (mCacheLimit - mCacheReduce);
if (remove > CACHE_THRESHOLD || if (remove > CACHE_THRESHOLD || mTilesToUpload > MAX_TILES_IN_QUEUE) {
mTilesForUpload > MAX_TILES_IN_QUEUE) synchronized (mTilelock) {
limitCache(pos, remove); limitCache(pos, remove);
}
}
return true; return true;
} }
@ -379,9 +393,11 @@ public class TileManager {
if (tile == null) { if (tile == null) {
TileNode n = mIndex.add(x, y, zoomLevel); TileNode n = mIndex.add(x, y, zoomLevel);
tile = n.item = new MapTile(n, x, y, zoomLevel); tile = n.item = new MapTile(n, x, y, zoomLevel);
tile.setState(LOADING);
mJobs.add(tile); mJobs.add(tile);
addToCache(tile); addToCache(tile);
} else if (!tile.isActive()) { } else if (!tile.isActive()) {
tile.setState(LOADING);
mJobs.add(tile); mJobs.add(tile);
} }
@ -393,10 +409,10 @@ public class TileManager {
p = n.item = new MapTile(n, x >> 1, y >> 1, zoomLevel - 1); p = n.item = new MapTile(n, x >> 1, y >> 1, zoomLevel - 1);
addToCache(p); addToCache(p);
/* this prevents to add tile twice to queue */ /* this prevents to add tile twice to queue */
p.state = LOADING; p.setState(LOADING);
mJobs.add(p); mJobs.add(p);
} else if (!p.isActive()) { } else if (!p.isActive()) {
p.state = LOADING; p.setState(LOADING);
mJobs.add(p); mJobs.add(p);
} }
} }
@ -405,133 +421,139 @@ public class TileManager {
private void addToCache(MapTile tile) { private void addToCache(MapTile tile) {
if (mTilesSize == mTiles.length) { if (mTilesEnd == mTiles.length) {
if (mTilesSize > mTilesCount) { if (mTilesEnd > mTilesCount) {
TileDistanceSort.sort(mTiles, 0, mTilesSize); TileDistanceSort.sort(mTiles, 0, mTilesEnd);
/* sorting also repacks the 'sparse' filled array /* sorting also repacks the 'sparse' filled array
* so end of mTiles is at mTilesCount now */ * so end of mTiles is at mTilesCount now */
mTilesSize = mTilesCount; mTilesEnd = mTilesCount;
} }
if (mTilesSize == mTiles.length) { if (mTilesEnd == mTiles.length) {
log.debug("realloc tiles {}", mTilesSize); log.debug("realloc tiles {}", mTilesEnd);
MapTile[] tmp = new MapTile[mTiles.length + 20]; MapTile[] tmp = new MapTile[mTiles.length + 20];
System.arraycopy(mTiles, 0, tmp, 0, mTilesCount); System.arraycopy(mTiles, 0, tmp, 0, mTilesCount);
mTiles = tmp; mTiles = tmp;
} }
} }
mTiles[mTilesSize++] = tile; mTiles[mTilesEnd++] = tile;
mTilesCount++; mTilesCount++;
} }
private void removeFromCache(MapTile t) { private boolean removeFromCache(MapTile t) {
/* TODO check valid states here:When in CANCEL state tile belongs to
* TileLoader thread, defer clearing to jobCompleted() */
if (dbg)
log.debug("remove from cache {} {} {}",
t, t.state(), t.isLocked());
if (t.isLocked())
return false;
if (t.state(NEW_DATA | READY)) if (t.state(NEW_DATA | READY))
events.fire(TILE_REMOVED, t); events.fire(TILE_REMOVED, t);
if (dbg)
log.debug("remove from cache {} {} {}", t, t.state, t.isLocked());
/* When in CANCEL state tile belongs to TileLoader thread,
* defer clearing to jobCompleted() */
if (t.state != CANCEL)
t.clear(); t.clear();
mIndex.removeItem(t); mIndex.removeItem(t);
mTilesCount--; mTilesCount--;
return true;
} }
private void limitCache(MapPosition pos, int remove) { private void limitCache(MapPosition pos, int remove) {
MapTile[] tiles = mTiles; MapTile[] tiles = mTiles;
int size = mTilesSize;
/* count tiles that have new data */ /* count tiles that have new data */
mTilesForUpload = 0;
int newTileCnt = 0; int newTileCnt = 0;
/* remove tiles that were never loaded */ /* remove tiles that were never loaded */
for (int i = 0; i < size; i++) { for (int i = 0; i < mTilesEnd; i++) {
MapTile t = tiles[i]; MapTile t = tiles[i];
if (t == null) if (t == null)
continue; continue;
if (t.state == NEW_DATA) if (t.state(NEW_DATA))
newTileCnt++; newTileCnt++;
if (t.state == DEADBEEF) { if (t.state(DEADBEEF)) {
//log.debug("found DEADBEEF {}", t); log.debug("found DEADBEEF {}", t);
t.clear();
tiles[i] = null; tiles[i] = null;
mTilesCount--;
continue;
}
/* make sure tile cannot be used by GL or MapWorker Thread */
if ((t.state != 0) || t.isLocked()) {
continue; continue;
} }
/* empty tile */ /* make sure tile cannot be used by GL or MapWorker Thread */
removeFromCache(t); if (t.state(NONE) && removeFromCache(t)) {
tiles[i] = null; tiles[i] = null;
remove--; remove--;
} }
}
if ((remove < CACHE_CLEAR_THRESHOLD) && (newTileCnt < MAX_TILES_IN_QUEUE)) if ((remove < CACHE_CLEAR_THRESHOLD) && (newTileCnt < MAX_TILES_IN_QUEUE))
return; return;
updateDistances(tiles, size, pos); updateDistances(tiles, mTilesEnd, pos);
TileDistanceSort.sort(tiles, 0, size); TileDistanceSort.sort(tiles, 0, mTilesEnd);
/* sorting also repacks the 'sparse' filled array /* sorting also repacks the 'sparse' filled array
* so end of mTiles is at mTilesCount now */ * so end of mTiles is at mTilesCount now */
size = mTilesSize = mTilesCount; mTilesEnd = mTilesCount;
// log.debug("remove:" + remove + " new:" + newTileCnt);
// log.debug("cur: " + mapPosition);
/* start with farest away tile */ /* start with farest away tile */
for (int i = size - 1; i >= 0 && remove > 0; i--) { for (int i = mTilesCount - 1; i >= 0 && remove > 0; i--) {
MapTile t = tiles[i]; MapTile t = tiles[i];
/* dont remove tile used by TileRenderer, or somewhere else /* dont remove tile used by TileRenderer, or somewhere else
* try again in next run. */ * try again in next run. */
if (t.isLocked()) { if (t.isLocked()) {
if (dbg) if (dbg)
log.debug("{} locked (state={}, d={})", t, t.state, t.distance); log.debug("{} locked (state={}, d={})",
t, t.state(), t.distance);
continue;
}
if (t.state(CANCEL)) {
continue; continue;
} }
/* cancel loading of tiles that should not even be cached */ /* cancel loading of tiles that should not even be cached */
if (t.state == LOADING) { if (t.state(LOADING)) {
t.state = CANCEL; t.setState(CANCEL);
if (dbg) if (dbg)
log.debug("{} canceled (d={})", t, t.distance); log.debug("{} canceled (d={})", t, t.distance);
continue;
} }
/* clear new and unused tile */ /* clear new and unused tile */
if (t.state == NEW_DATA) { if (t.state(NEW_DATA)) {
newTileCnt--; newTileCnt--;
if (dbg) if (dbg)
log.debug("{} unused (d=({})", t, t.distance); log.debug("{} unused (d=({})", t, t.distance);
} }
removeFromCache(t); if (!t.state(READY | NEW_DATA)) {
log.error("stuff that should be here! {} {}", t, t.state());
}
if (removeFromCache(t)) {
tiles[i] = null; tiles[i] = null;
remove--; remove--;
} }
}
remove = (newTileCnt - MAX_TILES_IN_QUEUE) + QUEUE_CLEAR_THRESHOLD; for (int i = mTilesCount - 1; i >= 0 && newTileCnt > MAX_TILES_IN_QUEUE; i--) {
for (int i = size - 1; i >= 0 && remove > 0; i--) {
MapTile t = tiles[i]; MapTile t = tiles[i];
if ((t != null) && (t.state == NEW_DATA) && !t.isLocked()) { if ((t != null) && (t.state(NEW_DATA))) {
removeFromCache(t); if (removeFromCache(t)) {
tiles[i] = null; tiles[i] = null;
remove--;
newTileCnt--; newTileCnt--;
} }
} }
}
mTilesForUpload += newTileCnt; mTilesToUpload = newTileCnt;
} }
/** /**
@ -563,21 +585,23 @@ public class TileManager {
@Override @Override
public void run() { public void run() {
if (success && tile.state != CANCEL) { if (success && tile.state(LOADING)) {
tile.state = NEW_DATA; tile.setState(NEW_DATA);
events.fire(TILE_LOADED, tile); events.fire(TILE_LOADED, tile);
mTilesToUpload++;
mTilesForUpload++;
//log.debug("loading {}: ready", tile);
return; return;
} }
log.debug("loading {}: {}", tile, // TODO use mMap.update(true) to retry tile loading?
success ? "canceled" log.debug("Load: {} {} state:{}",
: "failed"); tile, success ? "success" : "failed",
tile.state());
if (tile.state == LOADING) /* got orphaned tile */
mIndex.removeItem(tile); if (tile.state(DEADBEEF)) {
tile.clear();
return;
}
tile.clear(); tile.clear();
} }

View File

@ -20,7 +20,6 @@ import static org.oscim.layers.tile.MapTile.PROXY_PARENT;
import static org.oscim.layers.tile.MapTile.State.NEW_DATA; import static org.oscim.layers.tile.MapTile.State.NEW_DATA;
import static org.oscim.layers.tile.MapTile.State.READY; import static org.oscim.layers.tile.MapTile.State.READY;
import org.oscim.core.MapPosition;
import org.oscim.layers.tile.MapTile.TileNode; import org.oscim.layers.tile.MapTile.TileNode;
import org.oscim.renderer.BufferObject; import org.oscim.renderer.BufferObject;
import org.oscim.renderer.GLViewport; import org.oscim.renderer.GLViewport;
@ -79,50 +78,61 @@ public abstract class TileRenderer extends LayerRenderer {
*/ */
@Override @Override
public synchronized void update(GLViewport v) { public synchronized void update(GLViewport v) {
/* count placeholder tiles */
if (mAlpha == 0) { if (mAlpha == 0) {
mDrawTiles.releaseTiles(); mDrawTiles.releaseTiles();
setReady(false);
return; return;
} }
/* get current tiles to draw */
boolean tilesChanged;
synchronized (tilelock) {
tilesChanged = mTileManager.getActiveTiles(mDrawTiles);
}
if (mDrawTiles.cnt == 0)
return;
/* keep constant while rendering frame */ /* keep constant while rendering frame */
mLayerAlpha = mAlpha; mLayerAlpha = mAlpha;
mOverdrawColor = mOverdraw; mOverdrawColor = mOverdraw;
int tileCnt = mDrawTiles.cnt; /* get current tiles to draw */
MapTile[] tiles = mDrawTiles.tiles; synchronized (tilelock) {
boolean tilesChanged = mTileManager.getActiveTiles(mDrawTiles);
if (tilesChanged || v.changed()) { if (mDrawTiles.cnt == 0) {
updateTileVisibility(v.pos, v.plane); setReady(false);
mProxyTileCnt = 0;
return;
} }
tileCnt += mProxyTileCnt; /* update isVisible flag true for tiles that intersect view */
if (tilesChanged || v.changed()) {
/* lock tiles while updating isVisible state */
mProxyTileCnt = 0;
MapTile[] tiles = mDrawTiles.tiles;
int tileZoom = tiles[0].zoomLevel;
for (int i = 0; i < mDrawTiles.cnt; i++)
tiles[i].isVisible = false;
/* no renderable tile can be locked at this point */
if (tileZoom > v.pos.zoomLevel + 2 || tileZoom < v.pos.zoomLevel - 4) {
return;
}
/* check visibile tiles */
mScanBox.scan(v.pos.x, v.pos.y, v.pos.scale, tileZoom, v.plane);
}
}
/* prepare tiles for rendering */ /* prepare tiles for rendering */
if (compileTileLayers(tiles, tileCnt) > 0) { if (compileTileLayers(mDrawTiles.tiles, mDrawTiles.cnt + mProxyTileCnt) > 0) {
mUploadSerial++; mUploadSerial++;
BufferObject.checkBufferUsage(false); BufferObject.checkBufferUsage(false);
} }
} setReady(true);
@Override
public void render(GLViewport v) {
/* render in update() so that tiles cannot vanish in between. */
} }
public void clearTiles() { public void clearTiles() {
/* Clear all references to MapTiles as all current /* Clear all references to MapTiles as all current
* tiles will also be removed from TileManager. */ * tiles will also be removed from TileManager. */
//mDrawTiles = new TileSet(); mDrawTiles.releaseTiles();
mDrawTiles.tiles = new MapTile[1]; mDrawTiles.tiles = new MapTile[1];
mDrawTiles.cnt = 0; mDrawTiles.cnt = 0;
} }
@ -137,17 +147,17 @@ public abstract class TileRenderer extends LayerRenderer {
if (!tile.isVisible) if (!tile.isVisible)
continue; continue;
if (tile.state == READY) if (tile.state(READY))
continue; continue;
if (tile.state == NEW_DATA) { if (tile.state(NEW_DATA)) {
uploadCnt += uploadTileData(tile); uploadCnt += uploadTileData(tile);
continue; continue;
} }
/* load tile that is referenced by this holder */ /* load tile that is referenced by this holder */
MapTile proxy = tile.holder; MapTile proxy = tile.holder;
if (proxy != null && proxy.state == NEW_DATA) { if (proxy != null && proxy.state(NEW_DATA)) {
uploadCnt += uploadTileData(proxy); uploadCnt += uploadTileData(proxy);
tile.state = proxy.state; tile.state = proxy.state;
continue; continue;
@ -174,7 +184,7 @@ public abstract class TileRenderer extends LayerRenderer {
} }
private static int uploadTileData(MapTile tile) { private static int uploadTileData(MapTile tile) {
tile.state = READY; tile.setState(READY);
RenderBuckets buckets = tile.getBuckets(); RenderBuckets buckets = tile.getBuckets();
/* tile might only contain label layers */ /* tile might only contain label layers */
@ -191,30 +201,6 @@ public abstract class TileRenderer extends LayerRenderer {
private final Object tilelock = new Object(); private final Object tilelock = new Object();
/** set tile isVisible flag true for tiles that intersect view */
private void updateTileVisibility(MapPosition pos, float[] box) {
/* lock tiles while updating isVisible state */
synchronized (tilelock) {
MapTile[] tiles = mDrawTiles.tiles;
int tileZoom = tiles[0].zoomLevel;
for (int i = 0; i < mDrawTiles.cnt; i++)
tiles[i].isVisible = false;
if (tileZoom > pos.zoomLevel + 2 || tileZoom < pos.zoomLevel - 4) {
//log.debug("skip: zoomlevel diff " + (tileZoom - pos.zoomLevel));
return;
}
/* count placeholder tiles */
mProxyTileCnt = 0;
/* check visibile tiles */
mScanBox.scan(pos.x, pos.y, pos.scale, tileZoom, box);
}
}
/** /**
* Update tileSet with currently visible tiles get a TileSet of currently * Update tileSet with currently visible tiles get a TileSet of currently
* visible tiles * visible tiles
@ -248,7 +234,7 @@ public abstract class TileRenderer extends LayerRenderer {
tileSet.cnt = 0; tileSet.cnt = 0;
for (int i = 0; i < cnt; i++) { for (int i = 0; i < cnt; i++) {
MapTile t = newTiles[i]; MapTile t = newTiles[i];
if (t.isVisible && t.state == READY) { if (t.isVisible && t.state(READY)) {
t.lock(); t.lock();
tileSet.tiles[tileSet.cnt++] = t; tileSet.tiles[tileSet.cnt++] = t;
} }

View File

@ -45,8 +45,7 @@ public class VectorTileRenderer extends TileRenderer {
protected int mDrawSerial; protected int mDrawSerial;
@Override @Override
public synchronized void update(GLViewport v) { public synchronized void render(GLViewport v) {
super.update(v);
/* discard depth projection from tilt, depth buffer /* discard depth projection from tilt, depth buffer
* is used for clipping */ * is used for clipping */
@ -67,8 +66,8 @@ public class VectorTileRenderer extends TileRenderer {
for (int i = 0; i < tileCnt; i++) { for (int i = 0; i < tileCnt; i++) {
MapTile t = tiles[i]; MapTile t = tiles[i];
// TODO check if proxies are actually available
if (t.isVisible && t.state != READY) { if (t.isVisible && !t.state(READY)) {
GL.glDepthMask(true); GL.glDepthMask(true);
GL.glClear(GL20.GL_DEPTH_BUFFER_BIT); GL.glClear(GL20.GL_DEPTH_BUFFER_BIT);
@ -87,7 +86,7 @@ public class VectorTileRenderer extends TileRenderer {
/* draw visible tiles */ /* draw visible tiles */
for (int i = 0; i < tileCnt; i++) { for (int i = 0; i < tileCnt; i++) {
MapTile t = tiles[i]; MapTile t = tiles[i];
if (t.isVisible && t.state == READY) if (t.isVisible && t.state(READY))
drawTile(t, v, 0); drawTile(t, v, 0);
} }
@ -155,8 +154,10 @@ public class VectorTileRenderer extends TileRenderer {
? tile.getBuckets() ? tile.getBuckets()
: tile.holder.getBuckets(); : tile.holder.getBuckets();
if (buckets == null || buckets.vbo == null) if (buckets == null || buckets.vbo == null) {
//log.debug("{} no buckets!", tile);
return; return;
}
MapPosition pos = v.pos; MapPosition pos = v.pos;
/* place tile relative to map position */ /* place tile relative to map position */

View File

@ -33,7 +33,7 @@ public class BitmapTileLayer extends TileLayer {
protected static final Logger log = LoggerFactory.getLogger(BitmapTileLayer.class); protected static final Logger log = LoggerFactory.getLogger(BitmapTileLayer.class);
private final static int CACHE_LIMIT = 20; private final static int CACHE_LIMIT = 40;
protected final TileSource mTileSource; protected final TileSource mTileSource;
@ -110,7 +110,7 @@ public class BitmapTileLayer extends TileLayer {
pool.clear(); pool.clear();
} }
final static int POOL_FILL = 40; final static int POOL_FILL = 20;
/** pool shared by TextLayers */ /** pool shared by TextLayers */
final TexturePool pool = new TexturePool(POOL_FILL) { final TexturePool pool = new TexturePool(POOL_FILL) {

View File

@ -16,7 +16,7 @@
*/ */
package org.oscim.layers.tile.bitmap; package org.oscim.layers.tile.bitmap;
import static org.oscim.layers.tile.MapTile.State.CANCEL; import static org.oscim.layers.tile.MapTile.State.LOADING;
import org.oscim.backend.canvas.Bitmap; import org.oscim.backend.canvas.Bitmap;
import org.oscim.core.Tile; import org.oscim.core.Tile;
@ -55,23 +55,26 @@ public class BitmapTileLoader extends TileLoader {
@Override @Override
public void setTileImage(Bitmap bitmap) { public void setTileImage(Bitmap bitmap) {
if (isCanceled() || mTile.state(CANCEL)) if (isCanceled() || !mTile.state(LOADING)) {
bitmap.recycle();
return; return;
}
BitmapBucket l = new BitmapBucket(false); BitmapBucket l = new BitmapBucket(false);
l.setBitmap(bitmap, Tile.SIZE, Tile.SIZE, mLayer.pool); l.setBitmap(bitmap, Tile.SIZE, Tile.SIZE, mLayer.pool);
RenderBuckets b = new RenderBuckets(); RenderBuckets buckets = new RenderBuckets();
b.set(l); buckets.set(l);
mTile.data = b; mTile.data = buckets;
} }
@Override @Override
public void dispose() { public void dispose() {
mTileDataSource.cancel();
} }
@Override @Override
public void cancel() { public void cancel() {
mTileDataSource.cancel();
} }
} }

View File

@ -16,7 +16,7 @@
*/ */
package org.oscim.layers.tile.vector; package org.oscim.layers.tile.vector;
import static org.oscim.layers.tile.MapTile.State.CANCEL; import static org.oscim.layers.tile.MapTile.State.LOADING;
import org.oscim.core.GeometryBuffer.GeometryType; import org.oscim.core.GeometryBuffer.GeometryType;
import org.oscim.core.MapElement; import org.oscim.core.MapElement;
@ -134,14 +134,15 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
@Override @Override
public void completed(QueryResult result) { public void completed(QueryResult result) {
mTileLayer.callHooksComplete(mTile, result == QueryResult.SUCCESS); boolean ok = (result == QueryResult.SUCCESS);
mTileLayer.callHooksComplete(mTile, ok);
/* finish buckets- tessellate and cleanup on worker-thread */ /* finish buckets- tessellate and cleanup on worker-thread */
mBuckets.prepare(); mBuckets.prepare();
clearState();
super.completed(result); super.completed(result);
clearState();
} }
protected static int getValidLayer(int layer) { protected static int getValidLayer(int layer) {
@ -180,7 +181,7 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
@Override @Override
public void process(MapElement element) { public void process(MapElement element) {
if (isCanceled() || mTile.state(CANCEL)) if (isCanceled() || !mTile.state(LOADING))
return; return;
if (mTileLayer.callProcessHooks(mTile, mBuckets, element)) if (mTileLayer.callProcessHooks(mTile, mBuckets, element))