Bug 1449982 - Add the task queue for running things on the updater thread. r=botond
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 10 Apr 2018 12:29:55 -0400
changeset 412617 0c1ab4c3acd46a69c529bd0dffba031e046c854d
parent 412616 1c9e68c30a817cc840078999828d42d6dc713021
child 412618 3eab97d46f5ae899f2e5567b8922fa4e276c535d
push id33813
push userccoroiu@mozilla.com
push dateTue, 10 Apr 2018 21:54:55 +0000
treeherdermozilla-central@d42671c2e69d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1449982
milestone61.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 1449982 - Add the task queue for running things on the updater thread. r=botond If we're using the WR scene builder thread as the updater thread, we cannot just post a task to its message loop, because it's a rust thread that doesn't have a message loop. Instead, we put these tasks into a queue that we will process in callbacks that are invoked from the updater thread. This patch just adds the basic queue machinery, it will get more complicated in a future patch because we need to gate the tasks to make sure they don't run too early. MozReview-Commit-ID: 8DCYbsQ5cBC
gfx/layers/apz/public/APZUpdater.h
gfx/layers/apz/src/APZCTreeManager.h
gfx/layers/apz/src/APZUpdater.cpp
--- a/gfx/layers/apz/public/APZUpdater.h
+++ b/gfx/layers/apz/public/APZUpdater.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_APZUpdater_h
 #define mozilla_layers_APZUpdater_h
 
+#include <deque>
 #include <unordered_map>
 
 #include "base/platform_thread.h"   // for PlatformThreadId
 #include "LayersTypes.h"
 #include "mozilla/layers/APZTestData.h"
 #include "mozilla/StaticMutex.h"
 #include "nsThreadUtils.h"
 #include "Units.h"
@@ -47,16 +48,17 @@ public:
 
   /**
    * This function is invoked from rust on the scene builder thread when it
    * is created. It effectively tells the APZUpdater "the current thread is
    * the updater thread for this window id" and allows APZUpdater to remember
    * which thread it is.
    */
   static void SetUpdaterThread(const wr::WrWindowId& aWindowId);
+  static void ProcessPendingTasks(const wr::WrWindowId& aWindowId);
 
   void ClearTree();
   void UpdateFocusState(LayersId aRootLayerTreeId,
                         LayersId aOriginatingLayersId,
                         const FocusTarget& aFocusTarget);
   void UpdateHitTestingTree(LayersId aRootLayerTreeId,
                             Layer* aRoot,
                             bool aIsFirstPaint,
@@ -135,14 +137,21 @@ private:
   // from multiple threads during normal operation (after initialization)
   // without locking should be fine.
   Maybe<PlatformThreadId> mUpdaterThreadId;
 #ifdef DEBUG
   // This flag is used to ensure that we don't ever try to do updater-thread
   // stuff before the updater thread has been properly initialized.
   mutable bool mUpdaterThreadQueried;
 #endif
+
+  // Lock used to protect mUpdaterQueue
+  Mutex mQueueLock;
+  // Holds a FIFO queue of tasks to be run on the updater thread,
+  // when the updater thread is a WebRender thread, since it won't have a
+  // message loop we can dispatch to.
+  std::deque<RefPtr<Runnable>> mUpdaterQueue;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_APZUpdater_h
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -528,16 +528,20 @@ public:
       bool aScrollbarIsDescendant,
       AsyncTransformComponentMatrix* aOutClipTransform);
 
   // Assert that the current thread is the sampler thread for this APZCTM.
   void AssertOnSamplerThread();
   // Assert that the current thread is the updater thread for this APZCTM.
   void AssertOnUpdaterThread();
 
+  // Returns a pointer to the WebRenderAPI for the root layers id this APZCTreeManager
+  // is for. This might be null (for example, if WebRender is not enabled).
+  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI() const;
+
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~APZCTreeManager();
 
   APZSampler* GetSampler() const;
   APZUpdater* GetUpdater() const;
 
   // Protected hooks for gtests subclass
@@ -682,20 +686,16 @@ private:
                      const AsyncPanZoomController* apzc);
 
   void NotifyScrollbarDragRejected(const ScrollableLayerGuid& aGuid) const;
   void NotifyAutoscrollRejected(const ScrollableLayerGuid& aGuid) const;
 
   // Requires the caller to hold mTreeLock.
   LayerToParentLayerMatrix4x4 ComputeTransformForNode(const HitTestingTreeNode* aNode) const;
 
-  // Returns a pointer to the WebRenderAPI for the root layers id this APZCTreeManager
-  // is for. This might be null (for example, if WebRender is not enabled).
-  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI() const;
-
   // Returns a pointer to the GeckoContentController for the given layers id.
   already_AddRefed<GeckoContentController> GetContentController(LayersId aLayersId) const;
 
 protected:
   /* The input queue where input events are held until we know enough to
    * figure out where they're going. Protected so gtests can access it.
    */
   RefPtr<InputQueue> mInputQueue;
