Bug 1486659 - p2: expose native GL blitter to Java. r=snorp
authorJohn Lin <jolin@mozilla.com>
Mon, 26 Nov 2018 19:41:29 +0000
changeset 504529 5d897c0c7a25a155fae7347954d8dc9b58bb5703
parent 504528 b0fdcebfd20dd1319ea589ec79afc7dcad8a6c85
child 504530 9c2834ca8823472c042a7c9bf10e06fadc5ee692
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1486659
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1486659 - p2: expose native GL blitter to Java. r=snorp Differential Revision: https://phabricator.services.mozilla.com/D11938
gfx/gl/AndroidSurfaceTexture.cpp
gfx/gl/AndroidSurfaceTexture.h
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java
widget/android/nsAppShell.cpp
--- a/gfx/gl/AndroidSurfaceTexture.cpp
+++ b/gfx/gl/AndroidSurfaceTexture.cpp
@@ -3,16 +3,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef MOZ_WIDGET_ANDROID
 
 #include "AndroidSurfaceTexture.h"
 
+#include "GeneratedJNINatives.h"
+
+#include "AndroidNativeWindow.h"
+#include "GLContextEGL.h"
+#include "GLBlitHelper.h"
+#include "GLImages.h"
+
 using namespace mozilla;
 
 namespace mozilla {
 namespace gl {
 
 void
 AndroidSurfaceTexture::GetTransformMatrix(java::sdk::SurfaceTexture::Param surfaceTexture,
                                           gfx::Matrix4x4* outMatrix)
@@ -24,11 +31,189 @@ AndroidSurfaceTexture::GetTransformMatri
 
   jfloat* array = env->GetFloatArrayElements(jarray.Get(), nullptr);
 
   memcpy(&(outMatrix->_11), array, sizeof(float)*16);
 
   env->ReleaseFloatArrayElements(jarray.Get(), array, 0);
 }
 
+class SharedGL {
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedGL);
+
+  SharedGL(AndroidNativeWindow& window)
+  {
+    MutexAutoLock lock(sMutex);
+
+    if (!sContext) {
+      MOZ_ASSERT(sInstanceCount == 0);
+      sContext = CreateContext();
+      if (!sContext) {
+        return;
+      }
+    }
+
+    InitSurface(window);
+    ++sInstanceCount;
+  }
+
+  void Blit(const AndroidSurfaceTextureHandle& sourceTextureHandle,
+            const gfx::IntSize& imageSize)
+  {
+    MutexAutoLock lock(sMutex);
+    MOZ_ASSERT(sContext);
+
+    // Setting overide also makes conext and surface current.
+    sContext->SetEGLSurfaceOverride(mTargetSurface);
+    RefPtr<layers::SurfaceTextureImage> img =
+      new layers::SurfaceTextureImage(sourceTextureHandle,
+                                      imageSize,
+                                      false,
+                                      OriginPos::TopLeft);
+    sContext->BlitHelper()->BlitImage(img, imageSize, OriginPos::BottomLeft);
+    sContext->SwapBuffers();
+    // This method is called through binder IPC and could run on any thread in
+    // the pool. Release the context and surface from this thread after use so
+    // they can be bound to another thread later.
+    UnmakeCurrent(sContext);
+  }
+
+private:
+  ~SharedGL()
+  {
+    MutexAutoLock lock(sMutex);
+
+    if (mTargetSurface != EGL_NO_SURFACE) {
+      GLLibraryEGL::Get()->fDestroySurface(EGL_DISPLAY(), mTargetSurface);
+    }
+
+    // Destroy shared GL context when no one uses it.
+    if (--sInstanceCount == 0) {
+      sContext.reset();
+    }
+  }
+
+  static UniquePtr<GLContextEGL> CreateContext()
+  {
+    sMutex.AssertCurrentThreadOwns();
+    MOZ_ASSERT(!sContext);
+
+    auto* egl = gl::GLLibraryEGL::Get();
+    EGLDisplay eglDisplay = egl->fGetDisplay(EGL_DEFAULT_DISPLAY);
+    EGLConfig eglConfig;
+    CreateConfig(&eglConfig, /* bpp */ 24, /* depth buffer? */ false);
+    EGLint attributes[] = {
+      LOCAL_EGL_CONTEXT_CLIENT_VERSION, 2,
+      LOCAL_EGL_NONE
+    };
+    EGLContext eglContext =
+      egl->fCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attributes);
+    UniquePtr<GLContextEGL> gl = MakeUnique<GLContextEGL>(CreateContextFlags::NONE,
+                                                          SurfaceCaps::Any(),
+                                                          /* offscreen? */ false,
+                                                          eglConfig,
+                                                          EGL_NO_SURFACE,
+                                                          eglContext);
+    if (!gl->Init()) {
+      NS_WARNING("Fail to create GL context for native blitter.");
+      return nullptr;
+    }
+
+    // Yield the current state made in constructor.
+    UnmakeCurrent(gl);
+    return gl;
+  }
+
+  void InitSurface(AndroidNativeWindow& window)
+  {
+    sMutex.AssertCurrentThreadOwns();
+    MOZ_ASSERT(sContext);
+
+    mTargetSurface = gl::GLLibraryEGL::Get()->fCreateWindowSurface(sContext->GetEGLDisplay(),
+                                                                   sContext->mConfig,
+                                                                   window.NativeWindow(),
+                                                                   0);
+  }
+
+  static bool UnmakeCurrent(UniquePtr<GLContextEGL>& gl)
+  {
+    sMutex.AssertCurrentThreadOwns();
+    MOZ_ASSERT(gl);
+
+    if (!gl->IsCurrent()) {
+      return true;
+    }
+
+    return  gl::GLLibraryEGL::Get()->fMakeCurrent(EGL_DISPLAY(),
+                                                  EGL_NO_SURFACE,
+                                                  EGL_NO_SURFACE,
+                                                  EGL_NO_CONTEXT);
+  }
+
+  static Mutex sMutex;
+  static UniquePtr<GLContextEGL> sContext;
+  static size_t sInstanceCount;
+
+  EGLSurface mTargetSurface;
+};
+
+Mutex SharedGL::sMutex("SharedGLContext::sMutex");
+UniquePtr<GLContextEGL> SharedGL::sContext(nullptr);
+size_t SharedGL::sInstanceCount = 0;
+
+class GLBlitterSupport final
+  : public java::GeckoSurfaceTexture::NativeGLBlitHelper::Natives<GLBlitterSupport>
+{
+public:
+  using Base = java::GeckoSurfaceTexture::NativeGLBlitHelper::Natives<GLBlitterSupport>;
+  using Base::AttachNative;
+  using Base::GetNative;
+  using Base::DisposeNative;
+
+  static java::GeckoSurfaceTexture::NativeGLBlitHelper::LocalRef
+  Create(jint sourceTextureHandle,
+         jni::Object::Param targetSurface,
+         jint width,
+         jint height)
+  {
+    AndroidNativeWindow win(java::GeckoSurface::Ref::From(targetSurface));
+    auto helper = java::GeckoSurfaceTexture::NativeGLBlitHelper::New();
+    RefPtr<SharedGL> gl = new SharedGL(win);
+    GLBlitterSupport::AttachNative(
+      helper,
+      MakeUnique<GLBlitterSupport>(std::move(gl),
+                                   sourceTextureHandle,
+                                   width,
+                                   height));
+    return helper;
+  }
+
+  GLBlitterSupport(RefPtr<SharedGL>&& gl,
+                   jint sourceTextureHandle,
+                   jint width,
+                   jint height)
+    : mGl(gl)
+    , mSourceTextureHandle(sourceTextureHandle)
+    , mSize(width, height)
+  {
+  }
+
+  void Blit()
+  {
+    mGl->Blit(mSourceTextureHandle, mSize);
+  }
+
+private:
+  const RefPtr<SharedGL> mGl;
+  const AndroidSurfaceTextureHandle mSourceTextureHandle;
+  const gfx::IntSize mSize;
+};
+
+void
+AndroidSurfaceTexture::Init()
+{
+  GLBlitterSupport::Init();
+}
+
 } // gl
 } // mozilla
 #endif // MOZ_WIDGET_ANDROID
