summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Reck <jreck@google.com>2013-12-16 18:55:22 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2013-12-16 18:55:22 +0000
commit0a11eb10ff08ad7a683cfc7c458b6625b955df2c (patch)
tree6ba077f2a3599dc874b5bb08a38951894735fb9c
parent2ab25f15253e286ba6d5c03b70e72bfc30cd27b7 (diff)
parent3dfe19f72cbd95b201e89376ae1810c5c9229b00 (diff)
downloadframeworks_base-0a11eb10ff08ad7a683cfc7c458b6625b955df2c.zip
frameworks_base-0a11eb10ff08ad7a683cfc7c458b6625b955df2c.tar.gz
frameworks_base-0a11eb10ff08ad7a683cfc7c458b6625b955df2c.tar.bz2
Merge "Add drawGlFunctor support to RenderThread"
-rw-r--r--core/java/android/view/GLRenderer.java70
-rw-r--r--core/java/android/view/HardwareRenderer.java21
-rw-r--r--core/java/android/view/RemoteGLRenderer.java1538
-rw-r--r--core/java/android/view/ThreadedRenderer.java25
4 files changed, 1558 insertions, 96 deletions
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
index 70428bc..3c459b6 100644
--- a/core/java/android/view/GLRenderer.java
+++ b/core/java/android/view/GLRenderer.java
@@ -1124,66 +1124,6 @@ public class GLRenderer extends HardwareRenderer {
}
@Override
- void drawDisplayList(DisplayList displayList, View.AttachInfo attachInfo,
- HardwareDrawCallbacks callbacks, Rect dirty) {
- if (canDraw()) {
- if (!hasDirtyRegions()) {
- dirty = null;
- }
-
- // We are already on the correct thread
- final int surfaceState = checkRenderContextUnsafe();
- if (surfaceState != SURFACE_STATE_ERROR) {
- HardwareCanvas canvas = mCanvas;
-
- if (mProfileEnabled) {
- mProfileLock.lock();
- }
-
- dirty = beginFrame(canvas, dirty, surfaceState);
-
- int saveCount = 0;
- int status = DisplayList.STATUS_DONE;
-
- long start = getSystemTime();
- try {
- status = prepareFrame(dirty);
-
- saveCount = canvas.save();
- callbacks.onHardwarePreDraw(canvas);
-
- status |= drawDisplayList(attachInfo, canvas, displayList, status);
- } catch (Exception e) {
- Log.e(LOG_TAG, "An error has occurred while drawing:", e);
- } finally {
- callbacks.onHardwarePostDraw(canvas);
- canvas.restoreToCount(saveCount);
-
- mDrawDelta = getSystemTime() - start;
-
- if (mDrawDelta > 0) {
- mFrameCount++;
-
- debugOverdraw(attachInfo, dirty, canvas, displayList);
- debugDirtyRegions(dirty, canvas);
- drawProfileData(attachInfo);
- }
- }
-
- onPostDraw();
-
- swapBuffers(status);
-
- if (mProfileEnabled) {
- mProfileLock.unlock();
- }
-
- attachInfo.mIgnoreDirtyState = false;
- }
- }
- }
-
- @Override
void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
Rect dirty) {
if (canDraw()) {
@@ -1539,7 +1479,7 @@ public class GLRenderer extends HardwareRenderer {
return (int) (dp * density + 0.5f);
}
- private static native boolean loadProperties();
+ static native boolean loadProperties();
static native void setupShadersDiskCache(String cacheFile);
@@ -1547,14 +1487,14 @@ public class GLRenderer extends HardwareRenderer {
* Notifies EGL that the frame is about to be rendered.
* @param size
*/
- private static native void beginFrame(int[] size);
+ static native void beginFrame(int[] size);
/**
* Returns the current system time according to the renderer.
* This method is used for debugging only and should not be used
* as a clock.
*/
- private static native long getSystemTime();
+ static native long getSystemTime();
/**
* Preserves the back buffer of the current surface after a buffer swap.
@@ -1565,7 +1505,7 @@ public class GLRenderer extends HardwareRenderer {
* @return True if the swap behavior was successfully changed,
* false otherwise.
*/
- private static native boolean preserveBackBuffer();
+ static native boolean preserveBackBuffer();
/**
* Indicates whether the current surface preserves its back buffer
@@ -1574,7 +1514,7 @@ public class GLRenderer extends HardwareRenderer {
* @return True, if the surface's EGL_SWAP_BEHAVIOR is EGL_BUFFER_PRESERVED,
* false otherwise
*/
- private static native boolean isBackBufferPreserved();
+ static native boolean isBackBufferPreserved();
class DrawPerformanceDataProvider extends GraphDataProvider {
private final int mGraphType;
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 434d473..ac728f1 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -408,18 +408,6 @@ public abstract class HardwareRenderer {
Rect dirty);
/**
- * Temporary hook to draw a display list directly, only used if sUseRenderThread
- * is true.
- *
- * @param displayList The display list to draw
- * @param attachInfo AttachInfo tied to the specified view.
- * @param callbacks Callbacks invoked when drawing happens.
- * @param dirty The dirty rectangle to update, can be null.
- */
- abstract void drawDisplayList(DisplayList displayList, View.AttachInfo attachInfo,
- HardwareDrawCallbacks callbacks, Rect dirty);
-
- /**
* 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.
*
@@ -527,10 +515,11 @@ public abstract class HardwareRenderer {
static HardwareRenderer create(boolean translucent) {
HardwareRenderer renderer = null;
if (GLES20Canvas.isAvailable()) {
- renderer = new GLRenderer(translucent);
- }
- if (renderer != null && sUseRenderThread) {
- renderer = new ThreadedRenderer((GLRenderer)renderer);
+ if (sUseRenderThread) {
+ renderer = new ThreadedRenderer(translucent);
+ } else {
+ renderer = new GLRenderer(translucent);
+ }
}
return renderer;
}
diff --git a/core/java/android/view/RemoteGLRenderer.java b/core/java/android/view/RemoteGLRenderer.java
new file mode 100644
index 0000000..6195fcb
--- /dev/null
+++ b/core/java/android/view/RemoteGLRenderer.java
@@ -0,0 +1,1538 @@
+/*
+ * Copyright (C) 2013 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 static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_BAD_NATIVE_WINDOW;
+import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_CONFIG_CAVEAT;
+import static javax.microedition.khronos.egl.EGL10.EGL_DEFAULT_DISPLAY;
+import static javax.microedition.khronos.egl.EGL10.EGL_DEPTH_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_DRAW;
+import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_HEIGHT;
+import static javax.microedition.khronos.egl.EGL10.EGL_NONE;
+import static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT;
+import static javax.microedition.khronos.egl.EGL10.EGL_NO_DISPLAY;
+import static javax.microedition.khronos.egl.EGL10.EGL_NO_SURFACE;
+import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_RENDERABLE_TYPE;
+import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLES;
+import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLE_BUFFERS;
+import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE;
+import static javax.microedition.khronos.egl.EGL10.EGL_SUCCESS;
+import static javax.microedition.khronos.egl.EGL10.EGL_SURFACE_TYPE;
+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.Paint;
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
+import android.opengl.EGL14;
+import android.opengl.GLUtils;
+import android.opengl.ManagedEGLContext;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Surface.OutOfResourcesException;
+
+import com.google.android.gles_jni.EGLImpl;
+
+import java.io.PrintWriter;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL;
+
+/**
+ * Hardware renderer using OpenGL that's used as the remote endpoint
+ * of ThreadedRenderer
+ *
+ * Currently this is mostly a copy of GLRenderer, but with a few modifications
+ * to deal with the threading issues. Ideally native-side functionality
+ * will replace this, but we need this to bootstrap without risking breaking
+ * changes in GLRenderer
+ *
+ * @hide
+ */
+public class RemoteGLRenderer extends HardwareRenderer {
+ static final int SURFACE_STATE_ERROR = 0;
+ static final int SURFACE_STATE_SUCCESS = 1;
+ static final int SURFACE_STATE_UPDATED = 2;
+
+ static final int FUNCTOR_PROCESS_DELAY = 4;
+
+ /**
+ * Number of frames to profile.
+ */
+ private static final int PROFILE_MAX_FRAMES = 128;
+
+ /**
+ * Number of floats per profiled frame.
+ */
+ private static final int PROFILE_FRAME_DATA_COUNT = 3;
+
+ private static final int PROFILE_DRAW_MARGIN = 0;
+ private static final int PROFILE_DRAW_WIDTH = 3;
+ private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 };
+ private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d;
+ private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d;
+ private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
+ private static final int PROFILE_DRAW_DP_PER_MS = 7;
+
+ private static final String[] VISUALIZERS = {
+ PROFILE_PROPERTY_VISUALIZE_BARS,
+ PROFILE_PROPERTY_VISUALIZE_LINES
+ };
+
+ private static final String[] OVERDRAW = {
+ OVERDRAW_PROPERTY_SHOW,
+ OVERDRAW_PROPERTY_COUNT
+ };
+ private static final int OVERDRAW_TYPE_COUNT = 1;
+ private static final int GL_VERSION = 2;
+
+ static EGL10 sEgl;
+ static EGLDisplay sEglDisplay;
+ static EGLConfig sEglConfig;
+ static final Object[] sEglLock = new Object[0];
+ int mWidth = -1, mHeight = -1;
+
+ static final ThreadLocal<ManagedEGLContext> sEglContextStorage
+ = new ThreadLocal<ManagedEGLContext>();
+
+ EGLContext mEglContext;
+ Thread mEglThread;
+
+ EGLSurface mEglSurface;
+
+ GL mGl;
+ HardwareCanvas mCanvas;
+
+ String mName;
+
+ long mFrameCount;
+ Paint mDebugPaint;
+
+ static boolean sDirtyRegions;
+ static final boolean sDirtyRegionsRequested;
+ static {
+ String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
+ //noinspection PointlessBooleanExpression,ConstantConditions
+ sDirtyRegions = "true".equalsIgnoreCase(dirtyProperty);
+ sDirtyRegionsRequested = sDirtyRegions;
+ }
+
+ boolean mDirtyRegionsEnabled;
+ boolean mUpdateDirtyRegions;
+
+ boolean mProfileEnabled;
+ int mProfileVisualizerType = -1;
+ float[] mProfileData;
+ ReentrantLock mProfileLock;
+ int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
+
+ GraphDataProvider mDebugDataProvider;
+ float[][] mProfileShapes;
+ Paint mProfilePaint;
+
+ boolean mDebugDirtyRegions;
+ int mDebugOverdraw = -1;
+ HardwareLayer mDebugOverdrawLayer;
+ Paint mDebugOverdrawPaint;
+
+ final boolean mTranslucent;
+
+ private boolean mDestroyed;
+
+ private final Rect mRedrawClip = new Rect();
+
+ private final int[] mSurfaceSize = new int[2];
+ private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable();
+
+ private long mDrawDelta = Long.MAX_VALUE;
+
+ private GLES20Canvas mGlCanvas;
+
+ private DisplayMetrics mDisplayMetrics;
+ private ThreadedRenderer mOwningRenderer;
+
+ private static EGLSurface sPbuffer;
+ private static final Object[] sPbufferLock = new Object[0];
+
+ private static class GLRendererEglContext extends ManagedEGLContext {
+ final Handler mHandler = new Handler();
+
+ public GLRendererEglContext(EGLContext context) {
+ super(context);
+ }
+
+ @Override
+ public void onTerminate(final EGLContext eglContext) {
+ // Make sure we do this on the correct thread.
+ if (mHandler.getLooper() != Looper.myLooper()) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onTerminate(eglContext);
+ }
+ });
+ return;
+ }
+
+ synchronized (sEglLock) {
+ if (sEgl == null) return;
+
+ if (EGLImpl.getInitCount(sEglDisplay) == 1) {
+ usePbufferSurface(eglContext);
+ GLES20Canvas.terminateCaches();
+
+ sEgl.eglDestroyContext(sEglDisplay, eglContext);
+ sEglContextStorage.set(null);
+ sEglContextStorage.remove();
+
+ sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
+ sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+ sEgl.eglReleaseThread();
+ sEgl.eglTerminate(sEglDisplay);
+
+ sEgl = null;
+ sEglDisplay = null;
+ sEglConfig = null;
+ sPbuffer = null;
+ }
+ }
+ }
+ }
+
+ HardwareCanvas createCanvas() {
+ return mGlCanvas = new GLES20Canvas(mTranslucent);
+ }
+
+ ManagedEGLContext createManagedContext(EGLContext eglContext) {
+ return new GLRendererEglContext(mEglContext);
+ }
+
+ int[] getConfig(boolean dirtyRegions) {
+ //noinspection PointlessBooleanExpression,ConstantConditions
+ final int stencilSize = GLES20Canvas.getStencilSize();
+ final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+
+ return new int[] {
+ EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_DEPTH_SIZE, 0,
+ EGL_CONFIG_CAVEAT, EGL_NONE,
+ EGL_STENCIL_SIZE, stencilSize,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
+ EGL_NONE
+ };
+ }
+
+ void initCaches() {
+ if (GLES20Canvas.initCaches()) {
+ // Caches were (re)initialized, rebind atlas
+ initAtlas();
+ }
+ }
+
+ void initAtlas() {
+ IBinder binder = ServiceManager.getService("assetatlas");
+ if (binder == null) return;
+
+ IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
+ try {
+ if (atlas.isCompatible(android.os.Process.myPpid())) {
+ GraphicBuffer buffer = atlas.getBuffer();
+ if (buffer != null) {
+ int[] map = atlas.getMap();
+ if (map != null) {
+ GLES20Canvas.initAtlas(buffer, map);
+ }
+ // If IAssetAtlas is not the same class as the IBinder
+ // we are using a remote service and we can safely
+ // destroy the graphic buffer
+ if (atlas.getClass() != binder.getClass()) {
+ buffer.destroy();
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Could not acquire atlas", e);
+ }
+ }
+
+ boolean canDraw() {
+ return mGl != null && mCanvas != null && mGlCanvas != null;
+ }
+
+ int onPreDraw(Rect dirty) {
+ return mGlCanvas.onPreDraw(dirty);
+ }
+
+ void onPostDraw() {
+ mGlCanvas.onPostDraw();
+ }
+
+ void drawProfileData(View.AttachInfo attachInfo) {
+ if (mDebugDataProvider != null) {
+ final GraphDataProvider provider = mDebugDataProvider;
+ initProfileDrawData(attachInfo, provider);
+
+ final int height = provider.getVerticalUnitSize();
+ final int margin = provider.getHorizontaUnitMargin();
+ final int width = provider.getHorizontalUnitSize();
+
+ int x = 0;
+ int count = 0;
+ int current = 0;
+
+ final float[] data = provider.getData();
+ final int elementCount = provider.getElementCount();
+ final int graphType = provider.getGraphType();
+
+ int totalCount = provider.getFrameCount() * elementCount;
+ if (graphType == GraphDataProvider.GRAPH_TYPE_LINES) {
+ totalCount -= elementCount;
+ }
+
+ for (int i = 0; i < totalCount; i += elementCount) {
+ if (data[i] < 0.0f) break;
+
+ int index = count * 4;
+ if (i == provider.getCurrentFrame() * elementCount) current = index;
+
+ x += margin;
+ int x2 = x + width;
+
+ int y2 = mHeight;
+ int y1 = (int) (y2 - data[i] * height);
+
+ switch (graphType) {
+ case GraphDataProvider.GRAPH_TYPE_BARS: {
+ for (int j = 0; j < elementCount; j++) {
+ //noinspection MismatchedReadAndWriteOfArray
+ final float[] r = mProfileShapes[j];
+ r[index] = x;
+ r[index + 1] = y1;
+ r[index + 2] = x2;
+ r[index + 3] = y2;
+
+ y2 = y1;
+ if (j < elementCount - 1) {
+ y1 = (int) (y2 - data[i + j + 1] * height);
+ }
+ }
+ } break;
+ case GraphDataProvider.GRAPH_TYPE_LINES: {
+ for (int j = 0; j < elementCount; j++) {
+ //noinspection MismatchedReadAndWriteOfArray
+ final float[] r = mProfileShapes[j];
+ r[index] = (x + x2) * 0.5f;
+ r[index + 1] = index == 0 ? y1 : r[index - 1];
+ r[index + 2] = r[index] + width;
+ r[index + 3] = y1;
+
+ y2 = y1;
+ if (j < elementCount - 1) {
+ y1 = (int) (y2 - data[i + j + 1] * height);
+ }
+ }
+ } break;
+ }
+
+
+ x += width;
+ count++;
+ }
+
+ x += margin;
+
+ drawGraph(graphType, count);
+ drawCurrentFrame(graphType, current);
+ drawThreshold(x, height);
+ }
+ }
+
+ private void drawGraph(int graphType, int count) {
+ for (int i = 0; i < mProfileShapes.length; i++) {
+ mDebugDataProvider.setupGraphPaint(mProfilePaint, i);
+ switch (graphType) {
+ case GraphDataProvider.GRAPH_TYPE_BARS:
+ mGlCanvas.drawRects(mProfileShapes[i], count * 4, mProfilePaint);
+ break;
+ case GraphDataProvider.GRAPH_TYPE_LINES:
+ mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint);
+ break;
+ }
+ }
+ }
+
+ private void drawCurrentFrame(int graphType, int index) {
+ if (index >= 0) {
+ mDebugDataProvider.setupCurrentFramePaint(mProfilePaint);
+ switch (graphType) {
+ case GraphDataProvider.GRAPH_TYPE_BARS:
+ mGlCanvas.drawRect(mProfileShapes[2][index], mProfileShapes[2][index + 1],
+ mProfileShapes[2][index + 2], mProfileShapes[0][index + 3],
+ mProfilePaint);
+ break;
+ case GraphDataProvider.GRAPH_TYPE_LINES:
+ mGlCanvas.drawLine(mProfileShapes[2][index], mProfileShapes[2][index + 1],
+ mProfileShapes[2][index], mHeight, mProfilePaint);
+ break;
+ }
+ }
+ }
+
+ private void drawThreshold(int x, int height) {
+ float threshold = mDebugDataProvider.getThreshold();
+ if (threshold > 0.0f) {
+ mDebugDataProvider.setupThresholdPaint(mProfilePaint);
+ int y = (int) (mHeight - threshold * height);
+ mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint);
+ }
+ }
+
+ private void initProfileDrawData(View.AttachInfo attachInfo, GraphDataProvider provider) {
+ if (mProfileShapes == null) {
+ final int elementCount = provider.getElementCount();
+ final int frameCount = provider.getFrameCount();
+
+ mProfileShapes = new float[elementCount][];
+ for (int i = 0; i < elementCount; i++) {
+ mProfileShapes[i] = new float[frameCount * 4];
+ }
+
+ mProfilePaint = new Paint();
+ }
+
+ mProfilePaint.reset();
+ if (provider.getGraphType() == GraphDataProvider.GRAPH_TYPE_LINES) {
+ mProfilePaint.setAntiAlias(true);
+ }
+
+ if (mDisplayMetrics == null) {
+ mDisplayMetrics = new DisplayMetrics();
+ }
+
+ attachInfo.mDisplay.getMetrics(mDisplayMetrics);
+ provider.prepare(mDisplayMetrics);
+ }
+
+ @Override
+ void destroy(boolean full) {
+ try {
+ if (full && mCanvas != null) {
+ mCanvas = null;
+ }
+
+ if (!isEnabled() || mDestroyed) {
+ setEnabled(false);
+ return;
+ }
+
+ destroySurface();
+ setEnabled(false);
+
+ mDestroyed = true;
+ mGl = null;
+ } finally {
+ if (full && mGlCanvas != null) {
+ mGlCanvas = null;
+ }
+ }
+ }
+
+ @Override
+ void pushLayerUpdate(HardwareLayer layer) {
+ 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);
+ }
+
+ @Override
+ public HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
+ return new GLES20RenderLayer(width, height, isOpaque);
+ }
+
+ void countOverdraw(HardwareCanvas canvas) {
+ ((GLES20Canvas) canvas).setCountOverdrawEnabled(true);
+ }
+
+ float getOverdraw(HardwareCanvas canvas) {
+ return ((GLES20Canvas) canvas).getOverdraw();
+ }
+
+ @Override
+ public SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
+ return ((GLES20TextureLayer) layer).getSurfaceTexture();
+ }
+
+ @Override
+ void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) {
+ ((GLES20TextureLayer) layer).setSurfaceTexture(surfaceTexture);
+ }
+
+ @Override
+ boolean safelyRun(Runnable action) {
+ boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR;
+
+ if (needsContext) {
+ GLRendererEglContext managedContext =
+ (GLRendererEglContext) sEglContextStorage.get();
+ if (managedContext == null) return false;
+ usePbufferSurface(managedContext.getContext());
+ }
+
+ try {
+ action.run();
+ } finally {
+ if (needsContext) {
+ sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ void destroyLayers(final View view) {
+ if (view != null) {
+ safelyRun(new Runnable() {
+ @Override
+ public void run() {
+ if (mCanvas != null) {
+ mCanvas.clearLayerUpdates();
+ }
+ destroyHardwareLayer(view);
+ GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
+ }
+ });
+ }
+ }
+
+ private static void destroyHardwareLayer(View view) {
+ view.destroyLayer(true);
+
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+
+ int count = group.getChildCount();
+ for (int i = 0; i < count; i++) {
+ destroyHardwareLayer(group.getChildAt(i));
+ }
+ }
+ }
+
+ @Override
+ void destroyHardwareResources(final View view) {
+ if (view != null) {
+ safelyRun(new Runnable() {
+ @Override
+ public void run() {
+ if (mCanvas != null) {
+ mCanvas.clearLayerUpdates();
+ }
+ destroyResources(view);
+ GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
+ }
+ });
+ }
+ }
+
+ private static void destroyResources(View view) {
+ view.destroyHardwareResources();
+
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+
+ int count = group.getChildCount();
+ for (int i = 0; i < count; i++) {
+ destroyResources(group.getChildAt(i));
+ }
+ }
+ }
+
+ static void startTrimMemory(int level) {
+ if (sEgl == null || sEglConfig == null) return;
+
+ GLRendererEglContext managedContext =
+ (GLRendererEglContext) sEglContextStorage.get();
+ // We do not have OpenGL objects
+ if (managedContext == null) {
+ return;
+ } else {
+ usePbufferSurface(managedContext.getContext());
+ }
+
+ if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
+ GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
+ } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
+ GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
+ }
+ }
+
+ static void endTrimMemory() {
+ if (sEgl != null && sEglDisplay != null) {
+ sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ }
+ }
+
+ private static void usePbufferSurface(EGLContext eglContext) {
+ synchronized (sPbufferLock) {
+ // Create a temporary 1x1 pbuffer so we have a context
+ // to clear our OpenGL objects
+ if (sPbuffer == null) {
+ sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
+ EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
+ });
+ }
+ }
+ sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
+ }
+
+ RemoteGLRenderer(ThreadedRenderer owningRenderer, boolean translucent) {
+ mOwningRenderer = owningRenderer;
+ mTranslucent = translucent;
+
+ loadSystemProperties();
+ }
+
+ @Override
+ boolean loadSystemProperties() {
+ boolean value;
+ boolean changed = false;
+
+ String profiling = SystemProperties.get(PROFILE_PROPERTY);
+ int graphType = search(VISUALIZERS, profiling);
+ value = graphType >= 0;
+
+ if (graphType != mProfileVisualizerType) {
+ changed = true;
+ mProfileVisualizerType = graphType;
+
+ mProfileShapes = null;
+ mProfilePaint = null;
+
+ if (value) {
+ mDebugDataProvider = new DrawPerformanceDataProvider(graphType);
+ } else {
+ mDebugDataProvider = null;
+ }
+ }
+
+ // If on-screen profiling is not enabled, we need to check whether
+ // console profiling only is enabled
+ if (!value) {
+ value = Boolean.parseBoolean(profiling);
+ }
+
+ if (value != mProfileEnabled) {
+ changed = true;
+ mProfileEnabled = value;
+
+ if (mProfileEnabled) {
+ Log.d(LOG_TAG, "Profiling hardware renderer");
+
+ int maxProfileFrames = SystemProperties.getInt(PROFILE_MAXFRAMES_PROPERTY,
+ PROFILE_MAX_FRAMES);
+ mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
+ for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
+ mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
+ }
+
+ mProfileLock = new ReentrantLock();
+ } else {
+ mProfileData = null;
+ mProfileLock = null;
+ mProfileVisualizerType = -1;
+ }
+
+ mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
+ }
+
+ value = SystemProperties.getBoolean(DEBUG_DIRTY_REGIONS_PROPERTY, false);
+ if (value != mDebugDirtyRegions) {
+ changed = true;
+ mDebugDirtyRegions = value;
+
+ if (mDebugDirtyRegions) {
+ Log.d(LOG_TAG, "Debugging dirty regions");
+ }
+ }
+
+ String overdraw = SystemProperties.get(HardwareRenderer.DEBUG_OVERDRAW_PROPERTY);
+ int debugOverdraw = search(OVERDRAW, overdraw);
+ if (debugOverdraw != mDebugOverdraw) {
+ changed = true;
+ mDebugOverdraw = debugOverdraw;
+
+ if (mDebugOverdraw != OVERDRAW_TYPE_COUNT) {
+ if (mDebugOverdrawLayer != null) {
+ mDebugOverdrawLayer.destroy();
+ mDebugOverdrawLayer = null;
+ mDebugOverdrawPaint = null;
+ }
+ }
+ }
+
+ if (GLRenderer.loadProperties()) {
+ changed = true;
+ }
+
+ return changed;
+ }
+
+ private static int search(String[] values, String value) {
+ for (int i = 0; i < values.length; i++) {
+ if (values[i].equals(value)) return i;
+ }
+ return -1;
+ }
+
+ @Override
+ void dumpGfxInfo(PrintWriter pw) {
+ if (mProfileEnabled) {
+ pw.printf("\n\tDraw\tProcess\tExecute\n");
+
+ mProfileLock.lock();
+ try {
+ for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
+ if (mProfileData[i] < 0) {
+ break;
+ }
+ pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
+ mProfileData[i + 2]);
+ mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
+ }
+ mProfileCurrentFrame = mProfileData.length;
+ } finally {
+ mProfileLock.unlock();
+ }
+ }
+ }
+
+ @Override
+ long getFrameCount() {
+ return mFrameCount;
+ }
+
+ /**
+ * Indicates whether this renderer instance can track and update dirty regions.
+ */
+ boolean hasDirtyRegions() {
+ return mDirtyRegionsEnabled;
+ }
+
+ /**
+ * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
+ * is invoked and the requested flag is turned off. The error code is
+ * also logged as a warning.
+ */
+ void checkEglErrors() {
+ if (isEnabled()) {
+ checkEglErrorsForced();
+ }
+ }
+
+ private void checkEglErrorsForced() {
+ int error = sEgl.eglGetError();
+ if (error != EGL_SUCCESS) {
+ // something bad has happened revert to
+ // normal rendering.
+ Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error));
+ fallback(error != EGL11.EGL_CONTEXT_LOST);
+ }
+ }
+
+ private void fallback(boolean fallback) {
+ destroy(true);
+ if (fallback) {
+ // we'll try again if it was context lost
+ setRequested(false);
+ Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
+ + "Switching back to software rendering.");
+ }
+ }
+
+ @Override
+ boolean initialize(Surface surface) throws OutOfResourcesException {
+ if (isRequested() && !isEnabled()) {
+ boolean contextCreated = initializeEgl();
+ mGl = createEglSurface(surface);
+ mDestroyed = false;
+
+ if (mGl != null) {
+ int err = sEgl.eglGetError();
+ if (err != EGL_SUCCESS) {
+ destroy(true);
+ setRequested(false);
+ } else {
+ if (mCanvas == null) {
+ mCanvas = createCanvas();
+ mCanvas.setName(mName);
+ }
+ setEnabled(true);
+
+ if (contextCreated) {
+ initAtlas();
+ }
+ }
+
+ return mCanvas != null;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ void updateSurface(Surface surface) throws OutOfResourcesException {
+ if (isRequested() && isEnabled()) {
+ createEglSurface(surface);
+ }
+ }
+
+ boolean initializeEgl() {
+ synchronized (sEglLock) {
+ if (sEgl == null && sEglConfig == null) {
+ sEgl = (EGL10) EGLContext.getEGL();
+
+ // Get to the default display.
+ sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+ if (sEglDisplay == EGL_NO_DISPLAY) {
+ throw new RuntimeException("eglGetDisplay failed "
+ + GLUtils.getEGLErrorString(sEgl.eglGetError()));
+ }
+
+ // We can now initialize EGL for that display
+ int[] version = new int[2];
+ if (!sEgl.eglInitialize(sEglDisplay, version)) {
+ throw new RuntimeException("eglInitialize failed " +
+ GLUtils.getEGLErrorString(sEgl.eglGetError()));
+ }
+
+ checkEglErrorsForced();
+
+ sEglConfig = loadEglConfig();
+ }
+ }
+
+ ManagedEGLContext managedContext = sEglContextStorage.get();
+ mEglContext = managedContext != null ? managedContext.getContext() : null;
+ mEglThread = Thread.currentThread();
+
+ if (mEglContext == null) {
+ mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
+ sEglContextStorage.set(createManagedContext(mEglContext));
+ return true;
+ }
+
+ return false;
+ }
+
+ private EGLConfig loadEglConfig() {
+ EGLConfig eglConfig = chooseEglConfig();
+ if (eglConfig == null) {
+ // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
+ if (sDirtyRegions) {
+ sDirtyRegions = false;
+ eglConfig = chooseEglConfig();
+ if (eglConfig == null) {
+ throw new RuntimeException("eglConfig not initialized");
+ }
+ } else {
+ throw new RuntimeException("eglConfig not initialized");
+ }
+ }
+ return eglConfig;
+ }
+
+ private EGLConfig chooseEglConfig() {
+ EGLConfig[] configs = new EGLConfig[1];
+ int[] configsCount = new int[1];
+ int[] configSpec = getConfig(sDirtyRegions);
+
+ // Debug
+ final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, "");
+ if ("all".equalsIgnoreCase(debug)) {
+ sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount);
+
+ EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]];
+ sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs,
+ configsCount[0], configsCount);
+
+ for (EGLConfig config : debugConfigs) {
+ printConfig(config);
+ }
+ }
+
+ if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
+ throw new IllegalArgumentException("eglChooseConfig failed " +
+ GLUtils.getEGLErrorString(sEgl.eglGetError()));
+ } else if (configsCount[0] > 0) {
+ if ("choice".equalsIgnoreCase(debug)) {
+ printConfig(configs[0]);
+ }
+ return configs[0];
+ }
+
+ return null;
+ }
+
+ private static void printConfig(EGLConfig config) {
+ int[] value = new int[1];
+
+ Log.d(LOG_TAG, "EGL configuration " + config + ":");
+
+ sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value);
+ Log.d(LOG_TAG, " RED_SIZE = " + value[0]);
+
+ sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value);
+ Log.d(LOG_TAG, " GREEN_SIZE = " + value[0]);
+
+ sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value);
+ Log.d(LOG_TAG, " BLUE_SIZE = " + value[0]);
+
+ sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value);
+ Log.d(LOG_TAG, " ALPHA_SIZE = " + value[0]);
+
+ sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value);
+ Log.d(LOG_TAG, " DEPTH_SIZE = " + value[0]);
+
+ sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value);
+ Log.d(LOG_TAG, " STENCIL_SIZE = " + value[0]);
+
+ sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLE_BUFFERS, value);
+ Log.d(LOG_TAG, " SAMPLE_BUFFERS = " + value[0]);
+
+ sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLES, value);
+ Log.d(LOG_TAG, " SAMPLES = " + value[0]);
+
+ sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value);
+ Log.d(LOG_TAG, " SURFACE_TYPE = 0x" + Integer.toHexString(value[0]));
+
+ sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_CONFIG_CAVEAT, value);
+ Log.d(LOG_TAG, " CONFIG_CAVEAT = 0x" + Integer.toHexString(value[0]));
+ }
+
+ GL createEglSurface(Surface surface) throws OutOfResourcesException {
+ // Check preconditions.
+ if (sEgl == null) {
+ throw new RuntimeException("egl not initialized");
+ }
+ if (sEglDisplay == null) {
+ throw new RuntimeException("eglDisplay not initialized");
+ }
+ if (sEglConfig == null) {
+ throw new RuntimeException("eglConfig not initialized");
+ }
+ if (Thread.currentThread() != mEglThread) {
+ throw new IllegalStateException("HardwareRenderer cannot be used "
+ + "from multiple threads");
+ }
+
+ // In case we need to destroy an existing surface
+ destroySurface();
+
+ // Create an EGL surface we can render into.
+ if (!createSurface(surface)) {
+ return null;
+ }
+
+ initCaches();
+
+ return mEglContext.getGL();
+ }
+
+ private void enableDirtyRegions() {
+ // If mDirtyRegions is set, this means we have an EGL configuration
+ // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
+ if (sDirtyRegions) {
+ if (!(mDirtyRegionsEnabled = GLRenderer.preserveBackBuffer())) {
+ Log.w(LOG_TAG, "Backbuffer cannot be preserved");
+ }
+ } else if (sDirtyRegionsRequested) {
+ // If mDirtyRegions is not set, our EGL configuration does not
+ // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default
+ // swap behavior might be EGL_BUFFER_PRESERVED, which means we
+ // want to set mDirtyRegions. We try to do this only if dirty
+ // regions were initially requested as part of the device
+ // configuration (see RENDER_DIRTY_REGIONS)
+ mDirtyRegionsEnabled = GLRenderer.isBackBufferPreserved();
+ }
+ }
+
+ EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
+ final int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, GL_VERSION, EGL_NONE };
+
+ EGLContext context = egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT,
+ attribs);
+ if (context == null || context == EGL_NO_CONTEXT) {
+ //noinspection ConstantConditions
+ throw new IllegalStateException(
+ "Could not create an EGL context. eglCreateContext failed with error: " +
+ GLUtils.getEGLErrorString(sEgl.eglGetError()));
+ }
+
+ return context;
+ }
+
+ void destroySurface() {
+ if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
+ if (mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) {
+ sEgl.eglMakeCurrent(sEglDisplay,
+ EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ }
+ sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
+ mEglSurface = null;
+ }
+ }
+
+ @Override
+ void invalidate(Surface surface) {
+ // Cancels any existing buffer to ensure we'll get a buffer
+ // of the right size before we call eglSwapBuffers
+ sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+
+ if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
+ sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
+ mEglSurface = null;
+ setEnabled(false);
+ }
+
+ if (surface.isValid()) {
+ if (!createSurface(surface)) {
+ return;
+ }
+
+ mUpdateDirtyRegions = true;
+
+ if (mCanvas != null) {
+ setEnabled(true);
+ }
+ }
+ }
+
+ private boolean createSurface(Surface surface) {
+ mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, surface, null);
+
+ if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
+ int error = sEgl.eglGetError();
+ if (error == EGL_BAD_NATIVE_WINDOW) {
+ Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
+ return false;
+ }
+ throw new RuntimeException("createWindowSurface failed "
+ + GLUtils.getEGLErrorString(error));
+ }
+
+ if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ throw new IllegalStateException("eglMakeCurrent failed " +
+ GLUtils.getEGLErrorString(sEgl.eglGetError()));
+ }
+
+ enableDirtyRegions();
+
+ return true;
+ }
+
+ @Override
+ boolean validate() {
+ return checkRenderContext() != SURFACE_STATE_ERROR;
+ }
+
+ @Override
+ void setup(int width, int height) {
+ if (validate()) {
+ mCanvas.setViewport(width, height);
+ mWidth = width;
+ mHeight = height;
+ }
+ }
+
+ @Override
+ int getWidth() {
+ return mWidth;
+ }
+
+ @Override
+ int getHeight() {
+ return mHeight;
+ }
+
+ @Override
+ void setName(String name) {
+ mName = name;
+ }
+
+ // TODO: Ping pong is fun and all, but this isn't the time or place
+ // However we don't yet have the ability for the RenderThread to run
+ // independently nor have a way to postDelayed, so this will work for now
+ private Runnable mDispatchFunctorsRunnable = new Runnable() {
+ @Override
+ public void run() {
+ ThreadedRenderer.postToRenderThread(mFunctorsRunnable);
+ }
+ };
+
+ class FunctorsRunnable implements Runnable {
+ View.AttachInfo attachInfo;
+
+ @Override
+ public void run() {
+ final HardwareRenderer renderer = attachInfo.mHardwareRenderer;
+ if (renderer == null || !renderer.isEnabled() || renderer != mOwningRenderer) {
+ return;
+ }
+
+ if (checkRenderContext() != SURFACE_STATE_ERROR) {
+ int status = mCanvas.invokeFunctors(mRedrawClip);
+ handleFunctorStatus(attachInfo, status);
+ }
+ }
+ }
+
+ /**
+ * @param displayList The display list to draw
+ * @param attachInfo AttachInfo tied to the specified view.
+ * @param callbacks Callbacks invoked when drawing happens.
+ * @param dirty The dirty rectangle to update, can be null.
+ */
+ void drawDisplayList(DisplayList displayList, View.AttachInfo attachInfo,
+ HardwareDrawCallbacks callbacks, Rect dirty) {
+ if (canDraw()) {
+ if (!hasDirtyRegions()) {
+ dirty = null;
+ }
+
+ // We are already on the correct thread
+ final int surfaceState = checkRenderContextUnsafe();
+ if (surfaceState != SURFACE_STATE_ERROR) {
+ HardwareCanvas canvas = mCanvas;
+
+ if (mProfileEnabled) {
+ mProfileLock.lock();
+ }
+
+ dirty = beginFrame(canvas, dirty, surfaceState);
+
+ int saveCount = 0;
+ int status = DisplayList.STATUS_DONE;
+
+ long start = GLRenderer.getSystemTime();
+ try {
+ status = prepareFrame(dirty);
+
+ saveCount = canvas.save();
+ callbacks.onHardwarePreDraw(canvas);
+
+ status |= doDrawDisplayList(attachInfo, canvas, displayList, status);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "An error has occurred while drawing:", e);
+ } finally {
+ callbacks.onHardwarePostDraw(canvas);
+ canvas.restoreToCount(saveCount);
+
+ mDrawDelta = GLRenderer.getSystemTime() - start;
+
+ if (mDrawDelta > 0) {
+ mFrameCount++;
+
+ debugOverdraw(attachInfo, dirty, canvas, displayList);
+ debugDirtyRegions(dirty, canvas);
+ drawProfileData(attachInfo);
+ }
+ }
+
+ onPostDraw();
+
+ swapBuffers(status);
+
+ if (mProfileEnabled) {
+ mProfileLock.unlock();
+ }
+
+ attachInfo.mIgnoreDirtyState = false;
+ }
+ }
+ }
+
+ @Override
+ void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
+ Rect dirty) {
+ throw new IllegalAccessError();
+ }
+
+ private void debugOverdraw(View.AttachInfo attachInfo, Rect dirty,
+ HardwareCanvas canvas, DisplayList displayList) {
+
+ if (mDebugOverdraw == OVERDRAW_TYPE_COUNT) {
+ if (mDebugOverdrawLayer == null) {
+ mDebugOverdrawLayer = createHardwareLayer(mWidth, mHeight, true);
+ } else if (mDebugOverdrawLayer.getWidth() != mWidth ||
+ mDebugOverdrawLayer.getHeight() != mHeight) {
+ mDebugOverdrawLayer.resize(mWidth, mHeight);
+ }
+
+ if (!mDebugOverdrawLayer.isValid()) {
+ mDebugOverdraw = -1;
+ return;
+ }
+
+ HardwareCanvas layerCanvas = mDebugOverdrawLayer.start(canvas, dirty);
+ countOverdraw(layerCanvas);
+ final int restoreCount = layerCanvas.save();
+ layerCanvas.drawDisplayList(displayList, null, DisplayList.FLAG_CLIP_CHILDREN);
+ layerCanvas.restoreToCount(restoreCount);
+ mDebugOverdrawLayer.end(canvas);
+
+ float overdraw = getOverdraw(layerCanvas);
+ DisplayMetrics metrics = attachInfo.mRootView.getResources().getDisplayMetrics();
+
+ drawOverdrawCounter(canvas, overdraw, metrics.density);
+ }
+ }
+
+ private void drawOverdrawCounter(HardwareCanvas canvas, float overdraw, float density) {
+ final String text = String.format("%.2fx", overdraw);
+ final Paint paint = setupPaint(density);
+ // HSBtoColor will clamp the values in the 0..1 range
+ paint.setColor(Color.HSBtoColor(0.28f - 0.28f * overdraw / 3.5f, 0.8f, 1.0f));
+
+ canvas.drawText(text, density * 4.0f, mHeight - paint.getFontMetrics().bottom, paint);
+ }
+
+ private Paint setupPaint(float density) {
+ if (mDebugOverdrawPaint == null) {
+ mDebugOverdrawPaint = new Paint();
+ mDebugOverdrawPaint.setAntiAlias(true);
+ mDebugOverdrawPaint.setShadowLayer(density * 3.0f, 0.0f, 0.0f, 0xff000000);
+ mDebugOverdrawPaint.setTextSize(density * 20.0f);
+ }
+ return mDebugOverdrawPaint;
+ }
+
+ private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) {
+ // We had to change the current surface and/or context, redraw everything
+ if (surfaceState == SURFACE_STATE_UPDATED) {
+ dirty = null;
+ GLRenderer.beginFrame(null);
+ } else {
+ int[] size = mSurfaceSize;
+ GLRenderer.beginFrame(size);
+
+ if (size[1] != mHeight || size[0] != mWidth) {
+ mWidth = size[0];
+ mHeight = size[1];
+
+ canvas.setViewport(mWidth, mHeight);
+
+ dirty = null;
+ }
+ }
+
+ if (mDebugDataProvider != null) dirty = null;
+
+ return dirty;
+ }
+
+ private int prepareFrame(Rect dirty) {
+ int status;
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame");
+ try {
+ status = onPreDraw(dirty);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ return status;
+ }
+
+ private int doDrawDisplayList(View.AttachInfo attachInfo, HardwareCanvas canvas,
+ DisplayList displayList, int status) {
+
+ long drawDisplayListStartTime = 0;
+ if (mProfileEnabled) {
+ drawDisplayListStartTime = System.nanoTime();
+ }
+
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList");
+ try {
+ status |= canvas.drawDisplayList(displayList, mRedrawClip,
+ DisplayList.FLAG_CLIP_CHILDREN);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+
+ if (mProfileEnabled) {
+ long now = System.nanoTime();
+ float total = (now - drawDisplayListStartTime) * 0.000001f;
+ mProfileData[mProfileCurrentFrame + 1] = total;
+ }
+
+ handleFunctorStatus(attachInfo, status);
+ return status;
+ }
+
+ private void swapBuffers(int status) {
+ if ((status & DisplayList.STATUS_DREW) == DisplayList.STATUS_DREW) {
+ long eglSwapBuffersStartTime = 0;
+ if (mProfileEnabled) {
+ eglSwapBuffersStartTime = System.nanoTime();
+ }
+
+ sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
+
+ if (mProfileEnabled) {
+ long now = System.nanoTime();
+ float total = (now - eglSwapBuffersStartTime) * 0.000001f;
+ mProfileData[mProfileCurrentFrame + 2] = total;
+ }
+
+ checkEglErrors();
+ }
+ }
+
+ private void debugDirtyRegions(Rect dirty, HardwareCanvas canvas) {
+ if (mDebugDirtyRegions) {
+ if (mDebugPaint == null) {
+ mDebugPaint = new Paint();
+ mDebugPaint.setColor(0x7fff0000);
+ }
+
+ if (dirty != null && (mFrameCount & 1) == 0) {
+ canvas.drawRect(dirty, mDebugPaint);
+ }
+ }
+ }
+
+ private void handleFunctorStatus(final View.AttachInfo attachInfo, int status) {
+ // If the draw flag is set, functors will be invoked while executing
+ // the tree of display lists
+ if ((status & DisplayList.STATUS_DRAW) != 0) {
+ // TODO: Can we just re-queue ourselves up to draw next frame instead
+ // of bouncing back to the UI thread?
+ // TODO: Respect mRedrawClip - for now just full inval
+ attachInfo.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ attachInfo.mViewRootImpl.invalidate();
+ }
+ });
+ mRedrawClip.setEmpty();
+ }
+
+ if ((status & DisplayList.STATUS_INVOKE) != 0 ||
+ attachInfo.mHandler.hasCallbacks(mDispatchFunctorsRunnable)) {
+ attachInfo.mHandler.removeCallbacks(mDispatchFunctorsRunnable);
+ mFunctorsRunnable.attachInfo = attachInfo;
+ attachInfo.mHandler.postDelayed(mDispatchFunctorsRunnable, FUNCTOR_PROCESS_DELAY);
+ }
+ }
+
+ @Override
+ void detachFunctor(int functor) {
+ if (mCanvas != null) {
+ mCanvas.detachFunctor(functor);
+ }
+ }
+
+ @Override
+ boolean attachFunctor(View.AttachInfo attachInfo, int functor) {
+ if (mCanvas != null) {
+ mCanvas.attachFunctor(functor);
+ mFunctorsRunnable.attachInfo = attachInfo;
+ attachInfo.mHandler.removeCallbacks(mDispatchFunctorsRunnable);
+ attachInfo.mHandler.postDelayed(mDispatchFunctorsRunnable, 0);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Ensures the current EGL context and surface are the ones we expect.
+ * This method throws an IllegalStateException if invoked from a thread
+ * that did not initialize EGL.
+ *
+ * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
+ * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
+ * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
+ *
+ * @see #checkRenderContextUnsafe()
+ */
+ int checkRenderContext() {
+ if (mEglThread != Thread.currentThread()) {
+ throw new IllegalStateException("Hardware acceleration can only be used with a " +
+ "single UI thread.\nOriginal thread: " + mEglThread + "\n" +
+ "Current thread: " + Thread.currentThread());
+ }
+
+ return checkRenderContextUnsafe();
+ }
+
+ /**
+ * Ensures the current EGL context and surface are the ones we expect.
+ * This method does not check the current thread.
+ *
+ * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
+ * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
+ * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
+ *
+ * @see #checkRenderContext()
+ */
+ private int checkRenderContextUnsafe() {
+ if (!mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW)) ||
+ !mEglContext.equals(sEgl.eglGetCurrentContext())) {
+ if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ Log.e(LOG_TAG, "eglMakeCurrent failed " +
+ GLUtils.getEGLErrorString(sEgl.eglGetError()));
+ fallback(true);
+ return SURFACE_STATE_ERROR;
+ } else {
+ if (mUpdateDirtyRegions) {
+ enableDirtyRegions();
+ mUpdateDirtyRegions = false;
+ }
+ return SURFACE_STATE_UPDATED;
+ }
+ }
+ return SURFACE_STATE_SUCCESS;
+ }
+
+ private static int dpToPx(int dp, float density) {
+ return (int) (dp * density + 0.5f);
+ }
+
+ class DrawPerformanceDataProvider extends GraphDataProvider {
+ private final int mGraphType;
+
+ private int mVerticalUnit;
+ private int mHorizontalUnit;
+ private int mHorizontalMargin;
+ private int mThresholdStroke;
+
+ DrawPerformanceDataProvider(int graphType) {
+ mGraphType = graphType;
+ }
+
+ @Override
+ void prepare(DisplayMetrics metrics) {
+ final float density = metrics.density;
+
+ mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
+ mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
+ mHorizontalMargin = dpToPx(PROFILE_DRAW_MARGIN, density);
+ mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
+ }
+
+ @Override
+ int getGraphType() {
+ return mGraphType;
+ }
+
+ @Override
+ int getVerticalUnitSize() {
+ return mVerticalUnit;
+ }
+
+ @Override
+ int getHorizontalUnitSize() {
+ return mHorizontalUnit;
+ }
+
+ @Override
+ int getHorizontaUnitMargin() {
+ return mHorizontalMargin;
+ }
+
+ @Override
+ float[] getData() {
+ return mProfileData;
+ }
+
+ @Override
+ float getThreshold() {
+ return 16;
+ }
+
+ @Override
+ int getFrameCount() {
+ return mProfileData.length / PROFILE_FRAME_DATA_COUNT;
+ }
+
+ @Override
+ int getElementCount() {
+ return PROFILE_FRAME_DATA_COUNT;
+ }
+
+ @Override
+ int getCurrentFrame() {
+ return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT;
+ }
+
+ @Override
+ void setupGraphPaint(Paint paint, int elementIndex) {
+ paint.setColor(PROFILE_DRAW_COLORS[elementIndex]);
+ if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
+ }
+
+ @Override
+ void setupThresholdPaint(Paint paint) {
+ paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
+ paint.setStrokeWidth(mThresholdStroke);
+ }
+
+ @Override
+ void setupCurrentFramePaint(Paint paint) {
+ paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
+ if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
+ }
+ }
+}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 4087313..2cd1b6e 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -50,19 +50,20 @@ public class ThreadedRenderer extends HardwareRenderer {
@SuppressWarnings("serial")
static HashMap<String, Method> sMethodLut = new HashMap<String, Method>() {{
- Method[] methods = HardwareRenderer.class.getDeclaredMethods();
+ Method[] methods = RemoteGLRenderer.class.getDeclaredMethods();
for (Method m : methods) {
- put(m.getName(), m);
+ m.setAccessible(true);
+ put(m.getName() + ":" + m.getParameterTypes().length, m);
}
}};
static boolean sNeedsInit = true;
- private HardwareRenderer mRemoteRenderer;
+ private RemoteGLRenderer mRemoteRenderer;
private int mWidth, mHeight;
private RTJob mPreviousDraw;
- ThreadedRenderer(GLRenderer backingRenderer) {
- mRemoteRenderer = backingRenderer;
+ ThreadedRenderer(boolean translucent) {
+ mRemoteRenderer = new RemoteGLRenderer(this, translucent);
setEnabled(true);
if (sNeedsInit) {
sNeedsInit = false;
@@ -166,12 +167,6 @@ public class ThreadedRenderer extends HardwareRenderer {
throw new NoSuchMethodError();
}
- @Override
- void drawDisplayList(DisplayList displayList, AttachInfo attachInfo,
- HardwareDrawCallbacks callbacks, Rect dirty) {
- throw new NoSuchMethodError();
- }
-
/**
* TODO: Remove
* Temporary hack to allow RenderThreadTest prototype app to trigger
@@ -233,12 +228,12 @@ public class ThreadedRenderer extends HardwareRenderer {
@Override
void detachFunctor(int functor) {
- throw new NoSuchMethodError();
+ run("detachFunctor", functor);
}
@Override
boolean attachFunctor(AttachInfo attachInfo, int functor) {
- throw new NoSuchMethodError();
+ return (Boolean) run("attachFunctor", attachInfo, functor);
}
@Override
@@ -262,7 +257,7 @@ public class ThreadedRenderer extends HardwareRenderer {
private RTJob post(String method, Object... args) {
RTJob job = new RTJob();
- job.method = sMethodLut.get(method);
+ job.method = sMethodLut.get(method + ":" + args.length);
job.args = args;
job.target = mRemoteRenderer;
if (job.method == null) {
@@ -274,7 +269,7 @@ public class ThreadedRenderer extends HardwareRenderer {
private Object run(String method, Object... args) {
RTJob job = new RTJob();
- job.method = sMethodLut.get(method);
+ job.method = sMethodLut.get(method + ":" + args.length);
job.args = args;
job.target = mRemoteRenderer;
if (job.method == null) {