Bug 1179987 - Make tiled TextureSource recycling based on pointer comparisons rather than trying to do it geometrically. r=nical
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 15 Jul 2015 13:34:35 -0400
changeset 253081 680514de6b214e944426966afe480e716a08311e
parent 253080 daf988f59e90c4aa45a86c56deda6911ee689b54
child 253082 103db8392e11cdd7d8a38ec120fad3896d39dae8
push id29061
push userryanvm@gmail.com
push dateThu, 16 Jul 2015 18:53:45 +0000
treeherdermozilla-central@a0f4a688433d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1179987
milestone42.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 1179987 - Make tiled TextureSource recycling based on pointer comparisons rather than trying to do it geometrically. r=nical
gfx/layers/composite/TiledContentHost.cpp
gfx/layers/composite/TiledContentHost.h
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -104,38 +104,23 @@ TiledContentHost::UseTiledLayerBuffer(IS
   }
   return true;
 }
 
 void
 UseTileTexture(CompositableTextureHostRef& aTexture,
                CompositableTextureSourceRef& aTextureSource,
                const IntRect& aUpdateRect,
-               TextureHost* aNewTexture,
                Compositor* aCompositor)
 {
-  MOZ_ASSERT(aNewTexture);
-  if (!aNewTexture) {
+  MOZ_ASSERT(aTexture);
+  if (!aTexture) {
     return;
   }
 
-  if (aTexture) {
-    aTexture->SetCompositor(aCompositor);
-    aNewTexture->SetCompositor(aCompositor);
-
-    if (aTexture->GetFormat() != aNewTexture->GetFormat()) {
-      // Only reuse textures if their format match the new texture's.
-      aTextureSource = nullptr;
-      aTexture = nullptr;
-    }
-  }
-
-  aTexture = aNewTexture;
-
-
   if (aCompositor) {
     aTexture->SetCompositor(aCompositor);
   }
 
   if (!aUpdateRect.IsEmpty()) {
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
     aTexture->Updated(nullptr);
 #else
@@ -187,16 +172,76 @@ TiledLayerBufferComposite::MarkTilesForU
     // so won't have a lock at this point.
     if (tile.mTextureHost && tile.mSharedLock) {
       mDelayedUnlocks.AppendElement(tile.mSharedLock);
       tile.mSharedLock = nullptr;
     }
   }
 }
 
+class TextureSourceRecycler
+{
+public:
+  explicit TextureSourceRecycler(nsTArray<TileHost>&& aTileSet)
+    : mTiles(Move(aTileSet))
+    , mFirstPossibility(0)
+  {}
+
+  // Attempts to recycle a texture source that is already bound to the
+  // texture host for aTile.
+  void RecycleTextureSourceForTile(TileHost& aTile) {
+    for (size_t i = mFirstPossibility; i < mTiles.Length(); i++) {
+      // Skip over existing tiles without a retained texture source
+      // and make sure we don't iterate them in the future.
+      if (!mTiles[i].mTextureSource) {
+        if (i == mFirstPossibility) {
+          mFirstPossibility++;
+        }
+        continue;
+      }
+
+      // If this tile matches, then copy across the retained texture source (if
+      // any).
+      if (aTile.mTextureHost == mTiles[i].mTextureHost) {
+        aTile.mTextureSource = Move(mTiles[i].mTextureSource);
+        if (aTile.mTextureHostOnWhite) {
+          aTile.mTextureSourceOnWhite = Move(mTiles[i].mTextureSourceOnWhite);
+        }
+        break;
+      }
+    }
+  }
+
+  // Attempts to recycle any texture source to avoid needing to allocate
+  // a new one.
+  void RecycleTextureSource(TileHost& aTile) {
+    for (size_t i = mFirstPossibility; i < mTiles.Length(); i++) {
+      if (!mTiles[i].mTextureSource) {
+        if (i == mFirstPossibility) {
+          mFirstPossibility++;
+        }
+        continue;
+      }
+
+      if (mTiles[i].mTextureSource &&
+          mTiles[i].mTextureHost->GetFormat() == aTile.mTextureHost->GetFormat()) {
+        aTile.mTextureSource = Move(mTiles[i].mTextureSource);
+        if (aTile.mTextureHostOnWhite) {
+          aTile.mTextureSourceOnWhite = Move(mTiles[i].mTextureSourceOnWhite);
+        }
+        break;
+      }
+    }
+  }
+
+protected:
+  nsTArray<TileHost> mTiles;
+  size_t mFirstPossibility;
+};
+
 bool
 TiledLayerBufferComposite::UseTiles(const SurfaceDescriptorTiles& aTiles,
                                     Compositor* aCompositor,
                                     ISurfaceAllocator* aAllocator)
 {
   if (mResolution != aTiles.resolution()) {
     Clear();
   }
@@ -207,116 +252,107 @@ TiledLayerBufferComposite::UseTiles(cons
   }
 
   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;
   }
 
-  TilesPlacement oldTiles = mTiles;
   TilesPlacement newTiles(aTiles.firstTileX(), aTiles.firstTileY(),
                           aTiles.retainedWidth(), aTiles.retainedHeight());
 
   const InfallibleTArray<TileDescriptor>& tileDescriptors = aTiles.tiles();
 
   // Step 1, unlock all the old tiles that haven't been unlocked yet. Any tiles that
   // exist in both the old and new sets will have been locked again by content, so this
   // doesn't result in the surface being writeable again.
   MarkTilesForUnlock();
 
-  nsTArray<TileHost> oldRetainedTiles;
-  mRetainedTiles.SwapElements(oldRetainedTiles);
+  TextureSourceRecycler oldRetainedTiles(Move(mRetainedTiles));
   mRetainedTiles.SetLength(tileDescriptors.Length());
 
-  // 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.
+  // Step 2, deserialize the incoming set of tiles into mRetainedTiles, and attempt
+  // to recycle the TextureSource for any repeated tiles.
+  //
+  // Since we don't have any retained 'tile' object, we have to search for instances
+  // of the same TextureHost in the old tile set. The cost of binding a TextureHost
+  // to a TextureSource for gralloc (binding EGLImage to GL texture) can be really
+  // high, so we avoid this whenever possible.
   for (size_t i = 0; i < tileDescriptors.Length(); i++) {
-    const TileIntPoint tilePosition = newTiles.TilePosition(i);
-    // First, get the already existing tiles to the right place in the array,
-    // and use placeholders where there was no tiles.
-    if (!oldTiles.HasTile(tilePosition)) {
-      mRetainedTiles[i] = GetPlaceholderTile();
-    } else {
-      mRetainedTiles[i] = oldRetainedTiles[oldTiles.TileIndex(tilePosition)];
-      // 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(tilePosition.x == mRetainedTiles[i].x &&
-                 tilePosition.y == 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.
-  oldRetainedTiles.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();
+    if (tileDesc.type() != TileDescriptor::TTexturedTileDescriptor) {
+      NS_WARN_IF_FALSE(tileDesc.type() == TileDescriptor::TPlaceholderTileDescriptor,
+                       "Unrecognised tile descriptor type");
+      continue;
+    }
 
-        const TileLock& ipcLock = texturedDesc.sharedLock();
-        if (!GetCopyOnWriteLock(ipcLock, tile, aAllocator)) {
-          return false;
-        }
+    const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
 
-        RefPtr<TextureHost> textureHost = TextureHost::AsTextureHost(
-          texturedDesc.textureParent()
-        );
+    const TileLock& ipcLock = texturedDesc.sharedLock();
+    if (!GetCopyOnWriteLock(ipcLock, tile, aAllocator)) {
+      return false;
+    }
 
-        RefPtr<TextureHost> textureOnWhite = nullptr;
-        if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) {
-          textureOnWhite = TextureHost::AsTextureHost(
-            texturedDesc.textureOnWhite().get_PTextureParent()
-          );
-        }
+    tile.mTextureHost = TextureHost::AsTextureHost(texturedDesc.textureParent());
+
+    if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) {
+      tile.mTextureHostOnWhite =
+        TextureHost::AsTextureHost(texturedDesc.textureOnWhite().get_PTextureParent());
+    }
 
-        UseTileTexture(tile.mTextureHost,
-                       tile.mTextureSource,
-                       texturedDesc.updateRect(),
-                       textureHost,
-                       aCompositor);
+    tile.mTilePosition = newTiles.TilePosition(i);
+
+    // If this same tile texture existed in the old tile set then this will move the texture
+    // source into our new tile.
+    oldRetainedTiles.RecycleTextureSourceForTile(tile);
+  }
 