--- a/gfx/gl/AndroidSurfaceTexture.h
+++ b/gfx/gl/AndroidSurfaceTexture.h
@@ -13,16 +13,17 @@
 
 typedef uint32_t AndroidSurfaceTextureHandle;
 
 namespace mozilla {
 namespace gl {
 
 class AndroidSurfaceTexture {
 public:
+  static void Init();
   static void GetTransformMatrix(java::sdk::SurfaceTexture::Param surfaceTexture,
                                  mozilla::gfx::Matrix4x4* outMatrix);
 
 };
 
 } // gl
 } // mozilla
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java
@@ -9,16 +9,17 @@ import android.graphics.SurfaceTexture;
 import android.os.Build;
 import android.util.Log;
 
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.HashMap;
 import java.util.LinkedList;
 
 import org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.mozglue.JNIObject;
 
 /* package */ final class GeckoSurfaceTexture extends SurfaceTexture {
     private static final String LOGTAG = "GeckoSurfaceTexture";
     private static final int MAX_SURFACE_TEXTURES = 200;
     private static volatile int sNextHandle = 1;
     private static final HashMap<Integer, GeckoSurfaceTexture> sSurfaceTextures = new HashMap<Integer, GeckoSurfaceTexture>();
 
 
@@ -30,16 +31,18 @@ import org.mozilla.gecko.annotation.Wrap
 
     private long mAttachedContext;
     private int mTexName;
 
     private GeckoSurfaceTexture.Callbacks mListener;
     private AtomicInteger mUseCount;
     private boolean mFinalized;
 
+    private NativeGLBlitHelper mBlitter;
+
     private GeckoSurfaceTexture(int handle) {
         super(0);
         init(handle, false);
     }
 
     private GeckoSurfaceTexture(int handle, boolean singleBufferMode) {
         super(0, singleBufferMode);
         init(handle, singleBufferMode);
@@ -115,16 +118,19 @@ import org.mozilla.gecko.annotation.Wrap
             }
         } catch (Exception e) {
             Log.w(LOGTAG, "updateTexImage() failed", e);
         }
     }
 
     @Override
     public synchronized void release() {
+        if (mBlitter != null) {
+            mBlitter.disposeNative();
+        }
         try {
             super.release();
             synchronized (sSurfaceTextures) {
                 sSurfaceTextures.remove(mHandle);
             }
         } catch (Exception e) {
             Log.w(LOGTAG, "release() failed", e);
         }
@@ -269,13 +275,33 @@ import org.mozilla.gecko.annotation.Wrap
 
     @WrapForJNI
     public static GeckoSurfaceTexture lookup(int handle) {
         synchronized (sSurfaceTextures) {
             return sSurfaceTextures.get(handle);
         }
     }
 
+    /* package */ synchronized void configureSnapshot(GeckoSurface target, int width, int height) {
+        mBlitter = NativeGLBlitHelper.create(mHandle, target, width, height);
+    }
+
+    /* package */ synchronized void takeSnapshot() {
+        mBlitter.blit();
+    }
+
     public interface Callbacks {
         void onUpdateTexImage();
         void onReleaseTexImage();
     }
+
+    @WrapForJNI
+    public static class NativeGLBlitHelper extends JNIObject {
+        public native static NativeGLBlitHelper create(int textureHandle,
+                                                       GeckoSurface targetSurface,
+                                                       int width,
+                                                       int height);
+        public native void blit();
+
+        @Override
+        protected native void disposeNative();
+    }
 }
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -35,16 +35,17 @@
 #include "mozilla/Hal.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/intl/OSPreferences.h"
 #include "mozilla/widget/ScreenManager.h"
 #include "prenv.h"
 
 #include "AndroidBridge.h"
 #include "AndroidBridgeUtilities.h"
+#include "AndroidSurfaceTexture.h"
 #include "GeneratedJNINatives.h"
 #include <android/log.h>
 #include <pthread.h>
 #include <wchar.h>
 
 #include "GeckoProfiler.h"
 #ifdef MOZ_ANDROID_HISTORY
 #include "nsNetUtil.h"
@@ -433,16 +434,17 @@ nsAppShell::nsAppShell()
         mozilla::GeckoNetworkManager::Init();
         mozilla::GeckoProcessManager::Init();
         mozilla::GeckoScreenOrientation::Init();
         mozilla::GeckoSystemStateListener::Init();
         mozilla::PrefsHelper::Init();
         mozilla::widget::Telemetry::Init();
         mozilla::widget::WebExecutorSupport::Init();
         nsWindow::InitNatives();
+        mozilla::gl::AndroidSurfaceTexture::Init();
 
         if (jni::IsFennec()) {
             BrowserLocaleManagerSupport::Init();
             mozilla::ANRReporter::Init();
             mozilla::MemoryMonitor::Init();
             mozilla::ThumbnailHelper::Init();
         }