Bug 1150549 - Simplify TiledContentHost. r=jrmuizel
authorNicolas Silva <nsilva@mozilla.com>
Fri, 22 May 2015 13:38:13 +0200
changeset 268041 67efcb1efc4383180e645b9ee4de6bcbfc774b6a
parent 268040 ae9c77fa58d18a2129e6552a49c389a37e6b31c4
child 268042 d28aa0b0b47f24616efd6e58a1c7c352bf7db55a
push id2294
push userbsmedberg@mozilla.com
push dateWed, 27 May 2015 15:05:10 +0000
reviewersjrmuizel
bugs1150549
milestone41.0a1
Bug 1150549 - Simplify TiledContentHost. r=jrmuizel
b2g/app/b2g.js
gfx/layers/Effects.h
gfx/layers/TiledLayerBuffer.h
gfx/layers/client/TiledContentClient.cpp
gfx/layers/client/TiledContentClient.h
gfx/layers/composite/TiledContentHost.cpp
gfx/layers/composite/TiledContentHost.h
gfx/layers/ipc/LayersMessages.ipdlh
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -111,17 +111,19 @@ pref("browser.sessionstore.resume_from_c
 pref("browser.sessionstore.interval", 10000); // milliseconds
 pref("browser.sessionstore.max_tabs_undo", 1);
 
 /* these should help performance */
 pref("mozilla.widget.force-24bpp", true);
 pref("mozilla.widget.use-buffer-pixmap", true);
 pref("mozilla.widget.disable-native-theme", true);
 pref("layout.reflow.synthMouseMove", false);
+#ifndef MOZ_X11
 pref("layers.enable-tiles", true);
+#endif
 pref("layers.low-precision-buffer", true);
 pref("layers.low-precision-opacity", "0.5");
 pref("layers.progressive-paint", true);
 
 /* download manager (don't show the window or alert) */
 pref("browser.download.useDownloadDir", true);
 pref("browser.download.folderList", 1); // Default to ~/Downloads
 pref("browser.download.manager.showAlertOnComplete", false);
--- a/gfx/layers/Effects.h
+++ b/gfx/layers/Effects.h
@@ -281,17 +281,18 @@ inline TemporaryRef<TexturedEffect>
 CreateTexturedEffect(TextureSource* aSource,
                      TextureSource* aSourceOnWhite,
                      const gfx::Filter& aFilter,
                      bool isAlphaPremultiplied)
 {
   MOZ_ASSERT(aSource);
   if (aSourceOnWhite) {
     MOZ_ASSERT(aSource->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
-               aSourceOnWhite->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
+               aSource->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
+    MOZ_ASSERT(aSource->GetFormat() == aSourceOnWhite->GetFormat());
     return MakeAndAddRef<EffectComponentAlpha>(aSource, aSourceOnWhite, aFilter);
   }
 
   return CreateTexturedEffect(aSource->GetFormat(),
                               aSource,
                               aFilter,
                               isAlphaPremultiplied);
 }
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -89,37 +89,47 @@ namespace layers {
 // should always be a factor of the tile length, to avoid tiles covering
 // non-integer amounts of pixels.
 
 template<typename Derived, typename Tile>
 class TiledLayerBuffer
 {
 public:
   TiledLayerBuffer()
-    : mRetainedWidth(0)
+    : mFirstTileX(0)
+    , mFirstTileY(0)
+    , mRetainedWidth(0)
     , mRetainedHeight(0)
     , mResolution(1)
     , mTileSize(gfxPlatform::GetPlatform()->GetTileWidth(), gfxPlatform::GetPlatform()->GetTileHeight())
   {}
 
   ~TiledLayerBuffer() {}
 
   // Given a tile origin aligned to a multiple of GetScaledTileSize,
   // return the tile that describes that region.
   // NOTE: To get the valid area of that tile you must intersect
   //       (aTileOrigin.x, aTileOrigin.y,
   //        GetScaledTileSize().width, GetScaledTileSize().height)
   //       and GetValidRegion() to get the area of the tile that is valid.
-  Tile GetTile(const nsIntPoint& aTileOrigin) const;
-
+  Tile& GetTile(const gfx::IntPoint& aTileOrigin);
   // Given a tile x, y relative to the top left of the layer, this function
   // will return the tile for
   // (x*GetScaledTileSize().width, y*GetScaledTileSize().height,
   //  GetScaledTileSize().width, GetScaledTileSize().height)
-  Tile GetTile(int x, int y) const;
+  Tile& GetTile(int x, int y);
+
+  int TileIndex(const gfx::IntPoint& aTileOrigin) const;
+  int TileIndex(int x, int y) const { return x * mRetainedHeight + y; }
+
+  bool HasTile(int index) const { return index >= 0 && index < (int)mRetainedTiles.Length(); }
+  bool HasTile(const gfx::IntPoint& aTileOrigin) const;
+  bool HasTile(int x, int y) const {
+    return x >= 0 && x < mRetainedWidth && y >= 0 && y < mRetainedHeight;
+  }
 
   const gfx::IntSize& GetTileSize() const { return mTileSize; }
 
   gfx::IntSize GetScaledTileSize() const { return RoundedToInt(gfx::Size(mTileSize) / mResolution); }
 
   unsigned int GetTileCount() const { return mRetainedTiles.Length(); }
 
   const nsIntRegion& GetValidRegion() const { return mValidRegion; }
@@ -150,51 +160,49 @@ public:
   int RoundDownToTileEdge(int aX, int aTileLength) const { return aX - GetTileStart(aX, aTileLength); }
 
   // Get and set draw scaling. mResolution affects the resolution at which the
   // contents of the buffer are drawn. mResolution has no effect on the
   // coordinate space of the valid region, but does affect the size of an
   // individual tile's rect in relation to the valid region.
   // Setting the resolution will invalidate the buffer.
   float GetResolution() const { return mResolution; }
-  void SetResolution(float aResolution) {
-    if (mResolution == aResolution) {
-      return;
-    }
-
-    Update(nsIntRegion(), nsIntRegion());
-    mResolution = aResolution;
-  }
   bool IsLowPrecision() const { return mResolution < 1; }
 
   typedef Tile* Iterator;
   Iterator TilesBegin() { return mRetainedTiles.Elements(); }
   Iterator TilesEnd() { return mRetainedTiles.Elements() + mRetainedTiles.Length(); }
 
   void Dump(std::stringstream& aStream, const char* aPrefix, bool aDumpHtml);
 
 protected:
   // The implementor should call Update() to change
   // the new valid region. This implementation will call
   // validateTile on each tile that is dirty, which is left
   // to the implementor.
   void Update(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion);
 
+  // Return a reference to this tile in GetTile when the requested tile offset
+  // does not exist.
+  Tile mPlaceHolderTile;
+
   nsIntRegion     mValidRegion;
   nsIntRegion     mPaintedRegion;
 
   /**
    * mRetainedTiles is a rectangular buffer of mRetainedWidth x mRetainedHeight
    * stored as column major with the same origin as mValidRegion.GetBounds().
    * Any tile that does not intersect mValidRegion is a PlaceholderTile.
    * Only the region intersecting with mValidRegion should be read from a tile,
    * another other region is assumed to be uninitialized. The contents of the
    * tiles is scaled by mResolution.
    */
   nsTArray<Tile>  mRetainedTiles;
+  int             mFirstTileX;
+  int             mFirstTileY;
   int             mRetainedWidth;  // in tiles
   int             mRetainedHeight; // in tiles
   float           mResolution;
   gfx::IntSize    mTileSize;
 
 private:
   const Derived& AsDerived() const { return *static_cast<const Derived*>(this); }
   Derived& AsDerived() { return *static_cast<Derived*>(this); }
@@ -244,58 +252,73 @@ static inline int floor_div(int a, int b
     int sub;
     sub = a ^ b;
     // The results of this shift is either 0 or -1.
     sub >>= 8*sizeof(int)-1;
     return div+sub;
   }
 }
 
-template<typename Derived, typename Tile> Tile
-TiledLayerBuffer<Derived, Tile>::GetTile(const nsIntPoint& aTileOrigin) const
+template<typename Derived, typename Tile> bool
+TiledLayerBuffer<Derived, Tile>::HasTile(const gfx::IntPoint& aTileOrigin) const {
+  gfx::IntSize scaledTileSize = GetScaledTileSize();
+  return HasTile(floor_div(aTileOrigin.x, scaledTileSize.width) - mFirstTileX,
+                 floor_div(aTileOrigin.y, scaledTileSize.height) - mFirstTileY);
+}
+
+template<typename Derived, typename Tile> Tile&
+TiledLayerBuffer<Derived, Tile>::GetTile(const nsIntPoint& aTileOrigin)
 {
-  // TODO Cache firstTileOriginX/firstTileOriginY
+  if (HasTile(aTileOrigin)) {
+    return mRetainedTiles[TileIndex(aTileOrigin)];
+  }
+  return mPlaceHolderTile;
+}
+
+template<typename Derived, typename Tile> int
+TiledLayerBuffer<Derived, Tile>::TileIndex(const gfx::IntPoint& aTileOrigin) const
+{
   // Find the tile x/y of the first tile and the target tile relative to the (0, 0)
   // origin, the difference is the tile x/y relative to the start of the tile buffer.
   gfx::IntSize scaledTileSize = GetScaledTileSize();
-  int firstTileX = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
-  int firstTileY = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
-  return GetTile(floor_div(aTileOrigin.x, scaledTileSize.width) - firstTileX,
-                 floor_div(aTileOrigin.y, scaledTileSize.height) - firstTileY);
+  return TileIndex(floor_div(aTileOrigin.x, scaledTileSize.width) - mFirstTileX,
+                   floor_div(aTileOrigin.y, scaledTileSize.height) - mFirstTileY);
 }
 
-template<typename Derived, typename Tile> Tile
-TiledLayerBuffer<Derived, Tile>::GetTile(int x, int y) const
+template<typename Derived, typename Tile> Tile&
+TiledLayerBuffer<Derived, Tile>::GetTile(int x, int y)
 {
-  int index = x * mRetainedHeight + y;
-  return mRetainedTiles.SafeElementAt(index, AsDerived().GetPlaceholderTile());
+  if (HasTile(x, y)) {
+    return mRetainedTiles[TileIndex(x, y)];
+  }
+  return mPlaceHolderTile;
 }
 
 template<typename Derived, typename Tile> void
 TiledLayerBuffer<Derived, Tile>::Dump(std::stringstream& aStream,
                                       const char* aPrefix,
                                       bool aDumpHtml)
 {
   gfx::IntRect visibleRect = GetValidRegion().GetBounds();
   gfx::IntSize scaledTileSize = GetScaledTileSize();
   for (int32_t x = visibleRect.x; x < visibleRect.x + visibleRect.width;) {
     int32_t tileStartX = GetTileStart(x, scaledTileSize.width);
     int32_t w = scaledTileSize.width - tileStartX;
 
     for (int32_t y = visibleRect.y; y < visibleRect.y + visibleRect.height;) {
       int32_t tileStartY = GetTileStart(y, scaledTileSize.height);
-      Tile tileTexture =
-        GetTile(nsIntPoint(RoundDownToTileEdge(x, scaledTileSize.width),
-                           RoundDownToTileEdge(y, scaledTileSize.height)));
+      nsIntPoint tileOrigin = nsIntPoint(RoundDownToTileEdge(x, scaledTileSize.width),
+                                         RoundDownToTileEdge(y, scaledTileSize.height));
+      Tile& tileTexture = GetTile(tileOrigin);
       int32_t h = scaledTileSize.height - tileStartY;
 
       aStream << "\n" << aPrefix << "Tile (x=" <<
         RoundDownToTileEdge(x, scaledTileSize.width) << ", y=" <<
         RoundDownToTileEdge(y, scaledTileSize.height) << "): ";
-      if (tileTexture != AsDerived().GetPlaceholderTile()) {
+      if (!tileTexture.IsPlaceholderTile()) {
         tileTexture.DumpTexture(aStream);
       } else {
         aStream << "empty tile";
       }
       y += h;
     }
     x += w;
   }
@@ -592,15 +615,19 @@ TiledLayerBuffer<Derived, Tile>::Update(
   }
 #endif
 
   // At this point, oldTileCount should be zero
   MOZ_ASSERT(oldTileCount == 0, "Failed to release old tiles");
 
   mRetainedTiles = newRetainedTiles;
   mValidRegion = newValidRegion;
+
+  mFirstTileX = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
+  mFirstTileY = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
+
   mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
 }
 
 } // layers
 } // mozilla
 
 #endif // GFX_TILEDLAYERBUFFER_H
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -369,17 +369,17 @@ gfxMemorySharedReadLock::ReadLock()
 
   return PR_ATOMIC_INCREMENT(&mReadCount);
 }
 
 int32_t
 gfxMemorySharedReadLock::ReadUnlock()
 {
   int32_t readCount = PR_ATOMIC_DECREMENT(&mReadCount);
-  NS_ASSERTION(readCount >= 0, "ReadUnlock called without ReadLock.");
+  MOZ_ASSERT(readCount >= 0);
 
   return readCount;
 }
 
 int32_t
 gfxMemorySharedReadLock::GetReadCount()
 {
   NS_ASSERT_OWNINGTHREAD(gfxMemorySharedReadLock);
@@ -419,17 +419,17 @@ gfxShmSharedReadLock::ReadLock() {
 
 int32_t
 gfxShmSharedReadLock::ReadUnlock() {
   if (!mAllocSuccess) {
     return 0;
   }
   ShmReadLockInfo* info = GetShmReadLockInfoPtr();
   int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount);
-  NS_ASSERTION(readCount >= 0, "ReadUnlock called without a ReadLock.");
+  MOZ_ASSERT(readCount >= 0);
   if (readCount <= 0) {
     mAllocator->FreeShmemSection(mShmemSection);
   }
   return readCount;
 }
 
 int32_t
 gfxShmSharedReadLock::GetReadCount() {
@@ -515,16 +515,17 @@ TileClient::TileClient(const TileClient&
 {
   mBackBuffer.Set(this, o.mBackBuffer);
   mBackBufferOnWhite = o.mBackBufferOnWhite;
   mFrontBuffer = o.mFrontBuffer;
   mFrontBufferOnWhite = o.mFrontBufferOnWhite;
   mBackLock = o.mBackLock;
   mFrontLock = o.mFrontLock;
   mCompositableClient = o.mCompositableClient;
+  mUpdateRect = o.mUpdateRect;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   mLastUpdate = o.mLastUpdate;
 #endif
   mManager = o.mManager;
   mInvalidFront = o.mInvalidFront;
   mInvalidBack = o.mInvalidBack;
 }
 
@@ -534,16 +535,17 @@ TileClient::operator=(const TileClient& 
   if (this == &o) return *this;
   mBackBuffer.Set(this, o.mBackBuffer);
   mBackBufferOnWhite = o.mBackBufferOnWhite;
   mFrontBuffer = o.mFrontBuffer;
   mFrontBufferOnWhite = o.mFrontBufferOnWhite;
   mBackLock = o.mBackLock;
   mFrontLock = o.mFrontLock;
   mCompositableClient = o.mCompositableClient;
+  mUpdateRect = o.mUpdateRect;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   mLastUpdate = o.mLastUpdate;
 #endif
   mManager = o.mManager;
   mInvalidFront = o.mInvalidFront;
   mInvalidBack = o.mInvalidBack;
   return *this;
 }
@@ -604,16 +606,18 @@ CopyFrontToBack(TextureClient* aFront,
 
   if (!aBack->Lock(OpenMode::OPEN_READ_WRITE)) {
     gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's back buffer";
     return false;
   }
 
   gfx::IntPoint rectToCopyTopLeft = aRectToCopy.TopLeft();
   aFront->CopyToTextureClient(aBack, &aRectToCopy, &rectToCopyTopLeft);
+
+  aFront->Unlock();
   return true;
 }
 
 void
 TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion,
                                         nsIntRegion& aAddPaintedRegion)
 {
   if (mBackBuffer && mFrontBuffer) {
@@ -700,19 +704,19 @@ TileClient::DiscardBackBuffer()
       // Our current back-buffer is still locked by the compositor. This can occur
       // when the client is producing faster than the compositor can consume. In
       // this case we just want to drop it and not return it to the pool.
      mManager->ReportClientLost(*mBackBuffer);
      if (mBackBufferOnWhite) {
        mManager->ReportClientLost(*mBackBufferOnWhite);
      }
     } else {
-      mManager->ReturnTextureClient(*mBackBuffer);
+      mManager->ReturnTextureClientDeferred(*mBackBuffer);
       if (mBackBufferOnWhite) {
-        mManager->ReturnTextureClient(*mBackBufferOnWhite);
+        mManager->ReturnTextureClientDeferred(*mBackBufferOnWhite);
       }
     }
     mBackLock->ReadUnlock();
     if (mBackBuffer->IsLocked()) {
       mBackBuffer->Unlock();
     }
     if (mBackBufferOnWhite && mBackBufferOnWhite->IsLocked()) {
       mBackBufferOnWhite->Unlock();
@@ -811,34 +815,28 @@ TileClient::GetTileDescriptor()
     // reference count doesn't go to zero before the host receives the message.
     // see TiledLayerBufferComposite::TiledLayerBufferComposite
     mFrontLock.get()->AddRef();
   }
 
   if (mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY) {
     return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
                                   mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor()) : MaybeTexture(null_t()),
+                                  mUpdateRect,
                                   TileLock(uintptr_t(mFrontLock.get())));
   } else {
     gfxShmSharedReadLock *lock = static_cast<gfxShmSharedReadLock*>(mFrontLock.get());
     return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
                                   mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor()) : MaybeTexture(null_t()),
+                                  mUpdateRect,
                                   TileLock(lock->GetShmemSection()));
   }
 }
 
 void
