Use only one GL context per process, share chaches.
Change-Id: Ieabaa25338d2f4b8d4fd90e7401ad6e7452eae11
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 4c72e95..b058685 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -44,7 +44,7 @@
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
private final GL mGl;
private final boolean mOpaque;
- private final int mRenderer;
+ private int mRenderer;
private int mWidth;
private int mHeight;
@@ -76,16 +76,25 @@
mOpaque = !translucent;
mRenderer = nCreateRenderer();
+ if (mRenderer == 0) {
+ throw new IllegalStateException("Could not create GLES20Canvas renderer");
+ }
}
-
+
private native int nCreateRenderer();
- @Override
- protected void finalize() throws Throwable {
- try {
- super.finalize();
- } finally {
+ /**
+ * This method <strong>must</strong> be called before releasing a
+ * reference to a GLES20Canvas. This method is responsible for freeing
+ * native resources associated with the hardware. Not invoking this
+ * method properly can result in memory leaks.
+ *
+ * @hide
+ */
+ public synchronized void destroy() {
+ if (mRenderer != 0) {
nDestroyRenderer(mRenderer);
+ mRenderer = 0;
}
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index ca60a89..32d8123 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -35,9 +35,10 @@
* @hide
*/
public abstract class HardwareRenderer {
+ private static final String LOG_TAG = "HardwareRenderer";
+
private boolean mEnabled;
private boolean mRequested = true;
- private static final String LOG_TAG = "HardwareRenderer";
/**
* Indicates whether hardware acceleration is available under any form for
@@ -70,9 +71,8 @@
*
* @param width Width of the drawing surface.
* @param height Height of the drawing surface.
- * @param attachInfo The AttachInfo used to render the ViewRoot.
*/
- abstract void setup(int width, int height, View.AttachInfo attachInfo);
+ abstract void setup(int width, int height);
/**
* Draws the specified view.
@@ -96,12 +96,11 @@
*/
void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
SurfaceHolder holder) {
-
if (isRequested()) {
// We lost the gl context, so recreate it.
if (!isEnabled()) {
if (initialize(holder)) {
- setup(width, height, attachInfo);
+ setup(width, height);
}
}
}
@@ -165,18 +164,23 @@
static abstract class GlRenderer extends HardwareRenderer {
private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
- EGL10 mEgl;
- EGLDisplay mEglDisplay;
- EGLContext mEglContext;
- EGLSurface mEglSurface;
- EGLConfig mEglConfig;
+ static EGLContext sEglContext;
+ static EGL10 sEgl;
+ static EGLDisplay sEglDisplay;
+ static EGLConfig sEglConfig;
+ private static Thread sEglThread;
+
+ EGLSurface mEglSurface;
+
GL mGl;
- Canvas mCanvas;
+ GLES20Canvas mCanvas;
final int mGlVersion;
final boolean mTranslucent;
+ private boolean mDestroyed;
+
GlRenderer(int glVersion, boolean translucent) {
mGlVersion = glVersion;
mTranslucent = translucent;
@@ -189,7 +193,7 @@
*/
void checkErrors() {
if (isEnabled()) {
- int error = mEgl.eglGetError();
+ int error = sEgl.eglGetError();
if (error != EGL10.EGL_SUCCESS) {
// something bad has happened revert to
// normal rendering.
@@ -208,13 +212,17 @@
if (isRequested() && !isEnabled()) {
initializeEgl();
mGl = createEglSurface(holder);
+ mDestroyed = false;
if (mGl != null) {
- int err = mEgl.eglGetError();
+ int err = sEgl.eglGetError();
if (err != EGL10.EGL_SUCCESS) {
destroy();
setRequested(false);
} else {
+ if (mCanvas != null) {
+ destroyCanvas();
+ }
mCanvas = createCanvas();
if (mCanvas != null) {
setEnabled(true);
@@ -229,64 +237,75 @@
return false;
}
- abstract Canvas createCanvas();
+ private void destroyCanvas() {
+ mCanvas.destroy();
+ mCanvas = null;
+ }
+
+ abstract GLES20Canvas createCanvas();
void initializeEgl() {
- mEgl = (EGL10) EGLContext.getEGL();
+ if (sEglContext != null) return;
+
+ sEglThread = Thread.currentThread();
+ sEgl = (EGL10) EGLContext.getEGL();
// Get to the default display.
- mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+ sEglDisplay = sEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
- if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+ if (sEglDisplay == EGL10.EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay failed");
}
// We can now initialize EGL for that display
int[] version = new int[2];
- if (!mEgl.eglInitialize(mEglDisplay, version)) {
+ if (!sEgl.eglInitialize(sEglDisplay, version)) {
throw new RuntimeException("eglInitialize failed");
}
- mEglConfig = getConfigChooser(mGlVersion).chooseConfig(mEgl, mEglDisplay);
+ sEglConfig = getConfigChooser(mGlVersion).chooseConfig(sEgl, sEglDisplay);
/*
* Create an EGL context. We want to do this as rarely as we can, because an
* EGL context is a somewhat heavy object.
*/
- mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
+ sEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
}
GL createEglSurface(SurfaceHolder holder) {
// Check preconditions.
- if (mEgl == null) {
+ if (sEgl == null) {
throw new RuntimeException("egl not initialized");
}
- if (mEglDisplay == null) {
+ if (sEglDisplay == null) {
throw new RuntimeException("eglDisplay not initialized");
}
- if (mEglConfig == null) {
+ if (sEglConfig == null) {
throw new RuntimeException("mEglConfig not initialized");
}
+ if (Thread.currentThread() != sEglThread) {
+ throw new IllegalStateException("HardwareRenderer cannot be used "
+ + "from multiple threads");
+ }
/*
* The window size has changed, so we need to create a new
* surface.
*/
if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
-
/*
* Unbind and destroy the old EGL surface, if
* there is one.
*/
- mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+ sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
- mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+ sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
}
// Create an EGL surface we can render into.
- mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, holder, null);
+ mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, holder, null);
if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
- int error = mEgl.eglGetError();
+ int error = sEgl.eglGetError();
if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
return null;
@@ -298,11 +317,11 @@
* Before we can issue GL commands, we need to make sure
* the context is current and bound to a surface.
*/
- if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
throw new RuntimeException("eglMakeCurrent failed");
}
- return mEglContext.getGL();
+ return sEglContext.getGL();
}
EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
@@ -315,7 +334,6 @@
@Override
void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
SurfaceHolder holder) {
-
if (isRequested()) {
checkErrors();
super.initializeIfNeeded(width, height, attachInfo, holder);
@@ -324,28 +342,34 @@
@Override
void destroy() {
- if (!isEnabled()) return;
+ if (!isEnabled() || mDestroyed) return;
- mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+ mDestroyed = true;
+
+ checkCurrent();
+ // Destroy the Canvas first in case it needs to use a GL context
+ // to perform its cleanup.
+ destroyCanvas();
+
+ sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
- mEgl.eglDestroyContext(mEglDisplay, mEglContext);
- mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
- mEgl.eglTerminate(mEglDisplay);
+ sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
- mEglContext = null;
mEglSurface = null;
- mEglDisplay = null;
- mEgl = null;
mGl = null;
- mCanvas = null;
+
+ // mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+ // mEglContext = null;
+ // mEgl.eglTerminate(mEglDisplay);
+ // mEgl = null;
+ // mEglDisplay = null;
setEnabled(false);
- }
-
+ }
+
@Override
- void setup(int width, int height, View.AttachInfo attachInfo) {
- final float scale = attachInfo.mApplicationScale;
- mCanvas.setViewport((int) (width * scale + 0.5f), (int) (height * scale + 0.5f));
+ void setup(int width, int height) {
+ mCanvas.setViewport(width, height);
}
boolean canDraw() {
@@ -363,7 +387,8 @@
* @param glVersion
*/
EglConfigChooser getConfigChooser(int glVersion) {
- return new ComponentSizeChooser(glVersion, 8, 8, 8, mTranslucent ? 8 : 0, 0, 0);
+ // TODO: Check for mTranslucent here, which means at least 2 EGL contexts
+ return new ComponentSizeChooser(glVersion, 8, 8, 8, 8, 0, 0);
}
@Override
@@ -373,14 +398,7 @@
attachInfo.mIgnoreDirtyState = true;
view.mPrivateFlags |= View.DRAWN;
- // TODO: Don't check the current context when we have one per UI thread
- // TODO: Use a threadlocal flag to know whether the surface has changed
- if (mEgl.eglGetCurrentContext() != mEglContext ||
- mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW) != mEglSurface) {
- if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
- throw new RuntimeException("eglMakeCurrent failed");
- }
- }
+ checkCurrent();
onPreDraw();
@@ -396,11 +414,22 @@
attachInfo.mIgnoreDirtyState = false;
- mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
+ sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
checkErrors();
}
}
+ private void checkCurrent() {
+ // TODO: Don't check the current context when we have one per UI thread
+ // TODO: Use a threadlocal flag to know whether the surface has changed
+ if (sEgl.eglGetCurrentContext() != sEglContext ||
+ sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW) != mEglSurface) {
+ if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
+ throw new RuntimeException("eglMakeCurrent failed");
+ }
+ }
+ }
+
static abstract class EglConfigChooser {
final int[] mConfigSpec;
private final int mGlVersion;
@@ -496,7 +525,7 @@
int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
- if (r == mRedSize && g == mGreenSize && b == mBlueSize && a >= mAlphaSize) {
+ if (r >= mRedSize && g >= mGreenSize && b >= mBlueSize && a >= mAlphaSize) {
return config;
}
}
@@ -514,7 +543,7 @@
}
}
}
-
+
/**
* Hardware renderer using OpenGL ES 2.0.
*/
@@ -526,8 +555,9 @@
}
@Override
- Canvas createCanvas() {
- return mGlCanvas = new GLES20Canvas(mGl, mTranslucent);
+ GLES20Canvas createCanvas() {
+ // TODO: Pass mTranslucent instead of true
+ return mGlCanvas = new GLES20Canvas(mGl, true);
}
@Override
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index d32ccb1..5999aba 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -251,7 +251,7 @@
mTempRect = new Rect();
mVisRect = new Rect();
mWinFrame = new Rect();
- mWindow = new W(this, context);
+ mWindow = new W(this);
mInputMethodCallback = new InputMethodCallback(this);
mViewVisibility = View.GONE;
mTransparentRegion = new Region();
@@ -469,6 +469,7 @@
if (attrs != null &&
(attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) {
final boolean translucent = attrs.format != PixelFormat.OPAQUE;
+ destroyHardwareRenderer();
mHwRenderer = HardwareRenderer.createGlRenderer(2, translucent);
}
}
@@ -677,9 +678,7 @@
attachInfo.mWindowVisibility = viewVisibility;
host.dispatchWindowVisibilityChanged(viewVisibility);
if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
- if (mHwRenderer != null) {
- mHwRenderer.destroy();
- }
+ destroyHardwareRenderer();
}
if (viewVisibility == View.GONE) {
// After making a window gone, we will count it as being
@@ -963,7 +962,7 @@
}
if (hwIntialized) {
- mHwRenderer.setup(mWidth, mHeight, mAttachInfo);
+ mHwRenderer.setup(mWidth, mHeight);
}
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
@@ -1598,9 +1597,9 @@
mAttachInfo.mRootView = null;
mAttachInfo.mSurface = null;
- if (mHwRenderer != null) {
- mHwRenderer.destroy();
- }
+ destroyHardwareRenderer();
+ mHwRenderer = null;
+
mSurface.release();
if (mInputChannel != null) {
@@ -1625,6 +1624,12 @@
}
}
+ private void destroyHardwareRenderer() {
+ if (mHwRenderer != null) {
+ mHwRenderer.destroy();
+ }
+ }
+
void updateConfiguration(Configuration config, boolean force) {
if (DEBUG_CONFIGURATION) Log.v(TAG,
"Applying new config to window "
@@ -2734,10 +2739,6 @@
public void childDrawableStateChanged(View child) {
}
- protected Rect getWindowFrame() {
- return mWinFrame;
- }
-
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
@@ -2816,16 +2817,15 @@
static class W extends IWindow.Stub {
private final WeakReference<ViewRoot> mViewRoot;
- public W(ViewRoot viewRoot, Context context) {
+ W(ViewRoot viewRoot) {
mViewRoot = new WeakReference<ViewRoot>(viewRoot);
}
- public void resized(int w, int h, Rect coveredInsets,
- Rect visibleInsets, boolean reportDraw, Configuration newConfig) {
+ public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets,
+ boolean reportDraw, Configuration newConfig) {
final ViewRoot viewRoot = mViewRoot.get();
if (viewRoot != null) {
- viewRoot.dispatchResized(w, h, coveredInsets,
- visibleInsets, reportDraw, newConfig);
+ viewRoot.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, newConfig);
}
}
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 4c6eced..98c03a6 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -34,7 +34,6 @@
#include <SkiaShader.h>
#include <SkiaColorFilter.h>
#include <Rect.h>
-#include <ui/Rect.h>
#include "TextLayout.h"
@@ -418,7 +417,7 @@
{ "nDrawTextRun", "(ILjava/lang/String;IIIIFFII)V",
(void*) android_view_GLES20Canvas_drawTextRun },
- { "nGetClipBounds", "(ILandroid/graphics/Rect;)Z",
+ { "nGetClipBounds", "(ILandroid/graphics/Rect;)Z",
(void*) android_view_GLES20Canvas_getClipBounds },
#endif
};