Bug 1092294 - Use SurfaceTextures for painted content on android (preffed off). r=nical,snorp
authorJamie Nicol <jnicol@mozilla.com>
Sat, 28 Oct 2017 11:59:58 +0100
changeset 439830 2d2922ab35d243657108303c7262f4c2793cbe86
parent 439829 97031d4ea2bd29ca2d5f21ad76ef37e3ec4069d3
child 439831 4e36da3344e8c1c668b1932b1d4e57fc45ed6258
push id8114
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 16:33:21 +0000
treeherdermozilla-beta@73e0d89a540f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical, snorp
bugs1092294
milestone58.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 1092294 - Use SurfaceTextures for painted content on android (preffed off). r=nical,snorp Add a new TextureClientData type, AndroidNativeWindowTextureData, backed by a SurfaceTexture in single-buffer mode. It uses the NativeWindow API, which provides producer-side access to the buffer. This provides a DrawTarget which can be used to paint directly in to the SurfaceTexture, which can then be composited using a SurfaceTextureHost. Due to API restrictions it is not possible to read from a NativeWindow while the corresponding SurfaceTexture has ownership of the buffer. TiledContentClient now handles that by painting the additional region that it cannot copy from the front buffer, if required. MozReview-Commit-ID: 1NZq6MQqwFq
gfx/gl/SharedSurfaceEGL.cpp
gfx/layers/client/SingleTiledContentClient.cpp
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TextureClient.h
gfx/layers/client/TiledContentClient.cpp
gfx/layers/client/TiledContentClient.h
gfx/layers/ipc/LayersSurfaces.ipdlh
gfx/layers/opengl/CompositorOGL.cpp
gfx/layers/opengl/TextureClientOGL.cpp
gfx/layers/opengl/TextureClientOGL.h
gfx/layers/opengl/TextureHostOGL.cpp
gfx/layers/opengl/TextureHostOGL.h
gfx/thebes/gfxPrefs.h
mobile/android/app/mobile.js
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoSurfaceTexture.java
--- a/gfx/gl/SharedSurfaceEGL.cpp
+++ b/gfx/gl/SharedSurfaceEGL.cpp
@@ -269,17 +269,22 @@ SharedSurface_SurfaceTexture::WaitForBuf
 {
     MOZ_RELEASE_ASSERT(!mSurface->GetAvailable());
     mSurface->SetAvailable(true);
 }
 
 bool
 SharedSurface_SurfaceTexture::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
 {
-    *out_descriptor = layers::SurfaceTextureDescriptor(mSurface->GetHandle(), mSize, false /* NOT continuous */);
+    *out_descriptor =
+        layers::SurfaceTextureDescriptor(mSurface->GetHandle(),
+                                         mSize,
+                                         gfx::SurfaceFormat::R8G8B8A8,
+                                         false /* NOT continuous */,
+                                         false /* Do not ignore transform */);
     return true;
 }
 
 ////////////////////////////////////////////////////////////////////////
 
 /*static*/ UniquePtr<SurfaceFactory_SurfaceTexture>
 SurfaceFactory_SurfaceTexture::Create(GLContext* prodGL, const SurfaceCaps& caps,
                                       const RefPtr<layers::LayersIPCChannel>& allocator,
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -146,16 +146,25 @@ ClientSingleTiledLayerBuffer::PaintThebe
   RefPtr<TextureClient> backBufferOnWhite;
   RefPtr<TextureClient> backBuffer =
     mTile.GetBackBuffer(mCompositableClient,
                         tileDirtyRegion,
                         content, mode,
                         extraPainted,
                         &backBufferOnWhite);
 
+  // Mark the area we need to paint in the back buffer as invalid in the
+  // front buffer as they will become out of sync.
+  mTile.mInvalidFront.OrWith(tileDirtyRegion);
+
+  // Add backbuffer's invalid region to the dirty region to be painted.
+  // This will be empty if we were able to copy from the front in to the back.
+  paintRegion.OrWith(mTile.mInvalidBack.MovedBy(mTilingOrigin));
+  tileDirtyRegion.OrWith(mTile.mInvalidBack);
+
   mTile.mUpdateRect = tileDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
 
   extraPainted.MoveBy(mTilingOrigin);
   extraPainted.And(extraPainted, aNewValidRegion);
   mPaintedRegion.OrWith(paintRegion);
   mPaintedRegion.OrWith(extraPainted);
 
   if (!backBuffer) {
@@ -193,36 +202,36 @@ ClientSingleTiledLayerBuffer::PaintThebe
       TextureClientAutoLock frontLock(discardedFrontBuffer,
                                       OpenMode::OPEN_READ);
       if (frontLock.Succeeded()) {
         for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
           const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
           const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
           discardedFrontBuffer->CopyToTextureClient(backBuffer, &rect, &dest);
         }
-      }
+
+        if (discardedFrontBufferOnWhite && backBufferOnWhite) {
+          TextureClientAutoLock frontOnWhiteLock(discardedFrontBufferOnWhite,
+                                                OpenMode::OPEN_READ);
+          if (frontOnWhiteLock.Succeeded()) {
+            for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
+              const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
+              const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
 
-      if (discardedFrontBufferOnWhite && backBufferOnWhite) {
-        TextureClientAutoLock frontOnWhiteLock(discardedFrontBufferOnWhite,
-                                               OpenMode::OPEN_READ);
-        if (frontOnWhiteLock.Succeeded()) {
-          for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
-            const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
-            const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
+              discardedFrontBufferOnWhite->CopyToTextureClient(backBufferOnWhite,
+                                                              &rect, &dest);
+            }
 
-            discardedFrontBufferOnWhite->CopyToTextureClient(backBufferOnWhite,
-                                                             &rect, &dest);
+            TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str());
+
+            // We don't need to repaint valid content that was just copied.
+            paintRegion.SubOut(copyableRegion);
           }
         }
       }
