Bug 1271103 - Backs out the patch for Bug 1136364 and related patches. r=snorp
authorDylan Roeh <droeh@mozilla.com>
Tue, 07 Jun 2016 09:25:26 -0500
changeset 300936 dee200e969ee18b00cfb087f9624ef75f0453dae
parent 300935 d918f0e6388908251add66d89ba295d89abb2847
child 300937 95f9da5a14febf2a7c195453532fe9b696b20baa
push id19599
push usercbook@mozilla.com
push dateWed, 08 Jun 2016 10:16:21 +0000
treeherderfx-team@81f4cc3f6f4c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1271103, 1136364
milestone50.0a1
Bug 1271103 - Backs out the patch for Bug 1136364 and related patches. r=snorp
gfx/gl/GLContextProviderEGL.cpp
mobile/android/base/java/org/mozilla/gecko/gfx/GLController.java
mobile/android/base/java/org/mozilla/gecko/gfx/LayerView.java
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
widget/android/nsWindow.cpp
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -15,20 +15,16 @@
 
 #if defined(XP_UNIX)
     #ifdef MOZ_WIDGET_GONK
         #include "libdisplay/GonkDisplay.h"
         #include "nsWindow.h"
         #include "nsScreenManagerGonk.h"
     #endif
 
-    #ifdef MOZ_WIDGET_ANDROID
-        #include "AndroidBridge.h"
-    #endif
-
     #ifdef ANDROID
         #include <android/log.h>
         #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
 
         #ifdef MOZ_WIDGET_GONK
             #include "cutils/properties.h"
             #include <ui/GraphicBuffer.h>
 
