Bug 1014614 - Support attach/detach of GLContext to AndroidSurfaceTexture r=jgilbert
☠☠ backed out by 53e24fd12cd1 ☠ ☠
authorJames Willcox <snorp@snorp.net>
Fri, 17 Oct 2014 10:35:11 -0500
changeset 210951 f2b504bdd7c5
parent 210950 c878e29fbef9
child 210952 40dbd7c6ce65
push id50598
push userjwillcox@mozilla.com
push date2014-10-17 15:36 +0000
treeherdermozilla-inbound@40f99ba7f616 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs1014614
milestone36.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 1014614 - Support attach/detach of GLContext to AndroidSurfaceTexture r=jgilbert
dom/plugins/base/nsNPAPIPluginInstance.cpp
gfx/gl/AndroidSurfaceTexture.cpp
gfx/gl/AndroidSurfaceTexture.h
gfx/layers/opengl/TextureHostOGL.cpp
gfx/layers/opengl/TexturePoolOGL.cpp
gfx/layers/opengl/TexturePoolOGL.h
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -956,17 +956,18 @@ AndroidSurfaceTexture* nsNPAPIPluginInst
 {
   if (!EnsureGLContext())
     return nullptr;
 
   GLuint texture = TexturePoolOGL::AcquireTexture();
   if (!texture)
     return nullptr;
 
-  AndroidSurfaceTexture* surface = AndroidSurfaceTexture::Create(texture);
+  AndroidSurfaceTexture* surface = AndroidSurfaceTexture::Create(TexturePoolOGL::GetGLContext(),
+                                                                 texture);
   if (!surface)
     return nullptr;
 
   nsCOMPtr<nsIRunnable> frameCallback = NS_NewRunnableMethod(this, &nsNPAPIPluginInstance::OnSurfaceTextureFrameAvailable);
   surface->SetFrameAvailableCallback(frameCallback);
   return surface;
 }
 
--- a/gfx/gl/AndroidSurfaceTexture.cpp
+++ b/gfx/gl/AndroidSurfaceTexture.cpp
@@ -10,46 +10,71 @@
 #include <map>
 #include <android/log.h>
 #include "AndroidSurfaceTexture.h"
 #include "gfxImageSurface.h"
 #include "AndroidBridge.h"
 #include "nsThreadUtils.h"
 #include "mozilla/gfx/Matrix.h"
 #include "GeneratedJNIWrappers.h"
+#include "GLContext.h"
 
 using namespace mozilla;
 using namespace mozilla::widget::android;
 
 // UGH
 static std::map<int, AndroidSurfaceTexture*> sInstances;
 static int sNextID = 0;
 