-
-      TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str());
-
-      // We don't need to repaint valid content that was just copied.
-      paintRegion.SubOut(copyableRegion);
     }
   }
 
   if (dtOnWhite) {
     dt = gfx::Factory::CreateDualDrawTarget(dt, dtOnWhite);
     dtOnWhite = nullptr;
   }
 
@@ -232,20 +241,16 @@ ClientSingleTiledLayerBuffer::PaintThebe
       gfxDevCrash(gfx::LogReason::InvalidContext) << "SingleTiledContextClient context problem " << gfx::hexa(dt);
       return;
     }
     ctx->SetMatrix(ctx->CurrentMatrix().PreTranslate(-mTilingOrigin.x, -mTilingOrigin.y));
 
     aCallback(&mPaintedLayer, ctx, paintRegion, paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData);
   }
 
-  // Mark the area we just drew into the back buffer as invalid in the front buffer as they're
-  // now out of sync.
-  mTile.mInvalidFront.OrWith(tileDirtyRegion);
-
   // The new buffer is now validated, remove the dirty region from it.
   mTile.mInvalidBack.SubOut(tileDirtyRegion);
 
   dt = nullptr;
 
   mTile.Flip();
   UnlockTile(mTile);
 
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -497,18 +497,21 @@ TextureClient::Lock(OpenMode aMode)
   MOZ_ASSERT(!mIsLocked);
   if (!IsValid()) {
     return false;
   }
   if (mIsLocked) {
     return mOpenMode == aMode;
   }
 
-  if (aMode & OpenMode::OPEN_WRITE && !TryReadLock()) {
-    NS_WARNING("Attempt to Lock a texture that is being read by the compositor!");
+  if ((aMode & OpenMode::OPEN_WRITE || !mInfo.canConcurrentlyReadLock) && !TryReadLock()) {
+    // Only warn if attempting to write. Attempting to read is acceptable usage.
+    if (aMode & OpenMode::OPEN_WRITE) {
+      NS_WARNING("Attempt to Lock a texture that is being read by the compositor!");
+    }
     return false;
   }
 
   LockActor();
 
   mIsLocked = mData->Lock(aMode);
   mOpenMode = aMode;
 
@@ -589,16 +592,20 @@ TextureClient::EnableReadLock()
   if (!mReadLock) {
     mReadLock = NonBlockingTextureReadLock::Create(mAllocator);
   }
 }
 
 bool
 TextureClient::SerializeReadLock(ReadLockDescriptor& aDescriptor)
 {
+  if (mData) {
+    mData->OnForwardedToHost();
+  }
+
   if (mReadLock && mUpdated) {
     // Take a read lock on behalf of the TextureHost. The latter will unlock
     // after the shared data is available again for drawing.
     mReadLock->ReadLock();
     mUpdated = false;
     if (mReadLock->Serialize(aDescriptor, GetAllocator()->GetParentPid())) {
       return true;
     }
@@ -1119,16 +1126,22 @@ TextureClient::CreateForDrawing(TextureF
 #endif
 
 #ifdef XP_MACOSX
   if (!data && gfxPrefs::UseIOSurfaceTextures()) {
     data = MacIOSurfaceTextureData::Create(aSize, aFormat, moz2DBackend);
   }
 #endif
 
+#ifdef MOZ_WIDGET_ANDROID
+  if (!data && gfxPrefs::UseSurfaceTextureTextures()) {
+    data = AndroidNativeWindowTextureData::Create(aSize, aFormat);
+  }
+#endif
+
   if (data) {
     return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator);
   }
 
   if (moz2DBackend == BackendType::SKIA && aFormat == SurfaceFormat::B8G8R8X8) {
     // Skia doesn't support RGBX, so ensure we clear the buffer for the proper alpha values.
     aAllocFlags = TextureAllocationFlags(aAllocFlags | ALLOC_CLEAR_BUFFER);
   }
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -231,23 +231,25 @@ class TextureData {
 public:
   struct Info {
     gfx::IntSize size;
     gfx::SurfaceFormat format;
     bool hasIntermediateBuffer;
     bool hasSynchronization;
     bool supportsMoz2D;
     bool canExposeMappedData;
+    bool canConcurrentlyReadLock;
 
     Info()
     : format(gfx::SurfaceFormat::UNKNOWN)
     , hasIntermediateBuffer(false)
     , hasSynchronization(false)
     , supportsMoz2D(false)
     , canExposeMappedData(false)
+    , canConcurrentlyReadLock(true)
     {}
   };
 
   TextureData() { MOZ_COUNT_CTOR(TextureData); }
 
   virtual ~TextureData() { MOZ_COUNT_DTOR(TextureData); }
 
   virtual void FillInfo(TextureData::Info& aInfo) const = 0;
@@ -265,16 +267,18 @@ public:
   virtual void Deallocate(LayersIPCChannel* aAllocator) = 0;
 
   /// Depending on the texture's flags either Deallocate or Forget is called.
   virtual void Forget(LayersIPCChannel* aAllocator) {}
 
   virtual bool Serialize(SurfaceDescriptor& aDescriptor) = 0;
   virtual void GetSubDescriptor(GPUVideoSubDescriptor* aOutDesc) { }
 
+  virtual void OnForwardedToHost() {}
+
   virtual TextureData*
   CreateSimilar(LayersIPCChannel* aAllocator,
                 LayersBackend aLayersBackend,
                 TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const { return nullptr; }
 
   virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) { return false; };
 
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -475,17 +475,16 @@ TileClient::Flip()
 
 static bool
 CopyFrontToBack(TextureClient* aFront,
                 TextureClient* aBack,
                 const gfx::IntRect& aRectToCopy)
 {
   TextureClientAutoLock frontLock(aFront, OpenMode::OPEN_READ);
   if (!frontLock.Succeeded()) {
-    gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's front buffer";
     return false;
   }
 
   if (!aBack->Lock(OpenMode::OPEN_READ_WRITE)) {
     gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's back buffer";
     return false;
   }
 
@@ -519,24 +518,24 @@ TileClient::ValidateBackBufferFromFront(
         return;
       }
 
       // Copy the bounding rect of regionToCopy. As tiles are quite small, it
       // is unlikely that we'd save much by copying each individual rect of the
       // region, but we can reevaluate this if it becomes an issue.
       const IntRect rectToCopy = regionToCopy.GetBounds();
       gfx::IntRect gfxRectToCopy(rectToCopy.x, rectToCopy.y, rectToCopy.Width(), rectToCopy.Height());
-      CopyFrontToBack(mFrontBuffer, mBackBuffer, gfxRectToCopy);
-
-      if (mBackBufferOnWhite) {
-        MOZ_ASSERT(mFrontBufferOnWhite);
-        CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, gfxRectToCopy);
+      if (CopyFrontToBack(mFrontBuffer, mBackBuffer, gfxRectToCopy)) {
+        if (mBackBufferOnWhite) {
+          MOZ_ASSERT(mFrontBufferOnWhite);
+          if (CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, gfxRectToCopy)) {
+            mInvalidBack.SetEmpty();
+          }
+        }
       }
-
-      mInvalidBack.SetEmpty();
     }
   }
 }
 
 void
 TileClient::DiscardFrontBuffer()
 {
   if (mFrontBuffer) {
@@ -946,32 +945,38 @@ void ClientMultiTiledLayerBuffer::Update
       // release tiles that we are not going to reuse before allocating new ones
       // to avoid allocating unnecessarily.
       oldRetainedTiles[oldIndex].DiscardBuffers();
     }
   }
 
   oldRetainedTiles.Clear();
 