-ClientTiledLayerBuffer::ReadUnlock() {
-  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-    if (mRetainedTiles[i].IsPlaceholderTile()) continue;
-    mRetainedTiles[i].ReadUnlock();
-  }
-}
-
-void
 ClientTiledLayerBuffer::ReadLock() {
   for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
     if (mRetainedTiles[i].IsPlaceholderTile()) continue;
     mRetainedTiles[i].ReadLock();
   }
 }
 
 void
@@ -868,19 +866,22 @@ ClientTiledLayerBuffer::GetSurfaceDescri
   for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
     TileDescriptor tileDesc;
     if (mRetainedTiles.SafeElementAt(i, GetPlaceholderTile()) == GetPlaceholderTile()) {
       tileDesc = PlaceholderTileDescriptor();
     } else {
       tileDesc = mRetainedTiles[i].GetTileDescriptor();
     }
     tiles.AppendElement(tileDesc);
+    mRetainedTiles[i].mUpdateRect = IntRect();
   }
   return SurfaceDescriptorTiles(mValidRegion, mPaintedRegion,
-                                tiles, mRetainedWidth, mRetainedHeight,
+                                tiles,
+                                mFirstTileX, mFirstTileY,
+                                mRetainedWidth, mRetainedHeight,
                                 mResolution, mFrameResolution.xScale,
                                 mFrameResolution.yScale);
 }
 
 void
 ClientTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
                                    const nsIntRegion& aPaintRegion,
                                    LayerManager::DrawPaintedLayerCallback aCallback,