@@ -168,25 +164,17 @@ DestroySurface(EGLSurface oldSurface) {
 }
 
 static EGLSurface
 CreateSurfaceForWindow(nsIWidget* widget, const EGLConfig& config) {
     EGLSurface newSurface = nullptr;
 
     MOZ_ASSERT(widget);
 #ifdef MOZ_WIDGET_ANDROID
-    void* javaSurface = GET_NATIVE_WINDOW(widget);
-    if (!javaSurface) {
-        MOZ_CRASH("GFX: Failed to get Java surface.\n");
-    }
-    JNIEnv* const env = jni::GetEnvForThread();
-    void* nativeWindow = AndroidBridge::Bridge()->AcquireNativeWindow(env, reinterpret_cast<jobject>(javaSurface));
-    newSurface = sEGLLibrary.fCreateWindowSurface(sEGLLibrary.fGetDisplay(EGL_DEFAULT_DISPLAY), config,
-                                                  nativeWindow, 0);
-    AndroidBridge::Bridge()->ReleaseNativeWindow(nativeWindow);
+    newSurface = EGLSurface(widget->GetNativeData(NS_NATIVE_NEW_EGL_SURFACE));
 #else
     newSurface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config,
                                                   GET_NATIVE_WINDOW(widget), 0);
 #endif
     return newSurface;
 }
 
 GLContextEGL::GLContextEGL(
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/GLController.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/GLController.java
@@ -8,37 +8,69 @@ package org.mozilla.gecko.gfx;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.mozglue.JNIObject;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.util.Log;
 
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+
 /**
  * This class is a singleton that tracks EGL and compositor things over
  * the lifetime of Fennec running.
  * We only ever create one C++ compositor over Fennec's lifetime, but
  * most of the Java-side objects (e.g. LayerView, GeckoLayerClient,
  * LayerRenderer) can all get destroyed and re-created if the GeckoApp
  * activity is destroyed. This GLController is never destroyed, so that
  * the mCompositorCreated field and other state variables are always
  * accurate.
  */
 public class GLController extends JNIObject {
+    private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
     private static final String LOGTAG = "GeckoGLController";
 
     /* package */ LayerView mView;
     private boolean mServerSurfaceValid;
     private int mWidth, mHeight;
 
     /* This is written by the compositor thread (while the UI thread
      * is blocked on it) and read by the UI thread. */
     private volatile boolean mCompositorCreated;
 
+    private static EGL10 sEGL;
+    private static EGLDisplay sEGLDisplay;
+    private static EGLConfig sEGLConfig;
+    private EGLSurface mEGLSurfaceForCompositor;
+
+    private static final int LOCAL_EGL_OPENGL_ES2_BIT = 4;
+
+    private static final int[] CONFIG_SPEC_16BPP = {
+        EGL10.EGL_RED_SIZE, 5,
+        EGL10.EGL_GREEN_SIZE, 6,
+        EGL10.EGL_BLUE_SIZE, 5,
+        EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
+        EGL10.EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
+        EGL10.EGL_NONE
+    };
+
+    private static final int[] CONFIG_SPEC_24BPP = {
+        EGL10.EGL_RED_SIZE, 8,
+        EGL10.EGL_GREEN_SIZE, 8,
+        EGL10.EGL_BLUE_SIZE, 8,
+        EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
+        EGL10.EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
+        EGL10.EGL_NONE
+    };
+
     @WrapForJNI @Override // JNIObject
     protected native void disposeNative();
 
     // Gecko thread sets its Java instances; does not block UI thread.
     @WrapForJNI
     /* package */ native void attachToJava(GeckoLayerClient layerClient,
                                            NativePanZoomController npzc);
 
@@ -64,39 +96,52 @@ public class GLController extends JNIObj
     public GLController() {
     }
 
     synchronized void serverSurfaceDestroyed() {
         ThreadUtils.assertOnUiThread();
 
         mServerSurfaceValid = false;
 
+        if (mEGLSurfaceForCompositor != null) {
+          sEGL.eglDestroySurface(sEGLDisplay, mEGLSurfaceForCompositor);
+          mEGLSurfaceForCompositor = null;
+        }
+
         // We need to coordinate with Gecko when pausing composition, to ensure
         // that Gecko never executes a draw event while the compositor is paused.
         // This is sent synchronously to make sure that we don't attempt to use
         // any outstanding Surfaces after we call this (such as from a
         // serverSurfaceDestroyed notification), and to make sure that any in-flight
         // Gecko draw events have been processed.  When this returns, composition is
         // definitely paused -- it'll synchronize with the Gecko event loop, which
         // in turn will synchronize with the compositor thread.
         if (mCompositorCreated) {
             pauseCompositor();
         }
     }
 
-    void serverSurfaceChanged(int newWidth, int newHeight) {
+    synchronized void serverSurfaceChanged(int newWidth, int newHeight) {
         ThreadUtils.assertOnUiThread();
 
-        synchronized (this) {
-            mWidth = newWidth;
-            mHeight = newHeight;
-            mServerSurfaceValid = true;
-        }
+        mWidth = newWidth;
+        mHeight = newHeight;
+        mServerSurfaceValid = true;
 
-        updateCompositor();
+        // we defer to a runnable the task of updating the compositor, because this is going to
+        // call back into createEGLSurface, which will try to create an EGLSurface
+        // against mView, which we suspect might fail if called too early. By posting this to
+        // mView, we hope to ensure that it is deferred until mView is actually "ready" for some
+        // sense of "ready".
+        mView.post(new Runnable() {
+            @Override
+            public void run() {
+                updateCompositor();
+            }
+        });
     }
 
     void updateCompositor() {
         ThreadUtils.assertOnUiThread();
 
         if (mView == null) {
             return;
         }
@@ -104,43 +149,143 @@ public class GLController extends JNIObj
         if (mCompositorCreated) {
             // If the compositor has already been created, just resume it instead. We don't need
             // to block here because if the surface is destroyed before the compositor grabs it,
             // we can handle that gracefully (i.e. the compositor will remain paused).
             resumeCompositor(mWidth, mHeight);
             return;
         }
 
+        if (!AttemptPreallocateEGLSurfaceForCompositor()) {
+            return;
+        }
+
         // Only try to create the compositor if we have a valid surface and gecko is up. When these
         // two conditions are satisfied, we can be relatively sure that the compositor creation will
         // happen without needing to block anywhere. Do it with a synchronous Gecko event so that the
         // Android doesn't have a chance to destroy our surface in between.
         if (mView.getLayerClient().isGeckoReady()) {
             createCompositor(mWidth, mHeight);
-            compositorCreated();
         }
     }
 
     void compositorCreated() {
         // This is invoked on the compositor thread, while the java UI thread
         // is blocked on the gecko sync event in updateCompositor() above
         mCompositorCreated = true;
     }
 
     public boolean isServerSurfaceValid() {
         return mServerSurfaceValid;
     }
 
+    private static void initEGL() {
+        if (sEGL != null) {
+            return;
+        }
+
+        sEGL = (EGL10)EGLContext.getEGL();
+
+        sEGLDisplay = sEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+        if (sEGLDisplay == EGL10.EGL_NO_DISPLAY) {
+            Log.w(LOGTAG, "can't get EGL display!");
+            return;
+        }
+
+        // while calling eglInitialize here should not be necessary as it was already called
+        // by the EGLPreloadingThread, it really doesn't cost much to call it again here,
+        // and makes this code easier to think about: EGLPreloadingThread is only a
+        // preloading optimization, not something we rely on for anything else.
+        //
+        // Also note that while calling eglInitialize isn't necessary on Android 4.x
+        // (at least Android's HardwareRenderer does it for us already), it is necessary
+        // on Android 2.x.
+        int[] returnedVersion = new int[2];
+        if (!sEGL.eglInitialize(sEGLDisplay, returnedVersion)) {
+            Log.w(LOGTAG, "eglInitialize failed");
+            return;
+        }
+
+        sEGLConfig = chooseConfig();
+    }
+
+    private static EGLConfig chooseConfig() {
+        int[] desiredConfig;
+        int rSize, gSize, bSize;
+        int[] numConfigs = new int[1];
+
+        switch (GeckoAppShell.getScreenDepth()) {
+        case 24:
+            desiredConfig = CONFIG_SPEC_24BPP;
+            rSize = gSize = bSize = 8;
+            break;
+        case 16:
+        default:
+            desiredConfig = CONFIG_SPEC_16BPP;
+            rSize = 5; gSize = 6; bSize = 5;
+            break;
+        }
+
+        if (!sEGL.eglChooseConfig(sEGLDisplay, desiredConfig, null, 0, numConfigs) ||
+                numConfigs[0] <= 0) {
+            throw new GLControllerException("No available EGL configurations " +
+                                            getEGLError());
+        }
+
+        EGLConfig[] configs = new EGLConfig[numConfigs[0]];
+        if (!sEGL.eglChooseConfig(sEGLDisplay, desiredConfig, configs, numConfigs[0], numConfigs)) {
+            throw new GLControllerException("No EGL configuration for that specification " +
+                                            getEGLError());
+        }
+
+        // Select the first configuration that matches the screen depth.
+        int[] red = new int[1], green = new int[1], blue = new int[1];
+        for (EGLConfig config : configs) {
+            sEGL.eglGetConfigAttrib(sEGLDisplay, config, EGL10.EGL_RED_SIZE, red);
+            sEGL.eglGetConfigAttrib(sEGLDisplay, config, EGL10.EGL_GREEN_SIZE, green);
+            sEGL.eglGetConfigAttrib(sEGLDisplay, config, EGL10.EGL_BLUE_SIZE, blue);
+            if (red[0] == rSize && green[0] == gSize && blue[0] == bSize) {
+                return config;
+            }
+        }
+
+        throw new GLControllerException("No suitable EGL configuration found");
+    }
+
+    private synchronized boolean AttemptPreallocateEGLSurfaceForCompositor() {
+        if (mEGLSurfaceForCompositor == null) {
+            initEGL();
+            try {
+                mEGLSurfaceForCompositor = sEGL.eglCreateWindowSurface(sEGLDisplay, sEGLConfig, mView.getNativeWindow(), null);
+                // In failure cases, eglCreateWindowSurface should return EGL_NO_SURFACE.
+                // We currently normalize this to null, and compare to null in all our checks.
+                if (mEGLSurfaceForCompositor == EGL10.EGL_NO_SURFACE) {
+                    mEGLSurfaceForCompositor = null;
+                }
+            } catch (Exception e) {
+                Log.e(LOGTAG, "eglCreateWindowSurface threw", e);
+            }
+        }
+        if (mEGLSurfaceForCompositor == null) {
+            Log.w(LOGTAG, "eglCreateWindowSurface returned no surface!");
+        }
+        return mEGLSurfaceForCompositor != null;
+    }
+
     @WrapForJNI(allowMultithread = true)
-    private synchronized Object getSurface() {
-        if (mView != null && isServerSurfaceValid()) {
-            return mView.getSurface();
-        } else {
-            return null;
-        }
+    private synchronized EGLSurface createEGLSurface() {
+        compositorCreated();
+        AttemptPreallocateEGLSurfaceForCompositor();
+        EGLSurface result = mEGLSurfaceForCompositor;
+        mEGLSurfaceForCompositor = null;
+        return result;
+    }
+
+    private static String getEGLError() {
+        return "Error " + (sEGL == null ? "(no sEGL)" : sEGL.eglGetError());
     }
 
     void resumeCompositor(int width, int height) {
         // Asking Gecko to resume the compositor takes too long (see
         // https://bugzilla.mozilla.org/show_bug.cgi?id=735230#c23), so we
         // resume the compositor directly. We still need to inform Gecko about
         // the compositor resuming, so that Gecko knows that it can now draw.
         // It is important to not notify Gecko until after the compositor has
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/LayerView.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/LayerView.java
@@ -514,23 +514,16 @@ public class LayerView extends ScrollVie
 
     public Object getNativeWindow() {
         if (mSurfaceView != null)
             return mSurfaceView.getHolder();
 
         return mTextureView.getSurfaceTexture();
     }
 
-    public Object getSurface() {
-      if (mSurfaceView != null) {
-        return mSurfaceView.getHolder().getSurface();
-      }
-      return null;
-    }
-
     //This method is called on the Gecko main thread.
     @WrapForJNI(allowMultithread = true, stubName = "updateZoomedView")
     public static void updateZoomedView(ByteBuffer data) {
         LayerView layerView = GeckoAppShell.getLayerView();
         if (layerView != null) {
             LayerRenderer layerRenderer = layerView.getRenderer();
             if (layerRenderer != null) {
                 layerRenderer.updateZoomedView(data);
--- a/widget/android/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -1253,35 +1253,35 @@ template<> const char mozilla::jni::Cont
         "org/mozilla/gecko/gfx/GLController";
 
 constexpr char GLController::AttachToJava_t::name[];
 constexpr char GLController::AttachToJava_t::signature[];
 
 constexpr char GLController::CreateCompositor_t::name[];
 constexpr char GLController::CreateCompositor_t::signature[];
 
+constexpr char GLController::CreateEGLSurface_t::name[];
+constexpr char GLController::CreateEGLSurface_t::signature[];
+
+auto GLController::CreateEGLSurface() const -> mozilla::jni::Object::LocalRef
+{
+    return mozilla::jni::Method<CreateEGLSurface_t>::Call(GLController::mCtx, nullptr);
+}
+
 constexpr char GLController::Destroy_t::name[];
 constexpr char GLController::Destroy_t::signature[];
 
 auto GLController::Destroy() const -> void
 {
     return mozilla::jni::Method<Destroy_t>::Call(GLController::mCtx, nullptr);
 }
 
 constexpr char GLController::DisposeNative_t::name[];
 constexpr char GLController::DisposeNative_t::signature[];
 
-constexpr char GLController::GetSurface_t::name[];
-constexpr char GLController::GetSurface_t::signature[];
-
-auto GLController::GetSurface() const -> mozilla::jni::Object::LocalRef
-{
-    return mozilla::jni::Method<GetSurface_t>::Call(GLController::mCtx, nullptr);
-}
-
 constexpr char GLController::OnSizeChanged_t::name[];
 constexpr char GLController::OnSizeChanged_t::signature[];
 
 constexpr char GLController::PauseCompositor_t::name[];
 constexpr char GLController::PauseCompositor_t::signature[];
 
 constexpr char GLController::SyncInvalidateAndScheduleComposite_t::name[];
 constexpr char GLController::SyncInvalidateAndScheduleComposite_t::signature[];
--- a/widget/android/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -2982,16 +2982,31 @@ public:
         static constexpr char name[] = "createCompositor";
         static constexpr char signature[] =
                 "(II)V";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
+    struct CreateEGLSurface_t {
+        typedef GLController Owner;
+        typedef mozilla::jni::Object::LocalRef ReturnType;
+        typedef mozilla::jni::Object::Param SetterType;
+        typedef mozilla::jni::Args<> Args;
+        static constexpr char name[] = "createEGLSurface";
+        static constexpr char signature[] =
+                "()Ljavax/microedition/khronos/egl/EGLSurface;";
+        static const bool isStatic = false;
+        static const mozilla::jni::ExceptionMode exceptionMode =
+                mozilla::jni::ExceptionMode::ABORT;
+    };
+
+    auto CreateEGLSurface() const -> mozilla::jni::Object::LocalRef;
+
     struct Destroy_t {
         typedef GLController Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<> Args;
         static constexpr char name[] = "destroy";
         static constexpr char signature[] =
                 "()V";
@@ -3010,31 +3025,16 @@ public:
         static constexpr char name[] = "disposeNative";
         static constexpr char signature[] =
                 "()V";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
     };
 
-    struct GetSurface_t {
-        typedef GLController Owner;
-        typedef mozilla::jni::Object::LocalRef ReturnType;
-        typedef mozilla::jni::Object::Param SetterType;
-        typedef mozilla::jni::Args<> Args;
-        static constexpr char name[] = "getSurface";
-        static constexpr char signature[] =
-                "()Ljava/lang/Object;";
-        static const bool isStatic = false;
-        static const mozilla::jni::ExceptionMode exceptionMode =
-                mozilla::jni::ExceptionMode::ABORT;
-    };
-
-    auto GetSurface() const -> mozilla::jni::Object::LocalRef;
-
     struct OnSizeChanged_t {
         typedef GLController Owner;
         typedef void ReturnType;
         typedef void SetterType;
         typedef mozilla::jni::Args<
                 int32_t,
                 int32_t,
                 int32_t,
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -860,17 +860,16 @@ class nsWindow::GLControllerSupport fina
     : public GLController::Natives<GLControllerSupport>
     , public SupportsWeakPtr<GLControllerSupport>
     , public UsesGeckoThreadProxy
 {
     nsWindow& window;
     GLController::GlobalRef mGLController;
     GeckoLayerClient::GlobalRef mLayerClient;
     Atomic<bool, ReleaseAcquire> mCompositorPaused;
-    mozilla::jni::GlobalRef<mozilla::jni::Object> mSurface;
 
     // In order to use Event::HasSameTypeAs in PostTo(), we cannot make
     // GLControllerEvent a template because each template instantiation is
     // a different type. So implement GLControllerEvent as a ProxyEvent.
     class GLControllerEvent final : public nsAppShell::ProxyEvent
     {
         using Event = nsAppShell::Event;
 
@@ -967,20 +966,41 @@ public:
         return mLayerClient;
     }
 
     bool CompositorPaused() const
     {
         return mCompositorPaused;
     }
 
-    void* GetSurface()
+    EGLSurface CreateEGLSurface()
     {
-        mSurface = mGLController->GetSurface();
-        return mSurface.Get();
+        static jfieldID eglSurfacePointerField;
+
+        JNIEnv* const env = jni::GetEnvForThread();
+
+        if (!eglSurfacePointerField) {
+            AutoJNIClass egl(env, "com/google/android/gles_jni/EGLSurfaceImpl");
+            // The pointer type moved to a 'long' in Android L, API version 20
+            eglSurfacePointerField = egl.getField("mEGLSurface",
+                    AndroidBridge::Bridge()->GetAPIVersion() >= 20 ? "J" : "I");
+        }
+
+        // Called on the compositor thread.
+        auto eglSurface = mGLController->CreateEGLSurface();
+        if (!eglSurface) {
+            // We failed to create a surface, but because the compositor is
+            // able to handle this failure gracefully, we pass back null
+            // instead of crashing.
+            return nullptr;
+        }
+        return reinterpret_cast<EGLSurface>(
+                AndroidBridge::Bridge()->GetAPIVersion() >= 20 ?
+                env->GetLongField(eglSurface.Get(), eglSurfacePointerField) :
+                env->GetIntField(eglSurface.Get(), eglSurfacePointerField));
     }
 
 private:
     void OnResumedCompositor(int32_t aWidth, int32_t aHeight)
     {
         // When we receive this, the compositor has already been told to
         // resume. (It turns out that waiting till we reach here to tell
         // the compositor to resume takes too long, resulting in a black
@@ -1928,21 +1948,21 @@ nsWindow::GetNativeData(uint32_t aDataTy
             void* pseudoIMEContext = GetPseudoIMEContext();
             if (pseudoIMEContext) {
                 return pseudoIMEContext;
             }
             // We assume that there is only one context per process on Android
             return NS_ONLY_ONE_NATIVE_IME_CONTEXT;
         }
 
-        case NS_NATIVE_WINDOW:
+        case NS_NATIVE_NEW_EGL_SURFACE:
             if (!mGLControllerSupport) {
                 return nullptr;
             }
-            return mGLControllerSupport->GetSurface();
+            return static_cast<void*>(mGLControllerSupport->CreateEGLSurface());
     }
 
     return nullptr;
 }
 
 void
 nsWindow::OnMouseEvent(AndroidGeckoEvent *ae)
 {