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

View File

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