diff options
| author | John Reck <jreck@google.com> | 2013-11-11 17:38:18 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-11-11 17:38:18 +0000 |
| commit | e77a040f3adcdbfc8fea265126c2d27f73c01243 (patch) | |
| tree | a754e49395eb9af719540f8dfcdf83c0a9cf5b74 | |
| parent | 2ee011a3c4debc410cbc3f61a3de44616cc32496 (diff) | |
| parent | 3083579424785e55ca8f82856a6553ee983c3ffb (diff) | |
| download | frameworks_base-e77a040f3adcdbfc8fea265126c2d27f73c01243.zip frameworks_base-e77a040f3adcdbfc8fea265126c2d27f73c01243.tar.gz frameworks_base-e77a040f3adcdbfc8fea265126c2d27f73c01243.tar.bz2 | |
Merge "More refactoring"
| -rw-r--r-- | core/java/android/view/DisplayList.java | 19 | ||||
| -rw-r--r-- | core/java/android/view/GLRenderer.java | 1610 | ||||
| -rw-r--r-- | core/java/android/view/HardwareRenderer.java | 1650 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 14 | ||||
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 2 | ||||
| -rw-r--r-- | core/jni/Android.mk | 2 | ||||
| -rw-r--r-- | core/jni/AndroidRuntime.cpp | 4 | ||||
| -rw-r--r-- | core/jni/android_view_GLRenderer.cpp (renamed from core/jni/android_view_HardwareRenderer.cpp) | 32 |
8 files changed, 1645 insertions, 1688 deletions
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java index 54fb6ef..623472a 100644 --- a/core/java/android/view/DisplayList.java +++ b/core/java/android/view/DisplayList.java @@ -86,18 +86,15 @@ import android.graphics.Matrix; * * <pre class="prettyprint"> * private void createDisplayList() { - * HardwareRenderer renderer = getHardwareRenderer(); - * if (renderer != null) { - * mDisplayList = renderer.createDisplayList(); - * HardwareCanvas canvas = mDisplayList.start(width, height); - * try { - * for (Bitmap b : mBitmaps) { - * canvas.drawBitmap(b, 0.0f, 0.0f, null); - * canvas.translate(0.0f, b.getHeight()); - * } - * } finally { - * displayList.end(); + * mDisplayList = DisplayList.create("MyDisplayList"); + * HardwareCanvas canvas = mDisplayList.start(width, height); + * try { + * for (Bitmap b : mBitmaps) { + * canvas.drawBitmap(b, 0.0f, 0.0f, null); + * canvas.translate(0.0f, b.getHeight()); * } + * } finally { + * displayList.end(); * } * } * diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java new file mode 100644 index 0000000..a195231 --- /dev/null +++ b/core/java/android/view/GLRenderer.java @@ -0,0 +1,1610 @@ +/* + * 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.SystemClock; +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 + * + * @hide + */ +public class GLRenderer 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 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); + } + + GLRenderer(boolean translucent) { + mTranslucent = translucent; + + loadSystemProperties(null); + } + + @Override + boolean loadSystemProperties(Surface surface) { + 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 (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 = 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 = 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 + HardwareCanvas getCanvas() { + return mCanvas; + } + + @Override + void setName(String name) { + mName = name; + } + + class FunctorsRunnable implements Runnable { + View.AttachInfo attachInfo; + + @Override + public void run() { + final HardwareRenderer renderer = attachInfo.mHardwareRenderer; + if (renderer == null || !renderer.isEnabled() || renderer != GLRenderer.this) { + return; + } + + if (checkRenderContext() != SURFACE_STATE_ERROR) { + int status = mCanvas.invokeFunctors(mRedrawClip); + handleFunctorStatus(attachInfo, status); + } + } + } + + @Override + void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, + Rect dirty) { + if (canDraw()) { + if (!hasDirtyRegions()) { + dirty = null; + } + attachInfo.mIgnoreDirtyState = true; + attachInfo.mDrawingTime = SystemClock.uptimeMillis(); + + view.mPrivateFlags |= View.PFLAG_DRAWN; + + // We are already on the correct thread + final int surfaceState = checkRenderContextUnsafe(); + if (surfaceState != SURFACE_STATE_ERROR) { + HardwareCanvas canvas = mCanvas; + attachInfo.mHardwareCanvas = canvas; + + if (mProfileEnabled) { + mProfileLock.lock(); + } + + dirty = beginFrame(canvas, dirty, surfaceState); + + DisplayList displayList = buildDisplayList(view, canvas); + + // buildDisplayList() calls into user code which can cause + // an eglMakeCurrent to happen with a different surface/context. + // We must therefore check again here. + if (checkRenderContextUnsafe() == SURFACE_STATE_ERROR) { + return; + } + + int saveCount = 0; + int status = DisplayList.STATUS_DONE; + + long start = getSystemTime(); + try { + status = prepareFrame(dirty); + + saveCount = canvas.save(); + callbacks.onHardwarePreDraw(canvas); + + if (displayList != null) { + status |= drawDisplayList(attachInfo, canvas, displayList, status); + } else { + // Shouldn't reach here + view.draw(canvas); + } + } catch (Exception e) { + Log.e(LOG_TAG, "An error has occurred while drawing:", e); + } finally { + callbacks.onHardwarePostDraw(canvas); + canvas.restoreToCount(saveCount); + view.mRecreateDisplayList = false; + + 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; + } + } + } + + 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 DisplayList buildDisplayList(View view, HardwareCanvas canvas) { + if (mDrawDelta <= 0) { + return view.mDisplayList; + } + + view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) + == View.PFLAG_INVALIDATED; + view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; + + long buildDisplayListStartTime = startBuildDisplayListProfiling(); + canvas.clearLayerUpdates(); + + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList"); + DisplayList displayList = view.getDisplayList(); + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + + endBuildDisplayListProfiling(buildDisplayListStartTime); + + return displayList; + } + + 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; + beginFrame(null); + } else { + int[] size = mSurfaceSize; + 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 long startBuildDisplayListProfiling() { + if (mProfileEnabled) { + mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT; + if (mProfileCurrentFrame >= mProfileData.length) { + mProfileCurrentFrame = 0; + } + + return System.nanoTime(); + } + return 0; + } + + private void endBuildDisplayListProfiling(long getDisplayListStartTime) { + if (mProfileEnabled) { + long now = System.nanoTime(); + float total = (now - getDisplayListStartTime) * 0.000001f; + //noinspection PointlessArithmeticExpression + mProfileData[mProfileCurrentFrame] = total; + } + } + + 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 drawDisplayList(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(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) { + if (mRedrawClip.isEmpty()) { + attachInfo.mViewRootImpl.invalidate(); + } else { + attachInfo.mViewRootImpl.invalidateChildInParent(null, mRedrawClip); + mRedrawClip.setEmpty(); + } + } + + if ((status & DisplayList.STATUS_INVOKE) != 0 || + attachInfo.mHandler.hasCallbacks(mFunctorsRunnable)) { + attachInfo.mHandler.removeCallbacks(mFunctorsRunnable); + mFunctorsRunnable.attachInfo = attachInfo; + attachInfo.mHandler.postDelayed(mFunctorsRunnable, 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(mFunctorsRunnable); + attachInfo.mHandler.postDelayed(mFunctorsRunnable, 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); + } + + private static native boolean loadProperties(); + + static native void setupShadersDiskCache(String cacheFile); + + /** + * Notifies EGL that the frame is about to be rendered. + * @param size + */ + private 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(); + + /** + * Preserves the back buffer of the current surface after a buffer swap. + * Calling this method sets the EGL_SWAP_BEHAVIOR attribute of the current + * surface to EGL_BUFFER_PRESERVED. Calling this method requires an EGL + * config that supports EGL_SWAP_BEHAVIOR_PRESERVED_BIT. + * + * @return True if the swap behavior was successfully changed, + * false otherwise. + */ + private static native boolean preserveBackBuffer(); + + /** + * Indicates whether the current surface preserves its back buffer + * after a buffer swap. + * + * @return True, if the surface's EGL_SWAP_BEHAVIOR is EGL_BUFFER_PRESERVED, + * false otherwise + */ + private static native boolean isBackBufferPreserved(); + + 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/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 4ddae19..5c0be4a 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -16,41 +16,14 @@ package android.view; -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.SystemClock; -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 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; - import java.io.File; import java.io.PrintWriter; -import java.util.concurrent.locks.ReentrantLock; - -import static javax.microedition.khronos.egl.EGL10.*; /** * Interface for rendering a view hierarchy using hardware acceleration. @@ -215,16 +188,6 @@ public abstract class HardwareRenderer { */ public static boolean sSystemRendererDisabled = false; - /** - * 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 boolean mEnabled; private boolean mRequested = true; @@ -374,8 +337,6 @@ public abstract class HardwareRenderer { */ abstract boolean loadSystemProperties(Surface surface); - private static native boolean nLoadProperties(); - /** * Sets the directory to use as a persistent storage for hardware rendering * resources. @@ -385,60 +346,9 @@ public abstract class HardwareRenderer { * @hide */ public static void setupDiskCache(File cacheDir) { - nSetupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath()); - } - - private static native void nSetupShadersDiskCache(String cacheFile); - - /** - * Notifies EGL that the frame is about to be rendered. - * @param size - */ - static void beginFrame(int[] size) { - nBeginFrame(size); + GLRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath()); } - private static native void nBeginFrame(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. - */ - static long getSystemTime() { - return nGetSystemTime(); - } - - private static native long nGetSystemTime(); - - /** - * Preserves the back buffer of the current surface after a buffer swap. - * Calling this method sets the EGL_SWAP_BEHAVIOR attribute of the current - * surface to EGL_BUFFER_PRESERVED. Calling this method requires an EGL - * config that supports EGL_SWAP_BEHAVIOR_PRESERVED_BIT. - * - * @return True if the swap behavior was successfully changed, - * false otherwise. - */ - static boolean preserveBackBuffer() { - return nPreserveBackBuffer(); - } - - private static native boolean nPreserveBackBuffer(); - - /** - * Indicates whether the current surface preserves its back buffer - * after a buffer swap. - * - * @return True, if the surface's EGL_SWAP_BEHAVIOR is EGL_BUFFER_PRESERVED, - * false otherwise - */ - static boolean isBackBufferPreserved() { - return nIsBackBufferPreserved(); - } - - private static native boolean nIsBackBufferPreserved(); - /** * Indicates that the specified hardware layer needs to be updated * as soon as possible. @@ -602,17 +512,15 @@ public abstract class HardwareRenderer { /** * Creates a hardware renderer using OpenGL. * - * @param glVersion The version of OpenGL to use (1 for OpenGL 1, 11 for OpenGL 1.1, etc.) * @param translucent True if the surface is translucent, false otherwise * * @return A hardware renderer backed by OpenGL. */ - static HardwareRenderer createGlRenderer(int glVersion, boolean translucent) { - switch (glVersion) { - case 2: - return Gl20Renderer.create(translucent); + static HardwareRenderer create(boolean translucent) { + if (GLES20Canvas.isAvailable()) { + return new GLRenderer(translucent); } - throw new IllegalArgumentException("Unknown GL version: " + glVersion); + return null; } /** @@ -637,7 +545,7 @@ public abstract class HardwareRenderer { * see {@link android.content.ComponentCallbacks} */ static void startTrimMemory(int level) { - Gl20Renderer.startTrimMemory(level); + GLRenderer.startTrimMemory(level); } /** @@ -645,7 +553,7 @@ public abstract class HardwareRenderer { * cleanup special resources used by the memory trimming process. */ static void endTrimMemory() { - Gl20Renderer.endTrimMemory(); + GLRenderer.endTrimMemory(); } /** @@ -779,1548 +687,4 @@ public abstract class HardwareRenderer { */ abstract void setupCurrentFramePaint(Paint paint); } - - @SuppressWarnings({"deprecation"}) - static abstract class GlRenderer 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; - - 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; - - 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 int mGlVersion; - 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; - - GlRenderer(int glVersion, boolean translucent) { - mGlVersion = glVersion; - mTranslucent = translucent; - - loadSystemProperties(null); - } - - @Override - boolean loadSystemProperties(Surface surface) { - 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 (nLoadProperties()) { - 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); - } - } - - abstract HardwareCanvas createCanvas(); - - abstract int[] getConfig(boolean dirtyRegions); - - 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; - } - - abstract ManagedEGLContext createManagedContext(EGLContext eglContext); - - 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 = 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 = isBackBufferPreserved(); - } - } - - abstract void initCaches(); - abstract void initAtlas(); - - EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) { - int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE }; - - EGLContext context = egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, - mGlVersion != 0 ? attribs : null); - 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; - } - - @Override - void destroy(boolean full) { - if (full && mCanvas != null) { - mCanvas = null; - } - - if (!isEnabled() || mDestroyed) { - setEnabled(false); - return; - } - - destroySurface(); - setEnabled(false); - - mDestroyed = true; - mGl = null; - } - - 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 - HardwareCanvas getCanvas() { - return mCanvas; - } - - @Override - void setName(String name) { - mName = name; - } - - boolean canDraw() { - return mGl != null && mCanvas != null; - } - - int onPreDraw(Rect dirty) { - return DisplayList.STATUS_DONE; - } - - void onPostDraw() { - } - - class FunctorsRunnable implements Runnable { - View.AttachInfo attachInfo; - - @Override - public void run() { - final HardwareRenderer renderer = attachInfo.mHardwareRenderer; - if (renderer == null || !renderer.isEnabled() || renderer != GlRenderer.this) { - return; - } - - if (checkRenderContext() != SURFACE_STATE_ERROR) { - int status = mCanvas.invokeFunctors(mRedrawClip); - handleFunctorStatus(attachInfo, status); - } - } - } - - @Override - void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, - Rect dirty) { - if (canDraw()) { - if (!hasDirtyRegions()) { - dirty = null; - } - attachInfo.mIgnoreDirtyState = true; - attachInfo.mDrawingTime = SystemClock.uptimeMillis(); - - view.mPrivateFlags |= View.PFLAG_DRAWN; - - // We are already on the correct thread - final int surfaceState = checkRenderContextUnsafe(); - if (surfaceState != SURFACE_STATE_ERROR) { - HardwareCanvas canvas = mCanvas; - attachInfo.mHardwareCanvas = canvas; - - if (mProfileEnabled) { - mProfileLock.lock(); - } - - dirty = beginFrame(canvas, dirty, surfaceState); - - DisplayList displayList = buildDisplayList(view, canvas); - - // buildDisplayList() calls into user code which can cause - // an eglMakeCurrent to happen with a different surface/context. - // We must therefore check again here. - if (checkRenderContextUnsafe() == SURFACE_STATE_ERROR) { - return; - } - - int saveCount = 0; - int status = DisplayList.STATUS_DONE; - - long start = getSystemTime(); - try { - status = prepareFrame(dirty); - - saveCount = canvas.save(); - callbacks.onHardwarePreDraw(canvas); - - if (displayList != null) { - status |= drawDisplayList(attachInfo, canvas, displayList, status); - } else { - // Shouldn't reach here - view.draw(canvas); - } - } catch (Exception e) { - Log.e(LOG_TAG, "An error has occurred while drawing:", e); - } finally { - callbacks.onHardwarePostDraw(canvas); - canvas.restoreToCount(saveCount); - view.mRecreateDisplayList = false; - - 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; - } - } - } - - abstract void countOverdraw(HardwareCanvas canvas); - abstract float getOverdraw(HardwareCanvas canvas); - - 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 DisplayList buildDisplayList(View view, HardwareCanvas canvas) { - if (mDrawDelta <= 0) { - return view.mDisplayList; - } - - view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) - == View.PFLAG_INVALIDATED; - view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; - - long buildDisplayListStartTime = startBuildDisplayListProfiling(); - canvas.clearLayerUpdates(); - - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList"); - DisplayList displayList = view.getDisplayList(); - Trace.traceEnd(Trace.TRACE_TAG_VIEW); - - endBuildDisplayListProfiling(buildDisplayListStartTime); - - return displayList; - } - - abstract void drawProfileData(View.AttachInfo attachInfo); - - 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; - beginFrame(null); - } else { - int[] size = mSurfaceSize; - 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 long startBuildDisplayListProfiling() { - if (mProfileEnabled) { - mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT; - if (mProfileCurrentFrame >= mProfileData.length) { - mProfileCurrentFrame = 0; - } - - return System.nanoTime(); - } - return 0; - } - - private void endBuildDisplayListProfiling(long getDisplayListStartTime) { - if (mProfileEnabled) { - long now = System.nanoTime(); - float total = (now - getDisplayListStartTime) * 0.000001f; - //noinspection PointlessArithmeticExpression - mProfileData[mProfileCurrentFrame] = total; - } - } - - 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 drawDisplayList(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(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) { - if (mRedrawClip.isEmpty()) { - attachInfo.mViewRootImpl.invalidate(); - } else { - attachInfo.mViewRootImpl.invalidateChildInParent(null, mRedrawClip); - mRedrawClip.setEmpty(); - } - } - - if ((status & DisplayList.STATUS_INVOKE) != 0 || - attachInfo.mHandler.hasCallbacks(mFunctorsRunnable)) { - attachInfo.mHandler.removeCallbacks(mFunctorsRunnable); - mFunctorsRunnable.attachInfo = attachInfo; - attachInfo.mHandler.postDelayed(mFunctorsRunnable, 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(mFunctorsRunnable); - attachInfo.mHandler.postDelayed(mFunctorsRunnable, 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); - } - } - } - - /** - * Hardware renderer using OpenGL ES 2.0. - */ - static class Gl20Renderer extends GlRenderer { - private GLES20Canvas mGlCanvas; - - private DisplayMetrics mDisplayMetrics; - - private static EGLSurface sPbuffer; - private static final Object[] sPbufferLock = new Object[0]; - - static class Gl20RendererEglContext extends ManagedEGLContext { - final Handler mHandler = new Handler(); - - public Gl20RendererEglContext(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; - } - } - } - } - - Gl20Renderer(boolean translucent) { - super(2, translucent); - } - - @Override - HardwareCanvas createCanvas() { - return mGlCanvas = new GLES20Canvas(mTranslucent); - } - - @Override - ManagedEGLContext createManagedContext(EGLContext eglContext) { - return new Gl20Renderer.Gl20RendererEglContext(mEglContext); - } - - @Override - 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 - }; - } - - @Override - void initCaches() { - if (GLES20Canvas.initCaches()) { - // Caches were (re)initialized, rebind atlas - initAtlas(); - } - } - - @Override - 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); - } - } - - @Override - boolean canDraw() { - return super.canDraw() && mGlCanvas != null; - } - - @Override - int onPreDraw(Rect dirty) { - return mGlCanvas.onPreDraw(dirty); - } - - @Override - void onPostDraw() { - mGlCanvas.onPostDraw(); - } - - @Override - 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 { - super.destroy(full); - } 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); - } - - @Override - void countOverdraw(HardwareCanvas canvas) { - ((GLES20Canvas) canvas).setCountOverdrawEnabled(true); - } - - @Override - 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) { - Gl20RendererEglContext managedContext = - (Gl20RendererEglContext) 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 HardwareRenderer create(boolean translucent) { - if (GLES20Canvas.isAvailable()) { - return new Gl20Renderer(translucent); - } - return null; - } - - static void startTrimMemory(int level) { - if (sEgl == null || sEglConfig == null) return; - - Gl20RendererEglContext managedContext = - (Gl20RendererEglContext) 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); - } - } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index d8a7efc..f9e6b9f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -13358,20 +13358,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * @return The {@link HardwareRenderer} associated with that view or null if - * hardware rendering is not supported or this view is not attached - * to a window. - * - * @hide - */ - public HardwareRenderer getHardwareRenderer() { - if (mAttachInfo != null) { - return mAttachInfo.mHardwareRenderer; - } - return null; - } - - /** * Returns a DisplayList. If the incoming displayList is null, one will be created. * Otherwise, the same display list will be returned (after having been rendered into * along the way, depending on the invalidation state of the view). diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 909907c..a5f797e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -718,7 +718,7 @@ public final class ViewRootImpl implements ViewParent, } final boolean translucent = attrs.format != PixelFormat.OPAQUE; - mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent); + mAttachInfo.mHardwareRenderer = HardwareRenderer.create(translucent); if (mAttachInfo.mHardwareRenderer != null) { mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString()); mAttachInfo.mHardwareAccelerated = diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 4c9f8a3..2e0c28e 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -51,8 +51,8 @@ LOCAL_SRC_FILES:= \ android_view_InputQueue.cpp \ android_view_KeyEvent.cpp \ android_view_KeyCharacterMap.cpp \ - android_view_HardwareRenderer.cpp \ android_view_GraphicBuffer.cpp \ + android_view_GLRenderer.cpp \ android_view_GLES20DisplayList.cpp \ android_view_GLES20Canvas.cpp \ android_view_MotionEvent.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 1cce263..d501dd8 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -122,7 +122,7 @@ extern int register_android_view_DisplayEventReceiver(JNIEnv* env); extern int register_android_view_GraphicBuffer(JNIEnv* env); extern int register_android_view_GLES20DisplayList(JNIEnv* env); extern int register_android_view_GLES20Canvas(JNIEnv* env); -extern int register_android_view_HardwareRenderer(JNIEnv* env); +extern int register_android_view_GLRenderer(JNIEnv* env); extern int register_android_view_Surface(JNIEnv* env); extern int register_android_view_SurfaceControl(JNIEnv* env); extern int register_android_view_SurfaceSession(JNIEnv* env); @@ -1118,7 +1118,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_view_GraphicBuffer), REG_JNI(register_android_view_GLES20DisplayList), REG_JNI(register_android_view_GLES20Canvas), - REG_JNI(register_android_view_HardwareRenderer), + REG_JNI(register_android_view_GLRenderer), REG_JNI(register_android_view_Surface), REG_JNI(register_android_view_SurfaceControl), REG_JNI(register_android_view_SurfaceSession), diff --git a/core/jni/android_view_HardwareRenderer.cpp b/core/jni/android_view_GLRenderer.cpp index 479fbe2..7cf93d0 100644 --- a/core/jni/android_view_HardwareRenderer.cpp +++ b/core/jni/android_view_GLRenderer.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "HardwareRenderer" +#define LOG_TAG "GLRenderer" #include "jni.h" #include <nativehelper/JNIHelp.h> @@ -60,7 +60,7 @@ namespace android { // Surface and display management // ---------------------------------------------------------------------------- -static jboolean android_view_HardwareRenderer_preserveBackBuffer(JNIEnv* env, jobject clazz) { +static jboolean android_view_GLRenderer_preserveBackBuffer(JNIEnv* env, jobject clazz) { EGLDisplay display = eglGetCurrentDisplay(); EGLSurface surface = eglGetCurrentSurface(EGL_DRAW); @@ -75,7 +75,7 @@ static jboolean android_view_HardwareRenderer_preserveBackBuffer(JNIEnv* env, jo return error == EGL_SUCCESS; } -static jboolean android_view_HardwareRenderer_isBackBufferPreserved(JNIEnv* env, jobject clazz) { +static jboolean android_view_GLRenderer_isBackBufferPreserved(JNIEnv* env, jobject clazz) { EGLDisplay display = eglGetCurrentDisplay(); EGLSurface surface = eglGetCurrentSurface(EGL_DRAW); EGLint value; @@ -95,14 +95,14 @@ static jboolean android_view_HardwareRenderer_isBackBufferPreserved(JNIEnv* env, // Tracing and debugging // ---------------------------------------------------------------------------- -static bool android_view_HardwareRenderer_loadProperties(JNIEnv* env, jobject clazz) { +static bool android_view_GLRenderer_loadProperties(JNIEnv* env, jobject clazz) { if (uirenderer::Caches::hasInstance()) { return uirenderer::Caches::getInstance().initProperties(); } return false; } -static void android_view_HardwareRenderer_beginFrame(JNIEnv* env, jobject clazz, +static void android_view_GLRenderer_beginFrame(JNIEnv* env, jobject clazz, jintArray size) { EGLDisplay display = eglGetCurrentDisplay(); @@ -124,7 +124,7 @@ static void android_view_HardwareRenderer_beginFrame(JNIEnv* env, jobject clazz, eglBeginFrame(display, surface); } -static jlong android_view_HardwareRenderer_getSystemTime(JNIEnv* env, jobject clazz) { +static jlong android_view_GLRenderer_getSystemTime(JNIEnv* env, jobject clazz) { if (uirenderer::Extensions::getInstance().hasNvSystemTime()) { return eglGetSystemTimeNV(); } @@ -137,7 +137,7 @@ static jlong android_view_HardwareRenderer_getSystemTime(JNIEnv* env, jobject cl // Shaders // ---------------------------------------------------------------------------- -static void android_view_HardwareRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz, +static void android_view_GLRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz, jstring diskCachePath) { const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL); @@ -149,24 +149,24 @@ static void android_view_HardwareRenderer_setupShadersDiskCache(JNIEnv* env, job // JNI Glue // ---------------------------------------------------------------------------- -const char* const kClassPathName = "android/view/HardwareRenderer"; +const char* const kClassPathName = "android/view/GLRenderer"; static JNINativeMethod gMethods[] = { #ifdef USE_OPENGL_RENDERER - { "nIsBackBufferPreserved", "()Z", (void*) android_view_HardwareRenderer_isBackBufferPreserved }, - { "nPreserveBackBuffer", "()Z", (void*) android_view_HardwareRenderer_preserveBackBuffer }, - { "nLoadProperties", "()Z", (void*) android_view_HardwareRenderer_loadProperties }, + { "isBackBufferPreserved", "()Z", (void*) android_view_GLRenderer_isBackBufferPreserved }, + { "preserveBackBuffer", "()Z", (void*) android_view_GLRenderer_preserveBackBuffer }, + { "loadProperties", "()Z", (void*) android_view_GLRenderer_loadProperties }, - { "nBeginFrame", "([I)V", (void*) android_view_HardwareRenderer_beginFrame }, + { "beginFrame", "([I)V", (void*) android_view_GLRenderer_beginFrame }, - { "nGetSystemTime", "()J", (void*) android_view_HardwareRenderer_getSystemTime }, + { "getSystemTime", "()J", (void*) android_view_GLRenderer_getSystemTime }, #endif - { "nSetupShadersDiskCache", "(Ljava/lang/String;)V", - (void*) android_view_HardwareRenderer_setupShadersDiskCache }, + { "setupShadersDiskCache", "(Ljava/lang/String;)V", + (void*) android_view_GLRenderer_setupShadersDiskCache }, }; -int register_android_view_HardwareRenderer(JNIEnv* env) { +int register_android_view_GLRenderer(JNIEnv* env) { return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); } |
