OSDN Git Service

Fix EGL cleanup code for CTS android.nativeopengl
authorYahan Zhou <yahan@google.com>
Fri, 10 Jun 2016 00:54:34 +0000 (17:54 -0700)
committerYahan Zhou <yahan@google.com>
Mon, 20 Jun 2016 19:43:32 +0000 (12:43 -0700)
In CTS, android.nativeopengl.EGLCleanupTest tries to repeatatively
create and destroy egl context for 1000 times to check for memory leaks.
This patch releases resources when destroying contexts.

It turns android.nativeopengl.EGLCleanupTest from failure to flaky,
because of two other issue:

(1) There is a 1 min timeout in the test:
cts/tools/tradefed-host/src/com/android/cts/tradefed/testtype/
WrappedGTest.java
Our emulator can barely finish it within 1 min.

(2) There is another bug that may trigger UI rendering events after the
test begun, which crashes randomly because this test releases rendering
resources. It needs to be fixed in another patch.

Change-Id: Id285f526149638c63eb78c167c7d2daef7bd2412

system/egl/egl.cpp
system/egl/eglDisplay.cpp
system/egl/eglDisplay.h

index cebe93e..c2fac88 100644 (file)
@@ -14,6 +14,7 @@
 * limitations under the License.
 */
 
+#include <assert.h>
 #include "HostConnection.h"
 #include "ThreadInfo.h"
 #include "eglDisplay.h"
@@ -130,6 +131,8 @@ const char *  eglStrError(EGLint err)
             setErrorReturn(EGL_BAD_DISPLAY, EGL_FALSE);    \
     }
 
+// The one and only supported display object.
+static eglDisplay s_display;
 
 EGLContext_t::EGLContext_t(EGLDisplay dpy, EGLConfig config, EGLContext_t* shareCtx) :
     dpy(dpy),
