Bug 702883 - Use a native solution for locking/unlocking plugin surfaces
authorJames Willcox <jwillcox@mozilla.com>
Wed, 16 Nov 2011 10:42:09 -0500
changeset 83499 bcb7930881c9b8be02227411dd3c3507836b83b9
parent 83498 564b52d970dbd75cd9988010e3b5eda8d15fa1c5
child 83500 f2ca3fedd77115af24a1185696a865c75bf6a7c7
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs702883
milestone11.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 702883 - Use a native solution for locking/unlocking plugin surfaces
dom/plugins/base/android/ANPSurface.cpp
dom/plugins/base/android/Makefile.in
embedding/android/GeckoAppShell.java
--- a/dom/plugins/base/android/ANPSurface.cpp
+++ b/dom/plugins/base/android/ANPSurface.cpp
@@ -31,157 +31,217 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "assert.h"
+#include <dlfcn.h>
+#include <android/log.h>
 #include "ANPBase.h"
-#include <android/log.h>
-#include "AndroidBridge.h"
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #define ASSIGN(obj, name)   (obj)->name = anp_surface_##name
 
+#define CLEAR_EXCEPTION(env) if (env->ExceptionOccurred()) env->ExceptionClear();
+
+// Copied from Android headers
+enum {
+    PIXEL_FORMAT_RGBA_8888   = 1,
+    PIXEL_FORMAT_RGB_565     = 4,
+};
+
+struct SurfaceInfo {
+    uint32_t    w;
+    uint32_t    h;
+    uint32_t    s;
+    uint32_t    usage;
+    uint32_t    format;
+    unsigned char* bits;
+    uint32_t    reserved[2];
+};
 
 // used to cache JNI method and field IDs for Surface Objects
 static struct ANPSurfaceInterfaceJavaGlue {
-  bool        initialized;
-  jclass geckoAppShellClass;
-  jclass lockInfoCls;
-  jmethodID lockSurfaceANP;
-  jmethodID jUnlockSurfaceANP;
-  jfieldID jDirtyTop;
-  jfieldID jDirtyLeft;
-  jfieldID jDirtyBottom;
-  jfieldID jDirtyRight;
-  jfieldID jFormat;
-  jfieldID jWidth ;
-  jfieldID jHeight;
-  jfieldID jBuffer;
+    bool        initialized;
+    jmethodID   getSurfaceHolder;
+    jmethodID   getSurface;
+    jfieldID    surfacePointer;
 } gSurfaceJavaGlue;
 
-#define getClassGlobalRef(env, cname)                                    \
-     (jClass = jclass(env->NewGlobalRef(env->FindClass(cname))))
+static struct ANPSurfaceFunctions {
+    bool initialized;
+
+    int (* lock)(void*, SurfaceInfo*, void*);
+    int (* unlockAndPost)(void*);
+} gSurfaceFunctions;
+
+
+static inline void* getSurface(JNIEnv* env, jobject view) {
+  if (!env || !view) {
+    return NULL;
+  }
+
+  if (!gSurfaceJavaGlue.initialized) {
 
-static void init(JNIEnv* env) {
-  if (gSurfaceJavaGlue.initialized)
-    return;
-  
-  gSurfaceJavaGlue.geckoAppShellClass = mozilla::AndroidBridge::GetGeckoAppShellClass();
-  
-  jmethodID getClass = env->GetStaticMethodID(gSurfaceJavaGlue.geckoAppShellClass, 
-                                              "getSurfaceLockInfoClass",
-                                              "()Ljava/lang/Class;");
+    jclass surfaceViewClass = env->FindClass("android/view/SurfaceView");
+    gSurfaceJavaGlue.getSurfaceHolder = env->GetMethodID(surfaceViewClass, "getHolder", "()Landroid/view/SurfaceHolder;");
+
+    jclass surfaceHolderClass = env->FindClass("android/view/SurfaceHolder");
+    gSurfaceJavaGlue.getSurface = env->GetMethodID(surfaceHolderClass, "getSurface", "()Landroid/view/Surface;");
+
+    jclass surfaceClass = env->FindClass("android/view/Surface");
+    gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass,
+        "mSurfacePointer", "I");
+
+    if (!gSurfaceJavaGlue.surfacePointer) {
+      CLEAR_EXCEPTION(env);
+
+      // It was something else in 2.2.
+      gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass,
+          "mSurface", "I");
 
-  gSurfaceJavaGlue.lockInfoCls = (jclass) env->NewGlobalRef(env->CallStaticObjectMethod(gSurfaceJavaGlue.geckoAppShellClass, getClass));
+      if (!gSurfaceJavaGlue.surfacePointer) {
+        CLEAR_EXCEPTION(env);
 
-  gSurfaceJavaGlue.jDirtyTop = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "dirtyTop", "I");
-  gSurfaceJavaGlue.jDirtyLeft = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "dirtyLeft", "I");
-  gSurfaceJavaGlue.jDirtyBottom = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "dirtyBottom", "I");
-  gSurfaceJavaGlue.jDirtyRight = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "dirtyRight", "I");
+        // And something else in 2.3+
+        gSurfaceJavaGlue.surfacePointer = env->GetFieldID(surfaceClass,
+            "mNativeSurface", "I");
+        
+        CLEAR_EXCEPTION(env);
+      }
+    }
+
+    if (!gSurfaceJavaGlue.surfacePointer) {
+      LOG("Failed to acquire surface pointer");
+      return NULL;
+    }
 