-  if (!aPaintRegion.IsEmpty()) {
+  nsIntRegion paintRegion = aPaintRegion;
+  nsIntRegion dirtyRegion = aDirtyRegion;
+  if (!paintRegion.IsEmpty()) {
     for (size_t i = 0; i < newTileCount; ++i) {
       const TileIntPoint tilePosition = newTiles.TilePosition(i);
 
       IntPoint tileOffset = GetTileOffset(tilePosition);
       nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
-      tileDrawRegion.AndWith(aPaintRegion);
+      tileDrawRegion.AndWith(paintRegion);
 
       if (tileDrawRegion.IsEmpty()) {
         continue;
       }
 
       TileClient& tile = mRetainedTiles[i];
       if (!ValidateTile(tile, GetTileOffset(tilePosition), tileDrawRegion)) {
         gfxCriticalError() << "ValidateTile failed";
       }
+
+      // Validating the tile may have required more to be painted.
+      paintRegion.OrWith(tileDrawRegion);
+      dirtyRegion.OrWith(tileDrawRegion);
     }
 
     if (!mMoz2DTiles.empty()) {
       gfx::TileSet tileset;
       for (size_t i = 0; i < mMoz2DTiles.size(); ++i) {
         mMoz2DTiles[i].mTileOrigin -= mTilingOrigin;
       }
       tileset.mTiles = &mMoz2DTiles[0];
@@ -983,17 +988,17 @@ void ClientMultiTiledLayerBuffer::Update
       }
       drawTarget->SetTransform(Matrix());
 
       RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget);
       MOZ_ASSERT(ctx); // already checked the draw target above
       ctx->SetMatrix(
         ctx->CurrentMatrix().PreScale(mResolution, mResolution).PreTranslate(ThebesPoint(-mTilingOrigin)));
 
-      mCallback(&mPaintedLayer, ctx, aPaintRegion, aDirtyRegion,
+      mCallback(&mPaintedLayer, ctx, paintRegion, dirtyRegion,
                 DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
       mMoz2DTiles.clear();
       // Reset:
       mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
                                std::numeric_limits<int32_t>::max());
     }
 
     bool edgePaddingEnabled = gfxPrefs::TileEdgePaddingEnabled();
@@ -1010,17 +1015,17 @@ void ClientMultiTiledLayerBuffer::Update
         const TileIntPoint tilePosition = newTiles.TilePosition(i);
         IntPoint tileOffset = GetTileOffset(tilePosition);
         // Strictly speakig we want the unscaled rect here, but it doesn't matter
         // because we only run this code when the resolution is equal to 1.
         IntRect tileRect = IntRect(tileOffset.x, tileOffset.y,
                                    GetTileSize().width, GetTileSize().height);
 
         nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
-        tileDrawRegion.AndWith(aPaintRegion);
+        tileDrawRegion.AndWith(paintRegion);
 
         nsIntRegion tileValidRegion = mValidRegion;
         tileValidRegion.OrWith(tileDrawRegion);
 
         // We only need to pad out if the tile has area that's not valid
         if (!tileValidRegion.Contains(tileRect)) {
           tileValidRegion = tileValidRegion.Intersect(tileRect);
           // translate the region into tile space and pad
@@ -1030,23 +1035,23 @@ void ClientMultiTiledLayerBuffer::Update
         }
       }
       UnlockTile(tile);
     }
   }
 
   mTiles = newTiles;
   mValidRegion = newValidRegion;