@@ -152,10 +155,14 @@ EGLContext_t::EGLContext_t(EGLDisplay dpy, EGLConfig config, EGLContext_t* share
         sharedGroup = shareCtx->getSharedGroup();
     else
         sharedGroup = GLSharedGroupPtr(new GLSharedGroup());
+    assert(dpy == (EGLDisplay)&s_display);
+    s_display.onCreateContext((EGLContext)this);
 };
 
 EGLContext_t::~EGLContext_t()
 {
+    assert(dpy == (EGLDisplay)&s_display);
+    s_display.onDestroyContext((EGLContext)this);
     delete clientState;
     delete [] versionString;
     delete [] vendorString;
@@ -216,6 +223,8 @@ egl_surface_t::egl_surface_t(EGLDisplay dpy, EGLConfig config, EGLint surfaceTyp
     height = 0;
     texFormat = EGL_NO_TEXTURE;
     texTarget = EGL_NO_TEXTURE;
+    assert(dpy == (EGLDisplay)&s_display);
+    s_display.onCreateSurface((EGLSurface)this);
 }
 
 EGLint egl_surface_t::getSwapBehavior() const {
@@ -224,6 +233,8 @@ EGLint egl_surface_t::getSwapBehavior() const {
 
 egl_surface_t::~egl_surface_t()
 {
+    assert(dpy == (EGLDisplay)&s_display);
+    s_display.onDestroySurface((EGLSurface)this);
 }
 
 // ----------------------------------------------------------------------------
@@ -475,9 +486,6 @@ static const char *getGLString(int glEnum)
 
 // ----------------------------------------------------------------------------
 
-// The one and only supported display object.
-static eglDisplay s_display;
-
 static EGLClient_eglInterface s_eglIface = {
     getThreadInfo: getEGLThreadInfo,
     getGLString: getGLString
@@ -797,8 +805,34 @@ EGLBoolean eglWaitClient()
 EGLBoolean eglReleaseThread()
 {
     EGLThreadInfo *tInfo = getEGLThreadInfo();
-    if (tInfo && tInfo->currentContext) {
-        return eglMakeCurrent(&s_display, EGL_NO_CONTEXT, EGL_NO_SURFACE, EGL_NO_SURFACE);
+    if (tInfo) {
+        tInfo->eglError = EGL_SUCCESS;
+        EGLContext_t* context = tInfo->currentContext;
+        if (context) {
+            // The following code is doing pretty much the same thing as
+            // eglMakeCurrent(&s_display, EGL_NO_CONTEXT, EGL_NO_SURFACE, EGL_NO_SURFACE)
+            // with the only issue that we do not require a valid display here.
+            DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
+            rcEnc->rcMakeCurrent(rcEnc, 0, 0, 0);
+             if (context->version == 2) {
+                hostCon->gl2Encoder()->setClientState(NULL);
+                hostCon->gl2Encoder()->setSharedGroup(GLSharedGroupPtr());
+            }
+            else {
+                hostCon->glEncoder()->setClientState(NULL);
+                hostCon->glEncoder()->setSharedGroup(GLSharedGroupPtr());
+            }
+            context->flags &= ~EGLContext_t::IS_CURRENT;
+
+            if (context->deletePending) {
+                if (context->rcContext) {
+                    rcEnc->rcDestroyContext(rcEnc, context->rcContext);
+                    context->rcContext = 0;
+                }
+                delete context;
+            }
+            tInfo->currentContext = 0;
+        }
     }
     return EGL_TRUE;
 }
index 9340482..ce61ffd 100644 (file)
@@ -63,11 +63,16 @@ eglDisplay::eglDisplay() :
     m_extensionString(NULL)
 {
     pthread_mutex_init(&m_lock, NULL);
+    pthread_mutex_init(&m_ctxLock, NULL);
+    pthread_mutex_init(&m_surfaceLock, NULL);
 }
 
 eglDisplay::~eglDisplay()
 {
+    terminate();
     pthread_mutex_destroy(&m_lock);
+    pthread_mutex_destroy(&m_ctxLock);
+    pthread_mutex_destroy(&m_surfaceLock);
 }
 
 bool eglDisplay::initialize(EGLClient_eglInterface *eglIface)
@@ -209,6 +214,20 @@ void eglDisplay::terminate()
 {
     pthread_mutex_lock(&m_lock);
     if (m_initialized) {
+        // Cannot use the for loop in the following code because
+        // eglDestroyContext may erase elements.
+        auto ctxIte = m_contexts.begin();
+        while (ctxIte != m_contexts.end()) {
+            auto ctxToDelete = ctxIte;
+            ctxIte ++;
+            eglDestroyContext(static_cast<EGLDisplay>(this), *ctxToDelete);
+        }
+        auto surfaceIte = m_surfaces.begin();
+        while (surfaceIte != m_surfaces.end()) {
+            auto surfaceToDelete = surfaceIte;
+            surfaceIte ++;
+            eglDestroySurface(static_cast<EGLDisplay>(this), *surfaceToDelete);
+        }
         m_initialized = false;
         delete [] m_configs;
         m_configs = NULL;
@@ -483,3 +502,28 @@ EGLBoolean eglDisplay::getConfigGLPixelFormat(EGLConfig config, GLenum * format)
 
     return EGL_TRUE;
 }
+
+void eglDisplay::onCreateContext(EGLContext ctx) {
+    pthread_mutex_lock(&m_ctxLock);
+    m_contexts.insert(ctx);
+    pthread_mutex_unlock(&m_ctxLock);
+}
+
+void eglDisplay::onCreateSurface(EGLSurface surface) {
+    pthread_mutex_lock(&m_surfaceLock);
+    m_surfaces.insert(surface);
+    pthread_mutex_unlock(&m_surfaceLock);
+}
+
+void eglDisplay::onDestroyContext(EGLContext ctx) {
+    pthread_mutex_lock(&m_ctxLock);
+    m_contexts.erase(ctx);
+    pthread_mutex_unlock(&m_ctxLock);
+}
+
+void eglDisplay::onDestroySurface(EGLSurface surface) {
+    pthread_mutex_lock(&m_surfaceLock);
+    m_surfaces.erase(surface);
+    pthread_mutex_unlock(&m_surfaceLock);
+}
+
index 9d979d9..24116c9 100644 (file)
@@ -21,6 +21,7 @@
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
 #include "EGLClientIface.h"
+#include <unordered_set>
 #include <utils/KeyedVector.h>
 
 #include <ui/PixelFormat.h>
@@ -54,6 +55,12 @@ public:
     EGLBoolean getConfigNativePixelFormat(EGLConfig config, PixelFormat * format);
 
     void     dumpConfig(EGLConfig config);
+
+    void onCreateContext(EGLContext ctx);
+    void onCreateSurface(EGLSurface surface);
+
+    void onDestroyContext(EGLContext ctx);
+    void onDestroySurface(EGLSurface surface);
 private:
     EGLClient_glesInterface *loadGLESClientAPI(const char *libName,
                                                EGLClient_eglInterface *eglIface,
@@ -84,6 +91,11 @@ private:
     char *m_versionString;
     char *m_vendorString;
     char *m_extensionString;
+
+    std::unordered_set<EGLContext> m_contexts;
+    std::unordered_set<EGLSurface> m_surfaces;
+    pthread_mutex_t m_ctxLock;
+    pthread_mutex_t m_surfaceLock;
 };
 
 #endif