Bug 1449982 - Implement the WR updater thread registration. r?botond
This lets the APZUpdater know which thread is the actual updater thread.
This is only really used for the thread assertions, but might be useful
for debugging and such as well.
MozReview-Commit-ID: IIDm6Ui3Sh4
--- a/gfx/layers/apz/public/APZUpdater.h
+++ b/gfx/layers/apz/public/APZUpdater.h
@@ -4,16 +4,17 @@
* 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 <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"
namespace mozilla {
@@ -39,16 +40,24 @@ class APZUpdater {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZUpdater)
public:
explicit APZUpdater(const RefPtr<APZCTreeManager>& aApz);
bool HasTreeManager(const RefPtr<APZCTreeManager>& aApz);
void SetWebRenderWindowId(const wr::WindowId& aWindowId);
+ /**
+ * 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);
+
void ClearTree();
void UpdateFocusState(LayersId aRootLayerTreeId,
LayersId aOriginatingLayersId,
const FocusTarget& aFocusTarget);
void UpdateHitTestingTree(LayersId aRootLayerTreeId,
Layer* aRoot,
bool aIsFirstPaint,
LayersId aOriginatingLayersId,
@@ -99,23 +108,41 @@ public:
* thread), that is when the task gets dispatched to the controller thread.
* The controller thread then actually runs the task.
*/
void RunOnControllerThread(already_AddRefed<Runnable> aTask);
protected:
virtual ~APZUpdater();
+ bool UsingWebRenderUpdaterThread() const;
+ static already_AddRefed<APZUpdater> GetUpdater(const wr::WrWindowId& aWindowId);
+
private:
RefPtr<APZCTreeManager> mApz;
// Used to manage the mapping from a WR window id to APZUpdater. These are only
// used if WebRender is enabled. Both sWindowIdMap and mWindowId should only
// be used while holding the sWindowIdLock.
static StaticMutex sWindowIdLock;
static std::unordered_map<uint64_t, APZUpdater*> sWindowIdMap;
Maybe<wr::WrWindowId> mWindowId;
+
+ // If WebRender and async scene building are enabled, this holds the thread id
+ // of the scene builder thread (which is the updater thread) for the
+ // compositor associated with this APZUpdater instance. It may be populated
+ // even if async scene building is not enabled, but in that case we don't
+ // care about the contents.
+ // This is written to once during init and never cleared, and so reading it
+ // 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
};
} // namespace layers
} // namespace mozilla
#endif // mozilla_layers_APZUpdater_h
--- a/gfx/layers/apz/src/APZUpdater.cpp
+++ b/gfx/layers/apz/src/APZUpdater.cpp
@@ -19,16 +19,19 @@ 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
{
MOZ_ASSERT(aApz);
mApz->SetUpdater(this);
}
APZUpdater::~APZUpdater()
{
mApz->SetUpdater(nullptr);
@@ -50,16 +53,26 @@ void
APZUpdater::SetWebRenderWindowId(const wr::WindowId& aWindowId)
{
StaticMutexAutoLock lock(sWindowIdLock);
MOZ_ASSERT(!mWindowId);
mWindowId = Some(aWindowId);
sWindowIdMap[wr::AsUint64(aWindowId)] = this;
}
+/*static*/ void
+APZUpdater::SetUpdaterThread(const wr::WrWindowId& aWindowId)
+{
+ 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());
+ }
+}
+
void
APZUpdater::ClearTree()
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
RefPtr<APZUpdater> self = this;
RunOnUpdaterThread(NS_NewRunnableFunction(
"APZUpdater::ClearTree",
[=]() {
@@ -230,55 +243,96 @@ APZUpdater::AssertOnUpdaterThread()
}
}
void
APZUpdater::RunOnUpdaterThread(already_AddRefed<Runnable> aTask)
{
RefPtr<Runnable> task = aTask;
- MessageLoop* loop = CompositorThreadHolder::Loop();
- if (!loop) {
- // Could happen during startup
+ if (IsUpdaterThread()) {
+ task->Run();
+ return;
+ }
+
+ if (UsingWebRenderUpdaterThread()) {
+ // TODO
NS_WARNING("Dropping task posted to updater thread");
return;
}
- if (IsUpdaterThread()) {
- task->Run();
+ if (MessageLoop* loop = CompositorThreadHolder::Loop()) {
+ loop->PostTask(task.forget());
} else {
- loop->PostTask(task.forget());
+ // Could happen during startup
+ NS_WARNING("Dropping task posted to updater thread");
}
}
bool
APZUpdater::IsUpdaterThread()
{
+ if (UsingWebRenderUpdaterThread()) {
+ return PlatformThread::CurrentId() == *mUpdaterThreadId;
+ }
return CompositorThreadHolder::IsInCompositorThread();
}
void
APZUpdater::RunOnControllerThread(already_AddRefed<Runnable> aTask)
{
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
RunOnUpdaterThread(NewRunnableFunction(
"APZUpdater::RunOnControllerThread",
&APZThreadUtils::RunOnControllerThread,
Move(aTask)));
}
+bool
+APZUpdater::UsingWebRenderUpdaterThread() const
+{
+ if (!gfxPrefs::WebRenderAsyncSceneBuild()) {
+ return false;
+ }
+ // If mUpdaterThreadId is not set at the point that this is called, then
+ // that means that either (a) WebRender is not enabled for the compositor
+ // to which this APZUpdater is attached or (b) we are attempting to do
+ // something updater-related before WebRender is up and running. In case
+ // (a) falling back to the compositor thread is correct, and in case (b)
+ // we should stop doing the updater-related thing so early. We catch this
+ // case by setting the mUpdaterThreadQueried flag and asserting on WR
+ // initialization.
+#ifdef DEBUG
+ mUpdaterThreadQueried = true;
+#endif
+ return mUpdaterThreadId.isSome();
+}
+
+/*static*/ already_AddRefed<APZUpdater>
+APZUpdater::GetUpdater(const wr::WrWindowId& aWindowId)
+{
+ RefPtr<APZUpdater> updater;
+ StaticMutexAutoLock lock(sWindowIdLock);
+ auto it = sWindowIdMap.find(wr::AsUint64(aWindowId));
+ if (it != sWindowIdMap.end()) {
+ updater = it->second;
+ }
+ return updater.forget();
+}
+
} // namespace layers
} // namespace mozilla
// Rust callback implementations
void
apz_register_updater(mozilla::wr::WrWindowId aWindowId)
{
+ mozilla::layers::APZUpdater::SetUpdaterThread(aWindowId);
}
void
apz_pre_scene_swap(mozilla::wr::WrWindowId aWindowId)
{
}
void