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:
@@ -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
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 " +
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user