Bug 709120 - Use MultiTileLayer in Android native fennec. r=pcwalton

This uses MultiTileLayer instead of SingleTileLayer for devices that don't have
gralloc support. This has the advantage of reduced memory usage due to less
wastage from power-of-two textures, and can be a performance boost due to slow
sub-image updates on some devices.
This commit is contained in:
Chris Lord 2012-01-06 11:22:38 +00:00
parent 362960fc3a
commit f0a2ffe64f
2 changed files with 77 additions and 12 deletions

View File

@ -37,6 +37,7 @@
package org.mozilla.gecko;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.ViewportMetrics;
import android.os.*;
import android.app.*;
@ -78,6 +79,7 @@ public class GeckoEvent {
public static final int ACTIVITY_START = 17;
public static final int BROADCAST = 19;
public static final int VIEWPORT = 20;
public static final int TILE_SIZE = 21;
public static final int IME_COMPOSITION_END = 0;
public static final int IME_COMPOSITION_BEGIN = 1;
@ -228,6 +230,16 @@ public class GeckoEvent {
mP1 = new Point(screenw, screenh);
}
public GeckoEvent(int etype, IntSize size) {
if (etype != TILE_SIZE) {
mType = INVALID;
return;
}
mType = etype;
mP0 = new Point(size.width, size.height);
}
public GeckoEvent(String subject, String data) {
mType = BROADCAST;
mCharacters = subject;

View File

@ -43,8 +43,8 @@ import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.LayerClient;
import org.mozilla.gecko.gfx.LayerController;
import org.mozilla.gecko.gfx.LayerRenderer;
import org.mozilla.gecko.gfx.MultiTileLayer;
import org.mozilla.gecko.gfx.PointUtils;
import org.mozilla.gecko.gfx.SingleTileLayer;
import org.mozilla.gecko.gfx.WidgetTileLayer;
import org.mozilla.gecko.FloatUtils;
import org.mozilla.gecko.GeckoApp;
@ -53,6 +53,7 @@ import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoEventListener;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@ -84,6 +85,8 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
private CairoImage mCairoImage;
private static final IntSize TILE_SIZE = new IntSize(256, 256);
private static final long MIN_VIEWPORT_CHANGE_DELAY = 350L;
private long mLastViewportChangeTime;
private boolean mPendingViewportAdjust;
@ -112,7 +115,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
public int getFormat() { return mFormat; }
};
mTileLayer = new SingleTileLayer(mCairoImage);
mTileLayer = new MultiTileLayer(mCairoImage, TILE_SIZE);
}
@ -143,6 +146,14 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
GeckoAppShell.registerGeckoEventListener("Viewport:UpdateAndDraw", this);
GeckoAppShell.registerGeckoEventListener("Viewport:UpdateLater", this);
// This needs to happen before a call to sendResizeEventIfNecessary
// happens, but only needs to be called once. As that is only called by
// the layer controller or this, here is a safe place to do so.
if (mTileLayer instanceof MultiTileLayer) {
GeckoEvent event = new GeckoEvent(GeckoEvent.TILE_SIZE, TILE_SIZE);
GeckoAppShell.sendEventToGecko(event);
}
sendResizeEventIfNecessary();
}
@ -213,8 +224,8 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
mUpdateViewportOnEndDraw = false;
Rect rect = new Rect(x, y, x + width, y + height);
if (mTileLayer instanceof SingleTileLayer)
((SingleTileLayer)mTileLayer).invalidate(rect);
if (mTileLayer instanceof MultiTileLayer)
((MultiTileLayer)mTileLayer).invalidate(rect);
} finally {
endTransaction(mTileLayer);
}
@ -228,6 +239,33 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
return null;
}
public void copyPixelsFromMultiTileLayer(Bitmap target) {
Canvas c = new Canvas(target);
ByteBuffer tileBuffer = mBuffer.slice();
int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat) / 8;
for (int y = 0; y < mBufferSize.height; y += TILE_SIZE.height) {
for (int x = 0; x < mBufferSize.width; x += TILE_SIZE.width) {
// Calculate tile size
IntSize tileSize = new IntSize(Math.min(mBufferSize.width - x, TILE_SIZE.width),
Math.min(mBufferSize.height - y, TILE_SIZE.height));
// Create a Bitmap from this tile
Bitmap tile = Bitmap.createBitmap(tileSize.width, tileSize.height,
CairoUtils.cairoFormatTobitmapConfig(mFormat));
tile.copyPixelsFromBuffer(tileBuffer.asIntBuffer());
// Copy the tile to the master Bitmap and recycle it
c.drawBitmap(tile, x, y, null);
tile.recycle();
// Progress the buffer to the next tile
tileBuffer.position(tileSize.getArea() * bpp);
tileBuffer = tileBuffer.slice();
}
}
}
public Bitmap getBitmap() {
// Begin a tile transaction, otherwise the buffer can be destroyed while
// we're reading from it.
@ -238,7 +276,12 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
try {
Bitmap b = Bitmap.createBitmap(mBufferSize.width, mBufferSize.height,
CairoUtils.cairoFormatTobitmapConfig(mFormat));
b.copyPixelsFromBuffer(mBuffer.asIntBuffer());
if (mTileLayer instanceof MultiTileLayer)
copyPixelsFromMultiTileLayer(b);
else
b.copyPixelsFromBuffer(mBuffer.asIntBuffer());
return b;
} catch (OutOfMemoryError oom) {
Log.w(LOGTAG, "Unable to create bitmap", oom);
@ -280,15 +323,25 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
}
mScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
int maxSize = getLayerController().getView().getMaxTextureSize();
IntSize bufferSize;
// XXX Introduce tiling to solve this?
if (mScreenSize.width > maxSize || mScreenSize.height > maxSize)
throw new RuntimeException("Screen size of " + mScreenSize + " larger than maximum texture size of " + maxSize);
// Round up depending on layer implementation to remove texture wastage
if (mTileLayer instanceof MultiTileLayer) {
// Round to the next multiple of the tile size, respecting maximum texture size
bufferSize = new IntSize(((mScreenSize.width + LayerController.MIN_BUFFER.width - 1) / TILE_SIZE.width + 1) * TILE_SIZE.width,
((mScreenSize.height + LayerController.MIN_BUFFER.height - 1) / TILE_SIZE.height + 1) * TILE_SIZE.height);
// Round to next power of two until we use NPOT texture support
IntSize bufferSize = new IntSize(Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.width + LayerController.MIN_BUFFER.width)),
Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.height + LayerController.MIN_BUFFER.height)));
} else {
int maxSize = getLayerController().getView().getMaxTextureSize();
// XXX Integrate gralloc/tiling work to circumvent this
if (mScreenSize.width > maxSize || mScreenSize.height > maxSize)
throw new RuntimeException("Screen size of " + mScreenSize + " larger than maximum texture size of " + maxSize);
// Round to next power of two until we have NPOT texture support
bufferSize = new IntSize(Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.width + LayerController.MIN_BUFFER.width)),
Math.min(maxSize, IntSize.nextPowerOfTwo(mScreenSize.height + LayerController.MIN_BUFFER.height)));
}
Log.i(LOGTAG, "Screen-size changed to " + mScreenSize);
GeckoEvent event = new GeckoEvent(GeckoEvent.SIZE_CHANGED,