-  gSurfaceJavaGlue.jFormat = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "format", "I");
-  gSurfaceJavaGlue.jWidth = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "width", "I");
-  gSurfaceJavaGlue.jHeight = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "height", "I");
+    env->DeleteLocalRef(surfaceClass);
+    env->DeleteLocalRef(surfaceViewClass);
+    env->DeleteLocalRef(surfaceHolderClass);
+
+    gSurfaceJavaGlue.initialized = (gSurfaceJavaGlue.surfacePointer != NULL);
+  }
 
-  gSurfaceJavaGlue.jBuffer = env->GetFieldID(gSurfaceJavaGlue.lockInfoCls, "buffer", "Ljava/nio/Buffer;");
-  gSurfaceJavaGlue.lockSurfaceANP = env->GetStaticMethodID(gSurfaceJavaGlue.geckoAppShellClass, "lockSurfaceANP", "(Landroid/view/SurfaceView;IIII)Lorg/mozilla/gecko/SurfaceLockInfo;");
-  gSurfaceJavaGlue.jUnlockSurfaceANP = env->GetStaticMethodID(gSurfaceJavaGlue.geckoAppShellClass, "unlockSurfaceANP", "(Landroid/view/SurfaceView;)V");
-  gSurfaceJavaGlue.initialized = true;
+  jobject holder = env->CallObjectMethod(view, gSurfaceJavaGlue.getSurfaceHolder);
+  jobject surface = env->CallObjectMethod(holder, gSurfaceJavaGlue.getSurface);
+  jint surfacePointer = env->GetIntField(surface, gSurfaceJavaGlue.surfacePointer);
+
+  env->DeleteLocalRef(holder);
+  env->DeleteLocalRef(surface);
+
+  return (void*)surfacePointer;
 }
 
