Merge b2g-inbound to m-c
authorWes Kocher <wkocher@mozilla.com>
Fri, 22 Nov 2013 17:31:05 -0800
changeset 157193 98aa428a392c580d72248d4cd66a257ebc7a1e23
parent 157189 f7ad046f783f51e48169c457182a50dec3ea6ca8 (current diff)
parent 157192 c98cabc0d18024e6c53f563826faac13a58bd5c4 (diff)
child 157194 bf99e325f23856ffaf465b4b098c5b7df18e9526
push id36645
push userphilringnalda@gmail.com
push dateSat, 23 Nov 2013 16:20:35 +0000
treeherdermozilla-inbound@88b8078052ca [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone28.0a1
first release with
nightly linux32
98aa428a392c / 28.0a1 / 20131123030208 / files
nightly linux64
98aa428a392c / 28.0a1 / 20131123030208 / files
nightly mac
98aa428a392c / 28.0a1 / 20131123030208 / files
nightly win32
98aa428a392c / 28.0a1 / 20131123030208 / files
nightly win64
98aa428a392c / 28.0a1 / 20131123030208 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge b2g-inbound to m-c
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1271,27 +1271,29 @@ MediaManager::GetUserMedia(JSContext* aC
   StreamListeners* listeners = GetActiveWindows()->Get(windowID);
   if (!listeners) {
     listeners = new StreamListeners;
     GetActiveWindows()->Put(windowID, listeners);
   }
 
   if (!unknownConstraintFound.IsEmpty()) {
     // An unsupported mandatory constraint was found.
-    // Things are set up enough here that we can fire Error callback.
+    //
+    // We continue to ignore these for now, because we implement just
+    // facingMode, which means all existing uses of mandatory width/height would
+    // fail on Firefox only otherwise, which is undesirable.
+    //
+    // There's also basis for always ignoring them in a new proposal.
+    // TODO(jib): This is a super-low-risk fix for backport. Clean up later.
 
     LOG(("Unsupported mandatory constraint: %s\n",
           NS_ConvertUTF16toUTF8(unknownConstraintFound).get()));
 
-    nsString errormsg(NS_LITERAL_STRING("NOT_SUPPORTED_ERR: "));
-    errormsg.Append(unknownConstraintFound);
-    NS_DispatchToMainThread(new ErrorCallbackRunnable(onSuccess.forget(),
-                                                      onError.forget(),
-                                                      errormsg, windowID));
-    return NS_OK;
+    // unknown constraints existed in aRawConstraints only, which is unused
+    // from here, so continuing here effectively ignores them, as is desired.
   }
 
   // Ensure there's a thread for gum to proxy to off main thread
   nsIThread *mediaThread = MediaManager::GetThread();
 
   // Create a disabled listener to act as a placeholder
   GetUserMediaCallbackMediaStreamListener* listener =
     new GetUserMediaCallbackMediaStreamListener(mediaThread, windowID);
--- a/dom/media/tests/mochitest/test_getUserMedia_constraints.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_constraints.html
@@ -20,32 +20,25 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 /**
   Tests covering gUM constraints API for audio, video and fake video. Exercise
   successful parsing code and ensure that unknown mandatory constraints and
   overconstraining cases produce appropriate errors.
 */
 var tests = [
   // Each test here tests a different constraint or codepath.
-  { message: "unknown mandatory constraint on video fails",
-    constraints: { video: { mandatory: { somethingUnknown:0 } } },
-    error: "NOT_SUPPORTED_ERR: somethingUnknown",
-    pass: false },
-  { message: "unknown mandatory constraint on audio fails",
-    constraints: { audio: { mandatory: { somethingUnknown:0 } } },
-    error: "NOT_SUPPORTED_ERR: somethingUnknown",
-    pass: false },
   { message: "video overconstrained by facingMode fails",
     constraints: { video: { mandatory: { facingMode:'left' } } },
     error: "NO_DEVICES_FOUND",
     pass: false },
   { message: "Success-path: optional video facingMode + audio ignoring facingMode",
     constraints: { fake: true,
                    audio: { mandatory: { facingMode:'left' } },
-                   video: { optional: [{ facingMode:'left' },
+                   video: { mandatory: { somethingUnknown:0 },
+                            optional: [{ facingMode:'left' },
                                        { facingMode:'right' },
                                        { facingMode:'environment' },
                                        { facingMode:'user' },
                                        { foo:0 }] } },
     error: null,
     pass: false },
   { message: null },
 ];
--- a/mobile/android/base/gfx/GLController.java
+++ b/mobile/android/base/gfx/GLController.java
@@ -15,16 +15,58 @@ 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;
 
 /**
+ * EGLPreloadingThread is purely a preloading optimization, not something
+ * we rely on for anything else than performance. We will be initializing
+ * EGL in GLController::initEGL() when we need it, but having EGL initialization
+ * already previously done by EGLPreloadingThread::run() will make it much
+ * faster for GLController to do again.
+ *
+ * For example, here are some timings recorded on two devices:
+ *
+ * Device                 | EGLPreloadingThread::run() | GLController::initEGL()
+ * -----------------------+----------------------------+------------------------
+ * Nexus S (Android 2.3)  | ~ 80 ms                    | < 0.1 ms
+ * Nexus 10 (Android 4.3) | ~ 35 ms                    | < 0.1 ms
+ */
+class EGLPreloadingThread extends Thread
+{
+    private static final String LOGTAG = "EGLPreloadingThread";
+    private EGL10 mEGL;
+    private EGLDisplay mEGLDisplay;
+
+    public EGLPreloadingThread()
+    {
+    }
+
+    @Override
+    public void run()
+    {
+        mEGL = (EGL10)EGLContext.getEGL();
+        mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+        if (mEGLDisplay == EGL10.EGL_NO_DISPLAY) {
+            Log.w(LOGTAG, "Can't get EGL display!");
+            return;
+        }
+
+        int[] returnedVersion = new int[2];
+        if (!mEGL.eglInitialize(mEGLDisplay, returnedVersion)) {
+            Log.w(LOGTAG, "eglInitialize failed");
+            return;
+        }
+    }
+}
+
+/**
  * 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.
@@ -41,16 +83,18 @@ public class GLController {
 
     /* 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 EGL10 mEGL;
     private EGLDisplay mEGLDisplay;
     private EGLConfig mEGLConfig;
+    private EGLPreloadingThread mEGLPreloadingThread;
+    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,
@@ -63,32 +107,39 @@ public class GLController {
         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
     };
 
     private GLController() {
+        mEGLPreloadingThread = new EGLPreloadingThread();
+        mEGLPreloadingThread.start();
     }
 
     static GLController getInstance(LayerView view) {
         if (sInstance == null) {
             sInstance = new GLController();
         }
         sInstance.mView = view;
         return sInstance;
     }
 
     synchronized void serverSurfaceDestroyed() {
         ThreadUtils.assertOnUiThread();
         Log.w(LOGTAG, "GLController::serverSurfaceDestroyed() with mCompositorCreated=" + mCompositorCreated);
 
         mServerSurfaceValid = false;
 
+        if (mEGLSurfaceForCompositor != null) {
+          mEGL.eglDestroySurface(mEGLDisplay, 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.
@@ -127,16 +178,20 @@ public class GLController {
             // 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);
             Log.w(LOGTAG, "done GLController::updateCompositor with compositor resume");
             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 anyhwere. Do it with a sync gecko event so that the
         // android doesn't have a chance to destroy our surface in between.
         if (GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
             GeckoAppShell.sendEventToGeckoSync(GeckoEvent.createCompositorCreateEvent(mWidth, mHeight));
         }
         Log.w(LOGTAG, "done GLController::updateCompositor");
@@ -156,24 +211,50 @@ public class GLController {
     public boolean isCompositorCreated() {
         return mCompositorCreated;
     }
 
     private void initEGL() {
         if (mEGL != null) {
             return;
         }
+
+        // This join() should not be necessary, but makes this code a bit easier to think about.
+        // The EGLPreloadingThread should long be done by now, and even if it's not,
+        // it shouldn't be a problem to be initalizing EGL from two different threads.
+        // Still, having this join() here means that we don't have to wonder about what
+        // kind of caveats might exist with EGL initialization reentrancy on various drivers.
+        try {
+            mEGLPreloadingThread.join();
+        } catch (InterruptedException e) {
+            Log.w(LOGTAG, "EGLPreloadingThread interrupted", e);
+        }
+
         mEGL = (EGL10)EGLContext.getEGL();
 
         mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
         if (mEGLDisplay == 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 (!mEGL.eglInitialize(mEGLDisplay, returnedVersion)) {
+            Log.w(LOGTAG, "eglInitialize failed");
+            return;
+        }
+
         mEGLConfig = chooseConfig();
     }
 
     private EGLConfig chooseConfig() {
         int[] desiredConfig;
         int rSize, gSize, bSize;
         int[] numConfigs = new int[1];
 
@@ -210,20 +291,34 @@ public class GLController {
             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 = mEGL.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mView.getNativeWindow(), null);
+            } catch (Exception e) {
+                Log.e(LOGTAG, "eglCreateWindowSurface threw", e);
+            }
+        }
+        return mEGLSurfaceForCompositor != null;
+    }
+
     @WrapElementForJNI(allowMultithread = true, stubName = "CreateEGLSurfaceForCompositorWrapper")
-    private EGLSurface createEGLSurfaceForCompositor() {
-        initEGL();
-        return mEGL.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mView.getNativeWindow(), null);
+    private synchronized EGLSurface createEGLSurfaceForCompositor() {
+        AttemptPreallocateEGLSurfaceForCompositor();
+        EGLSurface result = mEGLSurfaceForCompositor;
+        mEGLSurfaceForCompositor = null;
+        return result;
     }
 
     private String getEGLError() {
         return "Error " + (mEGL == null ? "(no mEGL)" : mEGL.eglGetError());
     }
 
     void resumeCompositor(int width, int height) {
         Log.w(LOGTAG, "GLController::resumeCompositor(" + width + ", " + height + ") and mCompositorCreated=" + mCompositorCreated);