Bug 1279341 - Keep track of unused tiles in determining TextureClientPool size instead of overall size r=jnicol
authorGeorge Wright <george@mozilla.com>
Mon, 18 Jul 2016 22:58:26 -0400
changeset 390488 0affe22555807bf2b349f94e115ce0f038ff989d
parent 390487 e954e5a93482182eaddf52c9ba633d20c174c16e
child 390489 c8e5a0703821ec9bb41eb7a80800eed69b61d60d
push id23682
push usergpascutto@mozilla.com
push dateThu, 21 Jul 2016 08:49:11 +0000
reviewersjnicol
bugs1279341
milestone50.0a1
Bug 1279341 - Keep track of unused tiles in determining TextureClientPool size instead of overall size r=jnicol
gfx/layers/client/TextureClientPool.cpp
gfx/layers/client/TextureClientPool.h
gfx/layers/ipc/CompositorBridgeChild.cpp
gfx/thebes/gfxPrefs.h
--- a/gfx/layers/client/TextureClientPool.cpp
+++ b/gfx/layers/client/TextureClientPool.cpp
@@ -14,40 +14,35 @@
 #include "nsComponentManagerUtils.h"
 
 #define TCP_LOG(...)
 //#define TCP_LOG(...) printf_stderr(__VA_ARGS__);
 
 namespace mozilla {
 namespace layers {
 
-static void
-ShrinkCallback(nsITimer *aTimer, void *aClosure)
-{
-  static_cast<TextureClientPool*>(aClosure)->ShrinkToMinimumSize();
-}
 
 TextureClientPool::TextureClientPool(LayersBackend aLayersBackend,
                                      gfx::SurfaceFormat aFormat,
                                      gfx::IntSize aSize,
                                      TextureFlags aFlags,
-                                     uint32_t aMaxTextureClients,
-                                     uint32_t aShrinkTimeoutMsec,
+                                     uint32_t aInitialPoolSize,
+                                     uint32_t aPoolIncrementSize,
                                      TextureForwarder* aAllocator)
   : mBackend(aLayersBackend)
   , mFormat(aFormat)
   , mSize(aSize)
   , mFlags(aFlags)
-  , mMaxTextureClients(aMaxTextureClients)
-  , mShrinkTimeoutMsec(aShrinkTimeoutMsec)
+  , mInitialPoolSize(aInitialPoolSize)
+  , mPoolIncrementSize(aPoolIncrementSize)
   , mOutstandingClients(0)
   , mSurfaceAllocator(aAllocator)
 {
-  TCP_LOG("TexturePool %p created with max size %u and timeout %u\n",
-      this, mMaxTextureClients, aShrinkTimeoutMsec);
+  TCP_LOG("TexturePool %p created with maximum unused texture clients %u\n",
+      this, mInitialPoolSize);
   mTimer = do_CreateInstance("@mozilla.org/timer;1");
   if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
     gfxWarning() << "Creating texture pool for SurfaceFormat::UNKNOWN format";
   }
 }
 
 TextureClientPool::~TextureClientPool()
 {
@@ -87,56 +82,76 @@ static bool TestClientPool(const char* w
 }
 #endif
 
 already_AddRefed<TextureClient>
 TextureClientPool::GetTextureClient()
 {
   // Try to fetch a client from the pool
   RefPtr<TextureClient> textureClient;
-  if (mTextureClients.size()) {
-    mOutstandingClients++;
-    textureClient = mTextureClients.top();
-    mTextureClients.pop();
-#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
-    DebugOnly<bool> ok = TestClientPool("fetch", textureClient, this);
-    MOZ_ASSERT(ok);
-#endif
-    TCP_LOG("TexturePool %p giving %p from pool; size %u outstanding %u\n",
-        this, textureClient.get(), mTextureClients.size(), mOutstandingClients);
-    return textureClient.forget();
+
+  // We initially allocate mInitialPoolSize for our pool. If we run
+  // out of TextureClients, we allocate additional TextureClients to try and keep around
+  // mPoolIncrementSize
+  if (!mTextureClients.size()) {
+    size_t size = mOutstandingClients ? mPoolIncrementSize : mInitialPoolSize;
+    AllocateTextureClients(size);
   }
 
-  // We're increasing the number of outstanding TextureClients without reusing a
-  // client, we may need to free a deferred-return TextureClient.
-  ShrinkToMaximumSize();
-
-  // No unused clients in the pool, create one
-  if (gfxPrefs::ForceShmemTiles()) {
-    // gfx::BackendType::NONE means use the content backend
-    textureClient = TextureClient::CreateForRawBufferAccess(mSurfaceAllocator,
-      mFormat, mSize, gfx::BackendType::NONE,
-      mFlags, ALLOC_DEFAULT);
-  } else {
-    textureClient = TextureClient::CreateForDrawing(mSurfaceAllocator,
-      mFormat, mSize, mBackend, BackendSelector::Content, mFlags);
+  if (!mTextureClients.size()) {
+    // All our allocations failed, return nullptr
+    return nullptr;
   }
 
   mOutstandingClients++;
+  textureClient = mTextureClients.top();
+  mTextureClients.pop();
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
   if (textureClient) {
     textureClient->mPoolTracker = this;
   }
+  DebugOnly<bool> ok = TestClientPool("fetch", textureClient, this);
+  MOZ_ASSERT(ok);
 #endif
-  TCP_LOG("TexturePool %p giving new %p; size %u outstanding %u\n",
+  TCP_LOG("TexturePool %p giving %p from pool; size %u outstanding %u\n",
       this, textureClient.get(), mTextureClients.size(), mOutstandingClients);
+
   return textureClient.forget();
 }
 
 void
+TextureClientPool::AllocateTextureClients(size_t aSize)
+{
+  TCP_LOG("TexturePool %p allocating %u clients, outstanding %u\n",
+      this, aSize, mOutstandingClients);
+
+  RefPtr<TextureClient> newClient;
+  while (mTextureClients.size() < aSize) {
+    if (gfxPrefs::ForceShmemTiles()) {
+      // gfx::BackendType::NONE means use the content backend
+      newClient =
+        TextureClient::CreateForRawBufferAccess(mSurfaceAllocator,
+                                                mFormat, mSize,
+                                                gfx::BackendType::NONE,
+                                                mFlags, ALLOC_DEFAULT);
+    } else {
+      newClient =
+        TextureClient::CreateForDrawing(mSurfaceAllocator,
+                                        mFormat, mSize,
+                                        mBackend,
+                                        BackendSelector::Content,
+                                        mFlags);
+    }
+    if (newClient) {
+      mTextureClients.push(newClient);
+    }
+  }
+}
+
+void
 TextureClientPool::ReturnTextureClient(TextureClient *aClient)
 {
   if (!aClient) {
     return;
   }
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
   DebugOnly<bool> ok = TestClientPool("return", aClient, this);
   MOZ_ASSERT(ok);
@@ -145,24 +160,16 @@ TextureClientPool::ReturnTextureClient(T
   MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
   mOutstandingClients--;
   mTextureClients.push(aClient);
   TCP_LOG("TexturePool %p had client %p returned; size %u outstanding %u\n",
       this, aClient, mTextureClients.size(), mOutstandingClients);
 
   // Shrink down if we're beyond our maximum size
   ShrinkToMaximumSize();
-
-  // Kick off the pool shrinking timer if there are still more unused texture
-  // clients than our desired minimum cache size.
-  if (mTextureClients.size() > sMinCacheSize) {
-    TCP_LOG("TexturePool %p scheduling a shrink-to-min-size\n", this);
-    mTimer->InitWithFuncCallback(ShrinkCallback, this, mShrinkTimeoutMsec,
-                                 nsITimer::TYPE_ONE_SHOT);
-  }
 }
 
 void
 TextureClientPool::ReturnTextureClientDeferred(TextureClient* aClient)
 {
   if (!aClient) {
     return;
   }
@@ -175,90 +182,64 @@ TextureClientPool::ReturnTextureClientDe
   TCP_LOG("TexturePool %p had client %p defer-returned, size %u outstanding %u\n",
       this, aClient, mTextureClientsDeferred.size(), mOutstandingClients);
   ShrinkToMaximumSize();
 }
 
 void
 TextureClientPool::ShrinkToMaximumSize()
 {
-  uint32_t totalClientsOutstanding = mTextureClients.size() + mOutstandingClients;
-  TCP_LOG("TexturePool %p shrinking to max size %u; total outstanding %u\n",
-      this, mMaxTextureClients, totalClientsOutstanding);
-
   // We're over our desired maximum size, immediately shrink down to the
-  // maximum, or zero if we have too many outstanding texture clients.
+  // maximum.
+  //
   // We cull from the deferred TextureClients first, as we can't reuse those
   // until they get returned.
-  while (totalClientsOutstanding > mMaxTextureClients) {
+  uint32_t totalUnusedTextureClients = mTextureClients.size() + mTextureClientsDeferred.size();
+
+  // If we have > mInitialPoolSize outstanding, then we want to keep around
+  // mPoolIncrementSize at a maximum. If we have fewer than mInitialPoolSize
+  // outstanding, then keep around the entire initial pool size.
+  uint32_t targetUnusedClients;
+  if (mOutstandingClients > mInitialPoolSize) {
+    targetUnusedClients = mPoolIncrementSize;
+  } else {
+    targetUnusedClients = mInitialPoolSize;
+  }
+
+  TCP_LOG("TexturePool %p shrinking to maximum unused size %u; total outstanding %u\n",
+      this, targetUnusedClients, mOutstandingClients);
+
+  while (totalUnusedTextureClients > targetUnusedClients) {
     if (mTextureClientsDeferred.size()) {
-      MOZ_ASSERT(mOutstandingClients > 0);
       mOutstandingClients--;
       TCP_LOG("TexturePool %p dropped deferred client %p; %u remaining\n",
           this, mTextureClientsDeferred.front().get(),
           mTextureClientsDeferred.size() - 1);
       mTextureClientsDeferred.pop_front();
     } else {
-      if (!mTextureClients.size()) {
-        // Getting here means we're over our desired number of TextureClients
-        // with none in the pool. This can happen during shutdown, or for
-        // pathological cases, or it could mean that mMaxTextureClients needs
-        // adjusting for whatever device we're running on.
-        TCP_LOG("TexturePool %p encountering pathological case!\n", this);
-        break;
-      }
       TCP_LOG("TexturePool %p dropped non-deferred client %p; %u remaining\n",
           this, mTextureClients.top().get(), mTextureClients.size() - 1);
       mTextureClients.pop();
     }
-    totalClientsOutstanding--;
-  }
-}
-
-void
-TextureClientPool::ShrinkToMinimumSize()
-{
-  ReturnUnlockedClients();
-
-  while (!mTextureClientsDeferred.empty()) {
-    MOZ_ASSERT(mOutstandingClients > 0);
-    mOutstandingClients--;
-    TCP_LOG("TexturePool %p releasing deferred client %p\n",
-        this, mTextureClientsDeferred.front().get());
-    mTextureClientsDeferred.pop_front();
-  }
-
-  TCP_LOG("TexturePool %p shrinking to minimum size %u\n", this, sMinCacheSize);
-  while (mTextureClients.size() > sMinCacheSize) {
-    TCP_LOG("TexturePool %p popped %p; shrunk to %u\n",
-        this, mTextureClients.top().get(), mTextureClients.size() - 1);
-    mTextureClients.pop();
+    totalUnusedTextureClients--;
   }
 }
 
 void
 TextureClientPool::ReturnDeferredClients()
 {
-  TCP_LOG("TexturePool %p returning %u deferred clients to pool\n",
-      this, mTextureClientsDeferred.size());
-
   if (mTextureClientsDeferred.empty()) {
     return;
   }
 
+  TCP_LOG("TexturePool %p returning %u deferred clients to pool\n",
+      this, mTextureClientsDeferred.size());
+
   ReturnUnlockedClients();
   ShrinkToMaximumSize();
-
-  // Kick off the pool shrinking timer if there are still more unused texture
-  // clients than our desired minimum cache size.
-  if (mTextureClients.size() > sMinCacheSize) {
-    TCP_LOG("TexturePool %p kicking off shrink-to-min timer\n", this);
-    mTimer->InitWithFuncCallback(ShrinkCallback, this, mShrinkTimeoutMsec,
-                                 nsITimer::TYPE_ONE_SHOT);
-  }
 }
 
 void
 TextureClientPool::ReturnUnlockedClients()
 {
   for (auto it = mTextureClientsDeferred.begin(); it != mTextureClientsDeferred.end();) {
     MOZ_ASSERT((*it)->GetReadLock()->GetReadCount() >= 1);
     // Last count is held by the lock itself.
@@ -299,13 +280,14 @@ TextureClientPool::Clear()
         this, mTextureClientsDeferred.front().get());
     mTextureClientsDeferred.pop_front();
   }
 }
 
 void TextureClientPool::Destroy()
 {
   Clear();
-  mMaxTextureClients = 0;
+  mInitialPoolSize = 0;
+  mPoolIncrementSize = 0;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/TextureClientPool.h
+++ b/gfx/layers/client/TextureClientPool.h
@@ -43,18 +43,18 @@ class TextureClientPool final : public T
 {
   ~TextureClientPool();
 
 public:
   TextureClientPool(LayersBackend aBackend,
                     gfx::SurfaceFormat aFormat,
                     gfx::IntSize aSize,
                     TextureFlags aFlags,
-                    uint32_t aMaxTextureClients,
-                    uint32_t aShrinkTimeoutMsec,
+                    uint32_t aInitialPoolSize,
+                    uint32_t aPoolIncrementSize,
                     TextureForwarder* aAllocator);
 
   /**
    * Gets an allocated TextureClient of size and format that are determined
    * by the initialisation parameters given to the pool. This will either be
    * a cached client that was returned to the pool, or a newly allocated
    * client if one isn't available.
    *
@@ -71,34 +71,28 @@ public:
 
   /**
    * Return a TextureClient that is not yet ready to be reused, but will be
    * imminently.
    */
   void ReturnTextureClientDeferred(TextureClient *aClient) override;
 
   /**
-   * Attempt to shrink the pool so that there are no more than
-   * mMaxTextureClients clients outstanding.
-   */
-  void ShrinkToMaximumSize();
-
-  /**
-   * Attempt to shrink the pool so that there are no more than sMinCacheSize
-   * unused clients.
-   */
-  void ShrinkToMinimumSize();
-
-  /**
    * Return any clients to the pool that were previously returned in
    * ReturnTextureClientDeferred.
    */
   void ReturnDeferredClients();
 
   /**
+   * Attempt to shrink the pool so that there are no more than
+   * mInitialPoolSize outstanding.
+   */
+  void ShrinkToMaximumSize();
+
+  /**
    * Report that a client retrieved via GetTextureClient() has become
    * unusable, so that it will no longer be tracked.
    */
   virtual void ReportClientLost() override;
 
   /**
    * Calling this will cause the pool to attempt to relinquish any unused
    * clients.
@@ -112,39 +106,41 @@ public:
   /**
    * Clear the pool and put it in a state where it won't recycle any new texture.
    */
   void Destroy();
 
 private:
   void ReturnUnlockedClients();
 
-  // The minimum size of the pool (the number of tiles that will be kept after
-  // shrinking).
-  static const uint32_t sMinCacheSize = 0;
+  /// We maintain a number of unused texture clients for immediate return from
+  /// GetTextureClient(). This will normally be called if there are no
+  /// TextureClients available in the pool, which ideally should only ever
+  /// be at startup.
+  void AllocateTextureClients(size_t aSize);
 
   /// Backend passed to the TextureClient for buffer creation.
   LayersBackend mBackend;
 
   /// Format is passed to the TextureClient for buffer creation.
   gfx::SurfaceFormat mFormat;
 
   /// The width and height of the tiles to be used.
   gfx::IntSize mSize;
 
   /// Flags passed to the TextureClient for buffer creation.
   const TextureFlags mFlags;
 
-  // The maximum number of texture clients managed by this pool that we want
-  // to remain active.
-  uint32_t mMaxTextureClients;
+  // The initial number of unused texture clients to seed the pool with
+  // on construction
+  uint32_t mInitialPoolSize;
 
-  // The time in milliseconds before the pool will be shrunk to the minimum
-  // size after returning a client.
-  uint32_t mShrinkTimeoutMsec;
+  // How many unused texture clients to try and keep around if we go over
+  // the initial allocation
+  uint32_t mPoolIncrementSize;
 
   /// This is a total number of clients in the wild and in the stack of
   /// deferred clients (see below).  So, the total number of clients in
   /// existence is always mOutstandingClients + the size of mTextureClients.
   uint32_t mOutstandingClients;
 
   // On b2g gonk, std::queue might be a better choice.
   // On ICS, fence wait happens implicitly before drawing.
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -924,28 +924,28 @@ CompositorBridgeChild::GetTexturePool(La
     }
   }
 
   mTexturePools.AppendElement(
       new TextureClientPool(aBackend, aFormat,
                             IntSize(gfxPlatform::GetPlatform()->GetTileWidth(),
                                     gfxPlatform::GetPlatform()->GetTileHeight()),
                             aFlags,
-                            gfxPrefs::LayersTileMaxPoolSize(),
-                            gfxPrefs::LayersTileShrinkPoolTimeout(),
+                            gfxPrefs::LayersTileInitialPoolSize(),
+                            gfxPrefs::LayersTilePoolIncrementSize(),
                             this));
 
   return mTexturePools.LastElement();
 }
 
 void
 CompositorBridgeChild::HandleMemoryPressure()
 {
   for (size_t i = 0; i < mTexturePools.Length(); i++) {
-    mTexturePools[i]->ShrinkToMinimumSize();
+    mTexturePools[i]->Clear();
   }
 }
 
 void
 CompositorBridgeChild::ClearTexturePool()
 {
   for (size_t i = 0; i < mTexturePools.Length(); i++) {
     mTexturePools[i]->Clear();
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -479,18 +479,18 @@ private:
   DECL_GFX_PREF(Live, "layers.single-tile.enabled",            LayersSingleTileEnabled, bool, true);
   DECL_GFX_PREF(Once, "layers.stereo-video.enabled",           StereoVideoEnabled, bool, false);
 
   // We allow for configurable and rectangular tile size to avoid wasting memory on devices whose
   // screen size does not align nicely to the default tile size. Although layers can be any size,
   // they are often the same size as the screen, especially for width.
   DECL_GFX_PREF(Once, "layers.tile-width",                     LayersTileWidth, int32_t, 256);
   DECL_GFX_PREF(Once, "layers.tile-height",                    LayersTileHeight, int32_t, 256);
-  DECL_GFX_PREF(Once, "layers.tile-max-pool-size",             LayersTileMaxPoolSize, uint32_t, (uint32_t)50);
-  DECL_GFX_PREF(Once, "layers.tile-shrink-pool-timeout",       LayersTileShrinkPoolTimeout, uint32_t, (uint32_t)1000);
+  DECL_GFX_PREF(Once, "layers.tile-initial-pool-size",         LayersTileInitialPoolSize, uint32_t, (uint32_t)50);
+  DECL_GFX_PREF(Once, "layers.tile-pool-increment-size",       LayersTilePoolIncrementSize, uint32_t, (uint32_t)10);
   DECL_GFX_PREF(Once, "layers.tiles.adjust",                   LayersTilesAdjust, bool, true);
   DECL_GFX_PREF(Once, "layers.tiles.edge-padding",             TileEdgePaddingEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.tiles.fade-in.enabled",          LayerTileFadeInEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.tiles.fade-in.duration-ms",      LayerTileFadeInDuration, uint32_t, 250);
   DECL_GFX_PREF(Live, "layers.transaction.warning-ms",         LayerTransactionWarning, uint32_t, 200);
   DECL_GFX_PREF(Once, "layers.uniformity-info",                UniformityInfo, bool, false);
   DECL_GFX_PREF(Once, "layers.use-image-offscreen-surfaces",   UseImageOffscreenSurfaces, bool, true);