Skip to content

Commit 8ff6b9e

Browse files
author
Romain Guy
committed
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
1 parent 36a7f2a commit 8ff6b9e

File tree

13 files changed

+205
-25
lines changed

13 files changed

+205
-25
lines changed

core/java/android/view/GLES20Canvas.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,27 @@ public static void flushCaches(int level) {
315315

316316
private static native void nFlushCaches(int level);
317317

318+
/**
319+
* Release all resources associated with the underlying caches. This should
320+
* only be called after a full flushCaches().
321+
*
322+
* @hide
323+
*/
324+
public static void terminateCaches() {
325+
nTerminateCaches();
326+
}
327+
328+
private static native void nTerminateCaches();
329+
330+
/**
331+
* @hide
332+
*/
333+
public static void initCaches() {
334+
nInitCaches();
335+
}
336+
337+
private static native void nInitCaches();
338+
318339
///////////////////////////////////////////////////////////////////////////
319340
// Display list
320341
///////////////////////////////////////////////////////////////////////////

core/java/android/view/HardwareRenderer.java

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import android.os.SystemClock;
2626
import android.os.SystemProperties;
2727
import android.util.Log;
28+
import com.google.android.gles_jni.EGLImpl;
2829

2930
import javax.microedition.khronos.egl.EGL10;
3031
import javax.microedition.khronos.egl.EGL11;
@@ -324,6 +325,15 @@ static void trimMemory(int level) {
324325
Gl20Renderer.trimMemory(level);
325326
}
326327

328+
/**
329+
* Invoke this method when the system needs to clean up all resources
330+
* associated with hardware rendering.
331+
*/
332+
static void terminate() {
333+
Log.d(LOG_TAG, "Terminating hardware rendering");
334+
Gl20Renderer.terminate();
335+
}
336+
327337
/**
328338
* Indicates whether hardware acceleration is currently enabled.
329339
*
@@ -632,6 +642,8 @@ GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException
632642
throw new Surface.OutOfResourcesException("eglMakeCurrent failed "
633643
+ GLUtils.getEGLErrorString(sEgl.eglGetError()));
634644
}
645+
646+
initCaches();
635647

636648
// If mDirtyRegions is set, this means we have an EGL configuration
637649
// with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
@@ -652,6 +664,8 @@ GL createEglSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException
652664
return mEglContext.getGL();
653665
}
654666

667+
abstract void initCaches();
668+
655669
EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
656670
int[] attribs = { EGL_CONTEXT_CLIENT_VERSION, mGlVersion, EGL_NONE };
657671

@@ -895,6 +909,11 @@ int[] getConfig(boolean dirtyRegions) {
895909
EGL_NONE
896910
};
897911
}
912+
913+
@Override
914+
void initCaches() {
915+
GLES20Canvas.initCaches();
916+
}
898917

899918
@Override
900919
boolean canDraw() {
@@ -987,16 +1006,7 @@ static void trimMemory(int level) {
9871006
if (eglContext == null) {
9881007
return;
9891008
} else {
990-
synchronized (sPbufferLock) {
991-
// Create a temporary 1x1 pbuffer so we have a context
992-
// to clear our OpenGL objects
993-
if (sPbuffer == null) {
994-
sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
995-
EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
996-
});
997-
}
998-
}
999-
sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
1009+
usePbufferSurface(eglContext);
10001010
}
10011011

10021012
switch (level) {
@@ -1010,5 +1020,46 @@ static void trimMemory(int level) {
10101020
break;
10111021
}
10121022
}
1023+
1024+
private static void usePbufferSurface(EGLContext eglContext) {
1025+
synchronized (sPbufferLock) {
1026+
// Create a temporary 1x1 pbuffer so we have a context
1027+
// to clear our OpenGL objects
1028+
if (sPbuffer == null) {
1029+
sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
1030+
EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
1031+
});
1032+
}
1033+
}
1034+
sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
1035+
}
1036+
1037+
static void terminate() {
1038+
synchronized (sEglLock) {
1039+
if (sEgl == null) return;
1040+
1041+
if (EGLImpl.getInitCount(sEglDisplay) == 1) {
1042+
EGLContext eglContext = sEglContextStorage.get();
1043+
if (eglContext == null) return;
1044+
1045+
usePbufferSurface(eglContext);
1046+
GLES20Canvas.terminateCaches();
1047+
1048+
sEgl.eglDestroyContext(sEglDisplay, eglContext);
1049+
sEglContextStorage.remove();
1050+
1051+
sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
1052+
sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1053+
1054+
sEgl.eglReleaseThread();
1055+
sEgl.eglTerminate(sEglDisplay);
1056+
1057+
sEgl = null;
1058+
sEglDisplay = null;
1059+
sEglConfig = null;
1060+
sPbuffer = null;
1061+
}
1062+
}
1063+
}
10131064
}
10141065
}

core/java/android/view/ViewRootImpl.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ public void setView(View view, WindowManager.LayoutParams attrs, View panelParen
567567
}
568568
}
569569

570-
private void destroyHardwareResources() {
570+
void destroyHardwareResources() {
571571
if (mAttachInfo.mHardwareRenderer != null) {
572572
if (mAttachInfo.mHardwareRenderer.isEnabled()) {
573573
mAttachInfo.mHardwareRenderer.destroyLayers(mView);
@@ -880,12 +880,10 @@ private void performTraversals() {
880880
|| mNewSurfaceNeeded;
881881

882882
WindowManager.LayoutParams params = null;
883-
int windowAttributesChanges = 0;
884883
if (mWindowAttributesChanged) {
885884
mWindowAttributesChanged = false;
886885
surfaceChanged = true;
887886
params = lp;
888-
windowAttributesChanges = mWindowAttributesChangesFlag;
889887
}
890888
CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();
891889
if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {

core/java/android/view/WindowManagerImpl.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package android.view;
1818

19+
import android.app.ActivityManager;
20+
import android.content.ComponentCallbacks2;
1921
import android.content.res.CompatibilityInfo;
2022
import android.content.res.Configuration;
2123
import android.graphics.PixelFormat;
@@ -409,7 +411,30 @@ public void closeAll(IBinder token, String who, String what) {
409411
*/
410412
public void trimMemory(int level) {
411413
if (HardwareRenderer.isAvailable()) {
412-
HardwareRenderer.trimMemory(level);
414+
switch (level) {
415+
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
416+
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
417+
// On low and medium end gfx devices
418+
if (!ActivityManager.isHighEndGfx(getDefaultDisplay())) {
419+
// Force a full memory flush
420+
HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
421+
// Destroy all hardware surfaces and resources associated to
422+
// known windows
423+
synchronized (this) {
424+
if (mViews == null) return;
425+
int count = mViews.length;
426+
for (int i = 0; i < count; i++) {
427+
mRoots[i].destroyHardwareResources();
428+
}
429+
}
430+
// Terminate the hardware renderer to free all resources
431+
HardwareRenderer.terminate();
432+
break;
433+
}
434+
// high end gfx devices fall through to next case
435+
default:
436+
HardwareRenderer.trimMemory(level);
437+
}
413438
}
414439
}
415440

core/jni/Android.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ LOCAL_C_INCLUDES += \
160160
$(JNI_H_INCLUDE) \
161161
$(LOCAL_PATH)/android/graphics \
162162
$(LOCAL_PATH)/../../libs/hwui \
163+
$(LOCAL_PATH)/../../opengl/libs \
163164
$(call include-path-for, bluedroid) \
164165
$(call include-path-for, libhardware)/hardware \
165166
$(call include-path-for, libhardware_legacy)/hardware_legacy \

core/jni/android_view_GLES20Canvas.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,18 @@ static void android_view_GLES20Canvas_flushCaches(JNIEnv* env, jobject clazz,
134134
}
135135
}
136136

