Refactor HardwareLayer

Defer all the things!
 Groundwork to allow hardware layers to work in a renderthread world

Change-Id: Ib3aa47525f393083621254a743dbaa6352f933bd
This commit is contained in:
John Reck
2014-02-05 16:38:25 -08:00
parent 740dfefcfc
commit 04fc583c3d
22 changed files with 767 additions and 768 deletions

View File

@@ -31,7 +31,6 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.Shader;
import android.graphics.SurfaceTexture;
import android.graphics.TemporaryBuffer;
import android.text.GraphicsOperations;
import android.text.SpannableString;
@@ -87,15 +86,6 @@ class GLES20Canvas extends HardwareCanvas {
GLES20Canvas(boolean translucent) {
this(false, translucent);
}
/**
* Creates a canvas to render into an FBO.
*/
GLES20Canvas(long layer, boolean translucent) {
mOpaque = !translucent;
mRenderer = nCreateLayerRenderer(layer);
setupFinalizer();
}
protected GLES20Canvas(boolean record, boolean translucent) {
mOpaque = !translucent;
@@ -122,7 +112,6 @@ class GLES20Canvas extends HardwareCanvas {
}
private static native long nCreateRenderer();
private static native long nCreateLayerRenderer(long layer);
private static native long nCreateDisplayListRenderer();
private static native void nResetDisplayListRenderer(long renderer);
private static native void nDestroyRenderer(long renderer);
@@ -156,12 +145,12 @@ class GLES20Canvas extends HardwareCanvas {
@Override
void pushLayerUpdate(HardwareLayer layer) {
nPushLayerUpdate(mRenderer, ((GLES20RenderLayer) layer).mLayer);
nPushLayerUpdate(mRenderer, layer.getLayer());
}
@Override
void cancelLayerUpdate(HardwareLayer layer) {
nCancelLayerUpdate(mRenderer, ((GLES20RenderLayer) layer).mLayer);
nCancelLayerUpdate(mRenderer, layer.getLayer());
}
@Override
@@ -174,22 +163,7 @@ class GLES20Canvas extends HardwareCanvas {
nClearLayerUpdates(mRenderer);
}
static native long nCreateTextureLayer(boolean opaque, int[] layerInfo);
static native long nCreateLayer(int width, int height, boolean isOpaque, int[] layerInfo);
static native boolean nResizeLayer(long layerId, int width, int height, int[] layerInfo);
static native void nSetOpaqueLayer(long layerId, boolean isOpaque);
static native void nSetLayerPaint(long layerId, long nativePaint);
static native void nSetLayerColorFilter(long layerId, long nativeColorFilter);
static native void nUpdateTextureLayer(long layerId, int width, int height, boolean opaque,
SurfaceTexture surface);
static native void nClearLayerTexture(long layerId);
static native void nSetTextureLayerTransform(long layerId, long matrix);
static native void nDestroyLayer(long layerId);
static native void nDestroyLayerDeferred(long layerId);
static native void nUpdateRenderLayer(long layerId, long renderer, long displayList,
int left, int top, int right, int bottom);
static native boolean nCopyLayer(long layerId, long bitmap);
private static native void nClearLayerUpdates(long renderer);
private static native void nFlushLayerUpdates(long renderer);
private static native void nPushLayerUpdate(long renderer, long layer);
@@ -408,9 +382,7 @@ class GLES20Canvas extends HardwareCanvas {
void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) {
layer.setLayerPaint(paint);
final GLES20Layer glLayer = (GLES20Layer) layer;
nDrawLayer(mRenderer, glLayer.getLayer(), x, y);
nDrawLayer(mRenderer, layer.getLayer(), x, y);
}
private static native void nDrawLayer(long renderer, long layer, float x, float y);

View File

@@ -1,100 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view;
import android.graphics.Bitmap;
import android.graphics.Paint;
/**
* An OpenGL ES 2.0 implementation of {@link HardwareLayer}.
*/
abstract class GLES20Layer extends HardwareLayer {
long mLayer;
Finalizer mFinalizer;
GLES20Layer() {
}
GLES20Layer(int width, int height, boolean opaque) {
super(width, height, opaque);
}
/**
* Returns the native layer object used to render this layer.
*
* @return A pointer to the native layer object, or 0 if the object is NULL
*/
public long getLayer() {
return mLayer;
}
@Override
void setLayerPaint(Paint paint) {
if (paint != null) {
GLES20Canvas.nSetLayerPaint(mLayer, paint.mNativePaint);
GLES20Canvas.nSetLayerColorFilter(mLayer, paint.getColorFilter() != null ?
paint.getColorFilter().native_instance : 0);
}
}
@Override
public boolean copyInto(Bitmap bitmap) {
return GLES20Canvas.nCopyLayer(mLayer, bitmap.mNativeBitmap);
}
@Override
public void destroy() {
if (mDisplayList != null) {
mDisplayList.reset();
}
if (mFinalizer != null) {
mFinalizer.destroy();
mFinalizer = null;
}
mLayer = 0;
}
@Override
void clearStorage() {
if (mLayer != 0) GLES20Canvas.nClearLayerTexture(mLayer);
}
static class Finalizer {
private long mLayerId;
public Finalizer(long layerId) {
mLayerId = layerId;
}
@Override
protected void finalize() throws Throwable {
try {
if (mLayerId != 0) {
GLES20Canvas.nDestroyLayerDeferred(mLayerId);
}
} finally {
super.finalize();
}
}
void destroy() {
GLES20Canvas.nDestroyLayer(mLayerId);
mLayerId = 0;
}
}
}

View File

@@ -1,130 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
/**
* An OpenGL ES 2.0 implementation of {@link HardwareLayer}. This
* implementation can be used a rendering target. It generates a
* {@link Canvas} that can be used to render into an FBO using OpenGL.
*/
class GLES20RenderLayer extends GLES20Layer {
private int mLayerWidth;
private int mLayerHeight;
private final GLES20Canvas mCanvas;
GLES20RenderLayer(int width, int height, boolean isOpaque) {
super(width, height, isOpaque);
int[] layerInfo = new int[2];
mLayer = GLES20Canvas.nCreateLayer(width, height, isOpaque, layerInfo);
if (mLayer != 0) {
mLayerWidth = layerInfo[0];
mLayerHeight = layerInfo[1];
mCanvas = new GLES20Canvas(mLayer, !isOpaque);
mFinalizer = new Finalizer(mLayer);
} else {
mCanvas = null;
mFinalizer = null;
}
}
@Override
boolean isValid() {
return mLayer != 0 && mLayerWidth > 0 && mLayerHeight > 0;
}
@Override
boolean resize(int width, int height) {
if (!isValid() || width <= 0 || height <= 0) return false;
mWidth = width;
mHeight = height;
if (width != mLayerWidth || height != mLayerHeight) {
int[] layerInfo = new int[2];
if (GLES20Canvas.nResizeLayer(mLayer, width, height, layerInfo)) {
mLayerWidth = layerInfo[0];
mLayerHeight = layerInfo[1];
} else {
// Failure: not enough GPU resources for requested size
mLayer = 0;
mLayerWidth = 0;
mLayerHeight = 0;
}
}
return isValid();
}
@Override
void setOpaque(boolean isOpaque) {
mOpaque = isOpaque;
GLES20Canvas.nSetOpaqueLayer(mLayer, isOpaque);
}
@Override
HardwareCanvas getCanvas() {
return mCanvas;
}
@Override
void end(Canvas currentCanvas) {
HardwareCanvas canvas = getCanvas();
if (canvas != null) {
canvas.onPostDraw();
}
if (currentCanvas instanceof GLES20Canvas) {
((GLES20Canvas) currentCanvas).resume();
}
}
@Override
HardwareCanvas start(Canvas currentCanvas) {
return start(currentCanvas, null);
}
@Override
HardwareCanvas start(Canvas currentCanvas, Rect dirty) {
if (currentCanvas instanceof GLES20Canvas) {
((GLES20Canvas) currentCanvas).interrupt();
}
HardwareCanvas canvas = getCanvas();
canvas.setViewport(mWidth, mHeight);
canvas.onPreDraw(dirty);
return canvas;
}
/**
* Ignored
*/
@Override
void setTransform(Matrix matrix) {
}
@Override
void redrawLater(DisplayList displayList, Rect dirtyRect) {
GLES20Canvas.nUpdateRenderLayer(mLayer, mCanvas.getRenderer(),
displayList.getNativeDisplayList(),
dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
}
}

View File

@@ -1,108 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
/**
* An OpenGL ES 2.0 implementation of {@link HardwareLayer}. This
* implementation can be used as a texture. Rendering into this
* layer is not controlled by a {@link HardwareCanvas}.
*/
class GLES20TextureLayer extends GLES20Layer {
private int mTexture;
private SurfaceTexture mSurface;
GLES20TextureLayer(boolean isOpaque) {
int[] layerInfo = new int[2];
mLayer = GLES20Canvas.nCreateTextureLayer(isOpaque, layerInfo);
if (mLayer != 0) {
mTexture = layerInfo[0];
mFinalizer = new Finalizer(mLayer);
} else {
mFinalizer = null;
}
}
@Override
boolean isValid() {
return mLayer != 0 && mTexture != 0;
}
@Override
boolean resize(int width, int height) {
return isValid();
}
@Override
HardwareCanvas getCanvas() {
return null;
}
@Override
HardwareCanvas start(Canvas currentCanvas) {
return null;
}
@Override
HardwareCanvas start(Canvas currentCanvas, Rect dirty) {
return null;
}
@Override
void end(Canvas currentCanvas) {
}
SurfaceTexture getSurfaceTexture() {
if (mSurface == null) {
mSurface = new SurfaceTexture(mTexture);
}
return mSurface;
}
void setSurfaceTexture(SurfaceTexture surfaceTexture) {
if (mSurface != null) {
mSurface.release();
}
mSurface = surfaceTexture;
mSurface.attachToGLContext(mTexture);
}
@Override
void update(int width, int height, boolean isOpaque) {
super.update(width, height, isOpaque);
GLES20Canvas.nUpdateTextureLayer(mLayer, width, height, isOpaque, mSurface);
}
@Override
void setOpaque(boolean isOpaque) {
throw new UnsupportedOperationException("Use update(int, int, boolean) instead");
}
@Override
void setTransform(Matrix matrix) {
GLES20Canvas.nSetTextureLayerTransform(mLayer, matrix.native_instance);
}
@Override
void redrawLater(DisplayList displayList, Rect dirtyRect) {
}
}

View File

@@ -40,7 +40,7 @@ import static javax.microedition.khronos.egl.EGL10.EGL_WIDTH;
import static javax.microedition.khronos.egl.EGL10.EGL_WINDOW_BIT;
import android.content.ComponentCallbacks2;
import android.graphics.Color;
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
@@ -62,6 +62,8 @@ import android.view.Surface.OutOfResourcesException;
import com.google.android.gles_jni.EGLImpl;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import javax.microedition.khronos.egl.EGL10;
@@ -177,6 +179,8 @@ public class GLRenderer extends HardwareRenderer {
private static EGLSurface sPbuffer;
private static final Object[] sPbufferLock = new Object[0];
private List<HardwareLayer> mAttachedLayers = new ArrayList<HardwareLayer>();
private static class GLRendererEglContext extends ManagedEGLContext {
final Handler mHandler = new Handler();
@@ -471,34 +475,41 @@ public class GLRenderer extends HardwareRenderer {
mGlCanvas.pushLayerUpdate(layer);
}
@Override
void cancelLayerUpdate(HardwareLayer layer) {
mGlCanvas.cancelLayerUpdate(layer);
}
@Override
void flushLayerUpdates() {
mGlCanvas.flushLayerUpdates();
}
@Override
HardwareLayer createHardwareLayer(boolean isOpaque) {
return new GLES20TextureLayer(isOpaque);
HardwareLayer createTextureLayer() {
return HardwareLayer.createTextureLayer(this);
}
@Override
public HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
return new GLES20RenderLayer(width, height, isOpaque);
public HardwareLayer createDisplayListLayer(int width, int height) {
return HardwareLayer.createRenderLayer(this, width, height);
}
@Override
void onLayerCreated(HardwareLayer hardwareLayer) {
mAttachedLayers.add(hardwareLayer);
}
@Override
void onLayerDestroyed(HardwareLayer layer) {
mGlCanvas.cancelLayerUpdate(layer);
mAttachedLayers.remove(layer);
}
@Override
public SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
return ((GLES20TextureLayer) layer).getSurfaceTexture();
return layer.createSurfaceTexture();
}
@Override
void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) {
((GLES20TextureLayer) layer).setSurfaceTexture(surfaceTexture);
boolean copyLayerInto(HardwareLayer layer, Bitmap bitmap) {
layer.flushChanges();
return GLES20Canvas.nCopyLayer(layer.getLayer(), bitmap.mNativeBitmap);
}
@Override
@@ -1127,6 +1138,8 @@ public class GLRenderer extends HardwareRenderer {
DisplayList displayList = buildDisplayList(view, canvas);
flushLayerChanges();
// buildDisplayList() calls into user code which can cause
// an eglMakeCurrent to happen with a different surface/context.
// We must therefore check again here.
@@ -1180,6 +1193,20 @@ public class GLRenderer extends HardwareRenderer {
}
}
private void flushLayerChanges() {
// Loop through and apply any pending layer changes
for (int i = 0; i < mAttachedLayers.size(); i++) {
HardwareLayer layer = mAttachedLayers.get(i);
layer.flushChanges();
if (!layer.isValid()) {
// The layer was removed from mAttachedLayers, rewind i by 1
// Note that this shouldn't actually happen as View.getHardwareLayer()
// is already flushing for error checking reasons
i--;
}
}
}
private DisplayList buildDisplayList(View view, HardwareCanvas canvas) {
if (mDrawDelta <= 0) {
return view.mDisplayList;

View File

@@ -17,10 +17,10 @@
package android.view;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
/**
* A hardware layer can be used to render graphics operations into a hardware
@@ -28,38 +28,35 @@ import android.graphics.Rect;
* would use a Frame Buffer Object (FBO.) The hardware layer can be used as
* a drawing cache when a complex set of graphics operations needs to be
* drawn several times.
*
* @hide
*/
abstract class HardwareLayer {
/**
* Indicates an unknown dimension (width or height.)
*/
static final int DIMENSION_UNDEFINED = -1;
int mWidth;
int mHeight;
DisplayList mDisplayList;
final class HardwareLayer {
private static final int LAYER_TYPE_TEXTURE = 1;
private static final int LAYER_TYPE_RENDER = 2;
boolean mOpaque;
private HardwareRenderer mRenderer;
private Finalizer mFinalizer;
private DisplayList mDisplayList;
private final int mLayerType;
/**
* Creates a new hardware layer with undefined dimensions.
*/
HardwareLayer() {
this(DIMENSION_UNDEFINED, DIMENSION_UNDEFINED, false);
private HardwareLayer(HardwareRenderer renderer, long deferredUpdater, int type) {
if (renderer == null || deferredUpdater == 0) {
throw new IllegalArgumentException("Either hardware renderer: " + renderer
+ " or deferredUpdater: " + deferredUpdater + " is invalid");
}
mRenderer = renderer;
mLayerType = type;
mFinalizer = new Finalizer(deferredUpdater);
// Layer is considered initialized at this point, notify the HardwareRenderer
mRenderer.onLayerCreated(this);
}
/**
* Creates a new hardware layer at least as large as the supplied
* dimensions.
*
* @param width The minimum width of the layer
* @param height The minimum height of the layer
* @param isOpaque Whether the layer should be opaque or not
*/
HardwareLayer(int width, int height, boolean isOpaque) {
mWidth = width;
mHeight = height;
mOpaque = isOpaque;
private void assertType(int type) {
if (mLayerType != type) {
throw new IllegalAccessError("Method not appropriate for this layer type! " + mLayerType);
}
}
/**
@@ -68,158 +65,225 @@ abstract class HardwareLayer {
* @param paint The paint used when the layer is drawn into the destination canvas.
* @see View#setLayerPaint(android.graphics.Paint)
*/
void setLayerPaint(Paint paint) { }
/**
* Returns the minimum width of the layer.
*
* @return The minimum desired width of the hardware layer
*/
int getWidth() {
return mWidth;
public void setLayerPaint(Paint paint) {
nSetLayerPaint(mFinalizer.mDeferredUpdater, paint.mNativePaint,
paint.getColorFilter() != null ? paint.getColorFilter().native_instance : 0);
}
/**
* Returns the minimum height of the layer.
*
* @return The minimum desired height of the hardware layer
*/
int getHeight() {
return mHeight;
}
/**
* Returns the DisplayList for the layer.
*
* @return The DisplayList of the hardware layer
*/
DisplayList getDisplayList() {
return mDisplayList;
}
/**
* Sets the DisplayList for the layer.
*
* @param displayList The new DisplayList for this layer
*/
void setDisplayList(DisplayList displayList) {
mDisplayList = displayList;
}
/**
* Returns whether or not this layer is opaque.
*
* @return True if the layer is opaque, false otherwise
*/
boolean isOpaque() {
return mOpaque;
}
/**
* Sets whether or not this layer should be considered opaque.
*
* @param isOpaque True if the layer is opaque, false otherwise
*/
abstract void setOpaque(boolean isOpaque);
/**
* Indicates whether this layer can be rendered.
*
*
* @return True if the layer can be rendered into, false otherwise
*/
abstract boolean isValid();
public boolean isValid() {
return mFinalizer != null && mFinalizer.mDeferredUpdater != 0;
}
/**
* Resize the layer, if necessary, to be at least as large
* as the supplied dimensions.
*
* @param width The new desired minimum width for this layer
* @param height The new desired minimum height for this layer
* @return True if the resulting layer is valid, false otherwise
* Destroys resources without waiting for a GC.
*/
abstract boolean resize(int width, int height);
public void destroy() {
if (!isValid()) {
// Already destroyed
return;
}
if (mDisplayList != null) {
mDisplayList.reset();
mDisplayList = null;
}
if (mRenderer != null) {
mRenderer.onLayerDestroyed(this);
mRenderer = null;
}
doDestroyLayerUpdater();
}
/**
* Returns a hardware canvas that can be used to render onto
* this layer.
*
* @return A hardware canvas, or null if a canvas cannot be created
* Destroys the deferred layer updater but not the backing layer. The
* backing layer is instead returned and is the caller's responsibility
* to destroy/recycle as appropriate.
*
* @see #start(android.graphics.Canvas)
* @see #end(android.graphics.Canvas)
* It is safe to call this in onLayerDestroyed only
*/
abstract HardwareCanvas getCanvas();
public long detachBackingLayer() {
long backingLayer = nDetachBackingLayer(mFinalizer.mDeferredUpdater);
doDestroyLayerUpdater();
return backingLayer;
}
/**
* Destroys resources without waiting for a GC.
*/
abstract void destroy();
private void doDestroyLayerUpdater() {
if (mFinalizer != null) {
mFinalizer.destroy();
mFinalizer = null;
}
}
/**
* This must be invoked before drawing onto this layer.
*
* @param currentCanvas The canvas whose rendering needs to be interrupted
*/
abstract HardwareCanvas start(Canvas currentCanvas);
public DisplayList startRecording() {
assertType(LAYER_TYPE_RENDER);
/**
* This must be invoked before drawing onto this layer.
*
* @param dirty The dirty area to repaint
* @param currentCanvas The canvas whose rendering needs to be interrupted
*/
abstract HardwareCanvas start(Canvas currentCanvas, Rect dirty);
if (mDisplayList == null) {
mDisplayList = DisplayList.create("HardwareLayer");
}
return mDisplayList;
}
/**
* This must be invoked after drawing onto this layer.
*
* @param currentCanvas The canvas whose rendering needs to be resumed
*/
abstract void end(Canvas currentCanvas);
public void endRecording(Rect dirtyRect) {
nUpdateRenderLayer(mFinalizer.mDeferredUpdater, mDisplayList.getNativeDisplayList(),
dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
mRenderer.pushLayerUpdate(this);
}
/**
* Copies this layer into the specified bitmap.
*
*
* @param bitmap The bitmap to copy they layer into
*
*
* @return True if the copy was successful, false otherwise
*/
abstract boolean copyInto(Bitmap bitmap);
public boolean copyInto(Bitmap bitmap) {
return mRenderer.copyLayerInto(this, bitmap);
}
/**
* Update the layer's properties. This method should be used
* when the underlying storage is modified by an external entity.
* To change the underlying storage, use the {@link #resize(int, int)}
* method instead.
*
* Update the layer's properties. Note that after calling this isValid() may
* return false if the requested width/height cannot be satisfied
*
* @param width The new width of this layer
* @param height The new height of this layer
* @param isOpaque Whether this layer is opaque
*
* @return true if the layer's properties will change, false if they already
* match the desired values.
*/
void update(int width, int height, boolean isOpaque) {
mWidth = width;
mHeight = height;
mOpaque = isOpaque;
public boolean prepare(int width, int height, boolean isOpaque) {
return nPrepare(mFinalizer.mDeferredUpdater, width, height, isOpaque);
}
/**
* Sets an optional transform on this layer.
*
*
* @param matrix The transform to apply to the layer.
*/
abstract void setTransform(Matrix matrix);
public void setTransform(Matrix matrix) {
nSetTransform(mFinalizer.mDeferredUpdater, matrix.native_instance);
}
/**
* Specifies the display list to use to refresh the layer.
*
* @param displayList The display list containing the drawing commands to
* execute in this layer
* @param dirtyRect The dirty region of the layer that needs to be redrawn
* Indicates that this layer has lost its texture.
*/
abstract void redrawLater(DisplayList displayList, Rect dirtyRect);
public void onTextureDestroyed() {
assertType(LAYER_TYPE_TEXTURE);
nOnTextureDestroyed(mFinalizer.mDeferredUpdater);
}
/**
* Indicates that this layer has lost its underlying storage.
* This exists to minimize impact into the current HardwareLayer paths as
* some of the specifics of how to handle error cases in the fully
* deferred model will work
*/
abstract void clearStorage();
@Deprecated
public void flushChanges() {
if (HardwareRenderer.sUseRenderThread) {
// Not supported, don't try.
return;
}
boolean success = nFlushChanges(mFinalizer.mDeferredUpdater);
if (!success) {
destroy();
}
}
public long getLayer() {
return nGetLayer(mFinalizer.mDeferredUpdater);
}
public void setSurfaceTexture(SurfaceTexture surface) {
assertType(LAYER_TYPE_TEXTURE);
nSetSurfaceTexture(mFinalizer.mDeferredUpdater, surface, false);
}
public void updateSurfaceTexture() {
assertType(LAYER_TYPE_TEXTURE);
nUpdateSurfaceTexture(mFinalizer.mDeferredUpdater);
}
/**
* This should only be used by HardwareRenderer! Do not call directly
*/
SurfaceTexture createSurfaceTexture() {
assertType(LAYER_TYPE_TEXTURE);
SurfaceTexture st = new SurfaceTexture(nGetTexName(mFinalizer.mDeferredUpdater));
nSetSurfaceTexture(mFinalizer.mDeferredUpdater, st, true);
return st;
}
/**
* This should only be used by HardwareRenderer! Do not call directly
*/
static HardwareLayer createTextureLayer(HardwareRenderer renderer) {
return new HardwareLayer(renderer, nCreateTextureLayer(), LAYER_TYPE_TEXTURE);
}
/**
* This should only be used by HardwareRenderer! Do not call directly
*/
static HardwareLayer createRenderLayer(HardwareRenderer renderer,
int width, int height) {
return new HardwareLayer(renderer, nCreateRenderLayer(width, height), LAYER_TYPE_RENDER);
}
/** This also creates the underlying layer */
private static native long nCreateTextureLayer();
private static native long nCreateRenderLayer(int width, int height);
private static native void nOnTextureDestroyed(long layerUpdater);
private static native long nDetachBackingLayer(long layerUpdater);
/** This also destroys the underlying layer if it is still attached.
* Note it does not recycle the underlying layer, but instead queues it
* for deferred deletion.
* The HardwareRenderer should use detachBackingLayer() in the
* onLayerDestroyed() callback to do recycling if desired.
*/
private static native void nDestroyLayerUpdater(long layerUpdater);
private static native boolean nPrepare(long layerUpdater, int width, int height, boolean isOpaque);
private static native void nSetLayerPaint(long layerUpdater, long paint, long colorFilter);
private static native void nSetTransform(long layerUpdater, long matrix);
private static native void nSetSurfaceTexture(long layerUpdater,
SurfaceTexture surface, boolean isAlreadyAttached);
private static native void nUpdateSurfaceTexture(long layerUpdater);
private static native void nUpdateRenderLayer(long layerUpdater, long displayList,
int left, int top, int right, int bottom);
private static native boolean nFlushChanges(long layerUpdater);
private static native long nGetLayer(long layerUpdater);
private static native int nGetTexName(long layerUpdater);
private static class Finalizer {
private long mDeferredUpdater;
public Finalizer(long deferredUpdater) {
mDeferredUpdater = deferredUpdater;
}
@Override
protected void finalize() throws Throwable {
try {
destroy();
} finally {
super.finalize();
}
}
void destroy() {
if (mDeferredUpdater != 0) {
nDestroyLayerUpdater(mDeferredUpdater);
mDeferredUpdater = 0;
}
}
}
}

View File

@@ -16,6 +16,7 @@
package android.view;
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
@@ -344,19 +345,20 @@ public abstract class HardwareRenderer {
* @param layer The hardware layer that needs an update
*
* @see #flushLayerUpdates()
* @see #cancelLayerUpdate(HardwareLayer)
*/
abstract void pushLayerUpdate(HardwareLayer layer);
/**
* Cancels a queued layer update. If the specified layer was not
* queued for update, this method has no effect.
*
* @param layer The layer whose update to cancel
*
* @see #pushLayerUpdate(HardwareLayer)
* Tells the HardwareRenderer that a layer was created. The renderer should
* make sure to apply any pending layer changes at the start of a new frame
*/
abstract void cancelLayerUpdate(HardwareLayer layer);
abstract void onLayerCreated(HardwareLayer hardwareLayer);
/**
* Tells the HardwareRenderer that the layer is destroyed. The renderer
* should remove the layer from any update queues.
*/
abstract void onLayerDestroyed(HardwareLayer layer);
/**
* Forces all enqueued layer updates to be executed immediately.
@@ -403,22 +405,19 @@ public abstract class HardwareRenderer {
* Creates a new hardware layer. A hardware layer built by calling this
* method will be treated as a texture layer, instead of as a render target.
*
* @param isOpaque Whether the layer should be opaque or not
*
* @return A hardware layer
*/
abstract HardwareLayer createHardwareLayer(boolean isOpaque);
abstract HardwareLayer createTextureLayer();
/**
* Creates a new hardware layer.
*
* @param width The minimum width of the layer
* @param height The minimum height of the layer
* @param isOpaque Whether the layer should be opaque or not
*
* @return A hardware layer
*/
abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque);
abstract HardwareLayer createDisplayListLayer(int width, int height);
/**
* Creates a new {@link SurfaceTexture} that can be used to render into the
@@ -430,14 +429,7 @@ public abstract class HardwareRenderer {
*/
abstract SurfaceTexture createSurfaceTexture(HardwareLayer layer);
/**
* Sets the {@link android.graphics.SurfaceTexture} that will be used to
* render into the specified hardware layer.
*
* @param layer The layer to render into using a {@link android.graphics.SurfaceTexture}
* @param surfaceTexture The {@link android.graphics.SurfaceTexture} to use for the layer
*/
abstract void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture);
abstract boolean copyLayerInto(HardwareLayer layer, Bitmap bitmap);
/**
* Detaches the specified functor from the current functor execution queue.

View File

@@ -250,7 +250,7 @@ public class TextureView extends View {
mSurface.detachFromGLContext();
// SurfaceTexture owns the texture name and detachFromGLContext
// should have deleted it
mLayer.clearStorage();
mLayer.onTextureDestroyed();
boolean shouldRelease = true;
if (mListener != null) {
@@ -375,7 +375,7 @@ public class TextureView extends View {
return null;
}
mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(mOpaque);
mLayer = mAttachInfo.mHardwareRenderer.createTextureLayer();
if (!mUpdateSurface) {
// Create a new SurfaceTexture for the layer.
mSurface = mAttachInfo.mHardwareRenderer.createSurfaceTexture(mLayer);
@@ -416,7 +416,7 @@ public class TextureView extends View {
updateLayer();
mMatrixChanged = true;
mAttachInfo.mHardwareRenderer.setSurfaceTexture(mLayer, mSurface);
mLayer.setSurfaceTexture(mSurface);
mSurface.setDefaultBufferSize(getWidth(), getHeight());
}
@@ -469,7 +469,8 @@ public class TextureView extends View {
}
}
mLayer.update(getWidth(), getHeight(), mOpaque);
mLayer.prepare(getWidth(), getHeight(), mOpaque);
mLayer.updateSurfaceTexture();
if (mListener != null) {
mListener.onSurfaceTextureUpdated(mSurface);

View File

@@ -16,6 +16,7 @@
package android.view;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.SystemClock;
@@ -155,7 +156,12 @@ public class ThreadedRenderer extends HardwareRenderer {
}
@Override
void cancelLayerUpdate(HardwareLayer layer) {
void onLayerCreated(HardwareLayer layer) {
throw new NoSuchMethodError();
}
@Override
void onLayerDestroyed(HardwareLayer layer) {
throw new NoSuchMethodError();
}
@@ -197,12 +203,12 @@ public class ThreadedRenderer extends HardwareRenderer {
}
@Override
HardwareLayer createHardwareLayer(boolean isOpaque) {
HardwareLayer createTextureLayer() {
throw new NoSuchMethodError();
}
@Override
HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
HardwareLayer createDisplayListLayer(int width, int height) {
throw new NoSuchMethodError();
}
@@ -212,7 +218,7 @@ public class ThreadedRenderer extends HardwareRenderer {
}
@Override
void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) {
boolean copyLayerInto(HardwareLayer layer, Bitmap bitmap) {
throw new NoSuchMethodError();
}

View File

@@ -87,7 +87,6 @@ import static java.lang.Math.max;
import com.android.internal.R;
import com.android.internal.util.Predicate;
import com.android.internal.view.menu.MenuBuilder;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -13629,16 +13628,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || mHardwareLayer == null) {
if (mHardwareLayer == null) {
mHardwareLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(
width, height, isOpaque());
mHardwareLayer = mAttachInfo.mHardwareRenderer.createDisplayListLayer(
width, height);
mLocalDirtyRect.set(0, 0, width, height);
} else {
if (mHardwareLayer.getWidth() != width || mHardwareLayer.getHeight() != height) {
if (mHardwareLayer.resize(width, height)) {
mLocalDirtyRect.set(0, 0, width, height);
}
}
} else if (mHardwareLayer.isValid()) {
// This should not be necessary but applications that change
// the parameters of their background drawable without calling
// this.setBackground(Drawable) can leave the view in a bad state
@@ -13646,23 +13639,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// not opaque.)
computeOpaqueFlags();
final boolean opaque = isOpaque();
if (mHardwareLayer.isValid() && mHardwareLayer.isOpaque() != opaque) {
mHardwareLayer.setOpaque(opaque);
if (mHardwareLayer.prepare(width, height, isOpaque())) {
mLocalDirtyRect.set(0, 0, width, height);
}
}
// The layer is not valid if the underlying GPU resources cannot be allocated
mHardwareLayer.flushChanges();
if (!mHardwareLayer.isValid()) {
return null;
}
mHardwareLayer.setLayerPaint(mLayerPaint);
mHardwareLayer.redrawLater(getHardwareLayerDisplayList(mHardwareLayer), mLocalDirtyRect);
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null) viewRoot.pushHardwareLayerUpdate(mHardwareLayer);
DisplayList displayList = mHardwareLayer.startRecording();
if (getDisplayList(displayList, true) != displayList) {
throw new IllegalStateException("getDisplayList() didn't return"
+ " the input displaylist for a hardware layer!");
}
mHardwareLayer.endRecording(mLocalDirtyRect);
mLocalDirtyRect.setEmpty();
}
@@ -13679,18 +13673,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
boolean destroyLayer(boolean valid) {
if (mHardwareLayer != null) {
AttachInfo info = mAttachInfo;
if (info != null && info.mHardwareRenderer != null &&
info.mHardwareRenderer.isEnabled() &&
(valid || info.mHardwareRenderer.validate())) {
mHardwareLayer.destroy();
mHardwareLayer = null;
info.mHardwareRenderer.cancelLayerUpdate(mHardwareLayer);
mHardwareLayer.destroy();
mHardwareLayer = null;
invalidate(true);
invalidateParentCaches();
}
invalidate(true);
invalidateParentCaches();
return true;
}
return false;
@@ -13910,19 +13897,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return displayList;
}
/**
* Get the DisplayList for the HardwareLayer
*
* @param layer The HardwareLayer whose DisplayList we want
* @return A DisplayList fopr the specified HardwareLayer
*/
private DisplayList getHardwareLayerDisplayList(HardwareLayer layer) {
DisplayList displayList = getDisplayList(layer.getDisplayList(), true);
layer.setDisplayList(displayList);
return displayList;
}
/**
* <p>Returns a display list that can be used to draw this view again
* without executing its draw method.</p>

View File

@@ -642,12 +642,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
void pushHardwareLayerUpdate(HardwareLayer layer) {
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
mAttachInfo.mHardwareRenderer.pushLayerUpdate(layer);
}
}
void flushHardwareLayerUpdates() {
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled() &&
mAttachInfo.mHardwareRenderer.validate()) {

View File

@@ -57,6 +57,7 @@ LOCAL_SRC_FILES:= \
android_view_GraphicBuffer.cpp \
android_view_GLRenderer.cpp \
android_view_GLES20Canvas.cpp \
android_view_HardwareLayer.cpp \
android_view_ThreadedRenderer.cpp \
android_view_MotionEvent.cpp \
android_view_PointerIcon.cpp \

View File

@@ -123,6 +123,7 @@ extern int register_android_view_DisplayList(JNIEnv* env);
extern int register_android_view_GraphicBuffer(JNIEnv* env);
extern int register_android_view_GLES20Canvas(JNIEnv* env);
extern int register_android_view_GLRenderer(JNIEnv* env);
extern int register_android_view_HardwareLayer(JNIEnv* env);
extern int register_android_view_ThreadedRenderer(JNIEnv* env);
extern int register_android_view_Surface(JNIEnv* env);
extern int register_android_view_SurfaceControl(JNIEnv* env);
@@ -1127,6 +1128,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_view_GraphicBuffer),
REG_JNI(register_android_view_GLES20Canvas),
REG_JNI(register_android_view_GLRenderer),
REG_JNI(register_android_view_HardwareLayer),
REG_JNI(register_android_view_ThreadedRenderer),
REG_JNI(register_android_view_Surface),
REG_JNI(register_android_view_SurfaceControl),

View File

@@ -914,147 +914,6 @@ static void android_view_GLES20Canvas_resume(JNIEnv* env, jobject clazz,
renderer->resume();
}
static jint android_view_GLES20Canvas_createLayerRenderer(JNIEnv* env,
jobject clazz, jlong layerPtr) {
Layer* layer = reinterpret_cast<Layer*>(layerPtr);
if (layer) {
OpenGLRenderer* renderer = new LayerRenderer(layer);
renderer->initProperties();
return reinterpret_cast<jint>(renderer);
}
return NULL;
}
static jlong android_view_GLES20Canvas_createTextureLayer(JNIEnv* env, jobject clazz,
jboolean isOpaque, jintArray layerInfo) {
Layer* layer = LayerRenderer::createTextureLayer(isOpaque);
if (layer) {
jint* storage = env->GetIntArrayElements(layerInfo, NULL);
storage[0] = layer->getTexture();
env->ReleaseIntArrayElements(layerInfo, storage, 0);
}
return reinterpret_cast<jlong>(layer);
}
static jlong android_view_GLES20Canvas_createLayer(JNIEnv* env, jobject clazz,
jint width, jint height, jboolean isOpaque, jintArray layerInfo) {
Layer* layer = LayerRenderer::createLayer(width, height, isOpaque);
if (layer) {
jint* storage = env->GetIntArrayElements(layerInfo, NULL);
storage[0] = layer->getWidth();
storage[1] = layer->getHeight();
env->ReleaseIntArrayElements(layerInfo, storage, 0);
}
return reinterpret_cast<jlong>(layer);
}
static jboolean android_view_GLES20Canvas_resizeLayer(JNIEnv* env, jobject clazz,
jlong layerPtr, jint width, jint height, jintArray layerInfo) {
Layer* layer = reinterpret_cast<Layer*>(layerPtr);
if (LayerRenderer::resizeLayer(layer, width, height)) {
jint* storage = env->GetIntArrayElements(layerInfo, NULL);
storage[0] = layer->getWidth();
storage[1] = layer->getHeight();
env->ReleaseIntArrayElements(layerInfo, storage, 0);
return JNI_TRUE;
}
return JNI_FALSE;
}
static void android_view_GLES20Canvas_setLayerPaint(JNIEnv* env, jobject clazz,
jlong layerPtr, jlong paintPtr) {
Layer* layer = reinterpret_cast<Layer*>(layerPtr);
if (layer) {
SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
layer->setPaint(paint);
}
}
static void android_view_GLES20Canvas_setLayerColorFilter(JNIEnv* env, jobject clazz,
jlong layerPtr, jlong colorFilterPtr) {
Layer* layer = reinterpret_cast<Layer*>(layerPtr);
if (layer) {
SkColorFilter* colorFilter = reinterpret_cast<SkColorFilter*>(colorFilterPtr);
layer->setColorFilter(colorFilter);
}
}
static void android_view_GLES20Canvas_setOpaqueLayer(JNIEnv* env, jobject clazz,
jlong layerPtr, jboolean isOpaque) {
Layer* layer = reinterpret_cast<Layer*>(layerPtr);
if (layer) {
layer->setBlend(!isOpaque);
}
}
static void android_view_GLES20Canvas_updateTextureLayer(JNIEnv* env, jobject clazz,
jlong layerPtr, jint width, jint height, jboolean isOpaque, jobject surface) {
float transform[16];
sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface));
if (surfaceTexture->updateTexImage() == NO_ERROR) {
int64_t frameNumber = surfaceTexture->getFrameNumber();
// If the GLConsumer queue is in synchronous mode, need to discard all
// but latest frame, using the frame number to tell when we no longer
// have newer frames to target. Since we can't tell which mode it is in,
// do this unconditionally.
int dropCounter = 0;
while (surfaceTexture->updateTexImage() == NO_ERROR) {
int64_t newFrameNumber = surfaceTexture->getFrameNumber();
if (newFrameNumber == frameNumber) break;
frameNumber = newFrameNumber;
dropCounter++;
}
#if DEBUG_RENDERER
if (dropCounter > 0) {
RENDERER_LOGD("Dropped %d frames on texture layer update", dropCounter);
}
#endif
surfaceTexture->getTransformMatrix(transform);
GLenum renderTarget = surfaceTexture->getCurrentTextureTarget();
Layer* layer = reinterpret_cast<Layer*>(layerPtr);
LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, renderTarget, transform);
}
}
static void android_view_GLES20Canvas_updateRenderLayer(JNIEnv* env, jobject clazz,
jlong layerPtr, jlong rendererPtr, jlong displayListPtr,
jint left, jint top, jint right, jint bottom) {
Layer* layer = reinterpret_cast<Layer*>(layerPtr);
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
layer->updateDeferred(renderer, displayList, left, top, right, bottom);
}
static void android_view_GLES20Canvas_clearLayerTexture(JNIEnv* env, jobject clazz,
jlong layerPtr) {
Layer* layer = reinterpret_cast<Layer*>(layerPtr);
layer->clearTexture();
}
static void android_view_GLES20Canvas_setTextureLayerTransform(JNIEnv* env, jobject clazz,
jlong layerPtr, jlong matrixPtr) {
Layer* layer = reinterpret_cast<Layer*>(layerPtr);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
layer->getTransform().load(*matrix);
}
static void android_view_GLES20Canvas_destroyLayer(JNIEnv* env, jobject clazz, jlong layerPtr) {
Layer* layer = reinterpret_cast<Layer*>(layerPtr);
LayerRenderer::destroyLayer(layer);
}
static void android_view_GLES20Canvas_destroyLayerDeferred(JNIEnv* env,
jobject clazz, jlong layerPtr) {
Layer* layer = reinterpret_cast<Layer*>(layerPtr);
LayerRenderer::destroyLayerDeferred(layer);
}
static void android_view_GLES20Canvas_drawLayer(JNIEnv* env, jobject clazz,
jlong rendererPtr, jlong layerPtr, jfloat x, jfloat y) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
@@ -1247,19 +1106,6 @@ static JNINativeMethod gMethods[] = {
{ "nInterrupt", "(J)V", (void*) android_view_GLES20Canvas_interrupt },
{ "nResume", "(J)V", (void*) android_view_GLES20Canvas_resume },
{ "nCreateLayerRenderer", "(J)J", (void*) android_view_GLES20Canvas_createLayerRenderer },
{ "nCreateLayer", "(IIZ[I)J", (void*) android_view_GLES20Canvas_createLayer },
{ "nResizeLayer", "(JII[I)Z" , (void*) android_view_GLES20Canvas_resizeLayer },
{ "nSetLayerPaint", "(JJ)V", (void*) android_view_GLES20Canvas_setLayerPaint },
{ "nSetLayerColorFilter", "(JJ)V", (void*) android_view_GLES20Canvas_setLayerColorFilter },
{ "nSetOpaqueLayer", "(JZ)V", (void*) android_view_GLES20Canvas_setOpaqueLayer },
{ "nCreateTextureLayer", "(Z[I)J", (void*) android_view_GLES20Canvas_createTextureLayer },
{ "nUpdateTextureLayer", "(JIIZLandroid/graphics/SurfaceTexture;)V",
(void*) android_view_GLES20Canvas_updateTextureLayer },
{ "nUpdateRenderLayer", "(JJJIIII)V", (void*) android_view_GLES20Canvas_updateRenderLayer },
{ "nClearLayerTexture", "(J)V", (void*) android_view_GLES20Canvas_clearLayerTexture },
{ "nDestroyLayer", "(J)V", (void*) android_view_GLES20Canvas_destroyLayer },
{ "nDestroyLayerDeferred", "(J)V", (void*) android_view_GLES20Canvas_destroyLayerDeferred },
{ "nDrawLayer", "(JJFF)V", (void*) android_view_GLES20Canvas_drawLayer },
{ "nCopyLayer", "(JJ)Z", (void*) android_view_GLES20Canvas_copyLayer },
{ "nClearLayerUpdates", "(J)V", (void*) android_view_GLES20Canvas_clearLayerUpdates },
@@ -1267,8 +1113,6 @@ static JNINativeMethod gMethods[] = {
{ "nPushLayerUpdate", "(JJ)V", (void*) android_view_GLES20Canvas_pushLayerUpdate },
{ "nCancelLayerUpdate", "(JJ)V", (void*) android_view_GLES20Canvas_cancelLayerUpdate },
{ "nSetTextureLayerTransform", "(JJ)V", (void*) android_view_GLES20Canvas_setTextureLayerTransform },
{ "nGetMaximumTextureWidth", "()I", (void*) android_view_GLES20Canvas_getMaxTextureWidth },
{ "nGetMaximumTextureHeight", "()I", (void*) android_view_GLES20Canvas_getMaxTextureHeight },

View File

@@ -0,0 +1,183 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "OpenGLRenderer"
#include "jni.h"
#include "GraphicsJNI.h"
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_graphics_SurfaceTexture.h>
#include <gui/GLConsumer.h>
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkMatrix.h>
#include <SkPaint.h>
#include <SkXfermode.h>
#include <DeferredLayerUpdater.h>
#include <DisplayList.h>
#include <LayerRenderer.h>
#include <SkiaShader.h>
#include <Rect.h>
namespace android {
using namespace uirenderer;
#ifdef USE_OPENGL_RENDERER
static jlong android_view_HardwareLayer_createTextureLayer(JNIEnv* env, jobject clazz) {
Layer* layer = LayerRenderer::createTextureLayer();
if (!layer) return 0;
return reinterpret_cast<jlong>( new DeferredLayerUpdater(layer) );
}
static jlong android_view_HardwareLayer_createRenderLayer(JNIEnv* env, jobject clazz,
jint width, jint height) {
Layer* layer = LayerRenderer::createRenderLayer(width, height);
if (!layer) return 0;
OpenGLRenderer* renderer = new LayerRenderer(layer);
renderer->initProperties();
return reinterpret_cast<jlong>( new DeferredLayerUpdater(layer, renderer) );
}
static void android_view_HardwareLayer_onTextureDestroyed(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
layer->backingLayer()->clearTexture();
}
static jlong android_view_HardwareLayer_detachBackingLayer(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
return reinterpret_cast<jlong>( layer->detachBackingLayer() );
}
static void android_view_HardwareLayer_destroyLayerUpdater(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
delete layer;
}
static jboolean android_view_HardwareLayer_prepare(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr, jint width, jint height, jboolean isOpaque) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
bool changed = false;
changed |= layer->setSize(width, height);
changed |= layer->setBlend(!isOpaque);
return changed;
}
static void android_view_HardwareLayer_setLayerPaint(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr, jlong paintPtr, jlong colorFilterPtr) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
if (layer) {
SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
SkColorFilter* colorFilter = reinterpret_cast<SkColorFilter*>(colorFilterPtr);
layer->setPaint(paint);
layer->setColorFilter(colorFilter);
}
}
static void android_view_HardwareLayer_setTransform(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr, jlong matrixPtr) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
layer->setTransform(matrix);
}
static void android_view_HardwareLayer_setSurfaceTexture(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr, jobject surface, jboolean isAlreadyAttached) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface));
layer->setSurfaceTexture(surfaceTexture, !isAlreadyAttached);
}
static void android_view_HardwareLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
layer->updateTexImage();
}
static void android_view_HardwareLayer_updateRenderLayer(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr, jlong displayListPtr,
jint left, jint top, jint right, jint bottom) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
layer->setDisplayList(displayList, left, top, right, bottom);
}
static jboolean android_view_HardwareLayer_flushChanges(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
return layer->apply();
}
static jlong android_view_HardwareLayer_getLayer(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
return reinterpret_cast<jlong>( layer->backingLayer() );
}
static jint android_view_HardwareLayer_getTexName(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
return layer->backingLayer()->getTexture();
}
#endif // USE_OPENGL_RENDERER
// ----------------------------------------------------------------------------
// JNI Glue
// ----------------------------------------------------------------------------
const char* const kClassPathName = "android/view/HardwareLayer";
static JNINativeMethod gMethods[] = {
#ifdef USE_OPENGL_RENDERER
{ "nCreateTextureLayer", "()J", (void*) android_view_HardwareLayer_createTextureLayer },
{ "nCreateRenderLayer", "(II)J", (void*) android_view_HardwareLayer_createRenderLayer },
{ "nOnTextureDestroyed", "(J)V", (void*) android_view_HardwareLayer_onTextureDestroyed },
{ "nDetachBackingLayer", "(J)J", (void*) android_view_HardwareLayer_detachBackingLayer },
{ "nDestroyLayerUpdater", "(J)V", (void*) android_view_HardwareLayer_destroyLayerUpdater },
{ "nPrepare", "(JIIZ)Z", (void*) android_view_HardwareLayer_prepare },
{ "nSetLayerPaint", "(JJJ)V", (void*) android_view_HardwareLayer_setLayerPaint },
{ "nSetTransform", "(JJ)V", (void*) android_view_HardwareLayer_setTransform },
{ "nSetSurfaceTexture", "(JLandroid/graphics/SurfaceTexture;Z)V",
(void*) android_view_HardwareLayer_setSurfaceTexture },
{ "nUpdateSurfaceTexture", "(J)V", (void*) android_view_HardwareLayer_updateSurfaceTexture },
{ "nUpdateRenderLayer", "(JJIIII)V", (void*) android_view_HardwareLayer_updateRenderLayer },
{ "nFlushChanges", "(J)Z", (void*) android_view_HardwareLayer_flushChanges },
{ "nGetLayer", "(J)J", (void*) android_view_HardwareLayer_getLayer },
{ "nGetTexName", "(J)I", (void*) android_view_HardwareLayer_getTexName },
#endif
};
int register_android_view_HardwareLayer(JNIEnv* env) {
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
}
};

View File

@@ -17,6 +17,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
Caches.cpp \
DisplayList.cpp \
DeferredDisplayList.cpp \
DeferredLayerUpdater.cpp \
DisplayListLogBuffer.cpp \
DisplayListRenderer.cpp \
Dither.cpp \
@@ -70,7 +71,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui
LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui libgui
LOCAL_MODULE := libhwui
LOCAL_MODULE_TAGS := optional

View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "DeferredLayerUpdater.h"
#include "OpenGLRenderer.h"
#include "LayerRenderer.h"
namespace android {
namespace uirenderer {
DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, OpenGLRenderer* renderer)
: mDisplayList(0)
, mSurfaceTexture(0)
, mTransform(0)
, mNeedsGLContextAttach(false)
, mUpdateTexImage(false)
, mLayer(layer)
, mRenderer(renderer)
, mCaches(Caches::getInstance()) {
mCaches.resourceCache.incrementRefcount(mLayer);
mWidth = mLayer->layer.getWidth();
mHeight = mLayer->layer.getHeight();
mBlend = mLayer->isBlend();
mColorFilter = mLayer->getColorFilter();
mAlpha = mLayer->getAlpha();
mMode = mLayer->getMode();
mDirtyRect.setEmpty();
}
DeferredLayerUpdater::~DeferredLayerUpdater() {
setColorFilter(NULL);
if (mLayer) {
mCaches.resourceCache.decrementRefcount(mLayer);
}
delete mRenderer;
}
void DeferredLayerUpdater::setColorFilter(SkColorFilter* colorFilter) {
SkRefCnt_SafeAssign(mColorFilter, colorFilter);
}
void DeferredLayerUpdater::setDisplayList(DisplayList* displayList,
int left, int top, int right, int bottom) {
mDisplayList = displayList;
if (mDirtyRect.isEmpty()) {
mDirtyRect.set(left, top, right, bottom);
} else {
mDirtyRect.unionWith(Rect(left, top, right, bottom));
}
}
bool DeferredLayerUpdater::apply() {
bool success = true;
// These properties are applied the same to both layer types
mLayer->setColorFilter(mColorFilter);
mLayer->setAlpha(mAlpha, mMode);
if (mDisplayList) {
if (mWidth != mLayer->layer.getWidth() || mHeight != mLayer->layer.getHeight()) {
success = LayerRenderer::resizeLayer(mLayer, mWidth, mHeight);
}
mLayer->setBlend(mBlend);
mLayer->updateDeferred(mRenderer, mDisplayList,
mDirtyRect.left, mDirtyRect.top, mDirtyRect.right, mDirtyRect.bottom);
mDirtyRect.setEmpty();
mDisplayList = 0;
} else if (mSurfaceTexture.get()) {
if (mNeedsGLContextAttach) {
mNeedsGLContextAttach = false;
mSurfaceTexture->attachToContext(mLayer->getTexture());
}
if (mUpdateTexImage) {
mUpdateTexImage = false;
doUpdateTexImage();
}
if (mTransform) {
mLayer->getTransform().load(*mTransform);
setTransform(0);
}
}
return success;
}
void DeferredLayerUpdater::doUpdateTexImage() {
if (mSurfaceTexture->updateTexImage() == NO_ERROR) {
float transform[16];
int64_t frameNumber = mSurfaceTexture->getFrameNumber();
// If the GLConsumer queue is in synchronous mode, need to discard all
// but latest frame, using the frame number to tell when we no longer
// have newer frames to target. Since we can't tell which mode it is in,
// do this unconditionally.
int dropCounter = 0;
while (mSurfaceTexture->updateTexImage() == NO_ERROR) {
int64_t newFrameNumber = mSurfaceTexture->getFrameNumber();
if (newFrameNumber == frameNumber) break;
frameNumber = newFrameNumber;
dropCounter++;
}
#if DEBUG_RENDERER
if (dropCounter > 0) {
RENDERER_LOGD("Dropped %d frames on texture layer update", dropCounter);
}
#endif
mSurfaceTexture->getTransformMatrix(transform);
GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget();
LayerRenderer::updateTextureLayer(mLayer, mWidth, mHeight, !mBlend,
renderTarget, transform);
}
}
void DeferredLayerUpdater::applyDeferred(DeferredLayerUpdater* deferredApply) {
// Default assignment operator doesn't quite work, and fails due to mCaches anyway
deferredApply->mWidth = mWidth;
deferredApply->mHeight = mHeight;
deferredApply->mBlend = mBlend;
deferredApply->mAlpha = mAlpha;
deferredApply->mMode = mMode;
deferredApply->mDirtyRect.set(mDirtyRect);
deferredApply->mDisplayList = mDisplayList;
deferredApply->mSurfaceTexture = mSurfaceTexture;
deferredApply->mNeedsGLContextAttach = mNeedsGLContextAttach;
deferredApply->mUpdateTexImage = mUpdateTexImage;
deferredApply->setColorFilter(mColorFilter);
deferredApply->setTransform(mTransform);
mDisplayList = 0;
mDirtyRect.setEmpty();
mTransform = 0;
mNeedsGLContextAttach = false;
mUpdateTexImage = false;
}
} /* namespace uirenderer */
} /* namespace android */

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef DEFERREDLAYERUPDATE_H_
#define DEFERREDLAYERUPDATE_H_
#include <cutils/compiler.h>
#include <gui/GLConsumer.h>
#include <SkColorFilter.h>
#include <SkMatrix.h>
#include <utils/StrongPointer.h>
#include "DisplayList.h"
#include "Layer.h"
#include "OpenGLRenderer.h"
#include "Rect.h"
namespace android {
namespace uirenderer {
// Container to hold the properties a layer should be set to at the start
// of a render pass
class DeferredLayerUpdater {
public:
ANDROID_API DeferredLayerUpdater(Layer* layer, OpenGLRenderer* renderer = 0);
ANDROID_API ~DeferredLayerUpdater();
ANDROID_API bool setSize(uint32_t width, uint32_t height) {
if (mWidth != width || mHeight != height) {
mWidth = width;
mHeight = height;
return true;
}
return false;
}
ANDROID_API bool setBlend(bool blend) {
if (blend != mBlend) {
mBlend = blend;
return true;
}
return false;
}
ANDROID_API void setSurfaceTexture(const sp<GLConsumer>& texture, bool needsAttach) {
if (texture.get() != mSurfaceTexture.get()) {
mNeedsGLContextAttach = needsAttach;
mSurfaceTexture = texture;
}
}
ANDROID_API void updateTexImage() {
mUpdateTexImage = true;
}
ANDROID_API void setTransform(const SkMatrix* matrix) {
delete mTransform;
mTransform = matrix ? new SkMatrix(*matrix) : 0;
}
ANDROID_API void setDisplayList(DisplayList* displayList,
int left, int top, int right, int bottom);
ANDROID_API void setPaint(const SkPaint* paint) {
OpenGLRenderer::getAlphaAndModeDirect(paint, &mAlpha, &mMode);
}
ANDROID_API void setColorFilter(SkColorFilter* colorFilter);
ANDROID_API bool apply();
ANDROID_API void applyDeferred(DeferredLayerUpdater* deferredApply);
ANDROID_API Layer* backingLayer() {
return mLayer;
}
ANDROID_API Layer* detachBackingLayer() {
Layer* layer = mLayer;
mLayer = 0;
return layer;
}
private:
// Generic properties
uint32_t mWidth;
uint32_t mHeight;
bool mBlend;
SkColorFilter* mColorFilter;
int mAlpha;
SkXfermode::Mode mMode;
// Layer type specific properties
// displayList and surfaceTexture are mutually exclusive, only 1 may be set
// dirtyRect is only valid if displayList is set
DisplayList* mDisplayList;
Rect mDirtyRect;
sp<GLConsumer> mSurfaceTexture;
SkMatrix* mTransform;
bool mNeedsGLContextAttach;
bool mUpdateTexImage;
Layer* mLayer;
OpenGLRenderer* mRenderer;
Caches& mCaches;
void doUpdateTexImage();
};
} /* namespace uirenderer */
} /* namespace android */
#endif /* DEFERREDLAYERUPDATE_H_ */

View File

@@ -184,7 +184,7 @@ void LayerRenderer::generateMesh() {
// Layers management
///////////////////////////////////////////////////////////////////////////////
Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) {
Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) {
LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height);
Caches& caches = Caches::getInstance();
@@ -221,7 +221,6 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque
layer->texCoords.set(0.0f, height / float(layer->getHeight()),
width / float(layer->getWidth()), 0.0f);
layer->setAlpha(255, SkXfermode::kSrcOver_Mode);
layer->setBlend(!isOpaque);
layer->setColorFilter(NULL);
layer->setDirty(true);
layer->region.clear();
@@ -270,13 +269,12 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) {
return true;
}
Layer* LayerRenderer::createTextureLayer(bool isOpaque) {
Layer* LayerRenderer::createTextureLayer() {
LAYER_RENDERER_LOGD("Creating new texture layer");
Layer* layer = new Layer(0, 0);
layer->setCacheable(false);
layer->setTextureLayer(true);
layer->setBlend(!isOpaque);
layer->setEmpty(true);
layer->setFbo(0);
layer->setAlpha(255, SkXfermode::kSrcOver_Mode);

View File

@@ -52,8 +52,8 @@ public:
virtual status_t clear(float left, float top, float right, float bottom, bool opaque);
virtual void finish();
ANDROID_API static Layer* createTextureLayer(bool isOpaque);
ANDROID_API static Layer* createLayer(uint32_t width, uint32_t height, bool isOpaque = false);
ANDROID_API static Layer* createTextureLayer();
ANDROID_API static Layer* createRenderLayer(uint32_t width, uint32_t height);
ANDROID_API static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height);
ANDROID_API static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
bool isOpaque, GLenum renderTarget, float* transform);

View File

@@ -97,7 +97,7 @@ abstract public class CompareActivity extends Activity {
Log.d(LOG_TAG, "failure to access hardware layer");
return;
}
Method copyInto = hardwareLayer.getClass().getSuperclass()
Method copyInto = hardwareLayer.getClass()
.getDeclaredMethod("copyInto", Bitmap.class);
if (!copyInto.isAccessible())
copyInto.setAccessible(true);

View File

@@ -35,23 +35,24 @@ public class ErrorCalculator {
private static final boolean LOG_TIMING = false;
private static final boolean LOG_CALC = false;
private final RenderScript mRS;
private RenderScript mRS;
private Allocation mIdealPixelsAllocation;
private Allocation mGivenPixelsAllocation;
private Allocation mOutputPixelsAllocation;
private final Allocation mInputRowsAllocation;
private final Allocation mOutputRegionsAllocation;
private Allocation mInputRowsAllocation;
private Allocation mOutputRegionsAllocation;
private final ScriptC_errorCalculator mScript;
private ScriptC_errorCalculator mScript;
private final int[] mOutputRowRegions;
private int[] mOutputRowRegions;
public ErrorCalculator(Context c, Resources resources) {
int width = resources.getDimensionPixelSize(R.dimen.layer_width);
int height = resources.getDimensionPixelSize(R.dimen.layer_height);
mOutputRowRegions = new int[height / REGION_SIZE];
/*
mRS = RenderScript.create(c);
int[] rowIndices = new int[height / REGION_SIZE];
for (int i = 0; i < rowIndices.length; i++)
@@ -67,12 +68,15 @@ public class ErrorCalculator {
mInputRowsAllocation.copyFrom(rowIndices);
mOutputRegionsAllocation = Allocation.createSized(mRS, Element.I32(mRS),
mOutputRowRegions.length, Allocation.USAGE_SCRIPT);
*/
}
private static long startMillis, middleMillis;
public float calcErrorRS(Bitmap ideal, Bitmap given) {
if (true)
return calcError(ideal, given);
if (LOG_TIMING) {
startMillis = System.currentTimeMillis();
}