@@ -1131,16 +1132,18 @@ ClientTiledLayerBuffer::ValidateTile(Til
   nsIntRegion extraPainted;
   RefPtr<TextureClient> backBufferOnWhite;
   RefPtr<TextureClient> backBuffer =
     aTile.GetBackBuffer(offsetScaledDirtyRegion,
                         content, mode,
                         &createdTextureClient, extraPainted,
                         &backBufferOnWhite);
 
+  aTile.mUpdateRect = offsetScaledDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
+
   extraPainted.MoveBy(aTileOrigin);
   extraPainted.And(extraPainted, mNewValidRegion);
   mPaintedRegion.Or(mPaintedRegion, extraPainted);
 
   if (!backBuffer) {
     gfxCriticalError() << "[Tiling:Client] Failed to allocate a TextureClient";
     aTile.DiscardBackBuffer();
     aTile.DiscardFrontBuffer();
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -265,16 +265,17 @@ struct TileClient
       RefPtr<TextureClient> mBuffer;
   } mBackBuffer;
   RefPtr<TextureClient> mBackBufferOnWhite;
   RefPtr<TextureClient> mFrontBuffer;
   RefPtr<TextureClient> mFrontBufferOnWhite;
   RefPtr<gfxSharedReadLock> mBackLock;
   RefPtr<gfxSharedReadLock> mFrontLock;
   RefPtr<ClientLayerManager> mManager;
+  gfx::IntRect mUpdateRect;
   CompositableClient* mCompositableClient;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   TimeStamp        mLastUpdate;
 #endif
   nsIntRegion mInvalidFront;
   nsIntRegion mInvalidBack;
   nsExpirationState mExpirationState;
 
@@ -413,18 +414,16 @@ public:
                     std::numeric_limits<int32_t>::max())
   {}
 
   void PaintThebes(const nsIntRegion& aNewValidRegion,
                    const nsIntRegion& aPaintRegion,
                    LayerManager::DrawPaintedLayerCallback aCallback,
                    void* aCallbackData);
 
-  void ReadUnlock();
-
   void ReadLock();
 
   void Release();
 
   void DiscardBuffers();
 
   const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; }
 
@@ -440,16 +439,25 @@ public:
                          nsIntRegion& aInvalidRegion,
                          const nsIntRegion& aOldValidRegion,
                          BasicTiledLayerPaintData* aPaintData,
                          LayerManager::DrawPaintedLayerCallback aCallback,
                          void* aCallbackData);
 
   SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
 
