TileManager, JobQueue: use MapTile[] instead of list

This commit is contained in:
Hannes Janetzek 2013-01-06 21:24:48 +01:00
parent d94f8e2af4
commit e9d2c88022
3 changed files with 187 additions and 128 deletions

View File

@ -1,5 +1,4 @@
/* /*
* Copyright 2010, 2011, 2012 mapsforge.org
* Copyright 2012, 2013 OpenScienceMap * Copyright 2012, 2013 OpenScienceMap
* *
* This program is free software: you can redistribute it and/or modify it under the * This program is free software: you can redistribute it and/or modify it under the
@ -18,58 +17,71 @@ package org.oscim.generator;
import static org.oscim.generator.JobTile.STATE_LOADING; import static org.oscim.generator.JobTile.STATE_LOADING;
import static org.oscim.generator.JobTile.STATE_NONE; import static org.oscim.generator.JobTile.STATE_NONE;
import java.util.ArrayList; import java.util.Arrays;
import java.util.PriorityQueue;
/** /**
* A JobQueue keeps the list of pending jobs for a MapView and prioritizes them. * A JobQueue keeps the list of pending jobs for a MapView and prioritizes them.
*/ */
public class JobQueue { public class JobQueue {
private static final int INITIAL_CAPACITY = 64;
private PriorityQueue<JobTile> mPriorityQueue; private int mCurrentJob = 0;
private JobTile[] mJobs;
/**
*/
public JobQueue() {
mPriorityQueue = new PriorityQueue<JobTile>(INITIAL_CAPACITY);
}
/** /**
* @param tiles * @param tiles
* the job to be added to this queue. * the job to be added to this queue.
*/ */
public synchronized void setJobs(ArrayList<JobTile> tiles) { public synchronized void setJobs(JobTile[] tiles) {
for (JobTile t : tiles)
t.state = STATE_LOADING;
for (int i = 0, n = tiles.size(); i < n; i++) { mJobs = tiles;
JobTile tile = tiles.get(i); mCurrentJob = 0;
tile.state = STATE_LOADING;
mPriorityQueue.offer(tile);
}
} }
/** /**
* Removes all jobs from this queue. * Removes all jobs from this queue.
*/ */
public synchronized void clear() { public synchronized void clear() {
JobTile t; if (mJobs == null) {
while ((t = mPriorityQueue.poll()) != null) mCurrentJob = 0;
t.state = STATE_NONE; return;
}
JobTile[] tiles = mJobs;
mPriorityQueue.clear(); for (int i = mCurrentJob, n = mJobs.length; i < n; i++) {
tiles[i].state = STATE_NONE;
tiles[i] = null;
}
mCurrentJob = 0;
mJobs = null;
} }
/** /**
* @return true if this queue contains no jobs, false otherwise. * @return true if this queue contains no jobs, false otherwise.
*/ */
public synchronized boolean isEmpty() { public synchronized boolean isEmpty() {
return mPriorityQueue.isEmpty(); return (mJobs == null);
} }
/** /**
* @return the most important job from this queue or null, if empty. * @return the most important job from this queue or null, if empty.
*/ */
public synchronized JobTile poll() { public synchronized JobTile poll() {
return mPriorityQueue.poll(); if (mJobs == null)
return null;
if (mCurrentJob == 0)
Arrays.sort(mJobs);
//return mPriorityQueue.poll();
JobTile t = mJobs[mCurrentJob];
mJobs[mCurrentJob] = null;
if (++mCurrentJob == mJobs.length)
mJobs = null;
return t;
} }
} }

View File

