Bug 1289525 - Shrink the TextureClientPool to maximum size after a short delay, and clear the pool after a longer delay. r=jrmuizel, a=ritu
authorGeorge Wright <george@mozilla.com>
Fri, 29 Jul 2016 14:14:09 -0400
changeset 349883 a1d6e5e2864b11d5224c4002b4f8b9b41d2992cc
parent 349882 40076409f9b525ab4414e2919bfad49566fe55ae
child 349884 45e8335e629e371a21f4c1effe65c012956e1c45
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel, ritu
bugs1289525
milestone50.0a2
Bug 1289525 - Shrink the TextureClientPool to maximum size after a short delay, and clear the pool after a longer delay. r=jrmuizel, a=ritu
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,45 +14,68 @@
 #include "nsComponentManagerUtils.h"
 
 #define TCP_LOG(...)
 //#define TCP_LOG(...) printf_stderr(__VA_ARGS__);
 
 namespace mozilla {
 namespace layers {
 
+// We want to shrink to our maximum size of N unused tiles
+// after a timeout to allow for short-term budget requirements
+static void
+ShrinkCallback(nsITimer *aTimer, void *aClosure)
+{
+  static_cast<TextureClientPool*>(aClosure)->ShrinkToMaximumSize();
+}
+
+// After a certain amount of inactivity, let's clear the pool so that
+// we don't hold onto tiles needlessly. In general, allocations are
+// cheap enough that re-allocating isn't an issue unless we're allocating
+// at an inopportune time (e.g. mid-animation).
+static void
+ClearCallback(nsITimer *aTimer, void *aClosure)
+{
+  static_cast<TextureClientPool*>(aClosure)->Clear();
+}
 
 TextureClientPool::TextureClientPool(LayersBackend aLayersBackend,
                                      gfx::SurfaceFormat aFormat,
                                      gfx::IntSize aSize,
                                      TextureFlags aFlags,
+                                     uint32_t aShrinkTimeoutMsec,
+                                     uint32_t aClearTimeoutMsec,
                                      uint32_t aInitialPoolSize,
                                      uint32_t aPoolUnusedSize,
                                      TextureForwarder* aAllocator)
   : mBackend(aLayersBackend)
   , mFormat(aFormat)
   , mSize(aSize)
   , mFlags(aFlags)
+  , mShrinkTimeoutMsec(aShrinkTimeoutMsec)
+  , mClearTimeoutMsec(aClearTimeoutMsec)
   , mInitialPoolSize(aInitialPoolSize)
   , mPoolUnusedSize(aPoolUnusedSize)
   , mOutstandingClients(0)
   , mSurfaceAllocator(aAllocator)
   , mDestroyed(false)
 {
   TCP_LOG("TexturePool %p created with maximum unused texture clients %u\n",
       this, mInitialPoolSize);
-  mTimer = do_CreateInstance("@mozilla.org/timer;1");
+  mShrinkTimer = do_CreateInstance("@mozilla.org/timer;1");
+  mClearTimer = do_CreateInstance("@mozilla.org/timer;1");
   if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
     gfxWarning() << "Creating texture pool for SurfaceFormat::UNKNOWN format";
   }
 }
 
 TextureClientPool::~TextureClientPool()
 {
-  mTimer->Cancel();
+  mShrinkTimer->Cancel();
+  mClearTimer->Cancel();
 }
 
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
 static bool TestClientPool(const char* what,
                            TextureClient* aClient,
                            TextureClientPool* aPool)
 {
   if (!aClient || !aPool) {
@@ -141,51 +164,70 @@ TextureClientPool::AllocateTextureClient
   }
 
   if (newClient) {
     mTextureClients.push(newClient);
   }
 }
 
 void
+TextureClientPool::ResetTimers()
+{
+  // Shrink down if we're beyond our maximum size
+  if (mShrinkTimeoutMsec &&
+      mTextureClients.size() + mTextureClientsDeferred.size() > mPoolUnusedSize) {
+    TCP_LOG("TexturePool %p scheduling a shrink-to-max-size\n", this);
+    mShrinkTimer->InitWithFuncCallback(ShrinkCallback, this, mShrinkTimeoutMsec,
+                                       nsITimer::TYPE_ONE_SHOT);
+  }
+
+  // Clear pool after a period of inactivity to reduce memory consumption
+  if (mClearTimeoutMsec) {
+    TCP_LOG("TexturePool %p scheduling a clear\n", this);
+    mClearTimer->InitWithFuncCallback(ClearCallback, this, mClearTimeoutMsec,
+                                      nsITimer::TYPE_ONE_SHOT);
+  }
+}
+
+void
 TextureClientPool::ReturnTextureClient(TextureClient *aClient)
 {
   if (!aClient || mDestroyed) {
     return;
   }
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
   DebugOnly<bool> ok = TestClientPool("return", aClient, this);
   MOZ_ASSERT(ok);
 #endif
   // Add the client to the pool:
   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();
+  ResetTimers();
 }
 
 void
 TextureClientPool::ReturnTextureClientDeferred(TextureClient* aClient)
 {
   if (!aClient || mDestroyed) {
     return;
   }
   MOZ_ASSERT(aClient->GetReadLock());
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
   DebugOnly<bool> ok = TestClientPool("defer", aClient, this);
   MOZ_ASSERT(ok);
 #endif
   mTextureClientsDeferred.push_back(aClient);
   TCP_LOG("TexturePool %p had client %p defer-returned, size %u outstanding %u\n",
       this, aClient, mTextureClientsDeferred.size(), mOutstandingClients);
-  ShrinkToMaximumSize();
+
+  ResetTimers();
 }
 
 void
 TextureClientPool::ShrinkToMaximumSize()
 {
   // We're over our desired maximum size, immediately shrink down to the
   // maximum.
   //
--- a/gfx/layers/client/TextureClientPool.h
+++ b/gfx/layers/client/TextureClientPool.h
@@ -43,16 +43,18 @@ class TextureClientPool final : public T
 {
   ~TextureClientPool();
 
 public:
   TextureClientPool(LayersBackend aBackend,
                     gfx::SurfaceFormat aFormat,
                     gfx::IntSize aSize,
                     TextureFlags aFlags,
+                    uint32_t aShrinkTimeoutMsec,
+                    uint32_t aClearTimeoutMsec,
                     uint32_t aInitialPoolSize,
                     uint32_t aPoolUnusedSize,
                     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
@@ -109,28 +111,39 @@ public:
   void Destroy();
 
 private:
   void ReturnUnlockedClients();
 
   /// Allocate a single TextureClient to be returned from the pool.
   void AllocateTextureClient();
 
+  /// Reset and/or initialise timers for shrinking/clearing the pool.
+  void ResetTimers();
+
   /// 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;
 
+  /// How long to wait after a TextureClient is returned before trying
+  /// to shrink the pool to its maximum size of mPoolUnusedSize.
+  uint32_t mShrinkTimeoutMsec;
+
+  /// How long to wait after a TextureClient is returned before trying
+  /// to clear the pool.
+  uint32_t mClearTimeoutMsec;
+
   // The initial number of unused texture clients to seed the pool with
   // on construction
   uint32_t mInitialPoolSize;
 
   // How many unused texture clients to try and keep around if we go over
   // the initial allocation
   uint32_t mPoolUnusedSize;
 
@@ -140,17 +153,18 @@ private:
   uint32_t mOutstandingClients;
 
   // On b2g gonk, std::queue might be a better choice.
   // On ICS, fence wait happens implicitly before drawing.
   // Since JB, fence wait happens explicitly when fetching a client from the pool.
   std::stack<RefPtr<TextureClient> > mTextureClients;
 
   std::list<RefPtr<TextureClient>> mTextureClientsDeferred;
-  RefPtr<nsITimer> mTimer;
+  RefPtr<nsITimer> mShrinkTimer;
+  RefPtr<nsITimer> mClearTimer;
   // This mSurfaceAllocator owns us, so no need to hold a ref to it
   TextureForwarder* mSurfaceAllocator;
 
   // Keep track of whether this pool has been destroyed or not. If it has,
   // we won't accept returns of TextureClients anymore, and the refcounting
   // should take care of their destruction.
   bool mDestroyed;
 };
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -942,16 +942,18 @@ CompositorBridgeChild::GetTexturePool(La
     }
   }
 
   mTexturePools.AppendElement(
       new TextureClientPool(aBackend, aFormat,
                             IntSize(gfxPlatform::GetPlatform()->GetTileWidth(),
                                     gfxPlatform::GetPlatform()->GetTileHeight()),
                             aFlags,
+                            gfxPrefs::LayersTilePoolShrinkTimeout(),
+                            gfxPrefs::LayersTilePoolClearTimeout(),
                             gfxPrefs::LayersTileInitialPoolSize(),
                             gfxPrefs::LayersTilePoolUnusedSize(),
                             this));
 
   return mTexturePools.LastElement();
 }
 
 void
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -481,16 +481,18 @@ private:
 
   // 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-initial-pool-size",         LayersTileInitialPoolSize, uint32_t, (uint32_t)50);
   DECL_GFX_PREF(Once, "layers.tile-pool-unused-size",          LayersTilePoolUnusedSize, uint32_t, (uint32_t)10);
+  DECL_GFX_PREF(Once, "layers.tile-pool-shrink-timeout",       LayersTilePoolShrinkTimeout, uint32_t, (uint32_t)50);
+  DECL_GFX_PREF(Once, "layers.tile-pool-clear-timeout",        LayersTilePoolClearTimeout, uint32_t, (uint32_t)5000);
   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);