-        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;
-        }
+  // Step 3, attempt to recycle unused texture sources from the old tile set into new tiles.
+  //
+  // For gralloc, binding a new TextureHost to the existing TextureSource is the fastest way
+  // to ensure that any implicit locking on the old gralloc image is released.
+  for (TileHost& tile : mRetainedTiles) {
+    if (!tile.mTextureHost || tile.mTextureSource) {
+      continue;
+    }
+    oldRetainedTiles.RecycleTextureSource(tile);
+  }
+
+  // Step 4, handle the texture uploads, texture source binding and release the
+  // copy-on-write locks for textures with an internal buffer.
+  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+    TileHost& tile = mRetainedTiles[i];
+    if (!tile.mTextureHost) {
+      continue;
+    }
 
-        if (textureHost->HasInternalBuffer()) {
-          // Now that we did the texture upload (in UseTileTexture), we can release
-          // the lock.
-          tile.ReadUnlock();
-        }
+    const TileDescriptor& tileDesc = tileDescriptors[i];
+    const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
+
+    UseTileTexture(tile.mTextureHost,
+                   tile.mTextureSource,
+                   texturedDesc.updateRect(),
+                   aCompositor);
 
-        break;
-      }
-      default:
-        NS_WARNING("Unrecognised tile descriptor type");
-      case TileDescriptor::TPlaceholderTileDescriptor: {
-        break;
-      }
+    if (tile.mTextureHostOnWhite) {
+      UseTileTexture(tile.mTextureHostOnWhite,
+                     tile.mTextureSourceOnWhite,
+                     texturedDesc.updateRect(),
+                     aCompositor);
     }
