Merge "Use only one GL context per process, share chaches."
This commit is contained in:
@@ -44,7 +44,7 @@ class GLES20Canvas extends Canvas {
|
||||
@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 @@ class GLES20Canvas extends Canvas {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,9 +35,10 @@ import javax.microedition.khronos.opengles.GL;
|
||||
* @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 @@ public abstract class HardwareRenderer {
|
||||
*
|
||||
* @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 @@ public abstract class HardwareRenderer {
|
||||
*/
|
||||
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 @@ public abstract class HardwareRenderer {
|
||||
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 @@ public abstract class HardwareRenderer {
|
||||
*/
|
||||
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 @@ public abstract class HardwareRenderer {
|
||||
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 @@ public abstract class HardwareRenderer {
|
||||
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 @@ public abstract class HardwareRenderer {
|
||||
* 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 @@ public abstract class HardwareRenderer {
|
||||
@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 @@ public abstract class HardwareRenderer {
|
||||
|
||||
@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 @@ public abstract class HardwareRenderer {
|
||||
* @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 @@ public abstract class HardwareRenderer {
|
||||
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 @@ public abstract class HardwareRenderer {
|
||||
|
||||
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 @@ public abstract class HardwareRenderer {
|
||||
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 @@ public abstract class HardwareRenderer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hardware renderer using OpenGL ES 2.0.
|
||||
*/
|
||||
@@ -526,8 +555,9 @@ public abstract class HardwareRenderer {
|
||||
}
|
||||
|
||||
@Override
|
||||
Canvas createCanvas() {
|
||||
return mGlCanvas = new GLES20Canvas(mGl, mTranslucent);
|
||||
GLES20Canvas createCanvas() {
|
||||
// TODO: Pass mTranslucent instead of true
|
||||
return mGlCanvas = new GLES20Canvas(mGl, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -251,7 +251,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
|
||||
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 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
|
||||
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 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
|
||||
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 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
|
||||
}
|
||||
|
||||
if (hwIntialized) {
|
||||
mHwRenderer.setup(mWidth, mHeight, mAttachInfo);
|
||||
mHwRenderer.setup(mWidth, mHeight);
|
||||
}
|
||||
|
||||
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
|
||||
@@ -1598,9 +1597,9 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
|
||||
mAttachInfo.mRootView = null;
|
||||
mAttachInfo.mSurface = null;
|
||||
|
||||
if (mHwRenderer != null) {
|
||||
mHwRenderer.destroy();
|
||||
}
|
||||
destroyHardwareRenderer();
|
||||
mHwRenderer = null;
|
||||
|
||||
mSurface.release();
|
||||
|
||||
if (mInputChannel != null) {
|
||||
@@ -1625,6 +1624,12 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
|
||||
}
|
||||
}
|
||||
|
||||
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 final class ViewRoot extends Handler implements ViewParent, View.AttachIn
|
||||
public void childDrawableStateChanged(View child) {
|
||||
}
|
||||
|
||||
protected Rect getWindowFrame() {
|
||||
return mWinFrame;
|
||||
}
|
||||
|
||||
void checkThread() {
|
||||
if (mThread != Thread.currentThread()) {
|
||||
throw new CalledFromWrongThreadException(
|
||||
@@ -2816,16 +2817,15 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user