--- a/gfx/layers/apz/src/APZUpdater.cpp
+++ b/gfx/layers/apz/src/APZUpdater.cpp
@@ -8,30 +8,32 @@
 
 #include "APZCTreeManager.h"
 #include "AsyncPanZoomController.h"
 #include "base/task.h"
 #include "mozilla/layers/APZThreadUtils.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/SynchronousTask.h"
 #include "mozilla/layers/WebRenderScrollData.h"
+#include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
 StaticMutex APZUpdater::sWindowIdLock;
 std::unordered_map<uint64_t, APZUpdater*> APZUpdater::sWindowIdMap;
 
 
 APZUpdater::APZUpdater(const RefPtr<APZCTreeManager>& aApz)
   : mApz(aApz)
 #ifdef DEBUG
   , mUpdaterThreadQueried(false)
 #endif
+  , mQueueLock("APZUpdater::QueueLock")
 {
   MOZ_ASSERT(aApz);
   mApz->SetUpdater(this);
 }
 
 APZUpdater::~APZUpdater()
 {
   mApz->SetUpdater(nullptr);
@@ -63,16 +65,28 @@ APZUpdater::SetUpdaterThread(const wr::W
 {
   if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
     // Ensure nobody tried to use the updater thread before this point.
     MOZ_ASSERT(!updater->mUpdaterThreadQueried);
     updater->mUpdaterThreadId = Some(PlatformThread::CurrentId());
   }
 }
 
+/*static*/ void
+APZUpdater::ProcessPendingTasks(const wr::WrWindowId& aWindowId)
+{
+  if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
+    MutexAutoLock lock(updater->mQueueLock);
+    for (auto task : updater->mUpdaterQueue) {
+      task->Run();
+    }
+    updater->mUpdaterQueue.clear();
+  }
+}
+
 void
 APZUpdater::ClearTree()
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZUpdater> self = this;
   RunOnUpdaterThread(NS_NewRunnableFunction(
     "APZUpdater::ClearTree",
     [=]() {
@@ -249,18 +263,36 @@ APZUpdater::RunOnUpdaterThread(already_A
   RefPtr<Runnable> task = aTask;
 
   if (IsUpdaterThread()) {
     task->Run();
     return;
   }
 
   if (UsingWebRenderUpdaterThread()) {
-    // TODO
-    NS_WARNING("Dropping task posted to updater thread");
+    // If the updater thread is a WebRender thread, and we're not on it
+    // right now, save the task in the queue. We will run tasks from the queue
+    // during the callback from the updater thread, which we trigger by the
+    // call to WakeSceneBuilder.
+
+    { // scope lock
+      MutexAutoLock lock(mQueueLock);
+      mUpdaterQueue.push_back(task);
+    }
+    RefPtr<wr::WebRenderAPI> api = mApz->GetWebRenderAPI();
+    if (api) {
+      api->WakeSceneBuilder();
+    } else {
+      // Not sure if this can happen, but it might be possible. If it does,
+      // the task is in the queue, but if we didn't get a WebRenderAPI it
+      // might never run, or it might run later if we manage to get a
+      // WebRenderAPI later. For now let's just emit a warning, this can
+      // probably be upgraded to an assert later.
+      NS_WARNING("Possibly dropping task posted to updater thread");
+    }
     return;
   }
 
   if (MessageLoop* loop = CompositorThreadHolder::Loop()) {
     loop->PostTask(task.forget());
   } else {
     // Could happen during startup
     NS_WARNING("Dropping task posted to updater thread");
@@ -339,14 +371,24 @@ void
 apz_post_scene_swap(mozilla::wr::WrWindowId aWindowId,
                     mozilla::wr::WrPipelineInfo* aInfo)
 {
 }
 
 void
 apz_run_updater(mozilla::wr::WrWindowId aWindowId)
 {
+  // This should never get called unless async scene building is enabled.
+  MOZ_ASSERT(gfxPrefs::WebRenderAsyncSceneBuild());
+  mozilla::layers::APZUpdater::ProcessPendingTasks(aWindowId);
 }
 
 void
 apz_deregister_updater(mozilla::wr::WrWindowId aWindowId)
 {
+  // Run anything that's still left. Note that this function gets called even
+  // if async scene building is off, but in that case we don't want to do
+  // anything (because the updater thread will be the compositor thread, and
+  // this will be called on the scene builder thread).
+  if (gfxPrefs::WebRenderAsyncSceneBuild()) {
+    mozilla::layers::APZUpdater::ProcessPendingTasks(aWindowId);
+  }
 }