+static bool
+IsDetachSupported()
+{
+  return AndroidBridge::Bridge()->GetAPIVersion() >= 16; /* Jelly Bean */
+}
+
+static bool
+IsSTSupported()
+{
+  return AndroidBridge::Bridge()->GetAPIVersion() >= 14; /* ICS */
+}
+
 static class JNIFunctions {
 public:
 
   JNIFunctions() : mInitialized(false)
   {
   }
 
   bool EnsureInitialized()
   {
-    if (mInitialized)
+    if (mInitialized) {
       return true;
+    }
+
+    if (!IsSTSupported()) {
+      return false;
+    }
 
     JNIEnv* env = GetJNIForThread();
 
     AutoLocalJNIFrame jniFrame(env);
 
     jSurfaceTextureClass = (jclass)env->NewGlobalRef(env->FindClass("android/graphics/SurfaceTexture"));
     jSurfaceTexture_Ctor = env->GetMethodID(jSurfaceTextureClass, "<init>", "(I)V");
     jSurfaceTexture_updateTexImage = env->GetMethodID(jSurfaceTextureClass, "updateTexImage", "()V");
     jSurfaceTexture_getTransformMatrix = env->GetMethodID(jSurfaceTextureClass, "getTransformMatrix", "([F)V");
     jSurfaceTexture_setDefaultBufferSize = env->GetMethodID(jSurfaceTextureClass, "setDefaultBufferSize", "(II)V");
 
+    if (IsDetachSupported()) {
+      jSurfaceTexture_attachToGLContext = env->GetMethodID(jSurfaceTextureClass, "attachToGLContext", "(I)V");
+      jSurfaceTexture_detachFromGLContext = env->GetMethodID(jSurfaceTextureClass, "detachFromGLContext", "()V");
+    } else {
+      jSurfaceTexture_attachToGLContext = jSurfaceTexture_detachFromGLContext = 0;
+    }
+
     jSurfaceClass = (jclass)env->NewGlobalRef(env->FindClass("android/view/Surface"));
     jSurface_Ctor = env->GetMethodID(jSurfaceClass, "<init>", "(Landroid/graphics/SurfaceTexture;)V");
 
     mInitialized = true;
     return true;
   }
 
   jobject CreateSurfaceTexture(GLuint aTexture)
@@ -128,39 +153,74 @@ public:
   void SetDefaultBufferSize(jobject aSurfaceTexture, int32_t width, int32_t height)
   {
     JNIEnv* env = GetJNIForThread();
 
     AutoLocalJNIFrame jniFrame(env);
     env->CallVoidMethod(aSurfaceTexture, jSurfaceTexture_setDefaultBufferSize, width, height);
   }
 
+  void AttachToGLContext(jobject aSurfaceTexture, int32_t texName)
+  {
+    MOZ_ASSERT(jSurfaceTexture_attachToGLContext);
+
+    JNIEnv* env = GetJNIForThread();
+
+    env->CallVoidMethod(aSurfaceTexture, jSurfaceTexture_attachToGLContext, texName);
+    if (env->ExceptionCheck()) {
+      env->ExceptionDescribe();
+      env->ExceptionClear();
+    }
+  }
+
+  void DetachFromGLContext(jobject aSurfaceTexture)
+  {
+    MOZ_ASSERT(jSurfaceTexture_detachFromGLContext);
+
+    JNIEnv* env = GetJNIForThread();
+
+    env->CallVoidMethod(aSurfaceTexture, jSurfaceTexture_detachFromGLContext);
+    if (env->ExceptionCheck()) {
+      env->ExceptionDescribe();
+      env->ExceptionClear();
+    }
+  }
+
 private:
   bool mInitialized;
 
   jclass jSurfaceTextureClass;
   jmethodID jSurfaceTexture_Ctor;
   jmethodID jSurfaceTexture_updateTexImage;
   jmethodID jSurfaceTexture_getTransformMatrix;
   jmethodID jSurfaceTexture_setDefaultBufferSize;
 
+  jmethodID jSurfaceTexture_attachToGLContext;
+  jmethodID jSurfaceTexture_detachFromGLContext;
+
   jclass jSurfaceClass;
   jmethodID jSurface_Ctor;
 
 } sJNIFunctions;
 
 AndroidSurfaceTexture*