-static bool anp_lock(JNIEnv* env, jobject surfaceView, ANPBitmap* bitmap, ANPRectI* dirtyRect) {
-  LOG("%s", __PRETTY_FUNCTION__);
-  if (!bitmap || !surfaceView) {
-    LOG("%s, null bitmap or surface, exiting", __PRETTY_FUNCTION__);
+static ANPBitmapFormat convertPixelFormat(int32_t format) {
+  switch (format) {
+    case PIXEL_FORMAT_RGBA_8888:  return kRGBA_8888_ANPBitmapFormat;
+    case PIXEL_FORMAT_RGB_565:    return kRGB_565_ANPBitmapFormat;
+    default:            return kUnknown_ANPBitmapFormat;
+  }
+}
+
+static int bytesPerPixel(int32_t format) {
+  switch (format) {
+    case PIXEL_FORMAT_RGBA_8888: return 4;
+    case PIXEL_FORMAT_RGB_565: return 2;
+    default: return -1;
+  }
+}
+
+static bool init() {
+  if (gSurfaceFunctions.initialized)
+    return true;
+
+  void* handle = dlopen("/system/lib/libsurfaceflinger_client.so", RTLD_LAZY);
+
+  if (!handle) {
+    LOG("Failed to open libsurfaceflinger_client.so");
     return false;
   }
 
-  init(env);
+  gSurfaceFunctions.lock = (int (*)(void*, SurfaceInfo*, void*))dlsym(handle, "_ZN7android7Surface4lockEPNS0_11SurfaceInfoEPNS_6RegionEb");
+  gSurfaceFunctions.unlockAndPost = (int (*)(void*))dlsym(handle, "_ZN7android7Surface13unlockAndPostEv");
 
-  jvalue args[5];
-  args[0].l = surfaceView;
-  if (dirtyRect) {
-    args[1].i = dirtyRect->top;
-    args[2].i = dirtyRect->left;
-    args[3].i = dirtyRect->bottom;
-    args[4].i = dirtyRect->right;
-    LOG("dirty rect: %d, %d, %d, %d", dirtyRect->top, dirtyRect->left, dirtyRect->bottom, dirtyRect->right);
-  } else {
-    args[1].i = args[2].i = args[3].i = args[4].i = 0;
-  }
-  
-  jobject info = env->CallStaticObjectMethod(gSurfaceJavaGlue.geckoAppShellClass,
-                                             gSurfaceJavaGlue.lockSurfaceANP, 
-                                             surfaceView, args[1].i, args[2].i, args[3].i, args[4].i);
+  gSurfaceFunctions.initialized = (gSurfaceFunctions.lock && gSurfaceFunctions.unlockAndPost);
+  LOG("Initialized? %d\n", gSurfaceFunctions.initialized);
+  return gSurfaceFunctions.initialized;
+}
 
-  LOG("info: %p", info);
-  if (!info)
+static bool anp_surface_lock(JNIEnv* env, jobject surfaceView, ANPBitmap* bitmap, ANPRectI* dirtyRect) {
+  if (!bitmap || !surfaceView) {
     return false;
-
-  // the surface may have expanded the dirty region so we must to pass that
-  // information back to the plugin.
-  if (dirtyRect) {
-    dirtyRect->left   = env->GetIntField(info, gSurfaceJavaGlue.jDirtyLeft);
-    dirtyRect->right  = env->GetIntField(info, gSurfaceJavaGlue.jDirtyRight);
-    dirtyRect->top    = env->GetIntField(info, gSurfaceJavaGlue.jDirtyTop);
-    dirtyRect->bottom = env->GetIntField(info, gSurfaceJavaGlue.jDirtyBottom);
-    LOG("dirty rect: %d, %d, %d, %d", dirtyRect->top, dirtyRect->left, dirtyRect->bottom, dirtyRect->right);
   }
 
-  bitmap->width  = env->GetIntField(info, gSurfaceJavaGlue.jWidth);
-  bitmap->height = env->GetIntField(info, gSurfaceJavaGlue.jHeight);
+  void* surface = getSurface(env, surfaceView);
 
-  int format = env->GetIntField(info, gSurfaceJavaGlue.jFormat);
+  if (!bitmap || !surface) {
+    return false;
+  }
 
-  // format is PixelFormat
-  if (format & 0x00000001) {
-    bitmap->format = kRGBA_8888_ANPBitmapFormat;
-    bitmap->rowBytes = bitmap->width * 4;
+  if (!init()) {
+    return false;
   }
-  else if (format & 0x00000004) {
-    bitmap->format = kRGB_565_ANPBitmapFormat;
-    bitmap->rowBytes = bitmap->width * 2;
-  }
-  else {
-    LOG("format from glue is unknown %d\n", format);
+
+  SurfaceInfo info;
+  int err = gSurfaceFunctions.lock(surface, &info, NULL);
+  if (err < 0) {
     return false;
   }
 
-  jobject buf = env->GetObjectField(info, gSurfaceJavaGlue.jBuffer);
-  bitmap->baseAddr = env->GetDirectBufferAddress(buf);
-  
-  LOG("format: %d, width: %d, height: %d",  bitmap->format,  bitmap->width,  bitmap->height);
-  env->DeleteLocalRef(info);
-  env->DeleteLocalRef(buf);
-  return ( bitmap->width > 0 && bitmap->height > 0 );
+  if (dirtyRect) {
+    // We can't lock the specific region, so we must expand the dirty rect
+    // to be the whole surface
+    dirtyRect->left = dirtyRect->top = 0;
+    dirtyRect->right = info.w;
+    dirtyRect->bottom = info.h;
+  }
+
+  int bpr = info.s * bytesPerPixel(info.format);
+
+  bitmap->format = convertPixelFormat(info.format);
+  bitmap->width = info.w;
+  bitmap->height = info.h;
+  bitmap->rowBytes = bpr;
+
+  if (info.w > 0 && info.h > 0) {
+    bitmap->baseAddr = info.bits;
+  } else {
+    bitmap->baseAddr = NULL;
+    return false;
+  }
+
+  return true;
 }
 
-static void anp_unlock(JNIEnv* env, jobject surfaceView) {
-  LOG("%s", __PRETTY_FUNCTION__);
-
+static void anp_surface_unlock(JNIEnv* env, jobject surfaceView) {
   if (!surfaceView) {
-    LOG("null surface, exiting %s", __PRETTY_FUNCTION__);
     return;
   }
 
-  init(env);
-  env->CallStaticVoidMethod(gSurfaceJavaGlue.geckoAppShellClass, gSurfaceJavaGlue.jUnlockSurfaceANP, surfaceView);
-  LOG("returning from %s", __PRETTY_FUNCTION__);
+  if (!init()) {
+    return;
+  }
+
+  void* surface = getSurface(env, surfaceView);
 
+  if (!surface) {
+    return;
+  }
+
+  gSurfaceFunctions.unlockAndPost(surface);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-#define ASSIGN(obj, name)   (obj)->name = anp_##name
-
-void InitSurfaceInterface(ANPSurfaceInterfaceV0 *i) {
-
+void InitSurfaceInterface(ANPSurfaceInterfaceV0* i) {
   ASSIGN(i, lock);
   ASSIGN(i, unlock);
 
   // setup the java glue struct
   gSurfaceJavaGlue.initialized = false;
+
+  // setup the function struct
+  gSurfaceFunctions.initialized = false;
 }
--- a/dom/plugins/base/android/Makefile.in
+++ b/dom/plugins/base/android/Makefile.in
@@ -63,15 +63,16 @@ CPPSRCS += ANPAudio.cpp    \
            ANPWindow.cpp   \
            ANPBitmap.cpp   \
            ANPLog.cpp      \
            ANPSurface.cpp  \
            $(NULL)
 
 LOCAL_INCLUDES += \
   -I$(topsrcdir)/dom/plugins/base \
+  -I$(topsrcdir)/dom/plugins/base/android/include \
   $(MOZ_CAIRO_CFLAGS) \
   $(NULL)
 
 DEFINES += -DMOZ_APP_NAME='"$(MOZ_APP_NAME)"'
 
 include $(topsrcdir)/config/rules.mk
 
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -1285,115 +1285,16 @@ public class GeckoAppShell
             Log.i("GeckoAppShell", "class not found", cnfe);
         } catch (android.content.pm.PackageManager.NameNotFoundException nnfe) {
             Log.i("GeckoAppShell", "package not found", nnfe);
         }
         Log.e("GeckoAppShell", "couldn't find class");
         return null;
     }
 
-    static HashMap<SurfaceView, SurfaceLockInfo> sSufaceMap = new HashMap<SurfaceView, SurfaceLockInfo>();
-
-    public static void lockSurfaceANP()
-    {
-         Log.i("GeckoAppShell", "other lockSurfaceANP");
-    }
-
-    public static org.mozilla.gecko.SurfaceLockInfo lockSurfaceANP(android.view.SurfaceView sview, int top, int left, int bottom, int right)
-    {
-        Log.i("GeckoAppShell", "real lockSurfaceANP " + sview + ", " + top + ",  " + left + ", " + bottom + ", " + right);
-        if (sview == null)
-            return null;
-
-        int format = -1;
-        try {
-            Field privateFormatField = SurfaceView.class.getDeclaredField("mFormat");
-            privateFormatField.setAccessible(true);
-            format = privateFormatField.getInt(sview);
-        } catch (Exception e) {
-            Log.i("GeckoAppShell", "mFormat is not a field of sview: ", e);
-        }
-
-        int n = 0;
-        if (format == PixelFormat.RGB_565)
-            n = 2;
-        else if (format == PixelFormat.RGBA_8888)
-            n = 4;
-
-        if (n == 0)
-            return null;
-
-        SurfaceLockInfo info = sSufaceMap.get(sview);
-        if (info == null) {
-            info = new SurfaceLockInfo();
-            sSufaceMap.put(sview, info);
-        }
-
-        Rect r = new Rect(left, top, right, bottom);
-
-        info.canvas = sview.getHolder().lockCanvas(r);
-        int bufSizeRequired = info.canvas.getWidth() * info.canvas.getHeight() * n;
-        Log.i("GeckoAppShell", "lockSurfaceANP - bufSizeRequired: " + n + " " + info.canvas.getHeight() + " " + info.canvas.getWidth());
-
-        if (info.width != info.canvas.getWidth() || info.height != info.canvas.getHeight() || info.buffer == null || info.buffer.capacity() < bufSizeRequired) {
-            info.width = info.canvas.getWidth();
-            info.height = info.canvas.getHeight();
-
-            // XXX Bitmaps instead of ByteBuffer
-            info.buffer = ByteBuffer.allocateDirect(bufSizeRequired);  //leak
-            Log.i("GeckoAppShell", "!!!!!!!!!!!  lockSurfaceANP - Allocating buffer! " + bufSizeRequired);
-
-        }
-
-        info.canvas.drawColor(Color.WHITE, PorterDuff.Mode.CLEAR);
-
-        info.format = format;
-        info.dirtyTop = top;
-        info.dirtyBottom = bottom;
-        info.dirtyLeft = left;
-        info.dirtyRight = right;
-
-        return info;
-    }
-
-    public static void unlockSurfaceANP(SurfaceView sview) {
-        SurfaceLockInfo info = sSufaceMap.get(sview);
-
-        int n = 0;
-        Bitmap.Config config;
-        if (info.format == PixelFormat.RGB_565) {
-            n = 2;
-            config = Bitmap.Config.RGB_565;
-        } else {
-            n = 4;
-            config = Bitmap.Config.ARGB_8888;
-        }
-
-        Log.i("GeckoAppShell", "unlockSurfaceANP: " + (info.width * info.height * n));
-
-        Bitmap bm = Bitmap.createBitmap(info.width, info.height, config);
-        bm.copyPixelsFromBuffer(info.buffer);
-        info.canvas.drawBitmap(bm, 0, 0, null);
-        sview.getHolder().unlockCanvasAndPost(info.canvas);
-    }
-
-    public static Class getSurfaceLockInfoClass() {
-        Log.i("GeckoAppShell", "class name: " + SurfaceLockInfo.class.getName());
-        return SurfaceLockInfo.class;
-    }
-
-    public static Method getSurfaceLockMethod() {
-        Method[] m = GeckoAppShell.class.getMethods();
-        for (int i = 0; i < m.length; i++) {
-            if (m[i].getName().equals("lockSurfaceANP"))
-                return m[i];
-        }
-        return null;
-    }
-
     static native void executeNextRunnable();
 
     static class GeckoRunnableCallback implements Runnable {
         public void run() {
             Log.i("GeckoShell", "run GeckoRunnableCallback");
             GeckoAppShell.executeNextRunnable();
         }
     }