Bug 1150549 - Simplify TiledContentHost. r=jrmuizel
☠☠ backed out by 00f2351e7744 ☠ ☠
authorNicolas Silva <nsilva@mozilla.com>
Fri, 22 May 2015 13:38:13 +0200
changeset 245062 b35c88257a9cf62257a3c236c7506f5ae0bc6990
parent 245061 7c82838a230c23359c0b3484870cca99e262fe74
child 245063 55f99ca025a4fa42ed8724cca629b8130585d606
push id60099
push usernsilva@mozilla.com
push dateFri, 22 May 2015 11:39:28 +0000
treeherdermozilla-inbound@b35c88257a9c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1150549
milestone41.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 1150549 - Simplify TiledContentHost. r=jrmuizel
gfx/layers/TiledLayerBuffer.h
gfx/layers/client/TiledContentClient.cpp
gfx/layers/client/TiledContentClient.h
gfx/layers/composite/TextureHost.h
gfx/layers/composite/TiledContentHost.cpp
gfx/layers/composite/TiledContentHost.h
gfx/layers/ipc/LayersMessages.ipdlh
--- 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)) {
     NS_WARNING("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,19 @@ ClientTiledLayerBuffer::ValidateTile(Til
   nsIntRegion extraPainted;
   RefPtr<TextureClient> backBufferOnWhite;
   RefPtr<TextureClient> backBuffer =
     aTile.GetBackBuffer(offsetScaledDirtyRegion,
                         content, mode,
                         &createdTextureClient, extraPainted,
                         &backBufferOnWhite);
 
+  aTile.mUpdateRect = offsetScaledDirtyRegion.GetBounds();
+  aTile.mUpdateRect.Union(extraPainted.GetBounds());
+
   extraPainted.MoveBy(aTileOrigin);
   extraPainted.And(extraPainted, mNewValidRegion);
   mPaintedRegion.Or(mPaintedRegion, extraPainted);
 
   if (!backBuffer) {
     NS_WARNING("Failed to allocate a tile 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/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -321,16 +321,17 @@ class TextureHost
   friend class AtomicRefCountedWithFinalize<TextureHost>;
 public:
   explicit TextureHost(TextureFlags aFlags);
 
 protected:
   virtual ~TextureHost();
 
 public:
+
   /**
    * Factory method.
    */
   static TemporaryRef<TextureHost> Create(const SurfaceDescriptor& aDesc,
                                           ISurfaceAllocator* aDeallocator,
                                           TextureFlags aFlags);
 
   /**
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -24,382 +24,352 @@ 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)
+{
+  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()
+        );
+
+        UseTileTexture(tile.mTextureHost,
+                       tile.mTextureSource,
+                       texturedDesc.updateRect(),
+                       textureHost,
+                       aCompositor);
+
+        MaybeTexture onWhite = texturedDesc.textureOnWhite();
+        if (onWhite.type() == MaybeTexture::TPTextureParent) {
+          RefPtr<TextureHost> textureOnWhite = TextureHost::AsTextureHost(
+            onWhite.get_PTextureParent()
+          );
+          UseTileTexture(tile.mTextureHostOnWhite,
+                         tile.mTextureSourceOnWhite,
+                         texturedDesc.updateRect(),
+                         textureOnWhite,
+                         aCompositor);
+        }
+
+        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 +405,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 +476,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 +544,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 {