refactor: TextureItem:

- use different pools for TextLayer and bitmap tiles
  to always get the correct (matching) texture from
  pool.
- the common TextureItem pool is only used to provide
  the same api, it does not keep images or textures
This commit is contained in:
Hannes Janetzek 2014-01-26 00:01:06 +01:00
parent fa2d3dd7a4
commit eff5935068
7 changed files with 381 additions and 348 deletions

View File

@ -20,9 +20,14 @@ import org.oscim.backend.CanvasAdapter;
import org.oscim.backend.canvas.Canvas; import org.oscim.backend.canvas.Canvas;
import org.oscim.renderer.atlas.TextureAtlas.Rect; import org.oscim.renderer.atlas.TextureAtlas.Rect;
import org.oscim.renderer.elements.TextureItem; import org.oscim.renderer.elements.TextureItem;
import org.oscim.renderer.elements.TextureItem.TexturePool;
import org.oscim.utils.pool.Inlist; import org.oscim.utils.pool.Inlist;
/**
* UNUSED
* */
public abstract class SpriteManager<T> { public abstract class SpriteManager<T> {
TexturePool pool;
public class Sprite extends Inlist<Sprite> { public class Sprite extends Inlist<Sprite> {
@ -46,14 +51,11 @@ public abstract class SpriteManager<T> {
protected TextureItem mTexture; protected TextureItem mTexture;
public SpriteManager() { public SpriteManager() {
mTexture = TextureItem.get(); mTexture = pool.get();
//mTexture.ownBitmap = true; //mTexture.ownBitmap = true;
mAtlas = new TextureAtlas( mAtlas = new TextureAtlas(256, 256, 32);
TextureItem.TEXTURE_WIDTH,
TextureItem.TEXTURE_HEIGHT,
32);
mCanvas.setBitmap(mTexture.bitmap); mCanvas.setBitmap(mTexture.bitmap);
} }
@ -68,12 +70,12 @@ public abstract class SpriteManager<T> {
} }
public void clear() { public void clear() {
mTexture = TextureItem.pool.releaseAll(mTexture); for (TextureItem t = mTexture; t != null; t = t.dispose());
mAtlas.clear(); mAtlas.clear();
items = null; items = null;
//mTexture.bitmap.eraseColor(Color.TRANSPARENT); //mTexture.bitmap.eraseColor(Color.TRANSPARENT);
mTexture = TextureItem.get(); mTexture = pool.get();
mCanvas.setBitmap(mTexture.bitmap); mCanvas.setBitmap(mTexture.bitmap);
} }

View File

@ -29,15 +29,17 @@ import org.oscim.renderer.MapRenderer.Matrices;
* Renderer for a single bitmap, width and height must be power of 2. * Renderer for a single bitmap, width and height must be power of 2.
*/ */
public class BitmapLayer extends TextureLayer { public class BitmapLayer extends TextureLayer {
// TODO share layers.vbo() between BitmapTileLayers
// static final Logger log = LoggerFactory.getLogger(BitmapLayer.class); // static final Logger log = LoggerFactory.getLogger(BitmapLayer.class);
private Bitmap mBitmap; private Bitmap mBitmap;
private final boolean mReuseBitmap; private final boolean mReuseBitmap;
private final short[] mVertices; private final short[] mVertices;
private int mWidth, mHeight;
/** /**
* @param reuseBitmap false if the Bitmap should be recycled after * @param reuseBitmap false if the Bitmap should be disposed
* it is compiled to texture. * after loading to texture.
*/ */
public BitmapLayer(boolean reuseBitmap) { public BitmapLayer(boolean reuseBitmap) {
super(RenderElement.BITMAP); super(RenderElement.BITMAP);
@ -49,26 +51,19 @@ public class BitmapLayer extends TextureLayer {
verticesCnt = 4; verticesCnt = 4;
} }
/**
* w/h sets also target dimension to render the bitmap.
*/
public void setBitmap(Bitmap bitmap, int w, int h) { public void setBitmap(Bitmap bitmap, int w, int h) {
mWidth = w; mWidth = w;
mHeight = h; mHeight = h;
mBitmap = bitmap; mBitmap = bitmap;
if (this.textures == null) if (textures == null)
this.textures = new TextureItem(mBitmap); textures = new TextureItem(mBitmap);
TextureItem ti = this.textures; TextureItem t = textures;
ti.vertices = TextureLayer.Renderer.INDICES_PER_SPRITE; t.vertices = TextureLayer.INDICES_PER_SPRITE;
}
private int mWidth, mHeight;
/**
* Set target dimension to renderthe bitmap
*/
public void setSize(int w, int h) {
mWidth = w;
mHeight = h;
} }
private void setVertices(ShortBuffer sbuf) { private void setVertices(ShortBuffer sbuf) {
@ -76,37 +71,40 @@ public class BitmapLayer extends TextureLayer {
short w = (short) (mWidth * MapRenderer.COORD_SCALE); short w = (short) (mWidth * MapRenderer.COORD_SCALE);
short h = (short) (mHeight * MapRenderer.COORD_SCALE); short h = (short) (mHeight * MapRenderer.COORD_SCALE);
short t = 1; short texMin = 0;
short texMax = 1;
// putSprite(buf, pos, tx, ty, x1, y1, x2, y2, u1, v1, u2, v2);
int pos = 0; int pos = 0;
// top-left // top-left
buf[pos++] = 0; buf[pos++] = 0;
buf[pos++] = 0; buf[pos++] = 0;
buf[pos++] = -1; buf[pos++] = -1;
buf[pos++] = -1; buf[pos++] = -1;
buf[pos++] = 0; buf[pos++] = texMin;
buf[pos++] = 0; buf[pos++] = texMin;
// bot-left // bot-left
buf[pos++] = 0; buf[pos++] = 0;
buf[pos++] = h; buf[pos++] = h;
buf[pos++] = -1; buf[pos++] = -1;
buf[pos++] = -1; buf[pos++] = -1;
buf[pos++] = 0; buf[pos++] = texMin;
buf[pos++] = t; buf[pos++] = texMax;
// top-right // top-right
buf[pos++] = w; buf[pos++] = w;
buf[pos++] = 0; buf[pos++] = 0;
buf[pos++] = -1; buf[pos++] = -1;
buf[pos++] = -1; buf[pos++] = -1;
buf[pos++] = t; buf[pos++] = texMax;
buf[pos++] = 0; buf[pos++] = texMin;
// bot-right // bot-right
buf[pos++] = w; buf[pos++] = w;
buf[pos++] = h; buf[pos++] = h;
buf[pos++] = -1; buf[pos++] = -1;
buf[pos++] = -1; buf[pos++] = -1;
buf[pos++] = t; buf[pos++] = texMax;
buf[pos++] = t; buf[pos++] = texMax;
this.offset = sbuf.position() * 2; // bytes this.offset = sbuf.position() * 2; // bytes
sbuf.put(buf); sbuf.put(buf);
@ -137,26 +135,25 @@ public class BitmapLayer extends TextureLayer {
@Override @Override
protected void clear() { protected void clear() {
if (mBitmap != null) { // release textures and vertexItems
if (!mReuseBitmap) super.clear();
mBitmap.recycle();
mBitmap = null; if (mBitmap == null)
textures.bitmap = null; return;
}
TextureItem.releaseTexture(textures); if (!mReuseBitmap)
textures = null; mBitmap.recycle();
vertexItems = VertexItem.pool.releaseAll(vertexItems); mBitmap = null;
//textures.bitmap = null;
//textures.dispose();
//TextureItem.pool.releaseTexture(textures);
//textures = null;
} }
public static final class Renderer { public static final class Renderer {
//static final Logger log = LoggerFactory.getLogger(BitmapRenderer.class);
public final static boolean debug = true;
private static int mTextureProgram; private static int mTextureProgram;
private static int hTextureMVMatrix; private static int hTextureMVMatrix;
private static int hTextureProjMatrix; private static int hTextureProjMatrix;
@ -209,16 +206,16 @@ public class BitmapLayer extends TextureLayer {
MapRenderer.bindQuadIndicesVBO(true); MapRenderer.bindQuadIndicesVBO(true);
for (TextureItem ti = tl.textures; ti != null; ti = ti.next) { for (TextureItem t = tl.textures; t != null; t = t.next) {
ti.bind(); t.bind();
int maxVertices = MapRenderer.maxQuads * INDICES_PER_SPRITE; int maxVertices = MapRenderer.maxQuads * INDICES_PER_SPRITE;
// draw up to maxVertices in each iteration // draw up to maxVertices in each iteration
for (int i = 0; i < ti.vertices; i += maxVertices) { for (int i = 0; i < t.vertices; i += maxVertices) {
// to.offset * (24(shorts) * 2(short-bytes) / 6(indices) == 8) // to.offset * (24(shorts) * 2(short-bytes) / 6(indices) == 8)
int off = (ti.offset + i) * 8 + tl.offset; int off = (t.offset + i) * 8 + tl.offset;
GL.glVertexAttribPointer(hTextureVertex, 4, GL.glVertexAttribPointer(hTextureVertex, 4,
GL20.GL_SHORT, false, 12, off); GL20.GL_SHORT, false, 12, off);
@ -226,7 +223,7 @@ public class BitmapLayer extends TextureLayer {
GL.glVertexAttribPointer(hTextureTexCoord, 2, GL.glVertexAttribPointer(hTextureTexCoord, 2,
GL20.GL_SHORT, false, 12, off + 8); GL20.GL_SHORT, false, 12, off + 8);
int numVertices = ti.vertices - i; int numVertices = t.vertices - i;
if (numVertices > maxVertices) if (numVertices > maxVertices)
numVertices = maxVertices; numVertices = maxVertices;
@ -245,9 +242,6 @@ public class BitmapLayer extends TextureLayer {
+ "attribute vec4 vertex;" + "attribute vec4 vertex;"
+ "attribute vec2 tex_coord;" + "attribute vec2 tex_coord;"
+ "uniform mat4 u_mv;" + "uniform mat4 u_mv;"
+ "uniform mat4 u_proj;"
+ "uniform float u_scale;"
+ "uniform float u_swidth;"
+ "varying vec2 tex_c;" + "varying vec2 tex_c;"
+ "void main() {" + "void main() {"
+ " gl_Position = u_mv * vec4(vertex.xy, 0.0, 1.0);" + " gl_Position = u_mv * vec4(vertex.xy, 0.0, 1.0);"

View File

@ -38,7 +38,7 @@ public class ElementLayers {
BitmapLayer.Renderer.init(); BitmapLayer.Renderer.init();
MeshLayer.Renderer.init(); MeshLayer.Renderer.init();
TextureItem.init(gl, 0); TextureItem.init(gl);
} }
/** mixed Polygon- and LineLayer */ /** mixed Polygon- and LineLayer */

View File

@ -73,7 +73,7 @@ public final class SymbolLayer extends TextureLayer {
prevTextures = textures; prevTextures = textures;
textures = null; textures = null;
TextureItem to = null; TextureItem t = null;
for (SymbolItem it = symbols; it != null;) { for (SymbolItem it = symbols; it != null;) {
int width = 0, height = 0; int width = 0, height = 0;
@ -82,14 +82,13 @@ public final class SymbolLayer extends TextureLayer {
if (it.texRegion != null) { if (it.texRegion != null) {
// FIXME this work only with one TextureAtlas per // FIXME this work only with one TextureAtlas per SymbolLayer.
// SymbolLayer.
if (textures == null) { if (textures == null) {
to = it.texRegion.atlas.loadTexture(); t = it.texRegion.atlas.loadTexture();
// clone TextureItem to use same texID with // clone TextureItem to use same texID with
// multiple TextureItem // multiple TextureItem
to = TextureItem.clone(to); t = TextureItem.clone(t);
textures = Inlist.appendItem(textures, to); textures = Inlist.appendItem(textures, t);
} }
TextureAtlas.Rect r = it.texRegion.rect; TextureAtlas.Rect r = it.texRegion.rect;
@ -99,20 +98,20 @@ public final class SymbolLayer extends TextureLayer {
height = r.h; height = r.h;
} else if (it.bitmap != null) { } else if (it.bitmap != null) {
width = it.bitmap.getWidth(); t = getTexture(it.bitmap);
height = it.bitmap.getHeight();
to = getTexture(it.bitmap);
if (to == null) {
to = new TextureItem(it.bitmap);
textures = Inlist.appendItem(textures, to);
to.upload(); if (t == null) {
t = new TextureItem(it.bitmap);
textures = Inlist.appendItem(textures, t);
t.upload();
t.offset = numIndices;
t.vertices = 0;
} }
to.offset = numIndices; width = t.width;
to.vertices = 0; height = t.height;
}
if (to == null) { } else { //if (to == null) {
log.debug("Bad SymbolItem"); log.debug("Bad SymbolItem");
continue; continue;
} }
@ -126,9 +125,15 @@ public final class SymbolLayer extends TextureLayer {
short x1 = 0, y1 = 0, x2 = 0, y2 = 0; short x1 = 0, y1 = 0, x2 = 0, y2 = 0;
// add symbol items referencing the same bitmap / // add symbol items referencing the same bitmap /
for (SymbolItem it2 = it;; it2 = it2.next) { for (SymbolItem prev = it; it != null; it = it.next) {
if (it == it2 || it.offset != prevOffset) { if (prev.bitmap != null && prev.bitmap != it.bitmap)
break;
if (prev.texRegion != null && prev.texRegion != it.texRegion)
break;
if (it == prev || it.offset != prevOffset) {
prevOffset = it.offset; prevOffset = it.offset;
if (it.offset == null) { if (it.offset == null) {
float hw = width / 2f; float hw = width / 2f;
@ -147,18 +152,12 @@ public final class SymbolLayer extends TextureLayer {
y2 = (short) (SCALE * (-hh)); y2 = (short) (SCALE * (-hh));
} }
} }
if (it2 == null
|| (it.bitmap != null && it2.bitmap != it.bitmap)
|| (it.texRegion != null && it2.texRegion != it.texRegion)) {
it = it2;
break;
}
// add vertices // add vertices
short tx = (short) ((int) (SCALE * it2.x) & LBIT_MASK short tx = (short) ((int) (SCALE * it.x) & LBIT_MASK
| (it2.billboard ? 1 : 0)); | (it.billboard ? 1 : 0));
short ty = (short) (SCALE * it2.y); short ty = (short) (SCALE * it.y);
if (pos == VertexItem.SIZE) { if (pos == VertexItem.SIZE) {
sbuf.put(buf, 0, VertexItem.SIZE); sbuf.put(buf, 0, VertexItem.SIZE);
@ -168,14 +167,12 @@ public final class SymbolLayer extends TextureLayer {
TextureLayer.putSprite(buf, pos, tx, ty, TextureLayer.putSprite(buf, pos, tx, ty,
x1, y1, x2, y2, u1, v1, u2, v2); x1, y1, x2, y2, u1, v1, u2, v2);
// TextureRenderer.VERTICES_PER_SPRITE pos += TextLayer.VERTICES_PER_SPRITE * 6;
// * TextureRenderer.SHORTS_PER_VERTICE;
pos += 24;
// six elements used to draw the four vertices // six elements used to draw the four vertices
to.vertices += TextureLayer.Renderer.INDICES_PER_SPRITE; t.vertices += TextureLayer.INDICES_PER_SPRITE;
} }
numIndices += to.vertices; numIndices += t.vertices;
} }
if (pos > 0) if (pos > 0)
@ -183,35 +180,40 @@ public final class SymbolLayer extends TextureLayer {
si = VertexItem.pool.release(si); si = VertexItem.pool.release(si);
prevTextures = TextureItem.pool.releaseAll(prevTextures); for (t = prevTextures; t != null; t = t.dispose());
prevTextures = null;
} }
private TextureItem getTexture(Bitmap bitmap) { private TextureItem getTexture(Bitmap bitmap) {
TextureItem to; TextureItem t;
for (to = prevTextures; to != null; to = to.next) { for (t = prevTextures; t != null; t = t.next) {
if (to.bitmap == bitmap) { if (t.bitmap == bitmap) {
prevTextures = Inlist.remove(prevTextures, to); prevTextures = Inlist.remove(prevTextures, t);
textures = Inlist.appendItem(textures, to); textures = Inlist.appendItem(textures, t);
break;
t.offset = 0;
t.vertices = 0;
return t;
} }
} }
return null;
return to;
} }
public void clearItems() { public void clearItems() {
symbols = SymbolItem.pool.releaseAll(symbols); symbols = SymbolItem.pool.releaseAll(symbols);
verticesCnt = 0; //verticesCnt = 0;
} }
@Override @Override
public void clear() { public void clear() {
textures = TextureItem.pool.releaseAll(textures); // release textures
symbols = SymbolItem.pool.releaseAll(symbols); super.clear();
vertexItems = VertexItem.pool.releaseAll(vertexItems); clearItems();
verticesCnt = 0; //symbols = SymbolItem.pool.releaseAll(symbols);
//vertexItems = VertexItem.pool.releaseAll(vertexItems);
//verticesCnt = 0;
} }
@Override @Override

View File

@ -17,14 +17,11 @@
package org.oscim.renderer.elements; package org.oscim.renderer.elements;
import static org.oscim.renderer.MapRenderer.COORD_SCALE; import static org.oscim.renderer.MapRenderer.COORD_SCALE;
import static org.oscim.renderer.elements.TextureItem.TEXTURE_HEIGHT;
import static org.oscim.renderer.elements.TextureItem.TEXTURE_WIDTH;
import org.oscim.backend.CanvasAdapter; import org.oscim.backend.CanvasAdapter;
import org.oscim.backend.canvas.Canvas; import org.oscim.backend.canvas.Canvas;
public final class TextLayer extends TextureLayer { public final class TextLayer extends TextureLayer {
//static final Logger log = LoggerFactory.getLogger(TextureLayer.class); //static final Logger log = LoggerFactory.getLogger(TextureLayer.class);
private final static int LBIT_MASK = 0xfffffffe; private final static int LBIT_MASK = 0xfffffffe;
@ -94,15 +91,18 @@ public final class TextLayer extends TextureLayer {
float y = 0; float y = 0;
float yy; float yy;
TextureItem to = TextureItem.get(); TextureItem t = pool.get();
textures = to; textures = t;
mCanvas.setBitmap(to.bitmap); mCanvas.setBitmap(t.bitmap);
for (TextItem it = labels; it != null;) { for (TextItem it = labels; it != null;) {
float width = it.width + 2 * mFontPadX; float width = it.width + 2 * mFontPadX;
float height = (int) (it.text.fontHeight) + 0.5f; float height = (int) (it.text.fontHeight) + 0.5f;
if (height > TEXTURE_HEIGHT)
height = TEXTURE_HEIGHT;
if (height > advanceY) if (height > advanceY)
advanceY = (int) height; advanceY = (int) height;
@ -112,14 +112,14 @@ public final class TextLayer extends TextureLayer {
advanceY = (int) (height + 0.5f); advanceY = (int) (height + 0.5f);
if (y + height > TEXTURE_HEIGHT) { if (y + height > TEXTURE_HEIGHT) {
to.offset = offsetIndices; t.offset = offsetIndices;
to.vertices = (short) (numIndices - offsetIndices); t.vertices = (short) (numIndices - offsetIndices);
offsetIndices = numIndices; offsetIndices = numIndices;
to.next = TextureItem.get(); t.next = pool.get();
to = to.next; t = t.next;
mCanvas.setBitmap(to.bitmap); mCanvas.setBitmap(t.bitmap);
x = 0; x = 0;
y = 0; y = 0;
@ -246,8 +246,8 @@ public final class TextLayer extends TextureLayer {
buf[pos++] = v1; buf[pos++] = v1;
// six indices to draw the four vertices // six indices to draw the four vertices
numIndices += TextureLayer.Renderer.INDICES_PER_SPRITE;
verticesCnt += 4; verticesCnt += 4;
numIndices += TextureLayer.INDICES_PER_SPRITE;
if (it.next == null || (it.next.text != it.text) if (it.next == null || (it.next.text != it.text)
|| (it.next.string != it.string)) { || (it.next.string != it.string)) {
@ -262,18 +262,21 @@ public final class TextLayer extends TextureLayer {
vi.used = pos; vi.used = pos;
to.offset = offsetIndices; t.offset = offsetIndices;
to.vertices = (short) (numIndices - offsetIndices); t.vertices = (short) (numIndices - offsetIndices);
return true; return true;
} }
@Override @Override
public void clear() { public void clear() {
textures = TextureItem.pool.releaseAll(textures); // release textures
labels = TextItem.pool.releaseAll(labels); super.clear();
vertexItems = VertexItem.pool.releaseAll(vertexItems);
verticesCnt = 0; clearLabels();
//labels = TextItem.pool.releaseAll(labels);
//vertexItems = VertexItem.pool.releaseAll(vertexItems);
//verticesCnt = 0;
} }
public void clearLabels() { public void clearLabels() {

View File

@ -18,6 +18,8 @@ package org.oscim.renderer.elements;
import java.util.ArrayList; import java.util.ArrayList;
import javax.annotation.CheckReturnValue;
import org.oscim.backend.CanvasAdapter; import org.oscim.backend.CanvasAdapter;
import org.oscim.backend.GL20; import org.oscim.backend.GL20;
import org.oscim.backend.canvas.Bitmap; import org.oscim.backend.canvas.Bitmap;
@ -29,19 +31,16 @@ import org.oscim.utils.pool.SyncPool;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
// TODO use separate pools for different bitmap types and dimensions
public class TextureItem extends Inlist<TextureItem> { public class TextureItem extends Inlist<TextureItem> {
static final Logger log = LoggerFactory.getLogger(TextureItem.class); static final Logger log = LoggerFactory.getLogger(TextureItem.class);
private static GL20 GL;
/** texture ID */ /** texture ID */
public int id; public int id;
public int width; /** current settings */
public int height; public final int width;
public boolean repeat; public final int height;
public final boolean repeat;
/** vertex offset from which this texture is referenced */ /** vertex offset from which this texture is referenced */
public short offset; public short offset;
@ -50,272 +49,290 @@ public class TextureItem extends Inlist<TextureItem> {
/** temporary Bitmap */ /** temporary Bitmap */
public Bitmap bitmap; public Bitmap bitmap;
/** external bitmap (not from pool) */
private boolean ownBitmap;
/** do not release the texture when TextureItem is released. */ /** do not release the texture when TextureItem is released. */
private boolean isClone; private boolean ref;
/** texture data is ready */ /** texture data is ready */
private boolean isReady; private boolean ready;
private TextureItem(int id) { final TexturePool pool;
private TextureItem(TexturePool pool, int id) {
this.id = id; this.id = id;
} this.width = pool.mWidth;
this.height = pool.mHeight;
public static TextureItem clone(TextureItem ti) { this.pool = pool;
TextureItem clone = new TextureItem(ti.id); this.repeat = false;
clone.id = ti.id;
clone.width = ti.width;
clone.height = ti.height;
clone.isClone = true;
// original texture needs to be loaded
clone.isReady = true;
return clone;
} }
public TextureItem(Bitmap bitmap) { public TextureItem(Bitmap bitmap) {
this.bitmap = bitmap; this.bitmap = bitmap;
this.id = -1; this.id = -1;
this.ownBitmap = true;
this.width = bitmap.getWidth(); this.width = bitmap.getWidth();
this.height = bitmap.getHeight(); this.height = bitmap.getHeight();
this.pool = NOPOOL;
this.repeat = false;
} }
public TextureItem(Bitmap bitmap, boolean repeat) { public TextureItem(Bitmap bitmap, boolean repeat) {
this.bitmap = bitmap; this.bitmap = bitmap;
this.id = -1; this.id = -1;
this.ownBitmap = true;
this.width = bitmap.getWidth(); this.width = bitmap.getWidth();
this.height = bitmap.getHeight(); this.height = bitmap.getHeight();
this.repeat = repeat; this.repeat = repeat;
this.pool = NOPOOL;
} }
private TextureItem(TexturePool pool, int id, int width, int height) {
this.id = id;
this.width = width;
this.height = height;
this.pool = pool;
this.repeat = false;
}
public static TextureItem clone(TextureItem ti) {
// original texture needs to be loaded
if (!ti.ready)
throw new IllegalStateException();
TextureItem clone = new TextureItem(ti.pool, ti.id, ti.width, ti.height);
clone.id = ti.id;
clone.ref = true;
clone.ready = true;
return clone;
}
/**
* Upload Image to Texture
* [on GL-Thread]
*/
public void upload() { public void upload() {
if (!isReady) { if (!ready) {
TextureItem.uploadTexture(this); pool.uploadTexture(this);
isReady = true; ready = true;
} }
} }
/**
* Bind Texture for rendering
* [on GL-Thread]
*/
public void bind() { public void bind() {
if (!isReady) { if (!ready) {
TextureItem.uploadTexture(this); pool.uploadTexture(this);
isReady = true; ready = true;
} else { } else {
GLState.bindTex2D(id); GLState.bindTex2D(id);
} }
} }
/** /**
* Retrieve a TextureItem from pool with default Bitmap with dimension * Dispose TextureItem
* TextureRenderer.TEXTURE_WIDTH/HEIGHT. * [Threadsafe]
*
* @return this.next
*/ */
public synchronized static TextureItem get() { @CheckReturnValue
TextureItem ti = pool.get(); public TextureItem dispose() {
ti.bitmap = getBitmap(); TextureItem n = this.next;
ti.bitmap.eraseColor(Color.TRANSPARENT); this.next = null;
pool.release(this);
return ti; return n;
} }
static class TextureItemPool extends SyncPool<TextureItem> { public static class TexturePool extends SyncPool<TextureItem> {
public TextureItemPool() { private final ArrayList<Integer> mTexDisposed = new ArrayList<Integer>();
super(10); private final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(10);
private final int mHeight;
private final int mWidth;
//private final int mBitmapFormat;
//private final int mBitmapType;
protected int mTexCnt = 0;
public TexturePool(int maxFill, int width, int height) {
super(maxFill);
mWidth = width;
mHeight = height;
} }
@Override @Override
public void init(int num) { public TextureItem releaseAll(TextureItem t) {
if (pool != null) { throw new RuntimeException("use TextureItem.dispose()");
log.debug("still textures in pool! " + fill); }
pool = null;
}
if (num > 0) { /**
int[] textureIds = GLUtils.glGenTextures(num); * Retrieve a TextureItem from pool.
for (int i = 0; i < num; i++) { */
TextureItem to = new TextureItem(textureIds[i]); public synchronized TextureItem get() {
initTexture(to); TextureItem t = super.get();
pool = Inlist.push(pool, to);
synchronized (mBitmaps) {
int size = mBitmaps.size();
if (size == 0)
t.bitmap = CanvasAdapter.g.getBitmap(mWidth, mHeight, 0);
else {
t.bitmap = mBitmaps.remove(size - 1);
t.bitmap.eraseColor(Color.TRANSPARENT);
} }
} }
fill = num; return t;
}
public TextureItem get(int width, int height) {
return null;
} }
@Override @Override
protected TextureItem createItem() { protected TextureItem createItem() {
return new TextureItem(-1); return new TextureItem(this, -1);
} }
/** called when item is added back to pool */
@Override @Override
protected boolean clearItem(TextureItem t) { protected boolean clearItem(TextureItem t) {
if (t.ownBitmap) { //if (t.ownBitmap) {
t.bitmap = null; // t.bitmap = null;
t.ownBitmap = false; // t.ownBitmap = false;
releaseTexture(t); // releaseTexture(t);
// return false;
//}
if (t.ref)
return false; return false;
}
if (t.isClone) {
t.isClone = false;
t.id = -1;
t.width = -1;
t.height = -1;
return false;
}
t.isReady = false;
t.ready = false;
releaseBitmap(t); releaseBitmap(t);
return true; // only add back to pool when a texture
// is already assigned
return t.id >= 0;
} }
@Override @Override
protected void freeItem(TextureItem t) { protected void freeItem(TextureItem t) {
t.width = -1;
t.height = -1;
if (!t.isClone) if (!t.ref) {
releaseTexture(t); synchronized (mTexDisposed) {
if (t.id >= 0) {
mTexDisposed.add(Integer.valueOf(t.id));
t.id = -1;
}
}
}
}
protected void releaseBitmap(TextureItem t) {
if (t.bitmap == null)
return;
synchronized (mBitmaps) {
mBitmaps.add(t.bitmap);
t.bitmap = null;
}
}
private void uploadTexture(TextureItem t) {
if (t.bitmap == null)
throw new RuntimeException("Missing bitmap for texture");
synchronized (mTexDisposed) {
int size = mTexDisposed.size();
if (size > 0) {
int[] tmp = new int[size];
for (int i = 0; i < size; i++)
tmp[i] = mTexDisposed.get(i).intValue();
mTexDisposed.clear();
GLUtils.glDeleteTextures(size, tmp);
mTexCnt -= size;
}
}
if (t.id < 0) {
int[] textureIds = GLUtils.glGenTextures(1);
t.id = textureIds[0];
initTexture(t);
if (TextureLayer.Renderer.debug)
log.debug("fill:" + getFill()
+ " count:" + mTexCnt
+ " new texture " + t.id);
mTexCnt++;
t.bitmap.uploadToTexture(false);
} else {
GLState.bindTex2D(t.id);
// use faster subimage upload
t.bitmap.uploadToTexture(true);
}
//if (t.ownBitmap) {
// t.bitmap.uploadToTexture(false);
//} else
//if (t.width == mWidth && t.height == mHeight) {
// // use faster subimage upload
// t.bitmap.uploadToTexture(true);
//} else {
// t.bitmap.uploadToTexture(false);
//}
if (TextureLayer.Renderer.debug)
GLUtils.checkGlError(TextureItem.class.getName());
releaseBitmap(t);
}
protected void initTexture(TextureItem t) {
GLState.bindTex2D(t.id);
GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER,
GL20.GL_LINEAR);
GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER,
GL20.GL_LINEAR);
if (t.repeat) {
GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S,
GL20.GL_REPEAT);
GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T,
GL20.GL_REPEAT);
} else {
GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S,
GL20.GL_CLAMP_TO_EDGE);
GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T,
GL20.GL_CLAMP_TO_EDGE);
}
} }
}; };
public final static SyncPool<TextureItem> pool = new TextureItemPool(); // Pool for not-pooled textures. Disposed items will only be released
// on the GL-Thread and will not be put back in any pool.
final static TexturePool NOPOOL = new TexturePool(0, 0, 0) {
protected void releaseBitmap(TextureItem t) {
// TODO
};
};
private final static ArrayList<Integer> mTextures = new ArrayList<Integer>(); // public final static TexturePool nopool(){
private final static ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(10); // return NOPOOL;
// }
public final static int TEXTURE_WIDTH = 512; private static GL20 GL;
public final static int TEXTURE_HEIGHT = 256;
//private static int mBitmapFormat; static void init(GL20 gl) {
//private static int mBitmapType;
private static int mTexCnt = 0;
static void releaseTexture(TextureItem it) {
synchronized (mTextures) {
if (it.id >= 0) {
mTextures.add(Integer.valueOf(it.id));
it.id = -1;
}
}
}
/**
* This function may only be used in GLRenderer Thread.
*
* @param t
* the TextureObjet to compile and upload
*/
private static void uploadTexture(TextureItem t) {
GL.glBindTexture(GL20.GL_TEXTURE_2D, 0);
if (t.bitmap == null) {
throw new RuntimeException("Missing bitmap for texture");
}
// free unused textures -> TODO find a better place for this
synchronized (mTextures) {
int size = mTextures.size();
if (size > 0) {
int[] tmp = new int[size];
for (int i = 0; i < size; i++) {
tmp[i] = mTextures.get(i).intValue();
}
mTextures.clear();
GLUtils.glDeleteTextures(size, tmp);
mTexCnt -= size;
}
}
if (t.id < 0) {
mTexCnt++;
int[] textureIds = GLUtils.glGenTextures(1);
t.id = textureIds[0];
initTexture(t);
if (TextureLayer.Renderer.debug)
log.debug("fill:" + pool.getFill()
+ " count:" + mTexCnt
+ " new texture " + t.id);
}
GLState.bindTex2D(t.id);
if (t.ownBitmap) {
t.bitmap.uploadToTexture(false);
} else if (t.width == TEXTURE_WIDTH && t.height == TEXTURE_HEIGHT) {
t.bitmap.uploadToTexture(true);
} else {
t.bitmap.uploadToTexture(false);
t.width = TEXTURE_WIDTH;
t.height = TEXTURE_HEIGHT;
}
if (TextureLayer.Renderer.debug)
GLUtils.checkGlError(TextureItem.class.getName());
if (!t.ownBitmap)
TextureItem.releaseBitmap(t);
}
private static void initTexture(TextureItem t) {
GLState.bindTex2D(t.id);
GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER,
GL20.GL_LINEAR);
GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER,
GL20.GL_LINEAR);
if (t.repeat) {
GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S,
GL20.GL_REPEAT);
GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T,
GL20.GL_REPEAT);
} else {
GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S,
GL20.GL_CLAMP_TO_EDGE);
GL.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T,
GL20.GL_CLAMP_TO_EDGE);
}
}
static void init(GL20 gl, int num) {
GL = gl; GL = gl;
mTexCnt = num; // mTexCnt = num;
pool.init(num); // NOPOOL.init(num);
//
mBitmaps.clear(); // mBitmaps.clear();
mTextures.clear(); // mTexDisposed.clear();
}
static Bitmap getBitmap() {
synchronized (mBitmaps) {
int size = mBitmaps.size();
if (size == 0) {
return CanvasAdapter.g.getBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT, 0);
}
return mBitmaps.remove(size - 1);
}
}
static void releaseBitmap(TextureItem it) {
synchronized (mBitmaps) {
if (it.bitmap != null) {
mBitmaps.add(it.bitmap);
it.bitmap = null;
}
}
} }
} }

View File

@ -25,28 +25,38 @@ import org.oscim.renderer.GLState;
import org.oscim.renderer.GLUtils; import org.oscim.renderer.GLUtils;
import org.oscim.renderer.MapRenderer; import org.oscim.renderer.MapRenderer;
import org.oscim.renderer.MapRenderer.Matrices; import org.oscim.renderer.MapRenderer.Matrices;
import org.oscim.renderer.elements.TextureItem.TexturePool;
public abstract class TextureLayer extends RenderElement { public abstract class TextureLayer extends RenderElement {
public final static int INDICES_PER_SPRITE = 6;
final static int VERTICES_PER_SPRITE = 4;
final static int SHORTS_PER_VERTICE = 6;
final static int TEXTURE_HEIGHT = 128;
final static int TEXTURE_WIDTH = 512;
final static int POOL_FILL = 10;
/** pool shared by TextLayers */
final static TexturePool pool = new TexturePool(POOL_FILL,
TEXTURE_WIDTH,
TEXTURE_HEIGHT);
protected TextureLayer(byte type) { protected TextureLayer(byte type) {
super(type); super(type);
} }
// holds textures and offset in vbo /** holds textures and offset in vbo */
public TextureItem textures; public TextureItem textures;
// scale mode /** scale mode */
public boolean fixed; public boolean fixed;
/**
* @param sbuf
* buffer to add vertices
*/
@Override @Override
protected void compile(ShortBuffer sbuf) { protected void compile(ShortBuffer sbuf) {
for (TextureItem to = textures; to != null; to = to.next) for (TextureItem t = textures; t != null; t = t.next)
to.upload(); t.upload();
// add vertices to vbo // add vertices to vbo
ElementLayers.addPoolItems(this, sbuf); ElementLayers.addPoolItems(this, sbuf);
@ -54,6 +64,14 @@ public abstract class TextureLayer extends RenderElement {
abstract public boolean prepare(); abstract public boolean prepare();
protected void clear() {
while (textures != null)
textures = textures.dispose();
vertexItems = VertexItem.pool.releaseAll(vertexItems);
verticesCnt = 0;
}
static void putSprite(short buf[], int pos, static void putSprite(short buf[], int pos,
short tx, short ty, short tx, short ty,
short x1, short y1, short x1, short y1,
@ -105,10 +123,6 @@ public abstract class TextureLayer extends RenderElement {
private static int hTextureTexCoord; private static int hTextureTexCoord;
private static int hTextureSize; private static int hTextureSize;
public final static int INDICES_PER_SPRITE = 6;
final static int VERTICES_PER_SPRITE = 4;
final static int SHORTS_PER_VERTICE = 6;
static void init() { static void init() {
mTextureProgram = GLUtils.createProgram(textVertexShader, mTextureProgram = GLUtils.createProgram(textVertexShader,
@ -121,9 +135,12 @@ public abstract class TextureLayer extends RenderElement {
hTextureScreenScale = GL.glGetUniformLocation(mTextureProgram, "u_swidth"); hTextureScreenScale = GL.glGetUniformLocation(mTextureProgram, "u_swidth");
hTextureVertex = GL.glGetAttribLocation(mTextureProgram, "vertex"); hTextureVertex = GL.glGetAttribLocation(mTextureProgram, "vertex");
hTextureTexCoord = GL.glGetAttribLocation(mTextureProgram, "tex_coord"); hTextureTexCoord = GL.glGetAttribLocation(mTextureProgram, "tex_coord");
// FIXME pool should be disposed on exit...
pool.init(0);
} }
public static RenderElement draw(RenderElement renderElement, float scale, Matrices m) { public static RenderElement draw(RenderElement l, float scale, Matrices m) {
GLState.test(false, false); GLState.test(false, false);
GLState.blend(true); GLState.blend(true);
@ -132,7 +149,7 @@ public abstract class TextureLayer extends RenderElement {
GLState.enableVertexArrays(hTextureTexCoord, hTextureVertex); GLState.enableVertexArrays(hTextureTexCoord, hTextureVertex);
TextureLayer tl = (TextureLayer) renderElement; TextureLayer tl = (TextureLayer) l;
if (tl.fixed) if (tl.fixed)
GL.glUniform1f(hTextureScale, (float) Math.sqrt(scale)); GL.glUniform1f(hTextureScale, (float) Math.sqrt(scale));
@ -146,20 +163,18 @@ public abstract class TextureLayer extends RenderElement {
MapRenderer.bindQuadIndicesVBO(true); MapRenderer.bindQuadIndicesVBO(true);
for (TextureItem ti = tl.textures; ti != null; ti = ti.next) { for (TextureItem t = tl.textures; t != null; t = t.next) {
GL.glUniform2f(hTextureSize,
ti.bind(); 1f / (t.width * COORD_SCALE),
1f / (t.height * COORD_SCALE));
t.bind();
int maxVertices = MapRenderer.maxQuads * INDICES_PER_SPRITE; int maxVertices = MapRenderer.maxQuads * INDICES_PER_SPRITE;
GL.glUniform2f(hTextureSize,
1f / (ti.width * COORD_SCALE),
1f / (ti.height * COORD_SCALE));
// draw up to maxVertices in each iteration // draw up to maxVertices in each iteration
for (int i = 0; i < ti.vertices; i += maxVertices) { for (int i = 0; i < t.vertices; i += maxVertices) {
// to.offset * (24(shorts) * 2(short-bytes) / 6(indices) == 8) // to.offset * (24(shorts) * 2(short-bytes) / 6(indices) == 8)
int off = (ti.offset + i) * 8 + tl.offset; int off = (t.offset + i) * 8 + tl.offset;
GL.glVertexAttribPointer(hTextureVertex, 4, GL.glVertexAttribPointer(hTextureVertex, 4,
GL20.GL_SHORT, false, 12, off); GL20.GL_SHORT, false, 12, off);
@ -167,7 +182,7 @@ public abstract class TextureLayer extends RenderElement {
GL.glVertexAttribPointer(hTextureTexCoord, 2, GL.glVertexAttribPointer(hTextureTexCoord, 2,
GL20.GL_SHORT, false, 12, off + 8); GL20.GL_SHORT, false, 12, off + 8);
int numVertices = ti.vertices - i; int numVertices = t.vertices - i;
if (numVertices > maxVertices) if (numVertices > maxVertices)
numVertices = maxVertices; numVertices = maxVertices;
@ -178,7 +193,7 @@ public abstract class TextureLayer extends RenderElement {
MapRenderer.bindQuadIndicesVBO(false); MapRenderer.bindQuadIndicesVBO(false);
return renderElement.next; return l.next;
} }
private final static double COORD_DIV = 1.0 / MapRenderer.COORD_SCALE; private final static double COORD_DIV = 1.0 / MapRenderer.COORD_SCALE;