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
--- 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);
+ }
}