-AndroidSurfaceTexture::Create(GLuint aTexture)
+AndroidSurfaceTexture::Create()
 {
-  if (AndroidBridge::Bridge()->GetAPIVersion() < 14 /* Ice Cream Sandwich */) {
+  return Create(nullptr, 0);
+}
+
+AndroidSurfaceTexture*
+AndroidSurfaceTexture::Create(GLContext* aContext, GLuint aTexture)
+{
+  if (!IsSTSupported()) {
     return nullptr;
   }
 
   AndroidSurfaceTexture* st = new AndroidSurfaceTexture();
-  if (!st->Init(aTexture)) {
+  if (!st->Init(aContext, aTexture)) {
     printf_stderr("Failed to initialize AndroidSurfaceTexture");
     delete st;
     st = nullptr;
   }
 
   return st;
 }
 
@@ -178,45 +238,110 @@ AndroidSurfaceTexture::Find(int id)
 
 bool
 AndroidSurfaceTexture::Check()
 {
   return sJNIFunctions.EnsureInitialized();
 }
 
 bool
-AndroidSurfaceTexture::Init(GLuint aTexture)
+AndroidSurfaceTexture::Attach(GLContext* aContext, PRIntervalTime aTimeout)
 {
+  MonitorAutoLock lock(mMonitor);
+
+  if (mAttachedContext == aContext) {
+    NS_WARNING("Tried to attach same GLContext to AndroidSurfaceTexture");
+    return true;
+  }
+
+  if (!IsDetachSupported()) {
+    return false;
+  }
+
+  while (mAttachedContext) {
+    // Wait until it's detached (or we time out)
+    if (NS_FAILED(lock.Wait(aTimeout))) {
+      return false;
+    }
+  }
+
+  MOZ_ASSERT(aContext->IsOwningThreadCurrent(), "Trying to attach GLContext from different thread");
+
+  mAttachedContext = aContext;
+  mAttachedContext->MakeCurrent();
+  aContext->fGenTextures(1, &mTexture);
+
+  sJNIFunctions.AttachToGLContext(mSurfaceTexture, mTexture);
+  return true;
+}
+
+bool
+AndroidSurfaceTexture::Detach()
+{
+  MonitorAutoLock lock(mMonitor);
+
+  if (!IsDetachSupported() ||
+      !mAttachedContext || !mAttachedContext->IsOwningThreadCurrent()) {
+    return false;
+  }
+
+  mAttachedContext->MakeCurrent();
+
+  // This call takes care of deleting the texture
+  sJNIFunctions.DetachFromGLContext(mSurfaceTexture);
+
+  mTexture = 0;
+  mAttachedContext = nullptr;
+  lock.NotifyAll();
+  return true;
+}
+
+bool
+AndroidSurfaceTexture::Init(GLContext* aContext, GLuint aTexture)
+{
+  if (!aTexture && !IsDetachSupported()) {
+    // We have no texture and cannot initialize detached, bail out
+    return false;
+  }
+
   if (!sJNIFunctions.EnsureInitialized())
     return false;
 
   JNIEnv* env = GetJNIForThread();
 
   mSurfaceTexture = sJNIFunctions.CreateSurfaceTexture(aTexture);
   if (!mSurfaceTexture) {
     return false;
   }
 
+  if (!aTexture) {
+    sJNIFunctions.DetachFromGLContext(mSurfaceTexture);
+  }
+
+  mAttachedContext = aContext;
+
   mSurface = sJNIFunctions.CreateSurface(mSurfaceTexture);
   if (!mSurface) {
     return false;
   }
 
   mNativeWindow = AndroidNativeWindow::CreateFromSurface(env, mSurface);
 
   mID = ++sNextID;
   sInstances.insert(std::pair<int, AndroidSurfaceTexture*>(mID, this));
 
-  mTexture = aTexture;
-
   return true;
 }
 
 AndroidSurfaceTexture::AndroidSurfaceTexture()
-  : mTexture(0), mSurfaceTexture(nullptr), mSurface(nullptr)
+  : mTexture(0)
+  , mSurfaceTexture(nullptr)
+  , mSurface(nullptr)
+  , mMonitor("AndroidSurfaceTexture::mContextMonitor")
+  , mAttachedContext(nullptr)
 {
 }
 
 AndroidSurfaceTexture::~AndroidSurfaceTexture()
 {
   sInstances.erase(mID);
 
   mFrameAvailableCallback = nullptr;
--- a/gfx/gl/AndroidSurfaceTexture.h
+++ b/gfx/gl/AndroidSurfaceTexture.h
@@ -7,46 +7,73 @@
 #ifndef AndroidSurfaceTexture_h__
 #define AndroidSurfaceTexture_h__
 #ifdef MOZ_WIDGET_ANDROID
 
 #include <jni.h>
 #include "nsIRunnable.h"
 #include "gfxPlatform.h"
 #include "GLDefs.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/Monitor.h"
 
 #include "AndroidNativeWindow.h"
 
 class gfxASurface;
 
 namespace mozilla {
 namespace gfx {
 class Matrix4x4;
 }
 }
 
 namespace mozilla {
 namespace gl {
 
+class GLContext;
+
 /**
  * This class is a wrapper around Android's SurfaceTexture class.
  * Usage is pretty much exactly like the Java class, so see
  * the Android documentation for details.
  */
 class AndroidSurfaceTexture {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AndroidSurfaceTexture)
 
 public:
-  static AndroidSurfaceTexture* Create(GLuint aTexture);
+
+  // The SurfaceTexture is created in an attached state. This method requires
+  // Android Ice Cream Sandwich.
+  static AndroidSurfaceTexture* Create(GLContext* aGLContext, GLuint aTexture);
+
+  // Here the SurfaceTexture will be created in a detached state. You must call
+  // Attach() with the GLContext you wish to composite with. It must be done
+  // on the thread where that GLContext is current. This method requires
+  // Android Jelly Bean.
+  static AndroidSurfaceTexture* Create();
+
   static AndroidSurfaceTexture* Find(int id);
 
   // Returns with reasonable certainty whether or not we'll
   // be able to create and use a SurfaceTexture
   static bool Check();
 
+  // If we are on Jelly Bean, the SurfaceTexture can be detached and reattached
+  // to allow consumption from different GLContexts. It is recommended to only
+  // attach while you are consuming in order to allow this.
+  //
+  // Only one GLContext may be attached at any given time. If another is already
+  // attached, we try to wait for it to become detached.
+  bool Attach(GLContext* aContext, PRIntervalTime aTiemout = PR_INTERVAL_NO_TIMEOUT);
+
+  // This is a noop on ICS, and will always fail
+  bool Detach();
+
+  GLContext* GetAttachedContext() { return mAttachedContext; }
+
   AndroidNativeWindow* NativeWindow() {
     return mNativeWindow;
   }
 
   // This attaches the updated data to the TEXTURE_EXTERNAL target
   void UpdateTexImage();
 
   bool GetTransformMatrix(mozilla::gfx::Matrix4x4& aMatrix);
@@ -63,25 +90,28 @@ public:
   void NotifyFrameAvailable();
 
   GLuint Texture() { return mTexture; }
   jobject JavaSurface() { return mSurface; }
 private:
   AndroidSurfaceTexture();
   ~AndroidSurfaceTexture();
 
-  bool Init(GLuint aTexture);
+  bool Init(GLContext* aContext, GLuint aTexture);
 
   GLuint mTexture;
   jobject mSurfaceTexture;
   jobject mSurface;
 
+  Monitor mMonitor;
+  GLContext* mAttachedContext;
+
   RefPtr<AndroidNativeWindow> mNativeWindow;
   int mID;
   nsRefPtr<nsIRunnable> mFrameAvailableCallback;
 };
-  
+
 }
 }
 
 
 #endif
 #endif
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -529,22 +529,25 @@ SurfaceTextureHost::Lock()
     mTextureSource = new SurfaceTextureSource(mCompositor,
                                               mSurfTex,
                                               format,
                                               target,
                                               wrapMode,
                                               mSize);
   }
 
+  mSurfTex->Attach(gl());
+
   return true;
 }
 
 void
 SurfaceTextureHost::Unlock()
 {
+  mSurfTex->Detach();
 }
 
 void
 SurfaceTextureHost::SetCompositor(Compositor* aCompositor)
 {
   CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
   mCompositor = glCompositor;
   if (mTextureSource) {
--- a/gfx/layers/opengl/TexturePoolOGL.cpp
+++ b/gfx/layers/opengl/TexturePoolOGL.cpp
@@ -97,16 +97,21 @@ void TexturePoolOGL::Fill(GLContext* aCo
     texture = (GLuint*)malloc(sizeof(GLuint));
     sActiveContext->fGenTextures(1, texture);
     sTextures->Push((void*) texture);
   }
 
   sMonitor->NotifyAll();
 }
 
+GLContext* TexturePoolOGL::GetGLContext()
+{
+  return sActiveContext;
+}
+
 void TexturePoolOGL::Init()
 {
   sMonitor = new Monitor("TexturePoolOGL.sMonitor");
   sTextures = new nsDeque();
 }
 
 void TexturePoolOGL::Shutdown()
 {
--- a/gfx/layers/opengl/TexturePoolOGL.h
+++ b/gfx/layers/opengl/TexturePoolOGL.h
@@ -20,16 +20,18 @@ public:
   // Get a new texture from the pool. Will block
   // and wait for one to be created if necessary
   static GLuint AcquireTexture();
 
   // Called by the active LayerManagerOGL to fill
   // the pool
   static void Fill(GLContext* aContext);
 
+  static GLContext* GetGLContext();
+
   // Initializes the pool, but does not fill it. Called by gfxPlatform init.
   static void Init();
 
   // Clears all internal data structures in preparation for shutdown
   static void Shutdown();
 };
 
 } // gl