@ -21,7 +21,7 @@ import static org.oscim.generator.JobTile.STATE_NONE;
import static org.oscim.generator.JobTile.STATE_READY; import static org.oscim.generator.JobTile.STATE_READY;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Arrays;
import org.oscim.core.MapPosition; import org.oscim.core.MapPosition;
import org.oscim.core.Tile; import org.oscim.core.Tile;
@ -43,18 +43,24 @@ public class TileManager {
static final String TAG = TileManager.class.getSimpleName(); static final String TAG = TileManager.class.getSimpleName();
private static final int MAX_TILES_IN_QUEUE = 40; private static final int MAX_TILES_IN_QUEUE = 40;
private static final int CACHE_THRESHOLD = 10; private static final int CACHE_THRESHOLD = 30;
private static MapView mMapView; private static MapView mMapView;
private static final MapPosition mMapPosition = new MapPosition(); private static final MapPosition mMapPosition = new MapPosition();
private final MapViewPosition mMapViewPosition; private final MapViewPosition mMapViewPosition;
// new jobs for the MapWorkers
private static ArrayList<JobTile> mJobList;
// all tiles // all tiles
private static ArrayList<MapTile> mTiles; private static MapTile[] mTiles;
// actual number of tiles in mTiles
private static int mTilesCount;
// current end position in mTiles
private static int mTilesSize;
// first free slot in mTiles
//private static int mTilesFirst;
// new jobs for MapWorkers
private static ArrayList<JobTile> mJobs;
// tiles that have new data to upload, see passTile() // tiles that have new data to upload, see passTile()
private static ArrayList<MapTile> mTilesLoaded; private static ArrayList<MapTile> mTilesLoaded;
@ -75,8 +81,6 @@ public class TileManager {
static TileSet mCurrentTiles; static TileSet mCurrentTiles;
/* package */static TileSet mNewTiles; /* package */static TileSet mNewTiles;
static int tileCounter;
private static ScanBox mScanBox = new ScanBox() { private static ScanBox mScanBox = new ScanBox() {
@Override @Override
@ -138,7 +142,7 @@ public class TileManager {
// mRenderer = null; // mRenderer = null;
// mTiles = null; // mTiles = null;
// mTilesLoaded = null; // mTilesLoaded = null;
// mJobList = null; // mJobs = null;
// mOverlays = null; // mOverlays = null;
// ... free pools // ... free pools
} }
@ -148,18 +152,16 @@ public class TileManager {
mMapView = mapView; mMapView = mapView;
mMapViewPosition = mapView.getMapViewPosition(); mMapViewPosition = mapView.getMapViewPosition();
mJobList = new ArrayList<JobTile>(); mJobs = new ArrayList<JobTile>();
mTiles = new ArrayList<MapTile>(200); mTiles = new MapTile[GLRenderer.CACHE_TILES];
mTilesLoaded = new ArrayList<MapTile>(30); mTilesLoaded = new ArrayList<MapTile>(30);
// this is probably a good place to init these // this is probably a good place to init these
VertexPool.init(); //VertexPool.init();
QuadTree.init(); //QuadTree.init();
mUpdateCnt = 0; mUpdateCnt = 0;
mInitial = true; mInitial = true;
tileCounter = 0;
} }
/** /**
@ -176,7 +178,8 @@ public class TileManager {
return; return;
if (clear || mInitial) { if (clear || mInitial) {
// make sure onDrawFrame is not running, and labeling thread? // make sure onDrawFrame is not running
// - and labeling thread?
GLRenderer.drawlock.lock(); GLRenderer.drawlock.lock();
// clear all tiles references // clear all tiles references
@ -184,21 +187,26 @@ public class TileManager {
if (clear) { if (clear) {
// pass VBOs and VertexItems back to pools // pass VBOs and VertexItems back to pools
for (MapTile t : mTiles) for (int i = 0; i < mTilesSize; i++)
clearTile(t); clearTile(mTiles[i]);
} else { } else {
// mInitial is set when surface changed
// and VBOs might be lost
VertexPool.init(); VertexPool.init();
} }
//VertexPool.init();
QuadTree.init(); QuadTree.init();
mTiles.clear(); Arrays.fill(mTiles, null);
mTilesSize = 0;
mTilesCount = 0;
mTilesLoaded.clear(); mTilesLoaded.clear();
//mUpdateCnt = 0; for (TileSet td : mTileSets) {
for (TileSet td : mTileSets) Arrays.fill(td.tiles, null);
td.cnt = 0; td.cnt = 0;
}
// set up TileData arrays that are passed to gl-thread // set up TileData arrays that are passed to gl-thread
int num = Math.max(mWidth, mHeight); int num = Math.max(mWidth, mHeight);
@ -207,11 +215,11 @@ public class TileManager {
mNewTiles = new TileSet(numTiles); mNewTiles = new TileSet(numTiles);
mCurrentTiles = new TileSet(numTiles); mCurrentTiles = new TileSet(numTiles);
GLRenderer.drawlock.unlock();
// make sure mMapPosition will be updated // make sure mMapPosition will be updated
mMapPosition.zoomLevel = -1; mMapPosition.zoomLevel = -1;
mInitial = false; mInitial = false;
GLRenderer.drawlock.unlock();
} }
MapPosition mapPosition = mMapPosition; MapPosition mapPosition = mMapPosition;
@ -243,8 +251,7 @@ public class TileManager {
mMapView.render(); mMapView.render();
if (changed) { if (changed) {
int remove = mTiles.size() - GLRenderer.CACHE_TILES; int remove = mTilesCount - GLRenderer.CACHE_TILES;
if (remove > CACHE_THRESHOLD) if (remove > CACHE_THRESHOLD)
limitCache(mapPosition, remove); limitCache(mapPosition, remove);
@ -252,7 +259,7 @@ public class TileManager {
} }
} }
/// EEEK, need to keep track of TileSets to clear on reset... // need to keep track of TileSets to clear on reset...
private static ArrayList<TileSet> mTileSets = new ArrayList<TileSet>(2); private static ArrayList<TileSet> mTileSets = new ArrayList<TileSet>(2);
public static TileSet getActiveTiles(TileSet td) { public static TileSet getActiveTiles(TileSet td) {
@ -308,12 +315,6 @@ public class TileManager {
* @return true if new tiles were loaded * @return true if new tiles were loaded
*/ */
private static boolean updateVisibleList(MapPosition mapPosition, int zdir) { private static boolean updateVisibleList(MapPosition mapPosition, int zdir) {
// TODO keep mJobList and JobQueue in sync, no need to clear
mJobList.clear();
// sets non processed tiles to isLoading = false
// and clear job queue
mMapView.addJobs(null); mMapView.addJobs(null);
mNewTiles.cnt = 0; mNewTiles.cnt = 0;
@ -353,17 +354,19 @@ public class TileManager {
mUpdateCnt++; mUpdateCnt++;
} }
// Log.d(TAG, "tiles: " + tileCounter + " " + BufferObject.counter // Log.d(TAG, "tiles: " + mTilesCount + " " + BufferObject.counter
// + " sum:" + (tileCounter + BufferObject.counter)); // + " sum:" + (mTilesCount + BufferObject.counter));
} }
if (mJobList.size() > 0) { if (mJobs.size() > 0) {
updateTileDistances(mJobList, mapPosition);
Collections.sort(mJobList); JobTile[] jobs = new JobTile[mJobs.size()];
jobs = mJobs.toArray(jobs);
updateTileDistances(jobs, jobs.length, mapPosition);
// sets tiles to isLoading = true // sets tiles to isLoading = true
mMapView.addJobs(mJobList); mMapView.addJobs(jobs);
mJobs.clear();
return true; return true;
} }
return false; return false;
@ -388,7 +391,7 @@ public class TileManager {
if (tile != null) { if (tile != null) {
if (!tile.isActive()) if (!tile.isActive())
mJobList.add(tile); mJobs.add(tile);
return tile; return tile;
} }
@ -396,9 +399,16 @@ public class TileManager {
tile = new MapTile(x, y, zoomLevel); tile = new MapTile(x, y, zoomLevel);
QuadTree.add(tile); QuadTree.add(tile);
mTiles.add(tile); if (mTilesSize == mTiles.length) {
mJobList.add(tile); MapTile[] tmp = new MapTile[mTiles.length + 20];
tileCounter++; System.arraycopy(mTiles, 0, tmp, 0, mTilesSize);
mTiles = tmp;
Log.d(TAG, "increase tiles: " + mTiles.length);
}
mTiles[mTilesSize++] = tile;
mJobs.add(tile);
mTilesCount++;
return tile; return tile;
@ -422,7 +432,7 @@ public class TileManager {
// } // }
// //
// if (!c.isActive()) { // if (!c.isActive()) {
// mJobList.add(c); // mJobs.add(c);
// } // }
// } // }
// } // }
@ -437,19 +447,19 @@ public class TileManager {
// //
// QuadTree.add(p); // QuadTree.add(p);
// mTiles.add(p); // mTiles.add(p);
// mJobList.add(p); // mJobs.add(p);
// //
// } else if (!p.isActive()) { // } else if (!p.isActive()) {
// if (!mJobList.contains(p)) // if (!mJobs.contains(p))
// mJobList.add(p); // mJobs.add(p);
// } // }
// } // }
// } // }
} }
private static void clearTile(MapTile t) { private static void clearTile(MapTile t) {
if (t == null)
t.state = STATE_NONE; return;
if (t.layers != null) { if (t.layers != null) {
t.layers.clear(); t.layers.clear();
@ -462,14 +472,14 @@ public class TileManager {
BufferObject.release(t.vbo); BufferObject.release(t.vbo);
t.vbo = null; t.vbo = null;
} }
// if (t.texture != null)
// t.texture.tile = null;
tileCounter--; mTilesCount--;
QuadTree.remove(t); QuadTree.remove(t);
t.state = STATE_NONE;
} }
private static void updateTileDistances(ArrayList<?> tiles, MapPosition mapPosition) { private static void updateTileDistances(Object[] tiles, int size, MapPosition mapPosition) {
int h = (Tile.TILE_SIZE >> 1); int h = (Tile.TILE_SIZE >> 1);
byte zoom = mapPosition.zoomLevel; byte zoom = mapPosition.zoomLevel;
long x = (long) mapPosition.x; long x = (long) mapPosition.x;
@ -481,8 +491,11 @@ public class TileManager {
// TODO this could need some fixing, and optimization // TODO this could need some fixing, and optimization
// to consider move/zoom direction // to consider move/zoom direction
for (int i = 0, n = tiles.size(); i < n; i++) { for (int i = 0; i < size; i++) {
JobTile t = (JobTile) tiles.get(i); JobTile t = (JobTile) tiles[i];
if (t == null)
continue;
diff = (t.zoomLevel - zoom); diff = (t.zoomLevel - zoom);
if (diff == 0) { if (diff == 0) {
@ -490,8 +503,9 @@ public class TileManager {
dy = (t.pixelY + h) - y; dy = (t.pixelY + h) - y;
dx %= center; dx %= center;
dy %= center; dy %= center;
t.distance = (dx * dx + dy * dy) * 0.5f;
//t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) * 0.25f; //t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) * 0.25f;
t.distance = (float) Math.sqrt((dx * dx + dy * dy)) * 0.25f; //t.distance = (float) Math.sqrt((dx * dx + dy * dy)) * 0.25f;
} else if (diff > 0) { } else if (diff > 0) {
// tile zoom level is child of current // tile zoom level is child of current
@ -505,8 +519,9 @@ public class TileManager {
} }
dx %= center; dx %= center;
dy %= center; dy %= center;
t.distance = (dx * dx + dy * dy);
//t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)); //t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy));
t.distance = (float) Math.sqrt((dx * dx + dy * dy)); //t.distance = (float) Math.sqrt((dx * dx + dy * dy));
} else { } else {
// tile zoom level is parent of current // tile zoom level is parent of current
@ -514,42 +529,74 @@ public class TileManager {
dy = ((t.pixelY + h) << -diff) - y; dy = ((t.pixelY + h) << -diff) - y;
dx %= center; dx %= center;
dy %= center; dy %= center;
t.distance = (dx * dx + dy * dy) * (-diff * 0.7f);
//t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) * (-diff * 0.5f); //t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) * (-diff * 0.5f);
t.distance = (float) Math.sqrt((dx * dx + dy * dy)) * (-diff * 0.5f); //t.distance = (float) Math.sqrt((dx * dx + dy * dy)) * (-diff * 0.5f);
} }
} }
} }
private static void limitCache(MapPosition mapPosition, int remove) { private static void limitCache(MapPosition mapPosition, int remove) {
int size = mTiles.size(); MapTile[] tiles = mTiles;
// remove tiles that were never loaded // remove tiles that were never loaded
for (int i = 0; i < size;) { for (int i = 0, size = mTilesSize; i < size; i++) {
MapTile t = mTiles.get(i); MapTile t = tiles[i];
if (t == null)
continue;
// make sure tile cannot be used by GL or MapWorker Thread // make sure tile cannot be used by GL or MapWorker Thread
if (t.isLocked() || t.isActive()) { if (t.isLocked() || t.isActive()) {
i++; continue;
} else { }
clearTile(t); clearTile(t);
mTiles.remove(i); tiles[i] = null;
remove--; remove--;
size--; size--;
} }
if (remove > 5) {
int size = mTilesSize;
if (size > mTilesCount) {
Log.d(TAG, "repack: " + size + " " + mTilesCount);
int start = 0;
// get first position to shift
while (start < size && tiles[start] != null)
start++;
int space = start + 1;
for (int end = 0; end < size;) {
// get the number of slots to shift
while (space < size && tiles[space] == null)
space++;
// get the position of next free slots
end = space;
while (end < size && tiles[end] != null)
end++;
// number of items to shift
int len = end - space;
if (len > 0) {
System.arraycopy(tiles, space, tiles, start, len);
start = start + len;
space = end;
}
}
Log.d(TAG, "repacked tiles to: " + start);
Arrays.fill(mTiles, start, mTilesSize, null);
mTilesSize = size = start;
} }
if (remove <= 0) updateTileDistances(mTiles, size, mapPosition);
return; Arrays.sort(mTiles, 0, size);
updateTileDistances(mTiles, mapPosition);
Collections.sort(mTiles);
for (int i = 1; i < remove; i++) { for (int i = 1; i < remove; i++) {
MapTile t = mTiles.remove(size - i); MapTile t = tiles[size - i];
if (t.isLocked()) { if (t.isLocked()) {
// dont remove tile used by GLRenderer, or somewhere else // dont remove tile used by GLRenderer, or somewhere else
Log.d(TAG, "limitCache: tile still locked " + t + " " + t.distance); Log.d(TAG, "limitCache: tile still locked " + t + " " + t.distance);
mTiles.add(t); //mTiles.add(t);
} else if (t.state == STATE_LOADING) { } else if (t.state == STATE_LOADING) {
// NOTE: if we add tile back and set loading=false, on next // NOTE: if we add tile back and set loading=false, on next
// limitCache the tile will be removed. clearTile could // limitCache the tile will be removed. clearTile could
@ -559,10 +606,12 @@ public class TileManager {
// false tile could be added to load queue while still // false tile could be added to load queue while still
// processed in TileGenerator => need tile.cancel flag. // processed in TileGenerator => need tile.cancel flag.
// t.isLoading = false; // t.isLoading = false;
mTiles.add(t); //mTiles.add(t);
Log.d(TAG, "limitCache: cancel loading " + t + " " + t.distance); Log.d(TAG, "limitCache: cancel loading " + t + " " + t.distance);
} else { } else {
clearTile(t); clearTile(t);
tiles[size - i] = null;
}
} }
} }
} }
@ -574,12 +623,10 @@ public class TileManager {
return; return;
synchronized (mTilesLoaded) { synchronized (mTilesLoaded) {
// remove tiles already uploaded to vbo // remove tiles already uploaded to vbo
for (int i = 0; i < size;) { for (int i = 0; i < size;) {
MapTile t = mTilesLoaded.get(i); MapTile t = mTilesLoaded.get(i);
// t.rel == null means tile was removed in limitCache -- but then newdata is false anyway? if (t.state == STATE_READY || t.state == STATE_NONE) {
if (t.state == STATE_READY || t.state == STATE_NONE) {// || t.rel == null) {
mTilesLoaded.remove(i); mTilesLoaded.remove(i);
size--; size--;
continue; continue;
@ -592,19 +639,24 @@ public class TileManager {
// clear loaded but not used tiles // clear loaded but not used tiles
for (int i = 0, n = size - MAX_TILES_IN_QUEUE / 2; i < n; n--) { for (int i = 0, n = size - MAX_TILES_IN_QUEUE / 2; i < n; n--) {
MapTile t = mTilesLoaded.get(i); MapTile t = mTilesLoaded.get(i);
if (t.isLocked()) { if (t.isLocked()) {
// Log.d(TAG, "keep unused tile data: " + t + " " +
// t.isActive);
i++; i++;
continue; continue;
} }
// Log.d(TAG, "remove unused tile data: " + t);
mTilesLoaded.remove(i); mTilesLoaded.remove(i);
mTiles.remove(t);
// remove reference from mTiles
MapTile[] tiles = mTiles;
for (int j = 0, m = mTilesSize; j < m; j++) {
if (t == tiles[j]) {
mTiles[j] = null;
break;
}
}
clearTile(t); clearTile(t);
} }
} }
@ -636,16 +688,12 @@ public class TileManager {
if (tile.vbo == null) { if (tile.vbo == null) {
Log.d(TAG, "no VBOs left for " + tile); Log.d(TAG, "no VBOs left for " + tile);
//tile.isLoading = false;
clearTile(tile); clearTile(tile);
return true; return true;
} }
tile.state = STATE_NEW_DATA; tile.state = STATE_NEW_DATA;
//tile.newData = true;
//tile.isLoading = false;
mMapView.render(); mMapView.render();
synchronized (mTilesLoaded) { synchronized (mTilesLoaded) {

View File

@ -18,7 +18,6 @@ package org.oscim.view;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -611,7 +610,7 @@ public class MapView extends RelativeLayout {
* @param jobs * @param jobs
* tile jobs * tile jobs
*/ */
public void addJobs(ArrayList<JobTile> jobs) { public void addJobs(JobTile[] jobs) {
if (jobs == null) { if (jobs == null) {
mJobQueue.clear(); mJobQueue.clear();
return; return;