cleanup tile cache 'limiting functions'

This commit is contained in:
Hannes Janetzek 2012-09-08 01:32:59 +02:00
parent 4763ea8989
commit 6357c2fa2a
2 changed files with 123 additions and 121 deletions

View File

@ -17,7 +17,7 @@ package org.mapsforge.android.glrenderer;
import org.mapsforge.android.mapgenerator.MapTile; import org.mapsforge.android.mapgenerator.MapTile;
class GLMapTile extends MapTile { class GLMapTile extends MapTile {
long lastDraw = 0; byte lastDraw = 0;
// VBO layout: // VBO layout:
// 16 bytes fill coordinates // 16 bytes fill coordinates

View File

@ -114,7 +114,7 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
private static TilesData newTiles, curTiles, drawTiles; private static TilesData newTiles, curTiles, drawTiles;
// draw position is updated from current position in onDrawFrame // draw position is updated from current position in onDrawFrame
// keeping the position consistent while drawing // keeping the position and active tiles consistent while drawing
private static MapPosition mCurPosition, mDrawPosition; private static MapPosition mCurPosition, mDrawPosition;
// flag set by updateVisibleList when current visible tiles // flag set by updateVisibleList when current visible tiles
@ -122,10 +122,12 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
private static boolean mUpdateTiles; private static boolean mUpdateTiles;
private static boolean mInitial; private static boolean mInitial;
private static short mDrawCount = 0;
private float[] mClearColor = null; private float[] mClearColor = null;
private static long mRedrawCnt = 0;
// number of tiles drawn in one frame
private static short mDrawCount = 0;
private static boolean mUpdateColor = false; private static boolean mUpdateColor = false;
/** /**
@ -218,8 +220,8 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
return false; return false;
} }
// FIXME still the chance that one jumped two zoomlevels between cur and draw... // FIXME still the chance that one jumped two zoomlevels between
// and this is all a bit heavy in the first place // cur and draw. should use reference counter instead
private static boolean tileInUse(GLMapTile t) { private static boolean tileInUse(GLMapTile t) {
byte z = mPrevZoom; byte z = mPrevZoom;
@ -267,35 +269,115 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
return false; return false;
} }
private static void limitCache(int remove) { private static void limitCache(MapPosition mapPosition, int remove) {
boolean printAll = false; int removes = remove;
for (int j = mTiles.size() - 1, cnt = 0; cnt < remove && j > 0; j--, cnt++) { int size = mTiles.size();
GLMapTile t = mTiles.remove(j);
// remove orphaned tiles
for (int i = 0; i < size;) {
GLMapTile cur = mTiles.get(i);
// make sure tile cannot be used by GL or MapWorker Thread
if ((!cur.isActive) && (!cur.isLoading) && (!cur.newData)
&& (!cur.isReady) && (!tileInUse(cur))) {
clearTile(cur);
mTiles.remove(i);
removes--;
size--;
// Log.d(TAG, "remove empty tile" + cur);
continue;
}
i++;
}
if (removes <= 0)
return;
updateTileDistances(mTiles, mapPosition);
Log.d(TAG, "remove tiles: " + removes);
// boolean printAll = false;
for (int i = 1; i <= removes; i++) {
GLMapTile t = mTiles.get(0);
int pos = 0;
for (int j = 1; j < size; j++) {
GLMapTile t2 = mTiles.get(j);
if (t2.distance > t.distance) {
t = t2;
pos = j;
}
}
synchronized (t) { synchronized (t) {
if (t.isActive) { if (t.isActive) {
// dont remove tile used by renderthread or mapgenerator // dont remove tile used by renderthread or mapgenerator
Log.d(TAG, "X not removing active " + t + " " + t.distance); Log.d(TAG, "X not removing active " + t + " " + t.distance);
mTiles.add(t);
if (printAll) { // if (printAll) {
printAll = false; // printAll = false;
for (GLMapTile tt : mTiles) // for (GLMapTile tt : mTiles)
Log.d(TAG, ">>>" + tt + " " + tt.distance); // Log.d(TAG, ">>>" + tt + " " + tt.distance);
} // }
} else if ((t.isReady || t.newData) && tileInUse(t)) { } else if ((t.isReady || t.newData) && tileInUse(t)) {
// check if this tile could be used as proxy // check if this tile could be used as proxy
// for not yet drawn active tile // for not yet drawn active tile
Log.d(TAG, "X not removing proxy: " + t + " " + t.distance); Log.d(TAG, "X not removing proxy: " + t + " " + t.distance);
mTiles.add(t);
} else { } else {
if (t.isLoading) { if (t.isLoading) {
Log.d(TAG, ">>> cancel loading " + t + " " + t.distance); Log.d(TAG, ">>> cancel loading " + t + " " + t.distance);
t.isCanceled = true; t.isCanceled = true;
} }
mTiles.remove(pos);
clearTile(t);
size--;
}
}
}
}
private static void limitLoadQueue(int remove) {
int size = remove;
synchronized (mTilesLoaded) {
// remove uploaded tiles
for (int i = 0; i < size;) {
GLMapTile t = mTilesLoaded.get(i);
// rel == null means tile is already removed by limitCache
if (!t.newData || t.rel == null) {
mTilesLoaded.remove(i);
size--;
continue;
}
i++;
}
// clear loaded but not used tiles
if (size < MAX_TILES_IN_QUEUE)
return;
while (size-- > MAX_TILES_IN_QUEUE - 20) {
GLMapTile t = mTilesLoaded.get(size);
synchronized (t) {
if (t.rel == null) {
mTilesLoaded.remove(size);
continue;
}
if (tileInUse(t)) {
// Log.d(TAG, "keep unused tile data: " + t + " " + t.isActive);
continue;
}
mTilesLoaded.remove(size);
mTiles.remove(t);
// Log.d(TAG, "remove unused tile data: " + t);
clearTile(t); clearTile(t);
} }
} }
@ -409,51 +491,6 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
mMapView.addJobs(mJobList); mMapView.addJobs(mJobList);
} }
int removes = mTiles.size() - CACHE_TILES;
if (removes > 10) {
// int size = mTiles.size();
// for (int i = 0; i < size;) {
// GLMapTile cur = mTiles.get(i);
// if (!cur.isActive && !cur.isLoading && !cur.newData && !cur.isReady) {
// // Log.d(TAG, "remove empty tile" + cur);
// mTiles.remove(i);
// TreeTile.remove(cur);
// removes--;
// size--;
// continue;
// }
// i++;
// }
int size = mTiles.size();
updateTileDistances(mTiles, mapPosition);
// Log.d(TAG, "remove tiles: " + removes);
// find 'removes' tiles with longest distance and
// move them at the end
for (int i = 1; i <= removes; i++) {
int pos = 0;
GLMapTile t = mTiles.get(0);
for (int j = 1; j <= size - i; j++) {
GLMapTile t2 = mTiles.get(j);
if (t2.distance > t.distance) {
t = t2;
pos = j;
}
}
// Log.d(TAG, "mark for removal" + cur);
mTiles.add(size - i, mTiles.remove(pos));
}
// Collections.sort(mTiles);
limitCache(removes);
}
return true; return true;
} }
@ -560,47 +597,16 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
if (changedPos) if (changedPos)
updateVisibleList(mapPosition, zdir); updateVisibleList(mapPosition, zdir);
synchronized (mTilesLoaded) { if (changedPos || changedZoom) {
int size = mTilesLoaded.size(); int remove = mTiles.size() - CACHE_TILES;
if (size < MAX_TILES_IN_QUEUE) if (remove > 10)
return; limitCache(mapPosition, remove);
// remove uploaded tiles
for (int i = 0; i < size;) {
GLMapTile t = mTilesLoaded.get(i);
// rel == null means tile is already removed by limitCache
if (!t.newData || t.rel == null) {
mTilesLoaded.remove(i);
size--;
continue;
}
i++;
}
// clear loaded but not used tiles
if (size > MAX_TILES_IN_QUEUE) {
while (size-- > MAX_TILES_IN_QUEUE - 20) {
GLMapTile t = mTilesLoaded.get(size);
synchronized (t) {
if (t.rel == null) {
mTilesLoaded.remove(size);
continue;
}
if (tileInUse(t)) {
// Log.d(TAG, "keep unused tile data: " + t + " " + t.isActive);
continue;
}
mTilesLoaded.remove(size);
mTiles.remove(t);
// Log.d(TAG, "remove unused tile data: " + t);
clearTile(t);
}
}
}
} }
int size = mTilesLoaded.size();
if (size > MAX_TILES_IN_QUEUE)
limitLoadQueue(size);
} }
/** /**
@ -646,9 +652,7 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
} }
// depthRange: -1 to 1, bits: 2^24 => 2/2^24 one step // depthRange: -1 to 1, bits: 2^24 => 2/2^24 one step
// maybe one could avoid clearing the depth buffer for a few // ... asus has just 16 bit?!
// iterations when modifying glDepthRange before clipper
// gl_less test so that it does not fail
// private static final float depthStep = 0.00000011920928955078125f; // private static final float depthStep = 0.00000011920928955078125f;
private static void setMatrix(GLMapTile tile, float div, int offset) { private static void setMatrix(GLMapTile tile, float div, int offset) {
@ -811,7 +815,7 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
} }
private static void checkBufferUsage() { private static void checkBufferUsage() {
// not really tested, try to clear some vbo when exceding limit // try to clear some unused vbo when exceding limit
if (mBufferMemoryUsage > LIMIT_BUFFERS) { if (mBufferMemoryUsage > LIMIT_BUFFERS) {
Log.d(TAG, "buffer object usage: " + mBufferMemoryUsage / MB + "MB"); Log.d(TAG, "buffer object usage: " + mBufferMemoryUsage / MB + "MB");
@ -825,19 +829,19 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
continue; continue;
mBufferMemoryUsage -= vbo.size; mBufferMemoryUsage -= vbo.size;
GlUtils.checkGlError("before");
// this should free allocated memory but it does not. at least on HTC. // this should free allocated memory but it does not.
// on HTC it causes oom exception?!
// glBindBuffer(GL_ARRAY_BUFFER, vbo.id); // glBindBuffer(GL_ARRAY_BUFFER, vbo.id);
// glBufferData(GL_ARRAY_BUFFER, 0, null, GLES20.GL_STATIC_DRAW); // glBufferData(GL_ARRAY_BUFFER, 0, null, GLES20.GL_STATIC_DRAW);
// recreate vbo // recreate vbo instead
buf[0] = vbo.id; buf[0] = vbo.id;
GLES20.glDeleteBuffers(1, buf, 0); GLES20.glDeleteBuffers(1, buf, 0);
GLES20.glGenBuffers(1, buf, 0); GLES20.glGenBuffers(1, buf, 0);
vbo.id = buf[0]; vbo.id = buf[0];
GlUtils.checkGlError("after");
vbo.size = 0; vbo.size = 0;
} }
} }
@ -861,8 +865,7 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
start = SystemClock.uptimeMillis(); start = SystemClock.uptimeMillis();
if (mUpdateColor && mClearColor != null) { if (mUpdateColor && mClearColor != null) {
glClearColor(mClearColor[0], mClearColor[1], glClearColor(mClearColor[0], mClearColor[1], mClearColor[2], mClearColor[3]);
mClearColor[2], mClearColor[3]);
mUpdateColor = false; mUpdateColor = false;
} }
@ -937,7 +940,6 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
TextRenderer.compileTextures(); TextRenderer.compileTextures();
GLES20.glEnable(GLES20.GL_DEPTH_TEST); GLES20.glEnable(GLES20.GL_DEPTH_TEST);
// glEnable(GLES20.GL_POLYGON_OFFSET_FILL);
for (int i = 0; i < tileCnt; i++) { for (int i = 0; i < tileCnt; i++) {
if (tiles[i].isVisible && tiles[i].isReady) { if (tiles[i].isVisible && tiles[i].isReady) {
@ -954,11 +956,10 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
} }
// GlUtils.checkGlError("end draw"); // GlUtils.checkGlError("end draw");
// glDisable(GLES20.GL_POLYGON_OFFSET_FILL);
glDisable(GLES20.GL_DEPTH_TEST); glDisable(GLES20.GL_DEPTH_TEST);
//
mDrawCount = 0; mDrawCount = 0;
mDrawSerial++;
glEnable(GL_BLEND); glEnable(GL_BLEND);
int z = mDrawPosition.zoomLevel; int z = mDrawPosition.zoomLevel;
@ -987,16 +988,17 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer {
GLES20.glFinish(); GLES20.glFinish();
Log.d(TAG, "draw took " + (SystemClock.uptimeMillis() - start)); Log.d(TAG, "draw took " + (SystemClock.uptimeMillis() - start));
} }
mRedrawCnt++;
} }
// used to not draw a tile twice per frame...
private static byte mDrawSerial = 0;
private static void drawTile(GLMapTile tile, float div) { private static void drawTile(GLMapTile tile, float div) {
// draw parents only once // draw parents only once
if (tile.lastDraw == mRedrawCnt) if (tile.lastDraw == mDrawSerial)
return; return;
tile.lastDraw = mRedrawCnt; tile.lastDraw = mDrawSerial;
int z = mDrawPosition.zoomLevel; int z = mDrawPosition.zoomLevel;
float s = mDrawPosition.scale; float s = mDrawPosition.scale;