+  void SetResolution(float aResolution) {
+    if (mResolution == aResolution) {
+      return;
+    }
+
+    Update(nsIntRegion(), nsIntRegion());
+    mResolution = aResolution;
+  }
+
 protected:
   TileClient ValidateTile(TileClient aTile,
                           const nsIntPoint& aTileRect,
                           const nsIntRegion& dirtyRect);
 
   void PostValidate(const nsIntRegion& aPaintRegion);
 
   void UnlockTile(TileClient aTile);
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -24,382 +24,364 @@ class gfxReusableSurfaceWrapper;
 namespace mozilla {
 using namespace gfx;
 namespace layers {
 
 class Layer;
 
 TiledLayerBufferComposite::TiledLayerBufferComposite()
   : mFrameResolution()
-  , mHasDoubleBufferedTiles(false)
-  , mIsValid(false)
 {}
 
+TiledLayerBufferComposite::~TiledLayerBufferComposite()
+{
+  Clear();
+}
+
 /* static */ void
 TiledLayerBufferComposite::RecycleCallback(TextureHost* textureHost, void* aClosure)
 {
   textureHost->CompositorRecycle();
 }
 
-TiledLayerBufferComposite::TiledLayerBufferComposite(ISurfaceAllocator* aAllocator,
-                                                     const SurfaceDescriptorTiles& aDescriptor,
-                                                     const nsIntRegion& aOldPaintedRegion,
-                                                     Compositor* aCompositor)
-{
-  mIsValid = true;
-  mHasDoubleBufferedTiles = false;
-  mValidRegion = aDescriptor.validRegion();
-  mPaintedRegion = aDescriptor.paintedRegion();
-  mRetainedWidth = aDescriptor.retainedWidth();
-  mRetainedHeight = aDescriptor.retainedHeight();
-  mResolution = aDescriptor.resolution();
-  mFrameResolution = CSSToParentLayerScale2D(aDescriptor.frameXResolution(),
-                                             aDescriptor.frameYResolution());
-  if (mResolution == 0 || IsNaN(mResolution)) {
-    // There are divisions by mResolution so this protects the compositor process
-    // against malicious content processes and fuzzing.
-    mIsValid = false;
-    return;
-  }
-
-  // Combine any valid content that wasn't already uploaded
-  nsIntRegion oldPaintedRegion(aOldPaintedRegion);
-  oldPaintedRegion.And(oldPaintedRegion, mValidRegion);
-  mPaintedRegion.Or(mPaintedRegion, oldPaintedRegion);
-
-  bool isSameProcess = aAllocator->IsSameProcess();
-
-  const InfallibleTArray<TileDescriptor>& tiles = aDescriptor.tiles();
-  for(size_t i = 0; i < tiles.Length(); i++) {
-    CompositableTextureHostRef texture;
-    CompositableTextureHostRef textureOnWhite;
-    const TileDescriptor& tileDesc = tiles[i];
-    switch (tileDesc.type()) {
-      case TileDescriptor::TTexturedTileDescriptor : {
-        texture = TextureHost::AsTextureHost(tileDesc.get_TexturedTileDescriptor().textureParent());
-        MaybeTexture onWhite = tileDesc.get_TexturedTileDescriptor().textureOnWhite();
-        if (onWhite.type() == MaybeTexture::TPTextureParent) {
-          textureOnWhite = TextureHost::AsTextureHost(onWhite.get_PTextureParent());
-        }
-        const TileLock& ipcLock = tileDesc.get_TexturedTileDescriptor().sharedLock();
-        nsRefPtr<gfxSharedReadLock> sharedLock;
-        if (ipcLock.type() == TileLock::TShmemSection) {
-          sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_ShmemSection());
-        } else {
-          if (!isSameProcess) {
-            // Trying to use a memory based lock instead of a shmem based one in
-            // the cross-process case is a bad security violation.
-            NS_ERROR("A client process may be trying to peek at the host's address space!");
-            // This tells the TiledContentHost that deserialization failed so that
-            // it can propagate the error.
-            mIsValid = false;
-
-            mRetainedTiles.Clear();
-            return;
-          }
-          sharedLock = reinterpret_cast<gfxMemorySharedReadLock*>(ipcLock.get_uintptr_t());
-          if (sharedLock) {
-            // The corresponding AddRef is in TiledClient::GetTileDescriptor
-            sharedLock.get()->Release();
-          }
-        }
-
-        CompositableTextureSourceRef textureSource;
-        CompositableTextureSourceRef textureSourceOnWhite;
-        if (texture) {
-          texture->SetCompositor(aCompositor);
-          texture->PrepareTextureSource(textureSource);
-        }
-        if (textureOnWhite) {
-          textureOnWhite->SetCompositor(aCompositor);
-          textureOnWhite->PrepareTextureSource(textureSourceOnWhite);
-        }
-        mRetainedTiles.AppendElement(TileHost(sharedLock,
-                                              texture.get(),
-                                              textureOnWhite.get(),
-                                              textureSource.get(),
-                                              textureSourceOnWhite.get()));
-        break;
-      }
-      default:
-        NS_WARNING("Unrecognised tile descriptor type");
-        // Fall through
-      case TileDescriptor::TPlaceholderTileDescriptor :
-        mRetainedTiles.AppendElement(GetPlaceholderTile());
-        break;
-    }
-    if (texture && !texture->HasInternalBuffer()) {
-      mHasDoubleBufferedTiles = true;
-    }
-  }
-}
-
-void
-TiledLayerBufferComposite::ReadUnlock()
-{
-  if (!IsValid()) {
-    return;
-  }
-  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-    mRetainedTiles[i].ReadUnlock();
-  }
-}
-
-void
-TiledLayerBufferComposite::ReleaseTextureHosts()
-{
-  if (!IsValid()) {
-    return;
-  }
-  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-    mRetainedTiles[i].mTextureHost = nullptr;
-    mRetainedTiles[i].mTextureHostOnWhite = nullptr;
-    mRetainedTiles[i].mTextureSource = nullptr;
-    mRetainedTiles[i].mTextureSourceOnWhite = nullptr;
-  }
-}
-
-void
-TiledLayerBufferComposite::Upload()
-{
-  if(!IsValid()) {
-    return;
-  }
-  // The TextureClients were created with the TextureFlags::IMMEDIATE_UPLOAD flag,
-  // so calling Update on all the texture hosts will perform the texture upload.
-  Update(mValidRegion, mPaintedRegion);
-  ClearPaintedRegion();
-}
-
-TileHost
-TiledLayerBufferComposite::ValidateTile(TileHost aTile,
-                                        const IntPoint& aTileOrigin,
-                                        const nsIntRegion& aDirtyRect)
-{
-  if (aTile.IsPlaceholderTile()) {
-    NS_WARNING("Placeholder tile encountered in painted region");
-    return aTile;
-  }
-
-#ifdef GFX_TILEDLAYER_PREF_WARNINGS
-  printf_stderr("Upload tile %i, %i\n", aTileOrigin.x, aTileOrigin.y);
-  long start = PR_IntervalNow();
-#endif
-
-  MOZ_ASSERT(aTile.mTextureHost->GetFlags() & TextureFlags::IMMEDIATE_UPLOAD);
-
-#ifdef MOZ_GFX_OPTIMIZE_MOBILE
-  MOZ_ASSERT(!aTile.mTextureHostOnWhite);
-  // We possibly upload the entire texture contents here. This is a purposeful
-  // decision, as sub-image upload can often be slow and/or unreliable, but
-  // we may want to reevaluate this in the future.
-  // For !HasInternalBuffer() textures, this is likely a no-op.
-  aTile.mTextureHost->Updated(nullptr);
-#else
-  nsIntRegion tileUpdated = aDirtyRect.MovedBy(-aTileOrigin);
-  aTile.mTextureHost->Updated(&tileUpdated);
-  if (aTile.mTextureHostOnWhite) {
-    aTile.mTextureHostOnWhite->Updated(&tileUpdated);
-  }
-#endif
-
-#ifdef GFX_TILEDLAYER_PREF_WARNINGS
-  if (PR_IntervalNow() - start > 1) {
-    printf_stderr("Tile Time to upload %i\n", PR_IntervalNow() - start);
-  }
-#endif
-  return aTile;
-}
-
 void
 TiledLayerBufferComposite::SetCompositor(Compositor* aCompositor)
 {
   MOZ_ASSERT(aCompositor);
-  if (!IsValid()) {
-    return;
-  }
-  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-    if (mRetainedTiles[i].IsPlaceholderTile()) continue;
-    mRetainedTiles[i].mTextureHost->SetCompositor(aCompositor);
-    if (mRetainedTiles[i].mTextureHostOnWhite) {
-      mRetainedTiles[i].mTextureHostOnWhite->SetCompositor(aCompositor);
+  for (TileHost& tile : mRetainedTiles) {
+    if (tile.IsPlaceholderTile()) continue;
+    tile.mTextureHost->SetCompositor(aCompositor);
+    if (tile.mTextureHostOnWhite) {
+      tile.mTextureHostOnWhite->SetCompositor(aCompositor);
     }
   }
 }
 
 TiledContentHost::TiledContentHost(const TextureInfo& aTextureInfo)
   : ContentHost(aTextureInfo)
   , mTiledBuffer(TiledLayerBufferComposite())
   , mLowPrecisionTiledBuffer(TiledLayerBufferComposite())
-  , mOldTiledBuffer(TiledLayerBufferComposite())
-  , mOldLowPrecisionTiledBuffer(TiledLayerBufferComposite())
-  , mPendingUpload(false)
-  , mPendingLowPrecisionUpload(false)
 {
   MOZ_COUNT_CTOR(TiledContentHost);
 }
 
 TiledContentHost::~TiledContentHost()
 {
   MOZ_COUNT_DTOR(TiledContentHost);
-
-  // Unlock any buffers that may still be locked. If we have a pending upload,
-  // we will need to unlock the buffer that was about to be uploaded.
-  // If a buffer that was being composited had double-buffered tiles, we will
-  // need to unlock that buffer too.
-  if (mPendingUpload) {
-    mTiledBuffer.ReadUnlock();
-    if (mOldTiledBuffer.HasDoubleBufferedTiles()) {
-      mOldTiledBuffer.ReadUnlock();
-    }
-  } else if (mTiledBuffer.HasDoubleBufferedTiles()) {
-    mTiledBuffer.ReadUnlock();
-  }
-
-  if (mPendingLowPrecisionUpload) {
-    mLowPrecisionTiledBuffer.ReadUnlock();
-    if (mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-      mOldLowPrecisionTiledBuffer.ReadUnlock();
-    }
-  } else if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-    mLowPrecisionTiledBuffer.ReadUnlock();
-  }
 }
 
 void
 TiledContentHost::Attach(Layer* aLayer,
                          Compositor* aCompositor,
                          AttachFlags aFlags /* = NO_FLAGS */)
 {
   CompositableHost::Attach(aLayer, aCompositor, aFlags);
 }
 
 void
 TiledContentHost::Detach(Layer* aLayer,
                          AttachFlags aFlags /* = NO_FLAGS */)
 {
   if (!mKeepAttached || aLayer == mLayer || aFlags & FORCE_DETACH) {
-
-    // Unlock any buffers that may still be locked. If we have a pending upload,
-    // we will need to unlock the buffer that was about to be uploaded.
-    // If a buffer that was being composited had double-buffered tiles, we will
-    // need to unlock that buffer too.
-    if (mPendingUpload) {
-      mTiledBuffer.ReadUnlock();
-      if (mOldTiledBuffer.HasDoubleBufferedTiles()) {
-        mOldTiledBuffer.ReadUnlock();
-      }
-    } else if (mTiledBuffer.HasDoubleBufferedTiles()) {
-      mTiledBuffer.ReadUnlock();
-    }
-
-    if (mPendingLowPrecisionUpload) {
-      mLowPrecisionTiledBuffer.ReadUnlock();
-      if (mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-        mOldLowPrecisionTiledBuffer.ReadUnlock();
-      }
-    } else if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-      mLowPrecisionTiledBuffer.ReadUnlock();
-    }
-
-    mTiledBuffer = TiledLayerBufferComposite();
-    mLowPrecisionTiledBuffer = TiledLayerBufferComposite();
-    mOldTiledBuffer = TiledLayerBufferComposite();
-    mOldLowPrecisionTiledBuffer = TiledLayerBufferComposite();
+    // Clear the TiledLayerBuffers, which will take care of releasing the
+    // copy-on-write locks.
+    mTiledBuffer.Clear();
+    mLowPrecisionTiledBuffer.Clear();
   }
   CompositableHost::Detach(aLayer,aFlags);
 }
 
 bool
 TiledContentHost::UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
                                       const SurfaceDescriptorTiles& aTiledDescriptor)
 {
   if (aTiledDescriptor.resolution() < 1) {
-    if (mPendingLowPrecisionUpload) {
-      mLowPrecisionTiledBuffer.ReadUnlock();
-    } else {
-      mPendingLowPrecisionUpload = true;
-      // If the old buffer has double-buffered tiles, hang onto it so we can
-      // unlock it after we've composited the new buffer.
-      // We only need to hang onto the locks, but not the textures.
-      // Releasing the textures here can help prevent a memory spike in the
-      // situation that the client starts rendering new content before we get
-      // to composite the new buffer.
-      if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-        mOldLowPrecisionTiledBuffer = mLowPrecisionTiledBuffer;
-        mOldLowPrecisionTiledBuffer.ReleaseTextureHosts();
-      }
-    }
-    mLowPrecisionTiledBuffer =
-      TiledLayerBufferComposite(aAllocator,
-                                aTiledDescriptor,
-                                mLowPrecisionTiledBuffer.GetPaintedRegion(),
-                                mCompositor);
-    if (!mLowPrecisionTiledBuffer.IsValid()) {
-      // Something bad happened. Stop here, return false (kills the child process),
-      // and do as little work as possible on the received data as it appears
-      // to be corrupted.
-      mPendingLowPrecisionUpload = false;
-      mPendingUpload = false;
+    if (!mLowPrecisionTiledBuffer.UseTiles(aTiledDescriptor, mCompositor, aAllocator)) {
       return false;
     }
   } else {
-    if (mPendingUpload) {
-      mTiledBuffer.ReadUnlock();
-    } else {
-      mPendingUpload = true;
-      if (mTiledBuffer.HasDoubleBufferedTiles()) {
-        mOldTiledBuffer = mTiledBuffer;
-        mOldTiledBuffer.ReleaseTextureHosts();
-      }
-    }
-    mTiledBuffer = TiledLayerBufferComposite(aAllocator,
-                                             aTiledDescriptor,
-                                             mTiledBuffer.GetPaintedRegion(),
-                                             mCompositor);
-    if (!mTiledBuffer.IsValid()) {
-      // Something bad happened. Stop here, return false (kills the child process),
-      // and do as little work as possible on the received data as it appears
-      // to be corrupted.
-      mPendingLowPrecisionUpload = false;
-      mPendingUpload = false;
+    if (!mTiledBuffer.UseTiles(aTiledDescriptor, mCompositor, aAllocator)) {
       return false;
     }
   }
   return true;
 }
 
 void
+UseTileTexture(CompositableTextureHostRef& aTexture,
+               CompositableTextureSourceRef& aTextureSource,
+               const IntRect& aUpdateRect,
+               TextureHost* aNewTexture,
+               Compositor* aCompositor)
+{
+  if (aTexture && aTexture->GetFormat() != aNewTexture->GetFormat()) {
+    // Only reuse textures if their format match the new texture's.
+    aTextureSource = nullptr;
+    aTexture = nullptr;
+  }
+  aTexture = aNewTexture;
+  if (aTexture) {
+    if (aCompositor) {
+      aTexture->SetCompositor(aCompositor);
+    }
+
+    if (!aUpdateRect.IsEmpty()) {
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+      aTexture->Updated(nullptr);
+#else
+      // We possibly upload the entire texture contents here. This is a purposeful
+      // decision, as sub-image upload can often be slow and/or unreliable, but
+      // we may want to reevaluate this in the future.
+      // For !HasInternalBuffer() textures, this is likely a no-op.
+      nsIntRegion region = aUpdateRect;
+      aTexture->Updated(&region);
+#endif
+    }
+    aTexture->PrepareTextureSource(aTextureSource);
+  }
+}
+
+bool
+GetCopyOnWriteLock(const TileLock& ipcLock, TileHost& aTile, ISurfaceAllocator* aAllocator) {
+  MOZ_ASSERT(aAllocator);
+
+  nsRefPtr<gfxSharedReadLock> sharedLock;
+  if (ipcLock.type() == TileLock::TShmemSection) {
+    sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_ShmemSection());
+  } else {
+    if (!aAllocator->IsSameProcess()) {
+      // Trying to use a memory based lock instead of a shmem based one in
+      // the cross-process case is a bad security violation.
+      NS_ERROR("A client process may be trying to peek at the host's address space!");
+      return false;
+    }
+    sharedLock = reinterpret_cast<gfxMemorySharedReadLock*>(ipcLock.get_uintptr_t());
+    if (sharedLock) {
+      // The corresponding AddRef is in TiledClient::GetTileDescriptor
+      sharedLock.get()->Release();
+    }
+  }
+  aTile.mSharedLock = sharedLock;
+  return true;
+}
+
+bool
+TiledLayerBufferComposite::UseTiles(const SurfaceDescriptorTiles& aTiles,
+                                    Compositor* aCompositor,
+                                    ISurfaceAllocator* aAllocator)
+{
+  if (mResolution != aTiles.resolution()) {
+    Clear();
+  }
+  MOZ_ASSERT(aAllocator);
+  MOZ_ASSERT(aCompositor);
+  if (!aAllocator || !aCompositor) {
+    return false;
+  }
+
+  if (aTiles.resolution() == 0 || IsNaN(aTiles.resolution())) {
+    // There are divisions by mResolution so this protects the compositor process
+    // against malicious content processes and fuzzing.
+    return false;
+  }
+
+  int newFirstTileX = aTiles.firstTileX();
+  int newFirstTileY = aTiles.firstTileY();
+  int oldFirstTileX = mFirstTileX;
+  int oldFirstTileY = mFirstTileY;
+  int newRetainedWidth = aTiles.retainedWidth();
+  int newRetainedHeight = aTiles.retainedHeight();
+  int oldRetainedWidth = mRetainedWidth;
+  int oldRetainedHeight = mRetainedHeight;
+
+  const InfallibleTArray<TileDescriptor>& tileDescriptors = aTiles.tiles();
+
+  nsTArray<TileHost> oldTiles;
+  mRetainedTiles.SwapElements(oldTiles);
+  mRetainedTiles.SetLength(tileDescriptors.Length());
+
+  // Step 1, we need to unlock tiles that don't have an internal buffer after the
+  // next frame where they are replaced.
+  // Since we are about to replace the tiles' textures, we need to keep their locks
+  // somewhere (in mPreviousSharedLock) until we composite the layer.
+  for (size_t i = 0; i < oldTiles.Length(); ++i) {
+    TileHost& tile = oldTiles[i];
+    // It can happen that we still have a previous lock at this point,
+    // if we changed a tile's front buffer (causing mSharedLock to
+    // go into mPreviousSharedLock, and then did not composite that tile until
+    // the next transaction, either because the tile is offscreen or because the
+    // two transactions happened with no composition in between (over-production).
+    tile.ReadUnlockPrevious();
+
+    if (tile.mTextureHost && !tile.mTextureHost->HasInternalBuffer()) {
+      MOZ_ASSERT(tile.mSharedLock);
+      int tileX = i % oldRetainedWidth + oldFirstTileX;
+      int tileY = i / oldRetainedWidth + oldFirstTileY;
+
+      if (tileX >= newFirstTileX && tileY >= newFirstTileY &&
+          tileX < (newFirstTileX + newRetainedWidth) &&
+          tileY < (newFirstTileY + newRetainedHeight)) {
+        // This tile still exist in the new buffer
+        tile.mPreviousSharedLock = tile.mSharedLock;
+        tile.mSharedLock = nullptr;
+      } else {
+        // This tile does not exist anymore in the new buffer because the size
+        // changed.
+        tile.ReadUnlock();
+      }
+    }
+
+    // By now we should not have anything in mSharedLock.
+    MOZ_ASSERT(!tile.mSharedLock);
+  }
+
+  // Step 2, move the tiles in mRetainedTiles at places that correspond to where
+  // they should be with the new retained with and height rather than the
+  // old one.
+  for (size_t i = 0; i < tileDescriptors.Length(); i++) {
+    int tileX = i % newRetainedWidth + newFirstTileX;
+    int tileY = i / newRetainedWidth + newFirstTileY;
+
+    // First, get the already existing tiles to the right place in the array,
+    // and use placeholders where there was no tiles.
+    if (tileX < oldFirstTileX || tileY < oldFirstTileY ||
+        tileX >= (oldFirstTileX + oldRetainedWidth) ||
+        tileY >= (oldFirstTileY + oldRetainedHeight)) {
+      mRetainedTiles[i] = GetPlaceholderTile();
+    } else {
+      mRetainedTiles[i] = oldTiles[(tileY - oldFirstTileY) * oldRetainedWidth +
+                                   (tileX - oldFirstTileX)];
+      // If we hit this assertion it means we probably mixed something up in the
+      // logic that tries to reuse tiles on the compositor side. It is most likely
+      // benign, but we are missing some fast paths so let's try to make it not happen.
+      MOZ_ASSERT(tileX == mRetainedTiles[i].x && tileY == mRetainedTiles[i].y);
+    }
+  }
+
+  // It is important to remove the duplicated reference to tiles before calling
+  // TextureHost::PrepareTextureSource, etc. because depending on the textures
+  // ref counts we may or may not get some of the fast paths.
+  oldTiles.Clear();
+
+  // Step 3, handle the texture updates and release the copy-on-write locks.
+  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+    const TileDescriptor& tileDesc = tileDescriptors[i];
+
+    TileHost& tile = mRetainedTiles[i];
+
+    switch (tileDesc.type()) {
+      case TileDescriptor::TTexturedTileDescriptor: {
+        const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
+
+        const TileLock& ipcLock = texturedDesc.sharedLock();
+        if (!GetCopyOnWriteLock(ipcLock, tile, aAllocator)) {
+          return false;
+        }
+
+        RefPtr<TextureHost> textureHost = TextureHost::AsTextureHost(
+          texturedDesc.textureParent()
+        );
+
+        RefPtr<TextureHost> textureOnWhite = nullptr;
+        if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) {
+          textureOnWhite = TextureHost::AsTextureHost(
+            texturedDesc.textureOnWhite().get_PTextureParent()
+          );
+        }
+
+        UseTileTexture(tile.mTextureHost,
+                       tile.mTextureSource,
+                       texturedDesc.updateRect(),
+                       textureHost,
+                       aCompositor);
+
+        if (textureOnWhite) {
+          UseTileTexture(tile.mTextureHostOnWhite,
+                         tile.mTextureSourceOnWhite,
+                         texturedDesc.updateRect(),
+                         textureOnWhite,
+                         aCompositor);
+        } else {
+          // We could still have component alpha textures from a previous frame.
+          tile.mTextureSourceOnWhite = nullptr;
+          tile.mTextureHostOnWhite = nullptr;
+        }
+
+        if (textureHost->HasInternalBuffer()) {
+          // Now that we did the texture upload (in UseTileTexture), we can release
+          // the lock.
+          tile.ReadUnlock();
+        }
+
+        break;
+      }
+      default:
+        NS_WARNING("Unrecognised tile descriptor type");
+      case TileDescriptor::TPlaceholderTileDescriptor: {
+
+        if (tile.mTextureHost) {
+          tile.mTextureHost->UnbindTextureSource();
+          tile.mTextureSource = nullptr;
+        }
+        if (tile.mTextureHostOnWhite) {
+          tile.mTextureHostOnWhite->UnbindTextureSource();
+          tile.mTextureSourceOnWhite = nullptr;
+        }
+        // we may have a previous lock, and are about to loose our reference to it.
+        // It is okay to unlock it because we just destroyed the texture source.
+        tile.ReadUnlockPrevious();
+        tile = GetPlaceholderTile();
+
+        break;
+      }
+    }
+
+    tile.x = i % newRetainedWidth + newFirstTileX;
+    tile.y = i / newRetainedWidth + newFirstTileY;
+  }
+
+  mFirstTileX = newFirstTileX;
+  mFirstTileY = newFirstTileY;
+  mRetainedWidth = newRetainedWidth;
+  mRetainedHeight = newRetainedHeight;
+  mValidRegion = aTiles.validRegion();
+
+  mResolution = aTiles.resolution();
+  mFrameResolution = CSSToParentLayerScale2D(aTiles.frameXResolution(),
+                                             aTiles.frameYResolution());
+
+  return true;
+}
+
+void
+TiledLayerBufferComposite::Clear()
+{
+  for (TileHost& tile : mRetainedTiles) {
+    tile.ReadUnlock();
+    tile.ReadUnlockPrevious();
+  }
+  mRetainedTiles.Clear();
+  mFirstTileX = 0;
+  mFirstTileY = 0;
+  mRetainedWidth = 0;
+  mRetainedHeight = 0;
+  mValidRegion = nsIntRegion();
+  mPaintedRegion = nsIntRegion();
+  mResolution = 1.0;
+}
+
+void
 TiledContentHost::Composite(EffectChain& aEffectChain,
                             float aOpacity,
                             const gfx::Matrix4x4& aTransform,
                             const gfx::Filter& aFilter,
                             const gfx::Rect& aClipRect,
                             const nsIntRegion* aVisibleRegion /* = nullptr */)
 {
   MOZ_ASSERT(mCompositor);
-  if (mPendingUpload) {
-    mTiledBuffer.SetCompositor(mCompositor);
-    mTiledBuffer.Upload();
-
-    // For a single-buffered tiled buffer, Upload will upload the shared memory
-    // surface to texture memory and we no longer need to read from them.
-    if (!mTiledBuffer.HasDoubleBufferedTiles()) {
-      mTiledBuffer.ReadUnlock();
-    }
-  }
-  if (mPendingLowPrecisionUpload) {
-    mLowPrecisionTiledBuffer.SetCompositor(mCompositor);
-    mLowPrecisionTiledBuffer.Upload();
-
-    if (!mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-      mLowPrecisionTiledBuffer.ReadUnlock();
-    }
-  }
-
   // Reduce the opacity of the low-precision buffer to make it a
   // little more subtle and less jarring. In particular, text
   // rendered at low-resolution and scaled tends to look pretty
   // heavy and this helps mitigate that. When we reduce the opacity
   // we also make sure to draw the background color behind the
   // reduced-opacity tile so that content underneath doesn't show
   // through.
   // However, in cases where the background is transparent, or the layer
@@ -435,34 +417,21 @@ TiledContentHost::Composite(EffectChain&
 
   // Render the low and high precision buffers.
   RenderLayerBuffer(mLowPrecisionTiledBuffer,
                     lowPrecisionOpacityReduction < 1.0f ? &backgroundColor : nullptr,
                     aEffectChain, lowPrecisionOpacityReduction * aOpacity,
                     aFilter, aClipRect, *renderRegion, aTransform);
   RenderLayerBuffer(mTiledBuffer, nullptr, aEffectChain, aOpacity, aFilter,
                     aClipRect, *renderRegion, aTransform);
-
-  // Now release the old buffer if it had double-buffered tiles, as we can
-  // guarantee that they're no longer on the screen (and so any locks that may
-  // have been held have been released).
-  if (mPendingUpload && mOldTiledBuffer.HasDoubleBufferedTiles()) {
-    mOldTiledBuffer.ReadUnlock();
-    mOldTiledBuffer = TiledLayerBufferComposite();
-  }
-  if (mPendingLowPrecisionUpload && mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-    mOldLowPrecisionTiledBuffer.ReadUnlock();
-    mOldLowPrecisionTiledBuffer = TiledLayerBufferComposite();
-  }
-  mPendingUpload = mPendingLowPrecisionUpload = false;
 }
 
 
 void
-TiledContentHost::RenderTile(const TileHost& aTile,
+TiledContentHost::RenderTile(TileHost& aTile,
                              const gfxRGBA* aBackgroundColor,
                              EffectChain& aEffectChain,
                              float aOpacity,
                              const gfx::Matrix4x4& aTransform,
                              const gfx::Filter& aFilter,
                              const gfx::Rect& aClipRect,
                              const nsIntRegion& aScreenRegion,
                              const IntPoint& aTextureOffset,
@@ -519,16 +488,17 @@ TiledContentHost::RenderTile(const TileH
     mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, aOpacity, aTransform);
   }
   DiagnosticFlags flags = DiagnosticFlags::CONTENT | DiagnosticFlags::TILE;
   if (aTile.mTextureHostOnWhite) {
     flags |= DiagnosticFlags::COMPONENT_ALPHA;
   }
   mCompositor->DrawDiagnostics(flags,
                                aScreenRegion, aClipRect, aTransform, mFlashCounter);
+  aTile.ReadUnlockPrevious();
 }
 
 void
 TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
                                     const gfxRGBA* aBackgroundColor,
                                     EffectChain& aEffectChain,
                                     float aOpacity,
                                     const gfx::Filter& aFilter,
@@ -586,20 +556,20 @@ TiledContentHost::RenderLayerBuffer(Tile
     int tileY = 0;
     for (int32_t y = visibleRect.y; y < visibleRect.y + visibleRect.height;) {
       int32_t tileStartY = aLayerBuffer.GetTileStart(y, scaledTileSize.height);
       int32_t h = scaledTileSize.height - tileStartY;
       if (y + h > visibleRect.y + visibleRect.height) {
         h = visibleRect.y + visibleRect.height - y;
       }
 
-      TileHost tileTexture = aLayerBuffer.
-        GetTile(IntPoint(aLayerBuffer.RoundDownToTileEdge(x, scaledTileSize.width),
-                         aLayerBuffer.RoundDownToTileEdge(y, scaledTileSize.height)));
-      if (tileTexture != aLayerBuffer.GetPlaceholderTile()) {
+      nsIntPoint tileOrigin = nsIntPoint(aLayerBuffer.RoundDownToTileEdge(x, scaledTileSize.width),
+                                         aLayerBuffer.RoundDownToTileEdge(y, scaledTileSize.height));
+      TileHost& tileTexture = aLayerBuffer.GetTile(tileOrigin);
+      if (!tileTexture.IsPlaceholderTile()) {
         nsIntRegion tileDrawRegion;
         tileDrawRegion.And(IntRect(x, y, w, h), aLayerBuffer.GetValidRegion());
         tileDrawRegion.And(tileDrawRegion, aVisibleRegion);
         tileDrawRegion.Sub(tileDrawRegion, maskRegion);
 
         if (!tileDrawRegion.IsEmpty()) {
           tileDrawRegion.ScaleRoundOut(resolution, resolution);
           IntPoint tileOffset((x - tileStartX) * resolution,
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -47,138 +47,134 @@ struct EffectChain;
 
 class TileHost {
 public:
   // Constructs a placeholder TileHost. See the comments above
   // TiledLayerBuffer for more information on what this is used for;
   // essentially, this is a sentinel used to represent an invalid or blank
   // tile.
   TileHost()
+  : x(-1)
+  , y(-1)
   {}
 
   // Constructs a TileHost from a gfxSharedReadLock and TextureHost.
   TileHost(gfxSharedReadLock* aSharedLock,
                TextureHost* aTextureHost,
                TextureHost* aTextureHostOnWhite,
                TextureSource* aSource,
                TextureSource* aSourceOnWhite)
     : mSharedLock(aSharedLock)
     , mTextureHost(aTextureHost)
     , mTextureHostOnWhite(aTextureHostOnWhite)
     , mTextureSource(aSource)
     , mTextureSourceOnWhite(aSourceOnWhite)
+    , x(-1)
+    , y(-1)
   {}
 
   TileHost(const TileHost& o) {
     mTextureHost = o.mTextureHost;
     mTextureHostOnWhite = o.mTextureHostOnWhite;
     mTextureSource = o.mTextureSource;
     mTextureSourceOnWhite = o.mTextureSourceOnWhite;
     mSharedLock = o.mSharedLock;
+    mPreviousSharedLock = o.mPreviousSharedLock;
+    x = o.x;
+    y = o.y;
   }
   TileHost& operator=(const TileHost& o) {
     if (this == &o) {
       return *this;
     }
     mTextureHost = o.mTextureHost;
     mTextureHostOnWhite = o.mTextureHostOnWhite;
     mTextureSource = o.mTextureSource;
     mTextureSourceOnWhite = o.mTextureSourceOnWhite;
     mSharedLock = o.mSharedLock;
+    mPreviousSharedLock = o.mPreviousSharedLock;
+    x = o.x;
+    y = o.y;
     return *this;
   }
 
   bool operator== (const TileHost& o) const {
     return mTextureHost == o.mTextureHost;
   }
   bool operator!= (const TileHost& o) const {
     return mTextureHost != o.mTextureHost;
   }
 
   bool IsPlaceholderTile() const { return mTextureHost == nullptr; }
 
   void ReadUnlock() {
     if (mSharedLock) {
       mSharedLock->ReadUnlock();
+      mSharedLock = nullptr;
+    }
+  }
+
+  void ReadUnlockPrevious() {
+    if (mPreviousSharedLock) {
+      mPreviousSharedLock->ReadUnlock();
+      mPreviousSharedLock = nullptr;
     }
   }
 
   void Dump(std::stringstream& aStream) {
     aStream << "TileHost(...)"; // fill in as needed
   }
 
   void DumpTexture(std::stringstream& aStream) {
     // TODO We should combine the OnWhite/OnBlack here an just output a single image.
     CompositableHost::DumpTextureHost(aStream, mTextureHost);
   }
 
   RefPtr<gfxSharedReadLock> mSharedLock;
+  RefPtr<gfxSharedReadLock> mPreviousSharedLock;
   CompositableTextureHostRef mTextureHost;
   CompositableTextureHostRef mTextureHostOnWhite;
   mutable CompositableTextureSourceRef mTextureSource;
   mutable CompositableTextureSourceRef mTextureSourceOnWhite;
+  // This is not strictly necessary but makes debugging whole lot easier.
+  int x;
+  int y;
 };
 
 class TiledLayerBufferComposite
   : public TiledLayerBuffer<TiledLayerBufferComposite, TileHost>
 {
   friend class TiledLayerBuffer<TiledLayerBufferComposite, TileHost>;
 
 public:
-  typedef TiledLayerBuffer<TiledLayerBufferComposite, TileHost>::Iterator Iterator;
+  TiledLayerBufferComposite();
+  ~TiledLayerBufferComposite();
 
-  TiledLayerBufferComposite();
-  TiledLayerBufferComposite(ISurfaceAllocator* aAllocator,
-                            const SurfaceDescriptorTiles& aDescriptor,
-                            const nsIntRegion& aOldPaintedRegion,
-                            Compositor* aCompositor);
+  bool UseTiles(const SurfaceDescriptorTiles& aTileDescriptors,
+                Compositor* aCompositor,
+                ISurfaceAllocator* aAllocator);
+
+  void Clear();
 
   TileHost GetPlaceholderTile() const { return TileHost(); }
 
   // Stores the absolute resolution of the containing frame, calculated
   // by the sum of the resolutions of all parent layers' FrameMetrics.
   const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; }
 
-  void ReadUnlock();
-
-  void ReleaseTextureHosts();
-
-  /**
-   * This will synchronously upload any necessary texture contents, making the
-   * sources immediately available for compositing. For texture hosts that
-   * don't have an internal buffer, this is unlikely to actually do anything.
-   */
-  void Upload();
-
   void SetCompositor(Compositor* aCompositor);
 
-  bool HasDoubleBufferedTiles() { return mHasDoubleBufferedTiles; }
-
-  bool IsValid() const { return mIsValid; }
-
   // Recycle callback for TextureHost.
   // Used when TiledContentClient is present in client side.
   static void RecycleCallback(TextureHost* textureHost, void* aClosure);
 
 protected:
-  TileHost ValidateTile(TileHost aTile,
-                        const gfx::IntPoint& aTileRect,
-                        const nsIntRegion& dirtyRect);
-
-  // do nothing, the desctructor in the texture host takes care of releasing resources
-  void ReleaseTile(TileHost aTile) {}
-
   void SwapTiles(TileHost& aTileA, TileHost& aTileB) { std::swap(aTileA, aTileB); }
 
-  void UnlockTile(TileHost aTile) {}
-  void PostValidate(const nsIntRegion& aPaintRegion) {}
-private:
   CSSToParentLayerScale2D mFrameResolution;
-  bool mHasDoubleBufferedTiles;
-  bool mIsValid;
 };
 
 /**
  * ContentHost for tiled PaintedLayers. Since tiled layers are special snow
  * flakes, we have a unique update process. All the textures that back the
  * tiles are added in the usual way, but Updated is called on the host side
  * in response to a message that describes the transaction for every tile.
  * Composition happens in the normal way.
@@ -228,21 +224,20 @@ public:
 
   const nsIntRegion& GetValidRegion() const override
   {
     return mTiledBuffer.GetValidRegion();
   }
 
   virtual void SetCompositor(Compositor* aCompositor) override
   {
+    MOZ_ASSERT(aCompositor);
     CompositableHost::SetCompositor(aCompositor);
     mTiledBuffer.SetCompositor(aCompositor);
     mLowPrecisionTiledBuffer.SetCompositor(aCompositor);
-    mOldTiledBuffer.SetCompositor(aCompositor);
-    mOldLowPrecisionTiledBuffer.SetCompositor(aCompositor);
   }
 
   virtual bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
                                    const SurfaceDescriptorTiles& aTiledDescriptor) override;
 
   void Composite(EffectChain& aEffectChain,
                  float aOpacity,
                  const gfx::Matrix4x4& aTransform,
@@ -274,33 +269,29 @@ private:
                          EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Filter& aFilter,
                          const gfx::Rect& aClipRect,
                          nsIntRegion aMaskRegion,
                          gfx::Matrix4x4 aTransform);
 
   // Renders a single given tile.
-  void RenderTile(const TileHost& aTile,
+  void RenderTile(TileHost& aTile,
                   const gfxRGBA* aBackgroundColor,
                   EffectChain& aEffectChain,
                   float aOpacity,
                   const gfx::Matrix4x4& aTransform,
                   const gfx::Filter& aFilter,
                   const gfx::Rect& aClipRect,
                   const nsIntRegion& aScreenRegion,
                   const gfx::IntPoint& aTextureOffset,
                   const gfx::IntSize& aTextureBounds);
 
   void EnsureTileStore() {}
 
   TiledLayerBufferComposite    mTiledBuffer;
   TiledLayerBufferComposite    mLowPrecisionTiledBuffer;
-  TiledLayerBufferComposite    mOldTiledBuffer;
-  TiledLayerBufferComposite    mOldLowPrecisionTiledBuffer;
-  bool                         mPendingUpload;
-  bool                         mPendingLowPrecisionUpload;
 };
 
 }
 }
 
 #endif
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -310,31 +310,34 @@ union TileLock {
 union MaybeTexture {
   PTexture;
   null_t;
 };
 
 struct TexturedTileDescriptor {
   PTexture texture;
   MaybeTexture textureOnWhite;
+  IntRect updateRect;
   TileLock sharedLock;
 };
 
 struct PlaceholderTileDescriptor {
 };
 
 union TileDescriptor {
   TexturedTileDescriptor;
   PlaceholderTileDescriptor;
 };
 
 struct SurfaceDescriptorTiles {
   nsIntRegion validRegion;
   nsIntRegion paintedRegion;
   TileDescriptor[] tiles;
+  int         firstTileX;
+  int         firstTileY;
   int         retainedWidth;
   int         retainedHeight;
   float       resolution;
   float       frameXResolution;
   float       frameYResolution;
 };
 
 struct OpUseTiledLayerBuffer {