# HG changeset patch # User Kartikaya Gupta # Date 1523377795 14400 # Node ID 1c9e68c30a817cc840078999828d42d6dc713021 # Parent 983aebb0d5ef44acf3b3c12d3eee15e9b20cc7bb 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 diff --git a/gfx/layers/apz/public/APZUpdater.h b/gfx/layers/apz/public/APZUpdater.h --- 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 +#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& aApz); bool HasTreeManager(const RefPtr& 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 aTask); protected: virtual ~APZUpdater(); + bool UsingWebRenderUpdaterThread() const; + static already_AddRefed GetUpdater(const wr::WrWindowId& aWindowId); + private: RefPtr 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 sWindowIdMap; Maybe 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 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 diff --git a/gfx/layers/apz/src/APZUpdater.cpp b/gfx/layers/apz/src/APZUpdater.cpp --- 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 APZUpdater::sWindowIdMap; APZUpdater::APZUpdater(const RefPtr& 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 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 self = this; RunOnUpdaterThread(NS_NewRunnableFunction( "APZUpdater::ClearTree", [=]() { @@ -230,55 +243,96 @@ APZUpdater::AssertOnUpdaterThread() } } void APZUpdater::RunOnUpdaterThread(already_AddRefed aTask) { RefPtr 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 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::GetUpdater(const wr::WrWindowId& aWindowId) +{ + RefPtr 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