-  mPaintedRegion.OrWith(aPaintRegion);
+  mPaintedRegion.OrWith(paintRegion);
 }
 
 bool
 ClientMultiTiledLayerBuffer::ValidateTile(TileClient& aTile,
                                           const nsIntPoint& aTileOrigin,
-                                          const nsIntRegion& aDirtyRegion)
+                                          nsIntRegion& aDirtyRegion)
 {
   AUTO_PROFILER_LABEL("ClientMultiTiledLayerBuffer::ValidateTile", GRAPHICS);
 
 #ifdef GFX_TILEDLAYER_PREF_WARNINGS
   if (aDirtyRegion.IsComplex()) {
     printf_stderr("Complex region\n");
   }
 #endif
@@ -1069,16 +1074,29 @@ ClientMultiTiledLayerBuffer::ValidateTil
   RefPtr<TextureClient> backBufferOnWhite;
   RefPtr<TextureClient> backBuffer =
     aTile.GetBackBuffer(mCompositableClient,
                         offsetScaledDirtyRegion,
                         content, mode,
                         extraPainted,
                         &backBufferOnWhite);
 
+  // Mark the area we need to paint in the back buffer as invalid in the
+  // front buffer as they will become out of sync.
+  aTile.mInvalidFront.OrWith(offsetScaledDirtyRegion);
+
+  // Add backbuffer's invalid region to the dirty region to be painted.
+  // This will be empty if we were able to copy from the front in to the back.
+  nsIntRegion invalidBack = aTile.mInvalidBack;
+  invalidBack.MoveBy(aTileOrigin);
+  invalidBack.ScaleInverseRoundOut(mResolution, mResolution);
+  invalidBack.AndWith(mNewValidRegion);
+  aDirtyRegion.OrWith(invalidBack);
+  offsetScaledDirtyRegion.OrWith(aTile.mInvalidBack);
+
   aTile.mUpdateRect = offsetScaledDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
 
   extraPainted.MoveBy(aTileOrigin);
   extraPainted.And(extraPainted, mNewValidRegion);
   mPaintedRegion.Or(mPaintedRegion, extraPainted);
 
   if (!backBuffer) {
     return false;
@@ -1098,26 +1116,19 @@ ClientMultiTiledLayerBuffer::ValidateTil
     aTile.DiscardBuffers();
     return false;
   }
 
   mMoz2DTiles.push_back(moz2DTile);
   mTilingOrigin.x = std::min(mTilingOrigin.x, moz2DTile.mTileOrigin.x);
   mTilingOrigin.y = std::min(mTilingOrigin.y, moz2DTile.mTileOrigin.y);
 
-  for (auto iter = aDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
-    const IntRect& dirtyRect = iter.Get();
-    gfx::Rect drawRect(dirtyRect.x - aTileOrigin.x,
-                       dirtyRect.y - aTileOrigin.y,
-                       dirtyRect.Width(),
-                       dirtyRect.Height());
-    drawRect.Scale(mResolution);
-
-    // Mark the newly updated area as invalid in the front buffer
-    aTile.mInvalidFront.Or(aTile.mInvalidFront, IntRect::RoundOut(drawRect));
+  for (auto iter = offsetScaledDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
+    const gfx::Rect drawRect(iter.Get().x, iter.Get().y,
+                             iter.Get().width, iter.Get().height);
 
     if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
       dt->FillRect(drawRect, ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
       dtOnWhite->FillRect(drawRect, ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
     } else if (content == gfxContentType::COLOR_ALPHA) {
       dt->ClearRect(drawRect);
     }
   }
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -407,18 +407,18 @@ public:
 
     Update(nsIntRegion(), nsIntRegion(), nsIntRegion());
     mResolution = aResolution;
   }
 
 protected:
   bool ValidateTile(TileClient& aTile,
                     const nsIntPoint& aTileRect,
-                    const nsIntRegion& dirtyRect);
-  
+                    nsIntRegion& aDirtyRegion);
+
   void Update(const nsIntRegion& aNewValidRegion,
               const nsIntRegion& aPaintRegion,
               const nsIntRegion& aDirtyRegion);
 
   TileClient GetPlaceholderTile() const { return TileClient(); }
 
 private:
   RefPtr<ClientLayerManager> mManager;
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -57,17 +57,19 @@ struct SurfaceDescriptorMacIOSurface {
   uint32_t surfaceId;
   double scaleFactor;
   bool isOpaque;
 };
 
 struct SurfaceTextureDescriptor {
   uint64_t handle;
   IntSize size;
+  SurfaceFormat format;
   bool continuous;
+  bool ignoreTransform;
 };
 
 struct EGLImageDescriptor {
   uintptr_t image; // `EGLImage` is a `void*`.
   uintptr_t fence;
   IntSize size;
   bool hasAlpha;
 };
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -861,17 +861,20 @@ CompositorOGL::GetShaderConfigFor(Effect
   default:
   {
     MOZ_ASSERT(aEffect->mType == EffectTypes::RGB);
     TexturedEffect* texturedEffect =
         static_cast<TexturedEffect*>(aEffect);
     TextureSourceOGL* source = texturedEffect->mTexture->AsSourceOGL();
     MOZ_ASSERT_IF(source->GetTextureTarget() == LOCAL_GL_TEXTURE_EXTERNAL,
                   source->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
-                  source->GetFormat() == gfx::SurfaceFormat::R8G8B8X8);
+                  source->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
+                  source->GetFormat() == gfx::SurfaceFormat::B8G8R8A8 ||
+                  source->GetFormat() == gfx::SurfaceFormat::B8G8R8X8 ||
+                  source->GetFormat() == gfx::SurfaceFormat::R5G6B5_UINT16);
     MOZ_ASSERT_IF(source->GetTextureTarget() == LOCAL_GL_TEXTURE_RECTANGLE_ARB,
                   source->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
                   source->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
                   source->GetFormat() == gfx::SurfaceFormat::R5G6B5_UINT16 ||
                   source->GetFormat() == gfx::SurfaceFormat::YUV422 );
     config = ShaderConfigFromTargetAndFormat(source->GetTextureTarget(),
                                              source->GetFormat());
     if (!texturedEffect->mPremultiplied) {
--- a/gfx/layers/opengl/TextureClientOGL.cpp
+++ b/gfx/layers/opengl/TextureClientOGL.cpp
@@ -3,19 +3,26 @@
 /* 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/. */
 
 #include "GLContext.h"                  // for GLContext, etc
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/TextureClientOGL.h"
+#include "mozilla/gfx/2D.h"             // for Factory
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "GLLibraryEGL.h"
 
+#ifdef MOZ_WIDGET_ANDROID
+#include <jni.h>
+#include <android/native_window.h>
+#include <android/native_window_jni.h>
+#endif
+
 using namespace mozilla::gl;
 
 namespace mozilla {
 namespace layers {
 
 class CompositableForwarder;
 
 ////////////////////////////////////////////////////////////////////////
@@ -62,16 +69,176 @@ AndroidSurfaceTextureData::FillInfo(Text
   aInfo.hasSynchronization = false;
   aInfo.supportsMoz2D = false;
   aInfo.canExposeMappedData = false;
 }
 
 bool
 AndroidSurfaceTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
 {
-  aOutDescriptor = SurfaceTextureDescriptor(mHandle, mSize, mContinuous);
+  aOutDescriptor = SurfaceTextureDescriptor(mHandle,
+                                            mSize,
+                                            gfx::SurfaceFormat::R8G8B8A8,
+                                            mContinuous,
+                                            false /* do not ignore transform */);
   return true;
 }
 
 #endif // MOZ_WIDGET_ANDROID
 
+////////////////////////////////////////////////////////////////////////
+// AndroidNativeWindow
+
+#ifdef MOZ_WIDGET_ANDROID
+
+AndroidNativeWindowTextureData*
+AndroidNativeWindowTextureData::Create(gfx::IntSize aSize,
+                                       SurfaceFormat aFormat)
+{
+  if (aFormat != SurfaceFormat::R8G8B8A8 &&
+      aFormat != SurfaceFormat::R8G8B8X8 &&
+      aFormat != SurfaceFormat::B8G8R8A8 &&
+      aFormat != SurfaceFormat::B8G8R8X8 &&
+      aFormat != SurfaceFormat::R5G6B5_UINT16) {
+    return nullptr;
+  }
+
+  auto surface = java::GeckoSurface::LocalRef(
+      java::SurfaceAllocator::AcquireSurface(aSize.width, aSize.height,
+                                             true /* single-buffer mode */));
+  if (surface) {
+    return new AndroidNativeWindowTextureData(surface, aSize, aFormat);
+  }
+
+  return nullptr;
+}
+
+AndroidNativeWindowTextureData::AndroidNativeWindowTextureData(java::GeckoSurface::Param aSurface,
+                                                               gfx::IntSize aSize,
+                                                               SurfaceFormat aFormat)
+: mSurface(aSurface)
+, mIsLocked(false)
+, mSize(aSize)
+, mFormat(aFormat)
+{
+  mNativeWindow = ANativeWindow_fromSurface(jni::GetEnvForThread(),
+                                            mSurface.Get());
+  MOZ_ASSERT(mNativeWindow, "Failed to create NativeWindow.");
+
+  // SurfaceTextures don't technically support BGR, but we can just pretend to be RGB.
+  int32_t format = WINDOW_FORMAT_RGBA_8888;
+  switch (aFormat) {
+  case SurfaceFormat::R8G8B8A8:
+  case SurfaceFormat::B8G8R8A8:
+    format = WINDOW_FORMAT_RGBA_8888;
+    break;
+
+  case SurfaceFormat::R8G8B8X8:
+  case SurfaceFormat::B8G8R8X8:
+    format = WINDOW_FORMAT_RGBX_8888;
+    break;
+
+  case SurfaceFormat::R5G6B5_UINT16:
+    format = WINDOW_FORMAT_RGB_565;
+    break;
+
+  default:
+    MOZ_ASSERT(false, "Unsupported AndroidNativeWindowTextureData format.");
+  }
+
+  DebugOnly<int32_t> r = ANativeWindow_setBuffersGeometry(mNativeWindow,
+                                                          mSize.width,
+                                                          mSize.height,
+                                                          format);
+  MOZ_ASSERT(r == 0, "ANativeWindow_setBuffersGeometry failed.");
+
+  // Ideally here we'd call ANativeWindow_setBuffersTransform() with the
+  // identity transform, but that is only available on api level >= 26.
+  // Instead use the SurfaceDescriptor's ignoreTransform flag when serializing.
+}
+
+void
+AndroidNativeWindowTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+  aInfo.size = mSize;
+  aInfo.format = mFormat;
+  aInfo.hasIntermediateBuffer = false;
+  aInfo.hasSynchronization = false;
+  aInfo.supportsMoz2D = true;
+  aInfo.canExposeMappedData = false;
+  aInfo.canConcurrentlyReadLock = false;
+}
+
+bool
+AndroidNativeWindowTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+  aOutDescriptor = SurfaceTextureDescriptor(mSurface->GetHandle(),
+                                            mSize,
+                                            mFormat,
+                                            false /* not continuous */,
+                                            true /* ignore transform */);
+  return true;
+}
+
+bool
+AndroidNativeWindowTextureData::Lock(OpenMode)
+{
+  // ANativeWindows can only be locked and unlocked a single time, after which
+  // we must wait until they receive ownership back from the host.
+  // Therefore we must only actually call ANativeWindow_lock() once per cycle.
+  if (!mIsLocked) {
+    int32_t r = ANativeWindow_lock(mNativeWindow, &mBuffer, nullptr);
+    if (r < 0) {
+      MOZ_CRASH("ANativeWindow_lock failed\n.");
+      return false;
+    }
+    mIsLocked = true;
+  }
+  return true;
+}
+
+void
+AndroidNativeWindowTextureData::Unlock()
+{
+  // The TextureClient may want to call Lock again before handing ownership
+  // to the host, so we cannot call ANativeWindow_unlockAndPost yet.
+}
+
+void
+AndroidNativeWindowTextureData::Forget(LayersIPCChannel*)
+{
+  MOZ_ASSERT(!mIsLocked, "ANativeWindow should not be released while locked.\n");
+  ANativeWindow_release(mNativeWindow);
+  mNativeWindow = nullptr;
+  java::SurfaceAllocator::DisposeSurface(mSurface);
+  mSurface = nullptr;
+}
+
+already_AddRefed<DrawTarget>
+AndroidNativeWindowTextureData::BorrowDrawTarget()
+{
+  const int bpp = (mFormat == SurfaceFormat::R5G6B5_UINT16) ? 2 : 4;
+
+  return gfx::Factory::CreateDrawTargetForData(
+      gfx::BackendType::SKIA,
+      static_cast<unsigned char*>(mBuffer.bits),
+      IntSize(mBuffer.width, mBuffer.height),
+      mBuffer.stride * bpp,
+      mFormat,
+      true);
+}
+
+void
+AndroidNativeWindowTextureData::OnForwardedToHost()
+{
+  if (mIsLocked) {
+    int32_t r = ANativeWindow_unlockAndPost(mNativeWindow);
+    if (r < 0) {
+      MOZ_CRASH("ANativeWindow_unlockAndPost failed\n.");
+    }
+    mIsLocked = false;
+  }
+}
+
+#endif // MOZ_WIDGET_ANDROID
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/opengl/TextureClientOGL.h
+++ b/gfx/layers/opengl/TextureClientOGL.h
@@ -11,16 +11,20 @@
 #include "GLImages.h"
 #include "gfxTypes.h"
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/TextureClient.h"  // for TextureClient, etc
 #include "AndroidSurfaceTexture.h"
+#include "AndroidNativeWindow.h"
+#ifdef MOZ_WIDGET_ANDROID
+#include "GeneratedJNIWrappers.h"
+#endif
 
 namespace mozilla {
 
 namespace layers {
 
 #ifdef MOZ_WIDGET_ANDROID
 
 class AndroidSurfaceTextureData : public TextureData
@@ -53,12 +57,51 @@ protected:
 
   const AndroidSurfaceTextureHandle mHandle;
   const gfx::IntSize mSize;
   const bool mContinuous;
 };
 
 #endif // MOZ_WIDGET_ANDROID
 
+#ifdef MOZ_WIDGET_ANDROID
+
+class AndroidNativeWindowTextureData : public TextureData
+{
+public:
+  static AndroidNativeWindowTextureData* Create(gfx::IntSize aSize, SurfaceFormat aFormat);
+
+  virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+  virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+  virtual bool Lock(OpenMode) override;
+  virtual void Unlock() override;
+
+  virtual void Forget(LayersIPCChannel*) override;
+  virtual void Deallocate(LayersIPCChannel*) override {}
+
+  virtual already_AddRefed<DrawTarget> BorrowDrawTarget() override;
+
+  virtual void OnForwardedToHost() override;
+
+protected:
+  AndroidNativeWindowTextureData(java::GeckoSurface::Param aSurface,
+                                 gfx::IntSize aSize,
+                                 SurfaceFormat aFormat);
+
+private:
+  java::GeckoSurface::GlobalRef mSurface;
+  ANativeWindow* mNativeWindow;
+  ANativeWindow_Buffer mBuffer;
+  // Keeps track of whether the underlying NativeWindow is actually locked.
+  bool mIsLocked;
+
+  const gfx::IntSize mSize;
+  const SurfaceFormat mFormat;
+};
+
+#endif // MOZ_WIDGET_ANDROID
+
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -57,17 +57,19 @@ CreateTextureHostOGL(const SurfaceDescri
 #ifdef MOZ_WIDGET_ANDROID
     case SurfaceDescriptor::TSurfaceTextureDescriptor: {
       const SurfaceTextureDescriptor& desc = aDesc.get_SurfaceTextureDescriptor();
       java::GeckoSurfaceTexture::LocalRef surfaceTexture = java::GeckoSurfaceTexture::Lookup(desc.handle());
 
       result = new SurfaceTextureHost(aFlags,
                                       surfaceTexture,
                                       desc.size(),
-                                      desc.continuous());
+                                      desc.format(),
+                                      desc.continuous(),
+                                      desc.ignoreTransform());
       break;
     }
 #endif
 
     case SurfaceDescriptor::TEGLImageDescriptor: {
       const EGLImageDescriptor& desc = aDesc.get_EGLImageDescriptor();
       result = new EGLImageTextureHost(aFlags,
                                        (EGLImage)desc.image(),
@@ -338,23 +340,25 @@ GLTextureSource::IsValid() const
 
 #ifdef MOZ_WIDGET_ANDROID
 
 SurfaceTextureSource::SurfaceTextureSource(TextureSourceProvider* aProvider,
                                            mozilla::java::GeckoSurfaceTexture::Ref& aSurfTex,
                                            gfx::SurfaceFormat aFormat,
                                            GLenum aTarget,
                                            GLenum aWrapMode,
-                                           gfx::IntSize aSize)
+                                           gfx::IntSize aSize,
+                                           bool aIgnoreTransform)
   : mGL(aProvider->GetGLContext())
   , mSurfTex(aSurfTex)
   , mFormat(aFormat)
   , mTextureTarget(aTarget)
   , mWrapMode(aWrapMode)
   , mSize(aSize)
+  , mIgnoreTransform(aIgnoreTransform)
 {
 }
 
 void
 SurfaceTextureSource::BindTexture(GLenum aTextureUnit,
                                   gfx::SamplingFilter aSamplingFilter)
 {
   MOZ_ASSERT(mSurfTex);
@@ -390,38 +394,48 @@ SurfaceTextureSource::IsValid() const
 
 gfx::Matrix4x4
 SurfaceTextureSource::GetTextureTransform()
 {
   MOZ_ASSERT(mSurfTex);
 
   gfx::Matrix4x4 ret;
 
-  const auto& surf = java::sdk::SurfaceTexture::LocalRef(java::sdk::SurfaceTexture::Ref::From(mSurfTex));
-  AndroidSurfaceTexture::GetTransformMatrix(surf, ret);
+  // GetTransformMatrix() returns the transform set by the producer side of
+  // the SurfaceTexture. We should ignore this if we know the transform should
+  // be identity but the producer couldn't set it correctly, like is the
+  // case for AndroidNativeWindowTextureData.
+  if (!mIgnoreTransform) {
+    const auto& surf = java::sdk::SurfaceTexture::LocalRef(java::sdk::SurfaceTexture::Ref::From(mSurfTex));
+    AndroidSurfaceTexture::GetTransformMatrix(surf, ret);
+  }
 
   return ret;
 }
 
 void
 SurfaceTextureSource::DeallocateDeviceData()
 {
   mSurfTex = nullptr;
 }
 
 ////////////////////////////////////////////////////////////////////////
 
 SurfaceTextureHost::SurfaceTextureHost(TextureFlags aFlags,
                                        mozilla::java::GeckoSurfaceTexture::Ref& aSurfTex,
                                        gfx::IntSize aSize,
-                                       bool aContinuousUpdate)
+                                       gfx::SurfaceFormat aFormat,
+                                       bool aContinuousUpdate,
+                                       bool aIgnoreTransform)
   : TextureHost(aFlags)
   , mSurfTex(aSurfTex)
   , mSize(aSize)
+  , mFormat(aFormat)
   , mContinuousUpdate(aContinuousUpdate)
+  , mIgnoreTransform(aIgnoreTransform)
 {
   if (!mSurfTex) {
     return;
   }
 
   // Continuous update makes no sense with single buffer mode
   MOZ_ASSERT(!mSurfTex->IsSingleBuffer() || !mContinuousUpdate);
 
@@ -471,25 +485,25 @@ SurfaceTextureHost::Lock()
     return false;
   }
 
   if (mContinuousUpdate) {
     mSurfTex->UpdateTexImage();
   }
 
   if (!mTextureSource) {
-    gfx::SurfaceFormat format = gfx::SurfaceFormat::R8G8B8A8;
     GLenum target = LOCAL_GL_TEXTURE_EXTERNAL; // This is required by SurfaceTexture
     GLenum wrapMode = LOCAL_GL_CLAMP_TO_EDGE;
     mTextureSource = new SurfaceTextureSource(mProvider,
                                               mSurfTex,
-                                              format,
+                                              mFormat,
                                               target,
                                               wrapMode,
-                                              mSize);
+                                              mSize,
+                                              mIgnoreTransform);
   }
 
   return true;
 }
 
 void
 SurfaceTextureHost::SetTextureSourceProvider(TextureSourceProvider* aProvider)
 {
@@ -514,17 +528,17 @@ SurfaceTextureHost::NotifyNotUsed()
   }
 
   TextureHost::NotifyNotUsed();
 }
 
 gfx::SurfaceFormat
 SurfaceTextureHost::GetFormat() const
 {
-  return mTextureSource ? mTextureSource->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
+  return mFormat;
 }
 
 void
 SurfaceTextureHost::DeallocateDeviceData()
 {
   if (mTextureSource) {
     mTextureSource->DeallocateDeviceData();
   }
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -342,17 +342,18 @@ class SurfaceTextureSource : public Text
                            , public TextureSourceOGL
 {
 public:
   SurfaceTextureSource(TextureSourceProvider* aProvider,
                        java::GeckoSurfaceTexture::Ref& aSurfTex,
                        gfx::SurfaceFormat aFormat,
                        GLenum aTarget,
                        GLenum aWrapMode,
-                       gfx::IntSize aSize);
+                       gfx::IntSize aSize,
+                       bool aIgnoreTransform);
 
   virtual const char* Name() const override { return "SurfaceTextureSource"; }
 
   virtual TextureSourceOGL* AsSourceOGL() override { return this; }
 
   virtual void BindTexture(GLenum activetex,
                            gfx::SamplingFilter aSamplingFilter) override;
 
@@ -378,25 +379,28 @@ public:
 
 protected:
   RefPtr<gl::GLContext> mGL;
   mozilla::java::GeckoSurfaceTexture::GlobalRef mSurfTex;
   const gfx::SurfaceFormat mFormat;
   const GLenum mTextureTarget;
   const GLenum mWrapMode;
   const gfx::IntSize mSize;
+  const bool mIgnoreTransform;
 };
 
 class SurfaceTextureHost : public TextureHost
 {
 public:
   SurfaceTextureHost(TextureFlags aFlags,
                      mozilla::java::GeckoSurfaceTexture::Ref& aSurfTex,
                      gfx::IntSize aSize,
-                     bool aContinuousUpdate);
+                     gfx::SurfaceFormat aFormat,
+                     bool aContinuousUpdate,
+                     bool aIgnoreTransform);
 
   virtual ~SurfaceTextureHost();
 
   virtual void PrepareTextureSource(CompositableTextureSourceRef& aTexture) override;
 
   virtual void DeallocateDeviceData() override;
 
   virtual void SetTextureSourceProvider(TextureSourceProvider* aProvider) override;
@@ -422,17 +426,19 @@ public:
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual const char* Name() override { return "SurfaceTextureHost"; }
 
 protected:
   mozilla::java::GeckoSurfaceTexture::GlobalRef mSurfTex;
   const gfx::IntSize mSize;
+  const gfx::SurfaceFormat mFormat;
   bool mContinuousUpdate;
+  const bool mIgnoreTransform;
   RefPtr<CompositorOGL> mCompositor;
   RefPtr<SurfaceTextureSource> mTextureSource;
 };
 
 #endif // MOZ_WIDGET_ANDROID
 
 ////////////////////////////////////////////////////////////////////////
 // EGLImage
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -486,16 +486,17 @@ private:
   DECL_GFX_PREF(Once, "gfx.text.disable-aa",                   DisableAllTextAA, bool, false);
   DECL_GFX_PREF(Live, "gfx.ycbcr.accurate-conversion",         YCbCrAccurateConversion, bool, false);
 
   // Disable surface sharing due to issues with compatible FBConfigs on
   // NVIDIA drivers as described in bug 1193015.
   DECL_GFX_PREF(Live, "gfx.use-glx-texture-from-pixmap",       UseGLXTextureFromPixmap, bool, false);
   DECL_GFX_PREF(Once, "gfx.use-iosurface-textures",            UseIOSurfaceTextures, bool, false);
   DECL_GFX_PREF(Once, "gfx.use-mutex-on-present",              UseMutexOnPresent, bool, false);
+  DECL_GFX_PREF(Once, "gfx.use-surfacetexture-textures",       UseSurfaceTextureTextures, bool, false);
   // These times should be in milliseconds
   DECL_GFX_PREF(Once, "gfx.touch.resample.delay-threshold",    TouchResampleVsyncDelayThreshold, int32_t, 20);
   DECL_GFX_PREF(Once, "gfx.touch.resample.max-predict",        TouchResampleMaxPredict, int32_t, 8);
   DECL_GFX_PREF(Once, "gfx.touch.resample.min-delta",          TouchResampleMinDelta, int32_t, 2);
   DECL_GFX_PREF(Once, "gfx.touch.resample.old-touch-threshold",TouchResampleOldTouchThreshold, int32_t, 17);
   DECL_GFX_PREF(Once, "gfx.touch.resample.vsync-adjust",       TouchVsyncSampleAdjust, int32_t, 5);
 
   DECL_GFX_PREF(Live, "gfx.vsync.collect-scroll-transforms",   CollectScrollTransforms, bool, false);
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -328,16 +328,19 @@ pref("gfx.displayport.strategy_vb.danger
 pref("gfx.displayport.strategy_pb.threshold", -1); // velocity threshold in inches/frame
 
 // Allow 24-bit colour when the hardware supports it
 pref("gfx.android.rgb16.force", false);
 
 // Allow GLContexts to be attached/detached from SurfaceTextures
 pref("gfx.SurfaceTexture.detach.enabled", true);
 
+// Use SurfaceTextures as preferred backend for TextureClient/Host
+pref("gfx.use-surfacetexture-textures", false);
+
 // don't allow JS to move and resize existing windows
 pref("dom.disable_window_move_resize", true);
 
 // prevent click image resizing for nsImageDocument
 pref("browser.enable_click_image_resizing", false);
 
 // open in tab preferences
 // 0=default window, 1=current window/tab, 2=new window, 3=new tab in most window
--- 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
@@ -75,19 +75,23 @@ public final class GeckoSurfaceTexture e
 
     @Override
     @WrapForJNI
     public synchronized void releaseTexImage() {
         if (!mIsSingleBuffer) {
             return;
         }
 
-        super.releaseTexImage();
-        if (mListener != null) {
-            mListener.onReleaseTexImage();
+        try {
+            super.releaseTexImage();
+            if (mListener != null) {
+                mListener.onReleaseTexImage();
+            }
+        } catch (Exception e) {
+            Log.w(LOGTAG, "releaseTexImage() failed", e);
         }
     }
 
     public synchronized void setListener(GeckoSurfaceTexture.Callbacks listener) {
         mListener = listener;
     }
 
     @WrapForJNI