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 id21881
push usermbrubeck@mozilla.com
push dateThu, 19 Jan 2012 18:41:36 +0000
treeherdermozilla-central@e5e66f40c35b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey
bugs719233
milestone12.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 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());