Bug 1486659 - p2: expose native GL blitter to Java. r=snorp
authorJohn Lin <jolin@mozilla.com>
Fri, 14 Dec 2018 21:34:11 +0000
changeset 510956 6c89aae076dd9796a916d6b50611a7b2a7ee5d05
parent 510955 f491ebcda0760ef463721de61a9946b796d855d7
child 510957 62064231dfbc0eacabd2f3ddc85033c704c4b7ca
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1486659
milestone66.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) {
@@ -23,11 +30,154 @@ void AndroidSurfaceTexture::GetTransform
 
   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 = nullptr;
+    }
+  }
+
+  static already_AddRefed<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);
+    RefPtr<GLContextEGL> gl = new 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.forget();
+  }
+
+  void InitSurface(AndroidNativeWindow& window) {
+    sMutex.AssertCurrentThreadOwns();
+    MOZ_ASSERT(sContext);
+
+    mTargetSurface = gl::GLLibraryEGL::Get()->fCreateWindowSurface(
+        sContext->GetEGLDisplay(), sContext->mConfig, window.NativeWindow(), 0);
+  }
+
+  static bool UnmakeCurrent(RefPtr<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 RefPtr<GLContextEGL> sContext;
+  static size_t sInstanceCount;
+
+  EGLSurface mTargetSurface;
+};
+
+Mutex SharedGL::sMutex("SharedGLContext::sMutex");
+RefPtr<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::DisposeNative;
+  using Base::GetNative;
+
+  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(); }
+
 }  // namespace gl
 }  // namespace 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);
 };
 
 }  // namespace gl
 }  // namespace 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
@@ -10,16 +10,17 @@ import android.os.Build;
 import android.support.annotation.RequiresApi;
 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>();
 
 
@@ -31,16 +32,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);
     }
 
     @RequiresApi(api = Build.VERSION_CODES.KITKAT)
     private GeckoSurfaceTexture(int handle, boolean singleBufferMode) {
         super(0, singleBufferMode);
@@ -117,16 +120,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);
         }
@@ -271,13 +277,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"
@@ -406,16 +407,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();
     }