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

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

@ -16,9 +16,13 @@
*/
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.LOADING;
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.TIMEOUT;
import org.oscim.core.Tile;
import org.oscim.layers.tile.vector.VectorTileLoader;
@ -44,34 +48,38 @@ public class MapTile extends Tile {
}
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.
* 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.
* 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.
* 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,
* 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);
@ -83,7 +91,7 @@ public class MapTile extends Tile {
public final static int PROXY_HOLDER = (1 << 6);
/** Tile state */
byte state;
byte state = State.NONE;
/**
* 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}
*/
void lock() {
if (state == DEADBEEF) {
log.debug("Locking dead tile {}", this);
return;
}
if (locked++ > 0)
return;
@ -218,6 +231,12 @@ public class MapTile extends Tile {
if (--locked > 0)
return;
if (state == DEADBEEF) {
log.debug("Unlock dead tile {}", this);
clear();
return;
}
TileNode parent = node.parent;
if ((proxy & PROXY_PARENT) != 0)
parent.item.refs--;
@ -259,7 +278,7 @@ public class MapTile extends Tile {
data.dispose();
data = data.next;
}
state = DEADBEEF;
state = NONE;
}
/**
@ -367,4 +386,75 @@ public class MapTile extends Tile {
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;
}
}
}

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

@ -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.READY;
import org.oscim.core.MapPosition;
import org.oscim.layers.tile.MapTile.TileNode;
import org.oscim.renderer.BufferObject;
import org.oscim.renderer.GLViewport;
@ -79,50 +78,61 @@ public abstract class TileRenderer extends LayerRenderer {
*/
@Override
public synchronized void update(GLViewport v) {
/* count placeholder tiles */
if (mAlpha == 0) {
mDrawTiles.releaseTiles();
setReady(false);
return;
}
/* get current tiles to draw */
boolean tilesChanged;
synchronized (tilelock) {
tilesChanged = mTileManager.getActiveTiles(mDrawTiles);
}
if (mDrawTiles.cnt == 0)
return;
/* keep constant while rendering frame */
mLayerAlpha = mAlpha;
mOverdrawColor = mOverdraw;
int tileCnt = mDrawTiles.cnt;
MapTile[] tiles = mDrawTiles.tiles;
/* get current tiles to draw */
synchronized (tilelock) {
boolean tilesChanged = mTileManager.getActiveTiles(mDrawTiles);
if (tilesChanged || v.changed()) {
updateTileVisibility(v.pos, v.plane);
if (mDrawTiles.cnt == 0) {
setReady(false);
mProxyTileCnt = 0;
return;
}
/* 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);
}
}
tileCnt += mProxyTileCnt;
/* prepare tiles for rendering */
if (compileTileLayers(tiles, tileCnt) > 0) {
if (compileTileLayers(mDrawTiles.tiles, mDrawTiles.cnt + mProxyTileCnt) > 0) {
mUploadSerial++;
BufferObject.checkBufferUsage(false);
}
}
@Override
public void render(GLViewport v) {
/* render in update() so that tiles cannot vanish in between. */
setReady(true);
}
public void clearTiles() {
/* Clear all references to MapTiles as all current
* tiles will also be removed from TileManager. */
//mDrawTiles = new TileSet();
mDrawTiles.releaseTiles();
mDrawTiles.tiles = new MapTile[1];
mDrawTiles.cnt = 0;
}
@ -137,17 +147,17 @@ public abstract class TileRenderer extends LayerRenderer {
if (!tile.isVisible)
continue;
if (tile.state == READY)
if (tile.state(READY))
continue;
if (tile.state == NEW_DATA) {
if (tile.state(NEW_DATA)) {
uploadCnt += uploadTileData(tile);
continue;
}
/* load tile that is referenced by this holder */
MapTile proxy = tile.holder;
if (proxy != null && proxy.state == NEW_DATA) {
if (proxy != null && proxy.state(NEW_DATA)) {
uploadCnt += uploadTileData(proxy);
tile.state = proxy.state;
continue;
@ -174,7 +184,7 @@ public abstract class TileRenderer extends LayerRenderer {
}
private static int uploadTileData(MapTile tile) {
tile.state = READY;
tile.setState(READY);
RenderBuckets buckets = tile.getBuckets();
/* tile might only contain label layers */
@ -191,30 +201,6 @@ public abstract class TileRenderer extends LayerRenderer {
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
* visible tiles
@ -248,7 +234,7 @@ public abstract class TileRenderer extends LayerRenderer {
tileSet.cnt = 0;
for (int i = 0; i < cnt; i++) {
MapTile t = newTiles[i];
if (t.isVisible && t.state == READY) {
if (t.isVisible && t.state(READY)) {
t.lock();
tileSet.tiles[tileSet.cnt++] = t;
}

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

@ -33,7 +33,7 @@ public class BitmapTileLayer extends TileLayer {
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;
@ -110,7 +110,7 @@ public class BitmapTileLayer extends TileLayer {
pool.clear();
}
final static int POOL_FILL = 40;
final static int POOL_FILL = 20;
/** pool shared by TextLayers */
final TexturePool pool = new TexturePool(POOL_FILL) {

@ -16,7 +16,7 @@
*/
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.core.Tile;
@ -55,23 +55,26 @@ public class BitmapTileLoader extends TileLoader {
@Override
public void setTileImage(Bitmap bitmap) {
if (isCanceled() || mTile.state(CANCEL))
if (isCanceled() || !mTile.state(LOADING)) {
bitmap.recycle();
return;
}
BitmapBucket l = new BitmapBucket(false);
l.setBitmap(bitmap, Tile.SIZE, Tile.SIZE, mLayer.pool);
RenderBuckets b = new RenderBuckets();
b.set(l);
mTile.data = b;
RenderBuckets buckets = new RenderBuckets();
buckets.set(l);
mTile.data = buckets;
}
@Override
public void dispose() {
mTileDataSource.cancel();
}
@Override
public void cancel() {
mTileDataSource.cancel();
}
}

@ -16,7 +16,7 @@
*/
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.MapElement;
@ -134,14 +134,15 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
@Override
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 */
mBuckets.prepare();
clearState();
super.completed(result);
clearState();
}
protected static int getValidLayer(int layer) {
@ -180,7 +181,7 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
@Override
public void process(MapElement element) {
if (isCanceled() || mTile.state(CANCEL))
if (isCanceled() || !mTile.state(LOADING))
return;
if (mTileLayer.callProcessHooks(mTile, mBuckets, element))