-    TileIntPoint tilePosition = newTiles.TilePosition(i);
-    tile.x = tilePosition.x;
-    tile.y = tilePosition.y;
+
+    if (tile.mTextureHost->HasInternalBuffer()) {
+      // Now that we did the texture upload (in UseTileTexture), we can release
+      // the lock.
+      tile.ReadUnlock();
+    }
   }
 
   mTiles = newTiles;
   mValidRegion = aTiles.validRegion();
   mResolution = aTiles.resolution();
   mFrameResolution = CSSToParentLayerScale2D(aTiles.frameXResolution(),
                                              aTiles.frameYResolution());
 
@@ -540,17 +576,17 @@ TiledContentHost::RenderLayerBuffer(Tile
   for (size_t i = 0; i < aLayerBuffer.GetTileCount(); ++i) {
     TileHost& tile = aLayerBuffer.GetTile(i);
     if (tile.IsPlaceholderTile()) {
       continue;
     }
 
     TileIntPoint tilePosition = aLayerBuffer.GetPlacement().TilePosition(i);
     // A sanity check that catches a lot of mistakes.
-    MOZ_ASSERT(tilePosition.x == tile.x && tilePosition.y == tile.y);
+    MOZ_ASSERT(tilePosition.x == tile.mTilePosition.x && tilePosition.y == tile.mTilePosition.y);
 
     IntPoint tileOffset = aLayerBuffer.GetTileOffset(tilePosition);
     nsIntRegion tileDrawRegion = IntRect(tileOffset, aLayerBuffer.GetScaledTileSize());
     tileDrawRegion.AndWith(compositeRegion);
 
     if (tileDrawRegion.IsEmpty()) {
       continue;
     }
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -47,55 +47,49 @@ 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;
-    x = o.x;
-    y = o.y;
+    mTilePosition = o.mTilePosition;
   }
   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;
-    x = o.x;
-    y = o.y;
+    mTilePosition = o.mTilePosition;
     return *this;
   }
 
   bool operator== (const TileHost& o) const {
     return mTextureHost == o.mTextureHost;
   }
   bool operator!= (const TileHost& o) const {
     return mTextureHost != o.mTextureHost;
@@ -120,18 +114,17 @@ public:
   }
 
   RefPtr<gfxSharedReadLock> mSharedLock;
   CompositableTextureHostRef mTextureHost;
   CompositableTextureHostRef mTextureHostOnWhite;
   mutable CompositableTextureSourceRef mTextureSource;
   mutable CompositableTextureSourceRef mTextureSourceOnWhite;
   // This is not strictly necessary but makes debugging whole lot easier.
-  DebugOnly<int> x;
-  DebugOnly<int> y;
+  TileIntPoint mTilePosition;
 };
 
 class TiledLayerBufferComposite
   : public TiledLayerBuffer<TiledLayerBufferComposite, TileHost>
 {
   friend class TiledLayerBuffer<TiledLayerBufferComposite, TileHost>;
 
 public: