Bug 1502789 - Fix Texture release timing of AsyncImagePipelineManager::ProcessPipelineUpdates() r=nical
authorsotaro <sotaro.ikeda.g@gmail.com>
Wed, 31 Oct 2018 14:19:41 +0900
changeset 443672 5c3e0fc819ba0da8cdcd85eb16cedcb9d2c8f81e
parent 443671 b357da105c49a8ad0d444a8113ea211e6efbc66e
child 443673 cc2ab4017c274c13874aa400e50c888f71e61d59
push id109421
push usersikeda@mozilla.com
push dateWed, 31 Oct 2018 05:19:55 +0000
treeherdermozilla-inbound@5c3e0fc819ba [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1502789
milestone65.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 1502789 - Fix Texture release timing of AsyncImagePipelineManager::ProcessPipelineUpdates() r=nical
gfx/layers/wr/AsyncImagePipelineManager.cpp
gfx/layers/wr/AsyncImagePipelineManager.h
gfx/webrender_bindings/RenderThread.cpp
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -31,16 +31,17 @@ AsyncImagePipelineManager::AsyncImagePip
 AsyncImagePipelineManager::AsyncImagePipelineManager(already_AddRefed<wr::WebRenderAPI>&& aApi)
  : mApi(aApi)
  , mIdNamespace(mApi->GetNamespace())
  , mResourceId(0)
  , mAsyncImageEpoch{0}
  , mWillGenerateFrame(false)
  , mDestroyed(false)
  , mUpdatesLock("UpdatesLock")
+ , mUpdatesCount(0)
 {
   MOZ_COUNT_CTOR(AsyncImagePipelineManager);
 }
 
 AsyncImagePipelineManager::~AsyncImagePipelineManager()
 {
   MOZ_COUNT_DTOR(AsyncImagePipelineManager);
 }
@@ -537,62 +538,101 @@ AsyncImagePipelineManager::HoldExternalI
     SharedSurfacesParent::Release(aImageId);
     return;
   }
 
   holder->mExternalImages.push(ForwardingExternalImage(aEpoch, aImageId));
 }
 
 void
-AsyncImagePipelineManager::NotifyPipelinesUpdated(wr::WrPipelineInfo aInfo)
+AsyncImagePipelineManager::NotifyPipelinesUpdated(wr::WrPipelineInfo aInfo, bool aRender)
 {
   // This is called on the render thread, so we just stash the data into
-  // mUpdatesQueue and process it later on the compositor thread.
+  // UpdatesQueue and process it later on the compositor thread.
   MOZ_ASSERT(wr::RenderThread::IsInRenderThread());
 
-  MutexAutoLock lock(mUpdatesLock);
+  // Increment the count when render happens.
+  // XXX The count is going to be used to delay releasing TextureHosts for multiple rendering.
+  // See Bug 1500017.
+  uint64_t currCount = aRender ? ++mUpdatesCount : mUpdatesCount;
+  auto updates = MakeUnique<PipelineUpdates>(currCount, aRender);
+
   for (uintptr_t i = 0; i < aInfo.epochs.length; i++) {
-    mUpdatesQueue.push(std::make_pair(
+    updates->mQueue.emplace(std::make_pair(
         aInfo.epochs.data[i].pipeline_id,
         Some(aInfo.epochs.data[i].epoch)));
   }
   for (uintptr_t i = 0; i < aInfo.removed_pipelines.length; i++) {
-    mUpdatesQueue.push(std::make_pair(
+    updates->mQueue.emplace(std::make_pair(
         aInfo.removed_pipelines.data[i],
         Nothing()));
   }
+
+  {
+    // Scope lock to push UpdatesQueue to mUpdatesQueues.
+    MutexAutoLock lock(mUpdatesLock);
+    mUpdatesQueues.push(std::move(updates));
+  }
+
+  if (!aRender) {
+    // Do not post ProcessPipelineUpdate when rendering did not happen.
+    return;
+  }
+
   // Queue a runnable on the compositor thread to process the queue
   layers::CompositorThreadHolder::Loop()->PostTask(
       NewRunnableMethod("ProcessPipelineUpdates",
                         this,
                         &AsyncImagePipelineManager::ProcessPipelineUpdates));
 }
 
 void
 AsyncImagePipelineManager::ProcessPipelineUpdates()
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   if (mDestroyed) {
     return;
   }
 
-  while (true) {
-    wr::PipelineId pipelineId;
-    Maybe<wr::Epoch> epoch;
+  UniquePtr<PipelineUpdates> updates;
 
-    { // scope lock to extract one item from the queue
+  while (true) {
+    // Clear updates if it is empty. It is a preparation for next PipelineUpdates handling.
+    if (updates && updates->mQueue.empty()) {
+      updates = nullptr;
+    }
+
+    // Get new PipelineUpdates if necessary.
+    if (!updates) {
+      // Scope lock to extract UpdatesQueue from mUpdatesQueues.
       MutexAutoLock lock(mUpdatesLock);
-      if (mUpdatesQueue.empty()) {
+      if (mUpdatesQueues.empty()) {
+        // No more PipelineUpdates to process for now.
         break;
       }
-      pipelineId = mUpdatesQueue.front().first;
-      epoch = mUpdatesQueue.front().second;
-      mUpdatesQueue.pop();
+      // Check if PipelineUpdates is ready to process.
+      uint64_t currCount = mUpdatesCount;
+      if (mUpdatesQueues.front()->NeedsToWait(currCount)) {
+        // PipelineUpdates is not ready for processing for now.
+        break;
+      }
+      updates = std::move(mUpdatesQueues.front());
+      mUpdatesQueues.pop();
     }
+    MOZ_ASSERT(updates);
+
+    if (updates->mQueue.empty()) {
+      // Try next PipelineUpdates.
+      continue;
+    }
+
+    wr::PipelineId pipelineId = updates->mQueue.front().first;
+    Maybe<wr::Epoch> epoch = updates->mQueue.front().second;
+    updates->mQueue.pop();
 
     if (epoch.isSome()) {
       ProcessPipelineRendered(pipelineId, *epoch);
     } else {
       ProcessPipelineRemoved(pipelineId);
     }
   }
 }
--- a/gfx/layers/wr/AsyncImagePipelineManager.h
+++ b/gfx/layers/wr/AsyncImagePipelineManager.h
@@ -51,17 +51,17 @@ public:
 
   void HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHost* aTexture);
   void HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, WebRenderTextureHostWrapper* aWrTextureWrapper);
   void HoldExternalImage(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, const wr::ExternalImageId& aImageId);
 
   // This is called from the Renderer thread to notify this class about the
   // pipelines in the most recently completed render. A copy of the update
   // information is put into mUpdatesQueue.
-  void NotifyPipelinesUpdated(wr::WrPipelineInfo aInfo);
+  void NotifyPipelinesUpdated(wr::WrPipelineInfo aInfo, bool aRender);
 
   // This is run on the compositor thread to process mUpdatesQueue. We make
   // this a public entry point because we need to invoke it from other places.
   void ProcessPipelineUpdates();
 
   TimeStamp GetCompositionTime() const {
     return mCompositionTime;
   }
@@ -233,20 +233,41 @@ private:
   // change its rendering at this time. In order not to miss it, we composite
   // on every vsync until this time occurs (this is the latest such time).
   TimeStamp mCompositeUntilTime;
 
   nsTArray<ImageCompositeNotificationInfo> mImageCompositeNotifications;
 
   // The lock that protects mUpdatesQueue
   Mutex mUpdatesLock;
-  // Queue to store rendered pipeline epoch information. This is populated from
-  // the Renderer thread after a render, and is read from the compositor thread
-  // to free resources (e.g. textures) that are no longer needed. Each entry
-  // in the queue is a pair that holds the pipeline id and Some(x) for
-  // a render of epoch x, or Nothing() for a removed pipeline.
-  std::queue<std::pair<wr::PipelineId, Maybe<wr::Epoch>>> mUpdatesQueue;
+  // Used for checking if PipelineUpdates could be processed.
+  Atomic<uint64_t> mUpdatesCount;
+  struct PipelineUpdates {
+    PipelineUpdates(const uint64_t aUpdatesCount, const bool aRendered)
+      : mUpdatesCount(aUpdatesCount)
+      , mRendered(aRendered)
+    {}
+    bool NeedsToWait(const uint64_t aUpdatesCount) {
+      MOZ_ASSERT(mUpdatesCount <= aUpdatesCount);
+      // XXX Add support of delaying releasing TextureHosts for multiple rendering.
+      // See Bug 1500017.
+      if (mUpdatesCount == aUpdatesCount && !mRendered) {
+        // RenderTextureHosts related to this might be still used by GPU.
+        return true;
+      }
+      return false;
+    }
+    const uint64_t mUpdatesCount;
+    const bool mRendered;
+    // Queue to store rendered pipeline epoch information. This is populated from
+    // the Renderer thread after a render, and is read from the compositor thread
+    // to free resources (e.g. textures) that are no longer needed. Each entry
+    // in the queue is a pair that holds the pipeline id and Some(x) for
+    // a render of epoch x, or Nothing() for a removed pipeline.
+    std::queue<std::pair<wr::PipelineId, Maybe<wr::Epoch>>> mQueue;
+  };
+  std::queue<UniquePtr<PipelineUpdates>> mUpdatesQueues;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_WEBRENDERCOMPOSITABLE_HOLDER_H */
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -389,17 +389,17 @@ RenderThread::UpdateAndRender(wr::Window
   RefPtr<layers::AsyncImagePipelineManager> pipelineMgr =
       renderer->GetCompositorBridge()->GetAsyncImagePipelineManager();
   // pipelineMgr should always be non-null here because it is only nulled out
   // after the WebRenderAPI instance for the CompositorBridgeParent is
   // destroyed, and that destruction blocks until the renderer thread has
   // removed the relevant renderer. And after that happens we should never reach
   // this code at all; it would bail out at the mRenderers.find check above.
   MOZ_ASSERT(pipelineMgr);
-  pipelineMgr->NotifyPipelinesUpdated(info);
+  pipelineMgr->NotifyPipelinesUpdated(info, aRender);
 
   layers::CompositorThreadHolder::Loop()->PostTask(NewRunnableFunction(
     "NotifyDidRenderRunnable",
     &NotifyDidRender,
     renderer->GetCompositorBridge(),
     info,
     aStartTime, end,
     aRender