Merge "Terminate EGL when an app goes in the background" into ics-mr1

This commit is contained in:
Romain Guy
2011-11-10 17:10:41 -08:00
committed by Android (Google) Code Review
12 changed files with 204 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;
@@ -343,6 +344,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.
*
@@ -651,6 +661,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
@@ -671,6 +683,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 };
@@ -914,6 +928,11 @@ public abstract class HardwareRenderer {
EGL_NONE
};
}
@Override
void initCaches() {
GLES20Canvas.initCaches();
}
@Override
boolean canDraw() {
@@ -1006,16 +1025,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) {
@@ -1029,5 +1039,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

@@ -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;