Bug 719233 - Only use direct texturing on whitelisted devices r=blassey
authorJames Willcox <jwillcox@mozilla.com>
Wed, 18 Jan 2012 20:41:28 -0500
changeset 84851 aba1658ce66cf41eaf0a4fad9b817c73ed58c73f
parent 84840 cfd3838b4dc2420462e09d8bbf187e43b2a3af0e
child 84852 84b48b4d62a1dcdce5d8b415b4979e3a72c4c365
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersblassey
bugs719233
milestone12.0a1
Bug 719233 - Only use direct texturing on whitelisted devices r=blassey
mobile/android/app/mobile.js
mobile/android/base/GeckoAppShell.java
mobile/android/base/gfx/GeckoSoftwareLayerClient.java
mozglue/android/APKOpen.cpp
widget/android/AndroidGraphicBuffer.cpp
widget/android/AndroidGraphicBuffer.h
widget/android/AndroidJNI.cpp
widget/android/AndroidJavaWrappers.cpp
widget/android/AndroidJavaWrappers.h
widget/android/nsWindow.cpp
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -685,8 +685,11 @@ pref("browser.dom.window.dump.enabled", 
 // controls if we want camera support
 pref("device.camera.enabled", true);
 pref("media.realtime_decoder.enabled", true);
 
 pref("dom.report_all_js_exceptions", true);
 pref("javascript.options.showInConsole", true);
 
 pref("full-screen-api.enabled", true);
+
+pref("direct-texture.force.enabled", false);
+pref("direct-texture.force.disabled", false);
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -152,17 +152,16 @@ public class GeckoAppShell
     public static native void notifyNoMessageInList(int aRequestId, long aProcessId);
     public static native void notifyListCreated(int aListId, int aMessageId, String aReceiver, String aSender, String aBody, long aTimestamp, int aRequestId, long aProcessId);
     public static native void notifyGotNextMessage(int aMessageId, String aReceiver, String aSender, String aBody, long aTimestamp, int aRequestId, long aProcessId);
     public static native void notifyReadingMessageListFailed(int aError, int aRequestId, long aProcessId);
 
     public static native ByteBuffer allocateDirectBuffer(long size);
     public static native void freeDirectBuffer(ByteBuffer buf);
     public static native void bindWidgetTexture();
-    public static native boolean testDirectTexture();
 
     // A looper thread, accessed by GeckoAppShell.getHandler
     private static class LooperThread extends Thread {
         public SynchronousQueue<Handler> mHandlerQueue =
             new SynchronousQueue<Handler>();
         
         public void run() {
             setName("GeckoLooper Thread");
@@ -433,24 +432,16 @@ public class GeckoAppShell
     }
 
     public static void runGecko(String apkPath, String args, String url, boolean restoreSession) {
         // run gecko -- it will spawn its own thread
         GeckoAppShell.nativeInit();
 
         Log.i(LOGTAG, "post native init");
 
-        // If we have direct texture available, use it
-        if (GeckoAppShell.testDirectTexture()) {
-            Log.i(LOGTAG, "Using direct texture for widget layer");
-            GeckoApp.mAppContext.getSoftwareLayerClient().installWidgetLayer();
-        } else {
-            Log.i(LOGTAG, "Falling back to traditional texture upload");
-        }
-
         // Tell Gecko where the target byte buffer is for rendering
         GeckoAppShell.setSoftwareLayerClient(GeckoApp.mAppContext.getSoftwareLayerClient());
 
         Log.i(LOGTAG, "setSoftwareLayerClient called");
 
         // First argument is the .apk path
         String combinedArgs = apkPath + " -greomni " + apkPath;
         if (args != null)
--- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java
+++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java
@@ -87,16 +87,19 @@ public class GeckoSoftwareLayerClient ex
 
     private static final IntSize TILE_SIZE = new IntSize(256, 256);
 
     private static final long MIN_VIEWPORT_CHANGE_DELAY = 350L;
     private long mLastViewportChangeTime;
     private boolean mPendingViewportAdjust;
     private boolean mViewportSizeChanged;
 
+    // Whether or not the last paint we got used direct texturing
+    private boolean mHasDirectTexture;
+
     // mUpdateViewportOnEndDraw is used to indicate that we received a
     // viewport update notification while drawing. therefore, when the
     // draw finishes, we need to update the entire viewport rather than
     // just the page size. this boolean should always be accessed from
     // inside a transaction, so no synchronization is needed.
     private boolean mUpdateViewportOnEndDraw;
 
     public GeckoSoftwareLayerClient(Context context) {
@@ -131,20 +134,16 @@ public class GeckoSoftwareLayerClient ex
             if (mBuffer != null)
                 GeckoAppShell.freeDirectBuffer(mBuffer);
             mBuffer = null;
         } finally {
             super.finalize();
         }
     }
 
-    public void installWidgetLayer() {
-        mTileLayer = new WidgetTileLayer(mCairoImage);
-    }
-
     /** Attaches the root layer to the layer controller so that Gecko appears. */
     @Override
     public void setLayerController(LayerController layerController) {
         super.setLayerController(layerController);
 
         layerController.setRoot(mTileLayer);
         if (mGeckoViewport != null) {
             layerController.setViewportMetrics(mGeckoViewport);
@@ -159,16 +158,41 @@ public class GeckoSoftwareLayerClient ex
         if (mTileLayer instanceof MultiTileLayer) {
             GeckoEvent event = new GeckoEvent(GeckoEvent.TILE_SIZE, TILE_SIZE);
             GeckoAppShell.sendEventToGecko(event);
         }
 
         sendResizeEventIfNecessary();
     }
 
+    private void setHasDirectTexture(boolean hasDirectTexture) {
+        if (hasDirectTexture == mHasDirectTexture)
+            return;
+
+        mHasDirectTexture = hasDirectTexture;
+
+        IntSize tileSize;
+        if (mHasDirectTexture) {
+            mTileLayer = new WidgetTileLayer(mCairoImage);
+            tileSize = new IntSize(0, 0);
+        } else {
+            mTileLayer = new MultiTileLayer(mCairoImage, TILE_SIZE);
+            tileSize = TILE_SIZE;
+        }
+
+        getLayerController().setRoot(mTileLayer);
+
+        GeckoEvent event = new GeckoEvent(GeckoEvent.TILE_SIZE, tileSize);
+        GeckoAppShell.sendEventToGecko(event);
+
+        // Force a resize event to be sent because the results of this
+        // are different depending on what tile system we're using
+        sendResizeEventIfNecessary(true);
+    }
+
     public void beginDrawing(int width, int height) {
         beginTransaction(mTileLayer);
 
         if (mBufferSize.width != width || mBufferSize.height != height) {
             mBufferSize = new IntSize(width, height);
 
             // Reallocate the buffer if necessary
 
@@ -219,24 +243,26 @@ public class GeckoSoftwareLayerClient ex
             throw new RuntimeException(e);
         }
     }
 
     /*
      * TODO: Would be cleaner if this took an android.graphics.Rect instead, but that would require
      * a little more JNI magic.
      */
-    public void endDrawing(int x, int y, int width, int height, String metadata) {
+    public void endDrawing(int x, int y, int width, int height, String metadata, boolean hasDirectTexture) {
         synchronized (getLayerController()) {
             try {
                 updateViewport(metadata, !mUpdateViewportOnEndDraw);
                 mUpdateViewportOnEndDraw = false;
                 Rect rect = new Rect(x, y, x + width, y + height);
 
-                if (mTileLayer instanceof MultiTileLayer)
+                setHasDirectTexture(hasDirectTexture);
+
+                if (!mHasDirectTexture)
                     ((MultiTileLayer)mTileLayer).invalidate(rect);
             } finally {
                 endTransaction(mTileLayer);
             }
         }
     }
 
     public ViewportMetrics getGeckoViewportMetrics() {
@@ -320,23 +346,27 @@ public class GeckoSoftwareLayerClient ex
 
     @Override
     public void geometryChanged() {
         /* Let Gecko know if the screensize has changed */
         sendResizeEventIfNecessary();
         render();
     }
 
+    private void sendResizeEventIfNecessary() {
+        sendResizeEventIfNecessary(false);
+    }
+
     /* Informs Gecko that the screen size has changed. */
-    private void sendResizeEventIfNecessary() {
+    private void sendResizeEventIfNecessary(boolean force) {
         DisplayMetrics metrics = new DisplayMetrics();
         GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
 
-        if (metrics.widthPixels == mScreenSize.width &&
-                metrics.heightPixels == mScreenSize.height) {
+        if (!force && metrics.widthPixels == mScreenSize.width &&
+            metrics.heightPixels == mScreenSize.height) {
             return;
         }
 
         mScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
         IntSize bufferSize;
 
         // Round up depending on layer implementation to remove texture wastage
         if (mTileLayer instanceof MultiTileLayer) {
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -282,17 +282,16 @@ SHELL_WRAPPER3(callObserver, jstring, js
 SHELL_WRAPPER1(removeObserver, jstring)
 SHELL_WRAPPER1(onChangeNetworkLinkStatus, jstring)
 SHELL_WRAPPER1(reportJavaCrash, jstring)
 SHELL_WRAPPER0(executeNextRunnable)
 SHELL_WRAPPER1(cameraCallbackBridge, jbyteArray)
 SHELL_WRAPPER3(notifyBatteryChange, jdouble, jboolean, jdouble);
 SHELL_WRAPPER3(notifySmsReceived, jstring, jstring, jlong);
 SHELL_WRAPPER0(bindWidgetTexture);
-SHELL_WRAPPER0_WITH_RETURN(testDirectTexture, bool);
 SHELL_WRAPPER3_WITH_RETURN(saveMessageInSentbox, jint, jstring, jstring, jlong);
 SHELL_WRAPPER6(notifySmsSent, jint, jstring, jstring, jlong, jint, jlong);
 SHELL_WRAPPER4(notifySmsDelivered, jint, jstring, jstring, jlong);
 SHELL_WRAPPER3(notifySmsSendFailed, jint, jint, jlong);
 SHELL_WRAPPER7(notifyGetSms, jint, jstring, jstring, jstring, jlong, jint, jlong);
 SHELL_WRAPPER3(notifyGetSmsFailed, jint, jint, jlong);
 SHELL_WRAPPER3(notifySmsDeleted, jboolean, jint, jlong);
 SHELL_WRAPPER3(notifySmsDeleteFailed, jint, jint, jlong);
@@ -681,17 +680,16 @@ loadLibs(const char *apkName)
   GETFUNC(removeObserver);
   GETFUNC(onChangeNetworkLinkStatus);
   GETFUNC(reportJavaCrash);
   GETFUNC(executeNextRunnable);
   GETFUNC(cameraCallbackBridge);
   GETFUNC(notifyBatteryChange);
   GETFUNC(notifySmsReceived);
   GETFUNC(bindWidgetTexture);
-  GETFUNC(testDirectTexture);
   GETFUNC(saveMessageInSentbox);
   GETFUNC(notifySmsSent);
   GETFUNC(notifySmsDelivered);
   GETFUNC(notifySmsSendFailed);
   GETFUNC(notifyGetSms);
   GETFUNC(notifyGetSmsFailed);
   GETFUNC(notifySmsDeleted);
   GETFUNC(notifySmsDeleteFailed);
--- a/widget/android/AndroidGraphicBuffer.cpp
+++ b/widget/android/AndroidGraphicBuffer.cpp
@@ -34,16 +34,18 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <dlfcn.h>
 #include <android/log.h>
 #include <GLES2/gl2.h>
 #include "AndroidGraphicBuffer.h"
+#include "AndroidBridge.h"
+#include "mozilla/Preferences.h"
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "AndroidGraphicBuffer" , ## args)
 
 #define EGL_NATIVE_BUFFER_ANDROID 0x3140
 #define EGL_IMAGE_PRESERVED_KHR   0x30D2
 
 typedef void* EGLContext;
 typedef void* EGLImageKHR;
@@ -105,22 +107,22 @@ enum {
     HAL_PIXEL_FORMAT_RGBX_8888          = 2,
     HAL_PIXEL_FORMAT_RGB_888            = 3,
     HAL_PIXEL_FORMAT_RGB_565            = 4,
     HAL_PIXEL_FORMAT_BGRA_8888          = 5,
     HAL_PIXEL_FORMAT_RGBA_5551          = 6,
     HAL_PIXEL_FORMAT_RGBA_4444          = 7,
 };
 
-typedef struct AndroidRect {
+typedef struct ARect {
     int32_t left;
     int32_t top;
     int32_t right;
     int32_t bottom;
-} AndroidRect;
+} ARect;
 
 static bool gTryRealloc = true;
 
 static class GLFunctions
 {
 public:
   GLFunctions() : mInitialized(false)
   {
@@ -149,17 +151,17 @@ public:
   pfnGraphicBufferCtor fGraphicBufferCtor;
 
   typedef void (*pfnGraphicBufferDtor)(void*);
   pfnGraphicBufferDtor fGraphicBufferDtor;
 
   typedef int (*pfnGraphicBufferLock)(void*, PRUint32 usage, unsigned char **addr);
   pfnGraphicBufferLock fGraphicBufferLock;
 
-  typedef int (*pfnGraphicBufferLockRect)(void*, PRUint32 usage, const AndroidRect&, unsigned char **addr);
+  typedef int (*pfnGraphicBufferLockRect)(void*, PRUint32 usage, const ARect&, unsigned char **addr);
   pfnGraphicBufferLockRect fGraphicBufferLockRect;
 
   typedef int (*pfnGraphicBufferUnlock)(void*);
   pfnGraphicBufferUnlock fGraphicBufferUnlock;
 
   typedef void* (*pfnGraphicBufferGetNativeBuffer)(void*);
   pfnGraphicBufferGetNativeBuffer fGraphicBufferGetNativeBuffer;
 
@@ -334,17 +336,17 @@ AndroidGraphicBuffer::Lock(PRUint32 aUsa
 }
 
 int
 AndroidGraphicBuffer::Lock(PRUint32 aUsage, const nsIntRect& aRect, unsigned char **bits)
 {
   if (!EnsureInitialized())
     return false;
 
-  AndroidRect rect;
+  ARect rect;
   rect.left = aRect.x;
   rect.top = aRect.y;
   rect.right = aRect.x + aRect.width;
   rect.bottom = aRect.y + aRect.height;
 
   return sGLFunctions.fGraphicBufferLockRect(mHandle, GetAndroidUsage(aUsage), rect, bits);
 }
 
@@ -448,9 +450,45 @@ AndroidGraphicBuffer::Bind()
     return false;
   }
 
   clearGLError();
   sGLFunctions.fImageTargetTexture2DOES(GL_TEXTURE_2D, mEGLImage);
   return ensureNoGLError("glEGLImageTargetTexture2DOES");
 }
 
+static const char* sAllowedBoards[] = {
+  "venus2", // Motorola Droid Pro
+  "tuna", // Galaxy Nexus
+  NULL
+};
+
+bool
+AndroidGraphicBuffer::IsBlacklisted()
+{
+  nsAutoString board;
+  if (!AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "BOARD", board))
+    return true;
+
+  const char* boardUtf8 = NS_ConvertUTF16toUTF8(board).get();
+
+  if (Preferences::GetBool("direct-texture.force.enabled", false)) {
+    LOG("allowing board '%s' due to prefs override", boardUtf8);
+    return false;
+  }
+
+  if (Preferences::GetBool("direct-texture.force.disabled", false)) {
+    LOG("disallowing board '%s' due to prefs override", boardUtf8);
+    return true;
+  }
+
+  for (int i = 0; sAllowedBoards[i]; i++) {
+    if (board.Find(sAllowedBoards[i]) >= 0) {
+      LOG("allowing board '%s' based on '%s'\n", boardUtf8, sAllowedBoards[i]);
+      return false;
+    }
+  }
+
+  LOG("disallowing board: %s\n", boardUtf8);
+  return true;
+}
+
 } /* mozilla */
--- a/widget/android/AndroidGraphicBuffer.h
+++ b/widget/android/AndroidGraphicBuffer.h
@@ -71,16 +71,18 @@ public:
   int Unlock();
   bool Reallocate(PRUint32 aWidth, PRUint32 aHeight, gfxASurface::gfxImageFormat aFormat);
 
   PRUint32 Width() { return mWidth; }
   PRUint32 Height() { return mHeight; }
 
   bool Bind();
 
+  static bool IsBlacklisted();
+
 private:
   PRUint32 mWidth;
   PRUint32 mHeight;
   PRUint32 mUsage;
   gfxASurface::gfxImageFormat mFormat;
 
   bool EnsureInitialized();
   bool EnsureEGLImage();
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -101,17 +101,16 @@ extern "C" {
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifySmsDeleteFailed(JNIEnv* jenv, jclass, jint, jint, jlong);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyNoMessageInList(JNIEnv* jenv, jclass, jint, jlong);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyListCreated(JNIEnv* jenv, jclass, jint, jint, jstring, jstring, jstring, jlong, jint, jlong);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyGotNextMessage(JNIEnv* jenv, jclass, jint, jstring, jstring, jstring, jlong, jint, jlong);
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyReadingMessageListFailed(JNIEnv* jenv, jclass, jint, jint, jlong);
 
 #ifdef MOZ_JAVA_COMPOSITOR
     NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_bindWidgetTexture(JNIEnv* jenv, jclass);
-    NS_EXPORT bool JNICALL Java_org_mozilla_gecko_GeckoAppShell_testDirectTexture(JNIEnv* jenv, jclass);
 #endif
 }
 
 
 /*
  * Incoming JNI methods
  */
 
@@ -846,15 +845,9 @@ Java_org_mozilla_gecko_GeckoAppShell_not
 #ifdef MOZ_JAVA_COMPOSITOR
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_bindWidgetTexture(JNIEnv* jenv, jclass)
 {
     nsWindow::BindToTexture();
 }
 
-NS_EXPORT bool JNICALL
-Java_org_mozilla_gecko_GeckoAppShell_testDirectTexture(JNIEnv* jenv, jclass)
-{
-    return nsWindow::HasDirectTexture();
-}
-
 #endif
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -319,17 +319,17 @@ AndroidGeckoSoftwareLayerClient::InitGec
     initInit();
 
     jGeckoSoftwareLayerClientClass =
         getClassGlobalRef("org/mozilla/gecko/gfx/GeckoSoftwareLayerClient");
 
     jLockBufferMethod = getMethod("lockBuffer", "()Ljava/nio/ByteBuffer;");
     jUnlockBufferMethod = getMethod("unlockBuffer", "()V");
     jBeginDrawingMethod = getMethod("beginDrawing", "(II)V");
-    jEndDrawingMethod = getMethod("endDrawing", "(IIIILjava/lang/String;)V");
+    jEndDrawingMethod = getMethod("endDrawing", "(IIIILjava/lang/String;Z)V");
 #endif
 }
 
 #undef initInit
 #undef initClassGlobalRef
 #undef getField
 #undef getMethod
 
@@ -621,23 +621,23 @@ void
 AndroidGeckoSoftwareLayerClient::BeginDrawing(int aWidth, int aHeight)
 {
     NS_ASSERTION(!isNull(), "BeginDrawing() called on null software layer client!");
     AndroidBridge::AutoLocalJNIFrame(1);
     return JNI()->CallVoidMethod(wrapped_obj, jBeginDrawingMethod, aWidth, aHeight);
 }
 
 void
-AndroidGeckoSoftwareLayerClient::EndDrawing(const nsIntRect &aRect, const nsAString &aMetadata)
+AndroidGeckoSoftwareLayerClient::EndDrawing(const nsIntRect &aRect, const nsAString &aMetadata, bool aHasDirectTexture)
 {
     NS_ASSERTION(!isNull(), "EndDrawing() called on null software layer client!");
     AndroidBridge::AutoLocalJNIFrame(1);
     jstring jMetadata = JNI()->NewString(nsPromiseFlatString(aMetadata).get(), aMetadata.Length());
     return JNI()->CallVoidMethod(wrapped_obj, jEndDrawingMethod, aRect.x, aRect.y, aRect.width,
-                                 aRect.height, jMetadata);
+                                 aRect.height, jMetadata, aHasDirectTexture);
 }
 
 jobject
 AndroidGeckoSurfaceView::GetSoftwareDrawBitmap()
 {
     return JNI()->CallObjectMethod(wrapped_obj, jGetSoftwareDrawBitmapMethod);
 }
 
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -157,17 +157,17 @@ public:
  
     AndroidGeckoSoftwareLayerClient() {}
     AndroidGeckoSoftwareLayerClient(jobject jobj) { Init(jobj); }
 
     jobject LockBuffer();
     unsigned char *LockBufferBits();
     void UnlockBuffer();
     void BeginDrawing(int aWidth, int aHeight);
-    void EndDrawing(const nsIntRect &aRect, const nsAString &aMetadata);
+    void EndDrawing(const nsIntRect &aRect, const nsAString &aMetadata, bool aHasDirectTexture);
 
 private:
     static jclass jGeckoSoftwareLayerClientClass;
     static jmethodID jLockBufferMethod;
     static jmethodID jUnlockBufferMethod;
 
 protected:
      static jmethodID jBeginDrawingMethod;
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -98,18 +98,16 @@ bool nsWindow::sAccessibilityEnabled = f
 #include "mozilla/Mutex.h"
 #include "nsThreadUtils.h"
 #include "AndroidDirectTexture.h"
 
 static AndroidDirectTexture* sDirectTexture = new AndroidDirectTexture(2048, 2048,
         AndroidGraphicBuffer::UsageSoftwareWrite | AndroidGraphicBuffer::UsageTexture,
         gfxASurface::ImageFormatRGB16_565);
 
-static bool sHasDirectTexture = true;
-
 #endif
 
 
 class ContentCreationNotifier;
 static nsCOMPtr<ContentCreationNotifier> gContentCreationNotifier;
 // A helper class to send updates when content processes
 // are created. Currently an update for the screen size is sent.
 class ContentCreationNotifier : public nsIObserver
@@ -823,46 +821,59 @@ void
 nsWindow::BindToTexture()
 {
     sDirectTexture->Bind();
 }
 
 bool
 nsWindow::HasDirectTexture()
 {
+  static bool sTestedDirectTexture = false;
+  static bool sHasDirectTexture = false;
+
   // If we already tested, return early
-  if (!sHasDirectTexture)
-    return false;
+  if (sTestedDirectTexture)
+    return sHasDirectTexture;
+
+  sTestedDirectTexture = true;
 
-  AndroidGraphicBuffer* buffer = new AndroidGraphicBuffer(512, 512,
+  nsAutoString board;
+  AndroidGraphicBuffer* buffer = NULL;
+  unsigned char* bits = NULL;
+
+  if (AndroidGraphicBuffer::IsBlacklisted()) {
+    ALOG("device is blacklisted for direct texture");
+    goto cleanup;
+  }
+
+  buffer = new AndroidGraphicBuffer(512, 512,
       AndroidGraphicBuffer::UsageSoftwareWrite | AndroidGraphicBuffer::UsageTexture,
       gfxASurface::ImageFormatRGB16_565);
 
-  unsigned char* bits = NULL;
   if (buffer->Lock(AndroidGraphicBuffer::UsageSoftwareWrite, &bits) != 0 || !bits) {
     ALOG("failed to lock graphic buffer");
     buffer->Unlock();
-    sHasDirectTexture = false;
     goto cleanup;
   }
 
   if (buffer->Unlock() != 0) {
     ALOG("failed to unlock graphic buffer");
-    sHasDirectTexture = false;
     goto cleanup;
   }
 
   if (!buffer->Reallocate(1024, 1024, gfxASurface::ImageFormatRGB16_565)) {
     ALOG("failed to reallocate graphic buffer");
-    sHasDirectTexture = false;
     goto cleanup;
   }
 
+  sHasDirectTexture = true;
+
 cleanup:
-  delete buffer;
+  if (buffer)
+    delete buffer;
 
   return sHasDirectTexture;
 }
 
 #endif
 
 void
 nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
@@ -1187,17 +1198,17 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
     AndroidGeckoSoftwareLayerClient &client =
         AndroidBridge::Bridge()->GetSoftwareLayerClient();
     client.BeginDrawing(gAndroidBounds.width, gAndroidBounds.height);
 
     nsIntRect dirtyRect = ae->Rect().Intersect(nsIntRect(0, 0, gAndroidBounds.width, gAndroidBounds.height));
 
     nsAutoString metadata;
     unsigned char *bits = NULL;
-    if (sHasDirectTexture) {
+    if (HasDirectTexture()) {
       if (sDirectTexture->Width() != gAndroidBounds.width ||
           sDirectTexture->Height() != gAndroidBounds.height) {
         sDirectTexture->Reallocate(gAndroidBounds.width, gAndroidBounds.height);
       }
 
       sDirectTexture->Lock(AndroidGraphicBuffer::UsageSoftwareWrite, dirtyRect, &bits);
     } else {
       bits = client.LockBufferBits();
@@ -1237,23 +1248,23 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
         }
 
         // Don't fill in the draw metadata on an unsuccessful draw
         if (drawSuccess && metadataProvider) {
             metadataProvider->GetDrawMetadata(metadata);
         }
     }
 
-    if (sHasDirectTexture) {
+    if (HasDirectTexture()) {
         sDirectTexture->Unlock();
     } else {
         client.UnlockBuffer();
     }
 
-    client.EndDrawing(dirtyRect, metadata);
+    client.EndDrawing(dirtyRect, metadata, HasDirectTexture());
     return;
 #endif
 
     if (!sSurfaceExists) {
         return;
     }
 
     AndroidGeckoSurfaceView& sview(AndroidBridge::Bridge()->SurfaceView());