Kaydet (Commit) 0ccf2c77 authored tarafından Tomaž Vajngerl's avatar Tomaž Vajngerl Kaydeden (comit) Miklos Vajna

android: discard image buffer as soon as we upload to a texture

Before TileLayer (which SubTile was a subclass of) held the
image buffer for the whole life cycle of the tile, which wastes
memory in case of SubTile as the image doesn't change (or changes
only when invalidated) for the whole tile lifecycle. This change
changes the SubTile inheritance to a general Layer and adds modified
logic from SingleTileLayer and TileLayer. In addition it now
discards the buffer as soon as it has been uploaded to a texture
so that it doesn't takes up double the memory anymore.

Change-Id: Ia104687d2df529182af412102459952b53e87950
üst 0e98675b
......@@ -7,6 +7,7 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import org.mozilla.gecko.gfx.BufferedCairoImage;
import org.mozilla.gecko.gfx.CairoImage;
import org.mozilla.gecko.gfx.ComposedTileLayer;
import org.mozilla.gecko.gfx.GeckoLayerClient;
......@@ -38,7 +39,8 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation
CairoImage image = mTileProvider.createTile(tileId.x, tileId.y, tileId.size, tileId.zoom);
if (image != null) {
mLayerClient.beginDrawing();
SubTile tile = new SubTile(image, tileId);
SubTile tile = new SubTile(tileId);
tile.setImage(image);
composedTileLayer.addTile(tile);
mLayerClient.endDrawing();
mLayerClient.forceRender();
......@@ -57,7 +59,8 @@ public class LOKitThread extends Thread implements TileProvider.TileInvalidation
mLayerClient.invalidateTiles(tiles, rect);
for (SubTile tile : tiles) {
mTileProvider.rerenderTile(tile.getImage(), tile.id.x, tile.id.y, tile.id.size, tile.id.zoom);
CairoImage image = mTileProvider.createTile(tile.id.x, tile.id.y, tile.id.size, tile.id.zoom);
tile.setImage(image);
}
mLayerClient.beginDrawing();
......
......@@ -6,16 +6,64 @@
package org.mozilla.gecko.gfx;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.RegionIterator;
import android.opengl.GLES20;
import android.util.Log;
import org.libreoffice.TileIdentifier;
public class SubTile extends SingleTileLayer {
public boolean markedForRemoval = false;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
public class SubTile extends Layer {
private static String LOGTAG = SubTile.class.getSimpleName();
public final TileIdentifier id;
public SubTile(CairoImage mImage, TileIdentifier id) {
super(mImage);
private final RectF mBounds;
private final RectF mTextureBounds;
private final RectF mViewport;
private final Rect mIntBounds;
private final Rect mSubRect;
private final RectF mSubRectF;
private final Region mMaskedBounds;
private final Rect mCropRect;
private final RectF mObjRectF;
private final float[] mCoords;
public boolean markedForRemoval = false;
private CairoImage mImage;
private IntSize mSize;
private int[] mTextureIDs;
private boolean mDirtyTile;
public SubTile(TileIdentifier id) {
super();
this.id = id;
mBounds = new RectF();
mTextureBounds = new RectF();
mViewport = new RectF();
mIntBounds = new Rect();
mSubRect = new Rect();
mSubRectF = new RectF();
mMaskedBounds = new Region();
mCropRect = new Rect();
mObjRectF = new RectF();
mCoords = new float[20];
mImage = null;
mTextureIDs = null;
mSize = new IntSize(0, 0);
mDirtyTile = false;
}
public void setImage(CairoImage image) {
if (image.getSize().isPositive()) {
this.mImage = image;
}
}
public void refreshTileMetrics() {
......@@ -25,4 +73,184 @@ public class SubTile extends SingleTileLayer {
public void markForRemoval() {
markedForRemoval = true;
}
protected int getTextureID() {
return mTextureIDs[0];
}
protected boolean initialized() {
return mTextureIDs != null;
}
@Override
protected void finalize() throws Throwable {
try {
destroyImage();
cleanTexture(false);
} finally {
super.finalize();
}
}
private void cleanTexture(boolean immediately) {
if (mTextureIDs != null) {
TextureReaper.get().add(mTextureIDs);
mTextureIDs = null;
if (immediately) {
TextureReaper.get().reap();
}
}
}
public void destroy() {
try {
destroyImage();
cleanTexture(false);
} catch (Exception ex) {
Log.e(LOGTAG, "Error clearing buffers: ", ex);
}
}
public void destroyImage() {
if (mImage != null) {
mImage.destroy();
mImage = null;
}
}
/**
* Invalidates the entire buffer so that it will be uploaded again. Only valid inside a
* transaction.
*/
public void invalidate() {
if (!inTransaction()) {
throw new RuntimeException("invalidate() is only valid inside a transaction");
}
if (mImage == null) {
return;
}
mDirtyTile = true;
}
/**
* Remove the texture if the image is of different size than the current uploaded texture.
*/
private void validateTexture() {
IntSize textureSize = mImage.getSize().nextPowerOfTwo();
if (!textureSize.equals(mSize)) {
mSize = textureSize;
cleanTexture(true);
}
}
@Override
protected void performUpdates(RenderContext context) {
super.performUpdates(context);
if (mImage == null && !mDirtyTile) {
return;
}
validateTexture();
uploadNewTexture();
mDirtyTile = false;
}
private void uploadNewTexture() {
ByteBuffer imageBuffer = mImage.getBuffer();
if (imageBuffer == null) {
return;
}
if (mTextureIDs == null) {
mTextureIDs = new int[1];
GLES20.glGenTextures(mTextureIDs.length, mTextureIDs, 0);
}
int cairoFormat = mImage.getFormat();
CairoGLInfo glInfo = new CairoGLInfo(cairoFormat);
bindAndSetGLParameters();
IntSize bufferSize = mImage.getSize();
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, glInfo.internalFormat,
mSize.width, mSize.height, 0, glInfo.format, glInfo.type, imageBuffer);
destroyImage();
}
private void bindAndSetGLParameters() {
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIDs[0]);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
}
@Override
public void draw(RenderContext context) {
// mTextureIDs may be null here during startup if Layer.java's draw method
// failed to acquire the transaction lock and call performUpdates.
if (!initialized())
return;
mViewport.set(context.viewport);
mBounds.set(getBounds(context));
mTextureBounds.set(mBounds);
mBounds.roundOut(mIntBounds);
mMaskedBounds.set(mIntBounds);
// XXX Possible optimisation here, form this array so we can draw it in
// a single call.
RegionIterator iterator = new RegionIterator(mMaskedBounds);
while (iterator.next(mSubRect)) {
// Compensate for rounding errors at the edge of the tile caused by
// the roundOut above
mSubRectF.set(Math.max(mBounds.left, (float) mSubRect.left),
Math.max(mBounds.top, (float) mSubRect.top),
Math.min(mBounds.right, (float) mSubRect.right),
Math.min(mBounds.bottom, (float) mSubRect.bottom));
// This is the left/top/right/bottom of the rect, relative to the
// bottom-left of the layer, to use for texture coordinates.
mCropRect.set(Math.round(mSubRectF.left - mBounds.left),
Math.round(mBounds.bottom - mSubRectF.top),
Math.round(mSubRectF.right - mBounds.left),
Math.round(mBounds.bottom - mSubRectF.bottom));
mObjRectF.set(mSubRectF.left - mViewport.left,
mViewport.bottom - mSubRectF.bottom,
mSubRectF.right - mViewport.left,
mViewport.bottom - mSubRectF.top);
fillRectCoordBuffer(mCoords, mObjRectF, mViewport.width(), mViewport.height(), mCropRect, mTextureBounds.width(), mTextureBounds.height());
FloatBuffer coordBuffer = context.coordBuffer;
int positionHandle = context.positionHandle;
int textureHandle = context.textureHandle;
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());
// Make sure we are at position zero in the buffer
coordBuffer.position(0);
coordBuffer.put(mCoords);
// Unbind any the current array buffer so we can use client side buffers
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
// Vertex coordinates are x,y,z starting at position 0 into the buffer.
coordBuffer.position(0);
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer);
// Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
coordBuffer.position(3);
GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment