Terminate EGL when an app goes in the background

This does not happen on high end gfx devices. This happens
only if only one EGL context is initialized in the current
process.

Change-Id: Ibd1737efdf84eef8a84108b05795440d1ae9964e
This commit is contained in:
Romain Guy
2011-11-09 20:10:18 -08:00
parent 36a7f2a9ad
commit 8ff6b9ebee
13 changed files with 205 additions and 25 deletions

View File

@@ -315,6 +315,27 @@ class GLES20Canvas extends HardwareCanvas {
private static native void nFlushCaches(int level);
/**
* Release all resources associated with the underlying caches. This should
* only be called after a full flushCaches().
*
* @hide
*/
public static void terminateCaches() {
nTerminateCaches();
}
private static native void nTerminateCaches();
/**
* @hide
*/
public static void initCaches() {
nInitCaches();
}
private static native void nInitCaches();
///////////////////////////////////////////////////////////////////////////
// Display list
///////////////////////////////////////////////////////////////////////////

View File

@@ -25,6 +25,7 @@ import android.opengl.GLUtils;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
import com.google.android.gles_jni.EGLImpl;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
@@ -324,6 +325,15 @@ public abstract class HardwareRenderer {
Gl20Renderer.trimMemory(level);
}
/**
* Invoke this method when the system needs to clean up all resources
* associated with hardware rendering.
*/
static void terminate() {
Log.d(LOG_TAG, "Terminating hardware rendering");
Gl20Renderer.terminate();
}
/**
* Indicates whether hardware acceleration is currently enabled.
*
@@ -632,6 +642,8 @@ public abstract class HardwareRenderer {
throw new Surface.OutOfResourcesException("eglMakeCurrent failed "
+ GLUtils.getEGLErrorString(sEgl.eglGetError()));
}
initCaches();
// If mDirtyRegions is set, this means we have an EGL configuration
// with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
@@ -652,6 +664,8 @@ public abstract class HardwareRenderer {
return mEglContext.getGL();
}
abstract void initCaches();
EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
int[] attribs = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE };
@@ -895,6 +909,11 @@ public abstract class HardwareRenderer {
EGL_NONE
};
}
@Override
void initCaches() {
GLES20Canvas.initCaches();
}
@Override
boolean canDraw() {
@@ -987,16 +1006,7 @@ public abstract class HardwareRenderer {
if (eglContext == null) {
return;
} else {
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);
usePbufferSurface(eglContext);
}
switch (level) {
@@ -1010,5 +1020,46 @@ public abstract class HardwareRenderer {
break;
}
}
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);
}
static void terminate() {
synchronized (sEglLock) {
if (sEgl == null) return;
if (EGLImpl.getInitCount(sEglDisplay) == 1) {
EGLContext eglContext = sEglContextStorage.get();
if (eglContext == null) return;
usePbufferSurface(eglContext);
GLES20Canvas.terminateCaches();
sEgl.eglDestroyContext(sEglDisplay, eglContext);
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;
}
}
}
}
}

View File

@@ -567,7 +567,7 @@ public final class ViewRootImpl extends Handler implements ViewParent,
}
}
private void destroyHardwareResources() {
void destroyHardwareResources() {
if (mAttachInfo.mHardwareRenderer != null) {
if (mAttachInfo.mHardwareRenderer.isEnabled()) {
mAttachInfo.mHardwareRenderer.destroyLayers(mView);
@@ -880,12 +880,10 @@ public final class ViewRootImpl extends Handler implements ViewParent,
|| mNewSurfaceNeeded;
WindowManager.LayoutParams params = null;
int windowAttributesChanges = 0;
if (mWindowAttributesChanged) {
mWindowAttributesChanged = false;
surfaceChanged = true;
params = lp;
windowAttributesChanges = mWindowAttributesChangesFlag;
}
CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();
if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {

View File

@@ -16,6 +16,8 @@
package android.view;
import android.app.ActivityManager;
import android.content.ComponentCallbacks2;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
@@ -409,7 +411,30 @@ public class WindowManagerImpl implements WindowManager {
*/
public void trimMemory(int level) {
if (HardwareRenderer.isAvailable()) {
HardwareRenderer.trimMemory(level);
switch (level) {
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
// On low and medium end gfx devices
if (!ActivityManager.isHighEndGfx(getDefaultDisplay())) {
// Force a full memory flush
HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
// Destroy all hardware surfaces and resources associated to
// known windows
synchronized (this) {
if (mViews == null) return;
int count = mViews.length;
for (int i = 0; i < count; i++) {
mRoots[i].destroyHardwareResources();
}
}
// Terminate the hardware renderer to free all resources
HardwareRenderer.terminate();
break;
}
// high end gfx devices fall through to next case
default:
HardwareRenderer.trimMemory(level);
}
}
}

View File

@@ -160,6 +160,7 @@ LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
$(LOCAL_PATH)/android/graphics \
$(LOCAL_PATH)/../../libs/hwui \
$(LOCAL_PATH)/../../opengl/libs \
$(call include-path-for, bluedroid) \
$(call include-path-for, libhardware)/hardware \
$(call include-path-for, libhardware_legacy)/hardware_legacy \

View File

@@ -134,6 +134,18 @@ static void android_view_GLES20Canvas_flushCaches(JNIEnv* env, jobject clazz,
}
}
static void android_view_GLES20Canvas_initCaches(JNIEnv* env, jobject clazz) {
if (Caches::hasInstance()) {
Caches::getInstance().init();
}
}
static void android_view_GLES20Canvas_terminateCaches(JNIEnv* env, jobject clazz) {
if (Caches::hasInstance()) {
Caches::getInstance().terminate();
}
}
// ----------------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------------
@@ -756,6 +768,8 @@ static JNINativeMethod gMethods[] = {
{ "nPreserveBackBuffer", "()Z", (void*) android_view_GLES20Canvas_preserveBackBuffer },
{ "nDisableVsync", "()V", (void*) android_view_GLES20Canvas_disableVsync },
{ "nFlushCaches", "(I)V", (void*) android_view_GLES20Canvas_flushCaches },
{ "nInitCaches", "()V", (void*) android_view_GLES20Canvas_initCaches },
{ "nTerminateCaches", "()V", (void*) android_view_GLES20Canvas_terminateCaches },
{ "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer },
{ "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer },

View File

@@ -24,6 +24,8 @@
#include <EGL/egl.h>
#include <GLES/gl.h>
#include <EGL/egl_display.h>
#include <surfaceflinger/Surface.h>
#include <SkBitmap.h>
#include <SkPixelRef.h>
@@ -173,6 +175,16 @@ static jboolean jni_eglQuerySurface(JNIEnv *_env, jobject _this, jobject display
return success;
}
static jint jni_getInitCount(JNIEnv *_env, jobject _clazz, jobject display) {
EGLDisplay dpy = getDisplay(_env, display);
egl_display_t* eglDisplay = get_display(dpy);
return eglDisplay ? eglDisplay->getRefsCount() : 0;
}
static jboolean jni_eglReleaseThread(JNIEnv *_env, jobject _this) {
return eglReleaseThread();
}
static jboolean jni_eglChooseConfig(JNIEnv *_env, jobject _this, jobject display,
jintArray attrib_list, jobjectArray configs, jint config_size, jintArray num_config) {
if (display == NULL
@@ -526,6 +538,8 @@ static JNINativeMethod methods[] = {
{"eglInitialize", "(" DISPLAY "[I)Z", (void*)jni_eglInitialize },
{"eglQueryContext", "(" DISPLAY CONTEXT "I[I)Z", (void*)jni_eglQueryContext },
{"eglQuerySurface", "(" DISPLAY SURFACE "I[I)Z", (void*)jni_eglQuerySurface },
{"eglReleaseThread","()Z", (void*)jni_eglReleaseThread },
{"getInitCount", "(" DISPLAY ")I", (void*)jni_getInitCount },
{"eglChooseConfig", "(" DISPLAY "[I[" CONFIG "I[I)Z", (void*)jni_eglChooseConfig },
{"_eglCreateContext","(" DISPLAY CONFIG CONTEXT "[I)I", (void*)jni_eglCreateContext },
{"eglGetConfigs", "(" DISPLAY "[" CONFIG "I[I)Z", (void*)jni_eglGetConfigs },

View File

@@ -46,22 +46,16 @@ namespace uirenderer {
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
Caches::Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO),
lastDstMode(GL_ZERO), currentProgram(NULL) {
Caches::Caches(): Singleton<Caches>(), mInitialized(false) {
GLint maxTextureUnits;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) {
LOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT);
}
glGenBuffers(1, &meshBuffer);
glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
mCurrentBuffer = meshBuffer;
mRegionMesh = NULL;
init();
mDebugLevel = readDebugLevel();
LOGD("Enabling debug mode %d", mDebugLevel);
@@ -71,8 +65,40 @@ Caches::Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO),
#endif
}
Caches::~Caches() {
void Caches::init() {
if (mInitialized) return;
glGenBuffers(1, &meshBuffer);
glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
mCurrentBuffer = meshBuffer;
mRegionMesh = NULL;
blend = false;
lastSrcMode = GL_ZERO;
lastDstMode = GL_ZERO;
currentProgram = NULL;
mInitialized = true;
}
void Caches::terminate() {
if (!mInitialized) return;
glDeleteBuffers(1, &meshBuffer);
mCurrentBuffer = 0;
glDeleteBuffers(1, &mRegionMeshIndices);
delete[] mRegionMesh;
mRegionMesh = NULL;
fboCache.clear();
programCache.clear();
currentProgram = NULL;
mInitialized = false;
}
///////////////////////////////////////////////////////////////////////////////

View File

@@ -86,7 +86,6 @@ struct CacheLogger {
class ANDROID_API Caches: public Singleton<Caches> {
Caches();
~Caches();
friend class Singleton<Caches>;
@@ -108,6 +107,11 @@ public:
kFlushMode_Full
};
/**
* Initializes the cache.
*/
void init();
/**
* Flush the cache.
*
@@ -115,6 +119,12 @@ public:
*/
void flush(FlushMode mode);
/**
* Destroys all resources associated with this cache. This should
* be called after a flush(kFlushMode_Full).
*/
void terminate();
/**
* Indicates whether the renderer is in debug mode.
* This debug mode provides limited information to app developers.
@@ -194,6 +204,7 @@ public:
private:
DebugLevel mDebugLevel;
bool mInitialized;
}; // class Caches
}; // namespace uirenderer

View File

@@ -314,6 +314,16 @@ class EGLLogWrapper implements EGL11 {
checkError();
return result;
}
/** @hide **/
public boolean eglReleaseThread() {
begin("eglReleaseThread");
end();
boolean result = mEgl10.eglReleaseThread();
returns(result);
checkError();
return result;
}
public boolean eglSwapBuffers(EGLDisplay display, EGLSurface surface) {
begin("eglInitialize");

View File

@@ -31,6 +31,8 @@ public class EGLImpl implements EGL10 {
public native boolean eglInitialize(EGLDisplay display, int[] major_minor);
public native boolean eglQueryContext(EGLDisplay display, EGLContext context, int attribute, int[] value);
public native boolean eglQuerySurface(EGLDisplay display, EGLSurface surface, int attribute, int[] value);
/** @hide **/
public native boolean eglReleaseThread();
public native boolean eglChooseConfig(EGLDisplay display, int[] attrib_list, EGLConfig[] configs, int config_size, int[] num_config);
public native boolean eglGetConfigAttrib(EGLDisplay display, EGLConfig config, int attribute, int[] value);
public native boolean eglGetConfigs(EGLDisplay display, EGLConfig[] configs, int config_size, int[] num_config);
@@ -44,6 +46,9 @@ public class EGLImpl implements EGL10 {
public native boolean eglCopyBuffers(EGLDisplay display, EGLSurface surface, Object native_pixmap);
public native boolean eglWaitGL();
public native boolean eglWaitNative(int engine, Object bindTarget);
/** @hide **/
public static native int getInitCount(EGLDisplay display);
public EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, int[] attrib_list) {
int eglContextId = _eglCreateContext(display, config, share_context, attrib_list);
@@ -85,7 +90,7 @@ public class EGLImpl implements EGL10 {
eglSurfaceId = _eglCreateWindowSurface(display, config, sur, attrib_list);
} else if (native_window instanceof SurfaceTexture) {
eglSurfaceId = _eglCreateWindowSurfaceTexture(display, config,
(SurfaceTexture) native_window, attrib_list);
native_window, attrib_list);
} else {
throw new java.lang.UnsupportedOperationException(
"eglCreateWindowSurface() can only be called with an instance of " +

View File

@@ -114,6 +114,8 @@ public interface EGL10 extends EGL {
boolean eglQueryContext(EGLDisplay display, EGLContext context, int attribute, int[] value);
String eglQueryString(EGLDisplay display, int name);
boolean eglQuerySurface(EGLDisplay display, EGLSurface surface, int attribute, int[] value);
/** @hide **/
boolean eglReleaseThread();
boolean eglSwapBuffers(EGLDisplay display, EGLSurface surface);
boolean eglTerminate(EGLDisplay display);
boolean eglWaitGL();

View File

@@ -91,6 +91,8 @@ public:
inline bool isValid() const { return magic == '_dpy'; }
inline bool isAlive() const { return isValid(); }
inline uint32_t getRefsCount() const { return refs; }
struct strings_t {
char const * vendor;
char const * version;