diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index c8f65d34e29..045d1d6ce9f 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -63,6 +63,7 @@ #ifdef MOZ_ENABLE_PROFILER_SPS #include "ProfilerMarkers.h" #endif +#include "mozilla/VsyncDispatcher.h" #ifdef MOZ_WIDGET_GONK #include "GeckoTouchDispatcher.h" @@ -194,23 +195,20 @@ static void SetThreadPriority() hal::SetCurrentThreadPriority(hal::THREAD_PRIORITY_COMPOSITOR); } -StaticRefPtr sVsyncDispatcher; -VsyncDispatcher::VsyncDispatcher(CompositorParent* aCompositorParent, VsyncSource* aVsyncSource) +CompositorVsyncObserver::CompositorVsyncObserver(CompositorParent* aCompositorParent) : mNeedsComposite(false) , mIsObservingVsync(false) , mCompositorParent(aCompositorParent) - , mVsyncSource(aVsyncSource) , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor") , mCurrentCompositeTask(nullptr) { - sVsyncDispatcher = this; + } -VsyncDispatcher::~VsyncDispatcher() +CompositorVsyncObserver::~CompositorVsyncObserver() { MOZ_ASSERT(NS_IsMainThread()); - sVsyncDispatcher = nullptr; - DisableVsync(); + UnobserveVsync(); mCompositorParent = nullptr; mNeedsComposite = false; CancelCurrentCompositeTask(); @@ -224,30 +222,18 @@ VsyncDispatcher::~VsyncDispatcher() * How many skipped vsync events until we stop listening to vsync events? */ void -VsyncDispatcher::SetNeedsComposite(bool aNeedsComposite) +CompositorVsyncObserver::SetNeedsComposite(bool aNeedsComposite) { MOZ_ASSERT(CompositorParent::IsInCompositorThread()); mNeedsComposite = aNeedsComposite; if (!mIsObservingVsync && mNeedsComposite) { - EnableVsync(); + ObserveVsync(); } } -/* static*/ void -VsyncDispatcher::NotifyVsync(TimeStamp aVsyncTimestamp) -{ - // Called from the vsync dispatch thread - MOZ_ASSERT(!CompositorParent::IsInCompositorThread()); - MOZ_ASSERT(!NS_IsMainThread()); - - if (sVsyncDispatcher) { - sVsyncDispatcher->RunVsync(aVsyncTimestamp); - } -} - -void -VsyncDispatcher::RunVsync(TimeStamp aVsyncTimestamp) +bool +CompositorVsyncObserver::NotifyVsync(TimeStamp aVsyncTimestamp) { // Called from the vsync dispatch thread MOZ_ASSERT(!CompositorParent::IsInCompositorThread()); @@ -256,14 +242,15 @@ VsyncDispatcher::RunVsync(TimeStamp aVsyncTimestamp) MonitorAutoLock lock(mCurrentCompositeTaskMonitor); if (mCurrentCompositeTask == nullptr) { mCurrentCompositeTask = NewRunnableMethod(this, - &VsyncDispatcher::Composite, + &CompositorVsyncObserver::Composite, aVsyncTimestamp); CompositorParent::CompositorLoop()->PostTask(FROM_HERE, mCurrentCompositeTask); } + return true; } void -VsyncDispatcher::CancelCurrentCompositeTask() +CompositorVsyncObserver::CancelCurrentCompositeTask() { MOZ_ASSERT(CompositorParent::IsInCompositorThread() || NS_IsMainThread()); MonitorAutoLock lock(mCurrentCompositeTaskMonitor); @@ -274,7 +261,7 @@ VsyncDispatcher::CancelCurrentCompositeTask() } void -VsyncDispatcher::Composite(TimeStamp aVsyncTimestamp) +CompositorVsyncObserver::Composite(TimeStamp aVsyncTimestamp) { MOZ_ASSERT(CompositorParent::IsInCompositorThread()); { @@ -286,37 +273,43 @@ VsyncDispatcher::Composite(TimeStamp aVsyncTimestamp) mNeedsComposite = false; mCompositorParent->CompositeCallback(aVsyncTimestamp); } else { - DisableVsync(); + // We're getting vsync notifications but we don't need to composite so + // unregister the vsync. + UnobserveVsync(); } DispatchTouchEvents(aVsyncTimestamp); } bool -VsyncDispatcher::NeedsComposite() +CompositorVsyncObserver::NeedsComposite() { MOZ_ASSERT(CompositorParent::IsInCompositorThread()); return mNeedsComposite; } +/** + * Since the vsync thread has its own locks before notifying us of vsync + * we can't register/unregister from the vsync thread. Any other thread is fine + */ void -VsyncDispatcher::EnableVsync() +CompositorVsyncObserver::ObserveVsync() { MOZ_ASSERT(CompositorParent::IsInCompositorThread()); + VsyncDispatcher::GetInstance()->AddCompositorVsyncObserver(this); mIsObservingVsync = true; - mVsyncSource->EnableVsync(); } void -VsyncDispatcher::DisableVsync() +CompositorVsyncObserver::UnobserveVsync() { MOZ_ASSERT(CompositorParent::IsInCompositorThread() || NS_IsMainThread()); + VsyncDispatcher::GetInstance()->RemoveCompositorVsyncObserver(this); mIsObservingVsync = false; - mVsyncSource->DisableVsync(); } void -VsyncDispatcher::DispatchTouchEvents(TimeStamp aVsyncTimestamp) +CompositorVsyncObserver::DispatchTouchEvents(TimeStamp aVsyncTimestamp) { #ifdef MOZ_WIDGET_GONK if (gfxPrefs::TouchResampling()) { @@ -390,12 +383,11 @@ CompositorParent::CompositorParent(nsIWidget* aWidget, mApzcTreeManager = new APZCTreeManager(); } - gfxPlatform::GetPlatform()->ComputeTileSize(); - if (gfxPrefs::VsyncAlignedCompositor()) { - nsRefPtr platformVsyncSource = gfxPlatform::GetPlatform()->GetVsyncSource(); - mVsyncDispatcher = new VsyncDispatcher(this, platformVsyncSource.get()); + mCompositorVsyncObserver = new CompositorVsyncObserver(this); } + + gfxPlatform::GetPlatform()->ComputeTileSize(); } bool @@ -435,7 +427,7 @@ CompositorParent::Destroy() mApzcTreeManager = nullptr; } sIndirectLayerTrees.erase(mRootLayerTreeID); - mVsyncDispatcher = nullptr; + mCompositorVsyncObserver = nullptr; } void @@ -517,8 +509,8 @@ CompositorParent::RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot, bool CompositorParent::RecvFlushRendering() { - if (gfxPrefs::VsyncAlignedCompositor() && mVsyncDispatcher->NeedsComposite()) { - mVsyncDispatcher->SetNeedsComposite(false); + if (gfxPrefs::VsyncAlignedCompositor() && mCompositorVsyncObserver->NeedsComposite()) { + mCompositorVsyncObserver->SetNeedsComposite(false); CancelCurrentCompositeTask(); ForceComposeToTarget(nullptr); } else if (mCurrentCompositeTask) { @@ -655,7 +647,7 @@ void CompositorParent::CancelCurrentCompositeTask() { if (gfxPrefs::VsyncAlignedCompositor()) { - mVsyncDispatcher->CancelCurrentCompositeTask(); + mCompositorVsyncObserver->CancelCurrentCompositeTask(); } else if (mCurrentCompositeTask) { mCurrentCompositeTask->Cancel(); mCurrentCompositeTask = nullptr; @@ -805,7 +797,7 @@ CompositorParent::ScheduleComposition() { MOZ_ASSERT(IsInCompositorThread()); if (gfxPrefs::VsyncAlignedCompositor()) { - mVsyncDispatcher->SetNeedsComposite(true); + mCompositorVsyncObserver->SetNeedsComposite(true); } else { ScheduleSoftwareTimerComposition(); } @@ -1026,7 +1018,7 @@ CompositorParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree, bool needTestComposite = mIsTesting && root && (mCurrentCompositeTask || (gfxPrefs::VsyncAlignedCompositor() && - mVsyncDispatcher->NeedsComposite())); + mCompositorVsyncObserver->NeedsComposite())); if (needTestComposite) { AutoResolveRefLayers resolve(mCompositionManager); bool requestNextFrame = @@ -1060,7 +1052,7 @@ CompositorParent::SetTestSampleTime(LayerTransactionParent* aLayerTree, bool testComposite = mCompositionManager && (mCurrentCompositeTask || (gfxPrefs::VsyncAlignedCompositor() - && mVsyncDispatcher->NeedsComposite())); + && mCompositorVsyncObserver->NeedsComposite())); // Update but only if we were already scheduled to animate if (testComposite) { diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index b84cb40bb72..470c6922ec9 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -35,6 +35,7 @@ #include "nsISupportsImpl.h" #include "nsSize.h" // for nsIntSize #include "ThreadSafeRefcountingWithMainThreadDestruction.h" +#include "mozilla/VsyncDispatcher.h" class CancelableTask; class MessageLoop; @@ -88,53 +89,35 @@ private: friend class CompositorParent; }; -// Controls how and when to enable/disable vsync. -class VsyncSource -{ -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncSource) - virtual void EnableVsync() = 0; - virtual void DisableVsync() = 0; - virtual bool IsVsyncEnabled() = 0; - -protected: - virtual ~VsyncSource() {} -}; // VsyncSource - /** * Manages the vsync (de)registration and tracking on behalf of the * compositor when it need to paint. * Turns vsync notifications into scheduled composites. **/ -class VsyncDispatcher MOZ_FINAL +class CompositorVsyncObserver MOZ_FINAL : public VsyncObserver { - // Cleaned up on main thread along with the compositor. This has the same lifetime - // as the CompositorParent - NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(VsyncDispatcher) friend class CompositorParent; public: - VsyncDispatcher(CompositorParent* aCompositorParent, VsyncSource* aVsyncSource); - static void NotifyVsync(TimeStamp aVsyncTimestamp); - void RunVsync(TimeStamp aVsyncTimestamp); + CompositorVsyncObserver(CompositorParent* aCompositorParent); + virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) MOZ_OVERRIDE; void SetNeedsComposite(bool aSchedule); bool NeedsComposite(); void CancelCurrentCompositeTask(); - + private: - virtual ~VsyncDispatcher(); + virtual ~CompositorVsyncObserver(); void Composite(TimeStamp aVsyncTimestamp); void NotifyCompositeTaskExecuted(); - void EnableVsync(); - void DisableVsync(); + void ObserveVsync(); + void UnobserveVsync(); void DispatchTouchEvents(TimeStamp aVsyncTimestamp); bool mNeedsComposite; bool mIsObservingVsync; nsRefPtr mCompositorParent; - nsRefPtr mVsyncSource; mozilla::Monitor mCurrentCompositeTaskMonitor; CancelableTask* mCurrentCompositeTask; @@ -144,7 +127,7 @@ class CompositorParent MOZ_FINAL : public PCompositorParent, public ShadowLayersManager { NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CompositorParent) - friend class VsyncDispatcher; + friend class CompositorVsyncObserver; public: explicit CompositorParent(nsIWidget* aWidget, @@ -405,7 +388,7 @@ protected: nsRefPtr mApzcTreeManager; nsRefPtr mCompositorThreadHolder; - nsRefPtr mVsyncDispatcher; + nsRefPtr mCompositorVsyncObserver; DISALLOW_EVIL_CONSTRUCTORS(CompositorParent); }; diff --git a/widget/VsyncDispatcher.cpp b/widget/VsyncDispatcher.cpp new file mode 100644 index 00000000000..44168766e72 --- /dev/null +++ b/widget/VsyncDispatcher.cpp @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include "VsyncDispatcher.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/layers/CompositorParent.h" +#include "gfxPrefs.h" + +#ifdef MOZ_ENABLE_PROFILER_SPS +#include "GeckoProfiler.h" +#include "ProfilerMarkers.h" +#endif + +#ifdef MOZ_WIDGET_GONK +#include "GeckoTouchDispatcher.h" +#endif + +using namespace mozilla::layers; + +namespace mozilla { + +StaticRefPtr sVsyncDispatcher; + +/*static*/ VsyncDispatcher* +VsyncDispatcher::GetInstance() +{ + if (!sVsyncDispatcher) { + sVsyncDispatcher = new VsyncDispatcher(); + ClearOnShutdown(&sVsyncDispatcher); + } + + return sVsyncDispatcher; +} + +VsyncDispatcher::VsyncDispatcher() + : mCompositorObserverLock("CompositorObserverLock") +{ + +} + +VsyncDispatcher::~VsyncDispatcher() +{ + MutexAutoLock lock(mCompositorObserverLock); + mCompositorObservers.Clear(); +} + +void +VsyncDispatcher::SetVsyncSource(VsyncSource* aVsyncSource) +{ + mVsyncSource = aVsyncSource; +} + +void +VsyncDispatcher::DispatchTouchEvents(bool aNotifiedCompositors, TimeStamp aVsyncTime) +{ + // Touch events can sometimes start a composite, so make sure we dispatch touches + // even if we don't composite +#ifdef MOZ_WIDGET_GONK + if (!aNotifiedCompositors && gfxPrefs::TouchResampling()) { + GeckoTouchDispatcher::NotifyVsync(aVsyncTime); + } +#endif +} + +void +VsyncDispatcher::NotifyVsync(TimeStamp aVsyncTimestamp) +{ + bool notifiedCompositors = false; +#ifdef MOZ_ENABLE_PROFILER_SPS + if (profiler_is_active()) { + CompositorParent::PostInsertVsyncProfilerMarker(aVsyncTimestamp); + } +#endif + + if (gfxPrefs::VsyncAlignedCompositor()) { + MutexAutoLock lock(mCompositorObserverLock); + notifiedCompositors = NotifyVsyncObservers(aVsyncTimestamp, mCompositorObservers); + } + + DispatchTouchEvents(notifiedCompositors, aVsyncTimestamp); +} + +bool +VsyncDispatcher::NotifyVsyncObservers(TimeStamp aVsyncTimestamp, nsTArray>& aObservers) +{ + // Callers should lock the respective lock for the aObservers before calling this function + for (size_t i = 0; i < aObservers.Length(); i++) { + aObservers[i]->NotifyVsync(aVsyncTimestamp); + } + return !aObservers.IsEmpty(); +} + +void +VsyncDispatcher::AddCompositorVsyncObserver(VsyncObserver* aVsyncObserver) +{ + MOZ_ASSERT(CompositorParent::IsInCompositorThread()); + MutexAutoLock lock(mCompositorObserverLock); + if (!mCompositorObservers.Contains(aVsyncObserver)) { + mCompositorObservers.AppendElement(aVsyncObserver); + } +} + +void +VsyncDispatcher::RemoveCompositorVsyncObserver(VsyncObserver* aVsyncObserver) +{ + MOZ_ASSERT(CompositorParent::IsInCompositorThread() || NS_IsMainThread()); + MutexAutoLock lock(mCompositorObserverLock); + if (mCompositorObservers.Contains(aVsyncObserver)) { + mCompositorObservers.RemoveElement(aVsyncObserver); + } else { + NS_WARNING("Could not delete a compositor vsync observer\n"); + } +} + +} // namespace mozilla diff --git a/widget/VsyncDispatcher.h b/widget/VsyncDispatcher.h new file mode 100644 index 00000000000..797a0b426b8 --- /dev/null +++ b/widget/VsyncDispatcher.h @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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_widget_VsyncDispatcher_h +#define mozilla_widget_VsyncDispatcher_h + +#include "base/message_loop.h" +#include "mozilla/Mutex.h" +#include "nsISupportsImpl.h" +#include "nsTArray.h" +#include "ThreadSafeRefcountingWithMainThreadDestruction.h" + +class MessageLoop; + +namespace mozilla { +class TimeStamp; + +namespace layers { +class CompositorVsyncObserver; +} + +// Controls how and when to enable/disable vsync. +class VsyncSource +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncSource) + virtual void EnableVsync() = 0; + virtual void DisableVsync() = 0; + virtual bool IsVsyncEnabled() = 0; + +protected: + virtual ~VsyncSource() {} +}; // VsyncSource + +class VsyncObserver +{ + // Must be destroyed on main thread since the compositor is as well + NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(VsyncObserver) + +public: + // The method called when a vsync occurs. Return true if some work was done. + // Vsync notifications will occur on the hardware vsync thread + virtual bool NotifyVsync(TimeStamp aVsyncTimestamp) = 0; + +protected: + VsyncObserver() {} + virtual ~VsyncObserver() {} +}; // VsyncObserver + +// VsyncDispatcher is used to dispatch vsync events to the registered observers. +class VsyncDispatcher +{ + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncDispatcher) + +public: + static VsyncDispatcher* GetInstance(); + // Called on the vsync thread when a hardware vsync occurs + // The aVsyncTimestamp can mean different things depending on the platform: + // b2g - The vsync timestamp of the previous frame that was just displayed + // OSX - The vsync timestamp of the upcoming frame + // TODO: Windows / Linux. DOCUMENT THIS WHEN IMPLEMENTING ON THOSE PLATFORMS + // Android: TODO + void NotifyVsync(TimeStamp aVsyncTimestamp); + void SetVsyncSource(VsyncSource* aVsyncSource); + + // Compositor vsync observers must be added/removed on the compositor thread + void AddCompositorVsyncObserver(VsyncObserver* aVsyncObserver); + void RemoveCompositorVsyncObserver(VsyncObserver* aVsyncObserver); + +private: + VsyncDispatcher(); + virtual ~VsyncDispatcher(); + void DispatchTouchEvents(bool aNotifiedCompositors, TimeStamp aVsyncTime); + + // Called on the vsync thread. Returns true if observers were notified + bool NotifyVsyncObservers(TimeStamp aVsyncTimestamp, nsTArray>& aObservers); + + // Can have multiple compositors. On desktop, this is 1 compositor per window + Mutex mCompositorObserverLock; + nsTArray> mCompositorObservers; + nsRefPtr mVsyncSource; +}; // VsyncDispatcher + +} // namespace mozilla + +#endif // __mozilla_widget_VsyncDispatcher_h diff --git a/widget/gonk/HwcComposer2D.cpp b/widget/gonk/HwcComposer2D.cpp index 096f2a7ea0f..227593b3300 100644 --- a/widget/gonk/HwcComposer2D.cpp +++ b/widget/gonk/HwcComposer2D.cpp @@ -237,7 +237,7 @@ HwcComposer2D::Vsync(int aDisplay, nsecs_t aVsyncTimestamp) LOGE("Non-uniform vsync interval: %lld\n", vsyncInterval); } mLastVsyncTime = aVsyncTimestamp; - mozilla::layers::VsyncDispatcher::NotifyVsync(vsyncTime); + VsyncDispatcher::GetInstance()->NotifyVsync(vsyncTime); } // Called on the "invalidator" thread (run from HAL). diff --git a/widget/moz.build b/widget/moz.build index b6a6ebbd00b..26dcab0ca1b 100644 --- a/widget/moz.build +++ b/widget/moz.build @@ -123,6 +123,7 @@ EXPORTS.mozilla += [ 'TextEvents.h', 'TextRange.h', 'TouchEvents.h', + 'VsyncDispatcher.h', 'WidgetUtils.h', ] @@ -156,6 +157,7 @@ UNIFIED_SOURCES += [ 'PuppetWidget.cpp', 'ScreenProxy.cpp', 'SharedWidgetUtils.cpp', + 'VsyncDispatcher.cpp', 'WidgetEventImpl.cpp', 'WidgetUtils.cpp', ]