137+
static void android_view_GLES20Canvas_initCaches(JNIEnv* env, jobject clazz) {
138+
if (Caches::hasInstance()) {
139+
Caches::getInstance().init();
140+
}
141+
}
142+
143+
static void android_view_GLES20Canvas_terminateCaches(JNIEnv* env, jobject clazz) {
144+
if (Caches::hasInstance()) {
145+
Caches::getInstance().terminate();
146+
}
147+
}
148+
137149
// ----------------------------------------------------------------------------
138150
// Constructors
139151
// ----------------------------------------------------------------------------
@@ -756,6 +768,8 @@ static JNINativeMethod gMethods[] = {
756768
{ "nPreserveBackBuffer", "()Z", (void*) android_view_GLES20Canvas_preserveBackBuffer },
757769
{ "nDisableVsync", "()V", (void*) android_view_GLES20Canvas_disableVsync },
758770
{ "nFlushCaches", "(I)V", (void*) android_view_GLES20Canvas_flushCaches },
771+
{ "nInitCaches", "()V", (void*) android_view_GLES20Canvas_initCaches },
772+
{ "nTerminateCaches", "()V", (void*) android_view_GLES20Canvas_terminateCaches },
759773

760774
{ "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer },
761775
{ "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer },

core/jni/com_google_android_gles_jni_EGLImpl.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include <EGL/egl.h>
2525
#include <GLES/gl.h>
2626

27+
#include <EGL/egl_display.h>
28+
2729
#include <surfaceflinger/Surface.h>
2830
#include <SkBitmap.h>
2931
#include <SkPixelRef.h>
@@ -173,6 +175,16 @@ static jboolean jni_eglQuerySurface(JNIEnv *_env, jobject _this, jobject display
173175
return success;
174176
}
175177

178+
static jint jni_getInitCount(JNIEnv *_env, jobject _clazz, jobject display) {
179+
EGLDisplay dpy = getDisplay(_env, display);
180+
egl_display_t* eglDisplay = get_display(dpy);
181+
return eglDisplay ? eglDisplay->getRefsCount() : 0;
182+
}
183+
184+
static jboolean jni_eglReleaseThread(JNIEnv *_env, jobject _this) {
185+
return eglReleaseThread();
186+
}
187+
176188
static jboolean jni_eglChooseConfig(JNIEnv *_env, jobject _this, jobject display,
177189
jintArray attrib_list, jobjectArray configs, jint config_size, jintArray num_config) {
178190
if (display == NULL
@@ -526,6 +538,8 @@ static JNINativeMethod methods[] = {
526538
{"eglInitialize", "(" DISPLAY "[I)Z", (void*)jni_eglInitialize },
527539
{"eglQueryContext", "(" DISPLAY CONTEXT "I[I)Z", (void*)jni_eglQueryContext },
528540
{"eglQuerySurface", "(" DISPLAY SURFACE "I[I)Z", (void*)jni_eglQuerySurface },
541+
{"eglReleaseThread","()Z", (void*)jni_eglReleaseThread },
542+
{"getInitCount", "(" DISPLAY ")I", (void*)jni_getInitCount },
529543
{"eglChooseConfig", "(" DISPLAY "[I[" CONFIG "I[I)Z", (void*)jni_eglChooseConfig },
530544
{"_eglCreateContext","(" DISPLAY CONFIG CONTEXT "[I)I", (void*)jni_eglCreateContext },
531545
{"eglGetConfigs", "(" DISPLAY "[" CONFIG "I[I)Z", (void*)jni_eglGetConfigs },

libs/hwui/Caches.cpp

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,22 +46,16 @@ namespace uirenderer {
4646
// Constructors/destructor
4747
///////////////////////////////////////////////////////////////////////////////
4848

49-
Caches::Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO),
50-
lastDstMode(GL_ZERO), currentProgram(NULL) {
49+
Caches::Caches(): Singleton<Caches>(), mInitialized(false) {
5150
GLint maxTextureUnits;
5251
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
5352
if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) {
5453
LOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT);
5554
}
5655

57-
glGenBuffers(1, &meshBuffer);
58-
glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
59-
glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
60-
6156
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
6257

63-
mCurrentBuffer = meshBuffer;
64-
mRegionMesh = NULL;
58+
init();
6559

6660
mDebugLevel = readDebugLevel();
6761
LOGD("Enabling debug mode %d", mDebugLevel);
@@ -71,8 +65,40 @@ Caches::Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO),
7165
#endif
7266
}
7367

74-
Caches::~Caches() {
68+
void Caches::init() {
69+
if (mInitialized) return;
70+
71+
glGenBuffers(1, &meshBuffer);
72+
glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
73+
glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
74+
75+
mCurrentBuffer = meshBuffer;
76+
mRegionMesh = NULL;
77+
78+
blend = false;
79+
lastSrcMode = GL_ZERO;
80+
lastDstMode = GL_ZERO;
81+
currentProgram = NULL;
82+
83+
mInitialized = true;
84+
}
85+
86+
void Caches::terminate() {
87+
if (!mInitialized) return;
88+
89+
glDeleteBuffers(1, &meshBuffer);
90+
mCurrentBuffer = 0;
91+
92+
glDeleteBuffers(1, &mRegionMeshIndices);
7593
delete[] mRegionMesh;
94+
mRegionMesh = NULL;
95+
96+
fboCache.clear();
97+
98+
programCache.clear();
99+
currentProgram = NULL;
100+
101+
mInitialized = false;
76102
}
77103

78104
///////////////////////////////////////////////////////////////////////////////

libs/hwui/Caches.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ struct CacheLogger {
8686

8787
class ANDROID_API Caches: public Singleton<Caches> {
8888
Caches();
89-
~Caches();
9089

9190
friend class Singleton<Caches>;
9291

@@ -108,13 +107,24 @@ class ANDROID_API Caches: public Singleton<Caches> {
108107
kFlushMode_Full
109108
};
110109

110+
/**
111+
* Initializes the cache.
112+
*/
113+
void init();
114+
111115
/**
112116
* Flush the cache.
113117
*
114118
* @param mode Indicates how much of the cache should be flushed
115119
*/
116120
void flush(FlushMode mode);
117121

122+
/**
123+
* Destroys all resources associated with this cache. This should
124+
* be called after a flush(kFlushMode_Full).
125+
*/
126+
void terminate();
127+
118128
/**
119129
* Indicates whether the renderer is in debug mode.
120130
* This debug mode provides limited information to app developers.
@@ -194,6 +204,7 @@ class ANDROID_API Caches: public Singleton<Caches> {
194204

195205
private:
196206
DebugLevel mDebugLevel;
207+
bool mInitialized;
197208
}; // class Caches
198209

199210
}; // namespace uirenderer

0 commit comments

Comments
 (0)