From 529f433f650ce96a38211fe381b88ec222fd6cab Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Wed, 2 Dec 2020 09:47:53 +0000 Subject: [PATCH] Bug 1645528 - Connect nsRefreshDrivers in content processes with a widget-local vsync source r=mattwoodrow,emilio To allow `requestAnimationFrame()` and similar things to run at monitor speed if there is only a window-specific vsyncsource available. This is the case for Wayland and, in the future, EGL/X11. Other backends may opt for window specific vsyncsources as well at some point. The idea is to, instead of using global vsync objects, expose a vsyncsource from nsWindow and use it for refresh drivers. For the content process, move VsyncChild to BrowserChild, so for each Browserchild there is only one VsyncChild to which all refresh drivers connect. IPC in managed either by PBrowser or PBackground. Right now, PBrowser is only used on Wayland, as both PBrowser and the Wayland vsyncsource run on the main thread. Other backends keep using the background thread for now. While at it, make it so that we constantly update the refresh rate. This is necessary for Wayland, but also on other platforms variable refresh rates are increasingly common. Do that by transimitting the vsync rate `SendNotify()`. How to test: - run the Wayland backend - enable `widget.wayland_vsync.enabled` - optionally: disable `privacy.reduceTimerPrecision` - run `vsynctester.com` or `testufo.com` Expected results: Instead of fixed 60Hz, things should update at monitor refresh rate - e.g. 144Hz Original patch by Kenny Levinsen. Depends on D98254 Differential Revision: https://phabricator.services.mozilla.com/D93173 --- dom/ipc/BrowserChild.cpp | 40 +++++ dom/ipc/BrowserChild.h | 11 ++ dom/ipc/BrowserParent.cpp | 27 ++++ dom/ipc/BrowserParent.h | 9 ++ dom/ipc/PBrowser.ipdl | 7 + {layout => dom}/ipc/PVsync.ipdl | 22 ++- {layout => dom}/ipc/VsyncChild.cpp | 66 +++++--- {layout => dom}/ipc/VsyncChild.h | 42 +++-- {layout => dom}/ipc/VsyncParent.cpp | 81 +++++----- {layout => dom}/ipc/VsyncParent.h | 40 +++-- dom/ipc/moz.build | 5 + ipc/glue/BackgroundChildImpl.cpp | 12 +- ipc/glue/BackgroundParentImpl.cpp | 10 +- ipc/glue/BackgroundParentImpl.h | 5 - layout/base/nsPresContext.cpp | 3 - layout/base/nsRefreshDriver.cpp | 230 +++++++++++----------------- layout/base/nsRefreshDriver.h | 12 -- layout/base/nsRefreshObservers.h | 3 - layout/ipc/moz.build | 11 -- 19 files changed, 324 insertions(+), 312 deletions(-) rename {layout => dom}/ipc/PVsync.ipdl (65%) rename {layout => dom}/ipc/VsyncChild.cpp (60%) rename {layout => dom}/ipc/VsyncChild.h (68%) rename {layout => dom}/ipc/VsyncParent.cpp (55%) rename {layout => dom}/ipc/VsyncParent.h (67%) diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp index 38128882d7a8..62399427e484 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp @@ -14,6 +14,7 @@ #include #include +#include "BackgroundChild.h" #include "BrowserParent.h" #include "ClientLayerManager.h" #include "ContentChild.h" @@ -74,6 +75,7 @@ #include "mozilla/gfx/Matrix.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/ipc/PBackgroundChild.h" #include "mozilla/ipc/URIUtils.h" #include "mozilla/layers/APZCCallbackHelper.h" #include "mozilla/layers/APZCTreeManagerChild.h" @@ -142,6 +144,10 @@ # include "mozilla/plugins/PluginWidgetChild.h" #endif +#ifdef MOZ_WAYLAND +# include "nsAppRunner.h" +#endif + #ifdef NS_PRINTING # include "nsIPrintSession.h" # include "nsIPrintSettings.h" @@ -314,6 +320,7 @@ BrowserChild::BrowserChild(ContentChild* aManager, const TabId& aTabId, mDidFakeShow(false), mTriedBrowserInit(false), mOrientation(hal::eScreenOrientation_PortraitPrimary), + mVsyncChild(nullptr), mIgnoreKeyPressEvent(false), mHasValidInnerSize(false), mDestroyed(false), @@ -550,6 +557,8 @@ nsresult BrowserChild::Init(mozIDOMWindowProxy* aParent, NS_ENSURE_SUCCESS(rv, rv); #endif + InitVsyncChild(); + // We've all set up, make sure our visibility state is consistent. This is // important for OOP iframes, which start off as hidden. UpdateVisibility(); @@ -557,6 +566,20 @@ nsresult BrowserChild::Init(mozIDOMWindowProxy* aParent, return NS_OK; } +void BrowserChild::InitVsyncChild() { +#if defined(MOZ_WAYLAND) + if (!IsWaylandDisabled()) { + PVsyncChild* actor = SendPVsyncConstructor(); + mVsyncChild = static_cast(actor); + } else +#endif + { + PBackgroundChild* actorChild = + BackgroundChild::GetOrCreateForCurrentThread(); + mVsyncChild = static_cast(actorChild->SendPVsyncConstructor()); + } +} + void BrowserChild::NotifyTabContextUpdated() { nsCOMPtr docShell = do_GetInterface(WebNavigation()); MOZ_ASSERT(docShell); @@ -2143,6 +2166,23 @@ bool BrowserChild::DeallocPFilePickerChild(PFilePickerChild* actor) { return true; } +PVsyncChild* BrowserChild::AllocPVsyncChild() { + RefPtr actor = new VsyncChild(); + // There still has one ref-count after return, and it will be released in + // DeallocPVsyncChild(). + return actor.forget().take(); +} + +bool BrowserChild::DeallocPVsyncChild(PVsyncChild* aActor) { + MOZ_ASSERT(aActor); + + // This actor already has one ref-count. Please check AllocPVsyncChild(). + RefPtr actor = dont_AddRef(static_cast(aActor)); + return true; +} + +RefPtr BrowserChild::GetVsyncChild() { return mVsyncChild; } + mozilla::ipc::IPCResult BrowserChild::RecvActivateFrameEvent( const nsString& aType, const bool& capture) { nsCOMPtr window = do_GetInterface(WebNavigation()); diff --git a/dom/ipc/BrowserChild.h b/dom/ipc/BrowserChild.h index f93c71fc2107..03dfe3163aff 100644 --- a/dom/ipc/BrowserChild.h +++ b/dom/ipc/BrowserChild.h @@ -29,6 +29,7 @@ #include "mozilla/dom/CoalescedMouseData.h" #include "mozilla/dom/CoalescedWheelData.h" #include "mozilla/dom/MessageManagerCallback.h" +#include "mozilla/dom/VsyncChild.h" #include "mozilla/DOMEventTargetHelper.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventForwards.h" @@ -436,6 +437,12 @@ class BrowserChild final : public nsMessageManagerScriptExecutor, PFilePickerChild* AllocPFilePickerChild(const nsString& aTitle, const int16_t& aMode); + virtual PVsyncChild* AllocPVsyncChild(); + + virtual bool DeallocPVsyncChild(PVsyncChild* aActor); + + RefPtr GetVsyncChild(); + bool DeallocPFilePickerChild(PFilePickerChild* aActor); nsIWebNavigation* WebNavigation() const { return mWebNav; } @@ -747,6 +754,8 @@ class BrowserChild final : public nsMessageManagerScriptExecutor, const mozilla::layers::CompositorOptions& aCompositorOptions); void InitAPZState(); + void InitVsyncChild(); + void DestroyWindow(); void ApplyParentShowInfo(const ParentShowInfo&); @@ -814,6 +823,8 @@ class BrowserChild final : public nsMessageManagerScriptExecutor, bool mTriedBrowserInit; hal::ScreenOrientation mOrientation; + RefPtr mVsyncChild; + bool mIgnoreKeyPressEvent; RefPtr mAPZEventState; SetAllowedTouchBehaviorCallback mSetAllowedTouchBehaviorCallback; diff --git a/dom/ipc/BrowserParent.cpp b/dom/ipc/BrowserParent.cpp index 527d2186c2a0..cc5ba25fd17e 100644 --- a/dom/ipc/BrowserParent.cpp +++ b/dom/ipc/BrowserParent.cpp @@ -129,6 +129,7 @@ #include "mozilla/dom/CrashReport.h" #include "nsISecureBrowserUI.h" #include "nsIXULRuntime.h" +#include "VsyncSource.h" #ifdef XP_WIN # include "mozilla/plugins/PluginWidgetParent.h" @@ -224,6 +225,7 @@ BrowserParent::BrowserParent(ContentParent* aManager, const TabId& aTabId, mCustomCursorHotspotX(0), mCustomCursorHotspotY(0), mVerifyDropLinks{}, + mVsyncParent(nullptr), mDocShellIsActive(false), mMarkedDestroying(false), mIsDestroyed(false), @@ -563,6 +565,8 @@ void BrowserParent::SetOwnerElement(Element* aElement) { mBrowsingContext->SetEmbedderElement(mFrameElement); } + UpdateVsyncParentVsyncSource(); + VisitChildren([aElement](BrowserBridgeParent* aBrowser) { if (auto* browserParent = aBrowser->GetBrowserParent()) { browserParent->SetOwnerElement(aElement); @@ -1346,6 +1350,29 @@ IPCResult BrowserParent::RecvNewWindowGlobal( return IPC_OK(); } +PVsyncParent* BrowserParent::AllocPVsyncParent() { + MOZ_ASSERT(!mVsyncParent); + mVsyncParent = new VsyncParent(); + UpdateVsyncParentVsyncSource(); + return mVsyncParent.get(); +} + +bool BrowserParent::DeallocPVsyncParent(PVsyncParent* aActor) { + MOZ_ASSERT(aActor); + mVsyncParent = nullptr; + return true; +} + +void BrowserParent::UpdateVsyncParentVsyncSource() { + if (!mVsyncParent) { + return; + } + + if (nsCOMPtr widget = GetWidget()) { + mVsyncParent->UpdateVsyncSource(widget->GetVsyncSource()); + } +} + void BrowserParent::SendMouseEvent(const nsAString& aType, float aX, float aY, int32_t aButton, int32_t aClickCount, int32_t aModifiers) { diff --git a/dom/ipc/BrowserParent.h b/dom/ipc/BrowserParent.h index f4161f3dbe09..549daaef72aa 100644 --- a/dom/ipc/BrowserParent.h +++ b/dom/ipc/BrowserParent.h @@ -18,6 +18,7 @@ #include "mozilla/dom/BrowserBridgeParent.h" #include "mozilla/dom/PBrowserParent.h" #include "mozilla/dom/TabContext.h" +#include "mozilla/dom/VsyncParent.h" #include "mozilla/dom/ipc/IdType.h" #include "mozilla/layout/RemoteLayerTreeOwner.h" #include "nsCOMPtr.h" @@ -460,6 +461,10 @@ class BrowserParent final : public PBrowserParent, bool DeallocPColorPickerParent(PColorPickerParent* aColorPicker); + PVsyncParent* AllocPVsyncParent(); + + bool DeallocPVsyncParent(PVsyncParent* aActor); + #ifdef ACCESSIBILITY PDocAccessibleParent* AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&, @@ -872,6 +877,8 @@ class BrowserParent final : public PBrowserParent, void SendRealTouchMoveEvent(WidgetTouchEvent& aEvent, APZData& aAPZData, uint32_t aConsecutiveTouchMoveCount); + void UpdateVsyncParentVsyncSource(); + public: // Unsets sTopLevelWebFocus regardless of its current value. static void UnsetTopLevelWebFocusAll(); @@ -957,6 +964,8 @@ class BrowserParent final : public PBrowserParent, nsTArray mVerifyDropLinks; + RefPtr mVsyncParent; + #ifdef DEBUG int32_t mActiveSupressDisplayportCount = 0; #endif diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 98091cde41c2..1628f1a58e6b 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -20,6 +20,7 @@ include protocol PRemoteLazyInputStream; include protocol PPaymentRequest; include protocol PWindowGlobal; include protocol PBrowserBridge; +include protocol PVsync; include DOMTypes; include NeckoChannelParams; @@ -190,6 +191,7 @@ nested(upto inside_cpow) sync refcounted protocol PBrowser manages PPaymentRequest; manages PWindowGlobal; manages PBrowserBridge; + manages PVsync; both: async AsyncMessage(nsString aMessage, ClonedMessageData aData); @@ -217,6 +219,11 @@ parent: async PPaymentRequest(); + /** + * Create a new Vsync connection for our associated root widget + */ + async PVsync(); + /** * Sends an NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW to be adopted by the * widget's shareable window on the chrome side. Only used on Windows. diff --git a/layout/ipc/PVsync.ipdl b/dom/ipc/PVsync.ipdl similarity index 65% rename from layout/ipc/PVsync.ipdl rename to dom/ipc/PVsync.ipdl index 6cca91cb4bc1..19fdc4164d5f 100644 --- a/layout/ipc/PVsync.ipdl +++ b/dom/ipc/PVsync.ipdl @@ -4,41 +4,37 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ include protocol PBackground; +include protocol PBrowser; include "mozilla/layers/LayersMessageUtils.h"; using class mozilla::TimeStamp from "mozilla/TimeStamp.h"; using mozilla::VsyncEvent from "mozilla/VsyncDispatcher.h"; namespace mozilla { -namespace layout { +namespace dom { /* - * The PVsync is a sub-protocol in PBackground and it is used to notify - * the vsync event from chrome to content process. It also provides the + * The PVsync is a sub-protocol in PBackground or PBrowser and it is used to + * notify the vsync event from chrome to content process. It also provides the * interfaces for content to observe/unobserve vsync event notifications. */ async protocol PVsync { - manager PBackground; + manager PBackground or PBrowser; child: - // Send vsync event from chrome to content process. - prio(high) async Notify(VsyncEvent aVsync) compress; - - // Send the vsync rate to the content process. - async VsyncRate(float aVsyncRate); + // Send vsync event and vsync rate from chrome to content process. + prio(high) async Notify(VsyncEvent aVsync, float aVsyncRate) compress; parent: // Content process use these messages to acquire the vsync event. async Observe(); async Unobserve(); - async RequestVsyncRate(); // This message is never sent. Each PVsync actor will stay alive as long as - // its PBackground manager. + // its PBackground or PBrowser manager. async __delete__(); }; -} // namespace layout +} // namespace dom } // namespace mozilla - diff --git a/layout/ipc/VsyncChild.cpp b/dom/ipc/VsyncChild.cpp similarity index 60% rename from layout/ipc/VsyncChild.cpp rename to dom/ipc/VsyncChild.cpp index 1d358fe8e677..ef0b3524b6cc 100644 --- a/layout/ipc/VsyncChild.cpp +++ b/dom/ipc/VsyncChild.cpp @@ -10,17 +10,17 @@ #include "mozilla/VsyncDispatcher.h" #include "nsThreadUtils.h" -namespace mozilla::layout { +namespace mozilla::dom { VsyncChild::VsyncChild() - : mObservingVsync(false), - mIsShutdown(false), + : mIsShutdown(false), mVsyncRate(TimeDuration::Forever()) { MOZ_ASSERT(NS_IsMainThread()); } VsyncChild::~VsyncChild() { MOZ_ASSERT(NS_IsMainThread()); } +/* do not delete yet so the file history is preserved bool VsyncChild::SendObserve() { MOZ_ASSERT(NS_IsMainThread()); if (!mObservingVsync && !mIsShutdown) { @@ -38,43 +38,61 @@ bool VsyncChild::SendUnobserve() { } return true; } +*/ + +void VsyncChild::AddChildRefreshTimer(VsyncObserver* aVsyncObserver) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mObservers.Contains(aVsyncObserver)); + + if (mIsShutdown) { + return; + } + + if (mObservers.IsEmpty()) { + Unused << PVsyncChild::SendObserve(); + } + mObservers.AppendElement(std::move(aVsyncObserver)); +} + +void VsyncChild::RemoveChildRefreshTimer(VsyncObserver* aVsyncObserver) { + MOZ_ASSERT(NS_IsMainThread()); + if (mIsShutdown) { + return; + } + + if (mObservers.RemoveElement(aVsyncObserver) && mObservers.IsEmpty()) { + Unused << PVsyncChild::SendUnobserve(); + } +} void VsyncChild::ActorDestroy(ActorDestroyReason aActorDestroyReason) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mIsShutdown); mIsShutdown = true; - mObserver = nullptr; + + if (!mObservers.IsEmpty()) { + Unused << PVsyncChild::SendUnobserve(); + } + mObservers.Clear(); } -mozilla::ipc::IPCResult VsyncChild::RecvNotify(const VsyncEvent& aVsync) { +mozilla::ipc::IPCResult VsyncChild::RecvNotify(const VsyncEvent& aVsync, + const float& aVsyncRate) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mIsShutdown); SchedulerGroup::MarkVsyncRan(); - if (mObservingVsync && mObserver) { - mObserver->NotifyVsync(aVsync); + + mVsyncRate = TimeDuration::FromMilliseconds(aVsyncRate); + + for (VsyncObserver* observer : mObservers.ForwardRange()) { + observer->NotifyVsync(aVsync); } return IPC_OK(); } -void VsyncChild::SetVsyncObserver(VsyncObserver* aVsyncObserver) { - MOZ_ASSERT(NS_IsMainThread()); - mObserver = aVsyncObserver; -} - TimeDuration VsyncChild::GetVsyncRate() { - if (mVsyncRate == TimeDuration::Forever()) { - PVsyncChild::SendRequestVsyncRate(); - } - return mVsyncRate; } -TimeDuration VsyncChild::VsyncRate() { return mVsyncRate; } - -mozilla::ipc::IPCResult VsyncChild::RecvVsyncRate(const float& aVsyncRate) { - mVsyncRate = TimeDuration::FromMilliseconds(aVsyncRate); - return IPC_OK(); -} - -} // namespace mozilla::layout +} // namespace mozilla::dom diff --git a/layout/ipc/VsyncChild.h b/dom/ipc/VsyncChild.h similarity index 68% rename from layout/ipc/VsyncChild.h rename to dom/ipc/VsyncChild.h index 23c915f3358f..ad057b58ce5b 100644 --- a/layout/ipc/VsyncChild.h +++ b/dom/ipc/VsyncChild.h @@ -4,22 +4,19 @@ * 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_layout_ipc_VsyncChild_h -#define mozilla_layout_ipc_VsyncChild_h +#ifndef mozilla_dom_ipc_VsyncChild_h +#define mozilla_dom_ipc_VsyncChild_h -#include "mozilla/layout/PVsyncChild.h" -#include "nsISupportsImpl.h" +#include "mozilla/dom/PVsyncChild.h" #include "mozilla/RefPtr.h" +#include "nsISupportsImpl.h" +#include "nsTObserverArray.h" namespace mozilla { class VsyncObserver; -namespace ipc { -class BackgroundChildImpl; -} // namespace ipc - -namespace layout { +namespace dom { // The PVsyncChild actor receives a vsync event from the main process and // delivers it to the child process. Currently this is restricted to the main @@ -28,15 +25,17 @@ namespace layout { class VsyncChild final : public PVsyncChild { NS_INLINE_DECL_REFCOUNTING(VsyncChild) - friend class mozilla::ipc::BackgroundChildImpl; friend class PVsyncChild; public: - // Hide the SendObserve/SendUnobserve in PVsyncChild. We add an flag - // mObservingVsync to handle the race problem of unobserving vsync event. - bool SendObserve(); - bool SendUnobserve(); + VsyncChild(); + void AddChildRefreshTimer(VsyncObserver* aVsyncObserver); + void RemoveChildRefreshTimer(VsyncObserver* aVsyncObserver); + + TimeDuration GetVsyncRate(); + + /* // Bind a VsyncObserver into VsyncChild after ipc channel connected. void SetVsyncObserver(VsyncObserver* aVsyncObserver); // GetVsyncRate is a getter for mVsyncRate which sends a requests to @@ -48,24 +47,21 @@ class VsyncChild final : public PVsyncChild { // TimeDuration::Forever() if mVsyncRate hasn't been set by calling // GetVsyncRate. TimeDuration VsyncRate(); + */ private: - VsyncChild(); virtual ~VsyncChild(); - mozilla::ipc::IPCResult RecvNotify(const VsyncEvent& aVsync); - mozilla::ipc::IPCResult RecvVsyncRate(const float& aVsyncRate); + mozilla::ipc::IPCResult RecvNotify(const VsyncEvent& aVsync, + const float& aVsyncRate); virtual void ActorDestroy(ActorDestroyReason aActorDestroyReason) override; - bool mObservingVsync; bool mIsShutdown; - - // The content side vsync observer. - RefPtr mObserver; TimeDuration mVsyncRate; + nsTObserverArray mObservers; }; -} // namespace layout +} // namespace dom } // namespace mozilla -#endif // mozilla_layout_ipc_VsyncChild_h +#endif // mozilla_dom_ipc_VsyncChild_h diff --git a/layout/ipc/VsyncParent.cpp b/dom/ipc/VsyncParent.cpp similarity index 55% rename from layout/ipc/VsyncParent.cpp rename to dom/ipc/VsyncParent.cpp index c039af999b39..46f66f3c4eab 100644 --- a/layout/ipc/VsyncParent.cpp +++ b/dom/ipc/VsyncParent.cpp @@ -13,48 +13,45 @@ #include "nsThreadUtils.h" #include "VsyncSource.h" -namespace mozilla { - -using namespace ipc; - -namespace layout { - -/*static*/ -already_AddRefed VsyncParent::Create() { - AssertIsOnBackgroundThread(); - RefPtr vsyncSource = - gfxPlatform::GetPlatform()->GetHardwareVsync(); - RefPtr vsyncParent = new VsyncParent(); - vsyncParent->mVsyncDispatcher = vsyncSource->GetRefreshTimerVsyncDispatcher(); - return vsyncParent.forget(); -} +namespace mozilla::dom { VsyncParent::VsyncParent() : mObservingVsync(false), mDestroyed(false), - mBackgroundThread(NS_GetCurrentThread()) { - MOZ_ASSERT(mBackgroundThread); - AssertIsOnBackgroundThread(); -} + mInitialThread(NS_GetCurrentThread()) {} -VsyncParent::~VsyncParent() { - // Since we use NS_INLINE_DECL_THREADSAFE_REFCOUNTING, we can't make sure - // VsyncParent is always released on the background thread. +void VsyncParent::UpdateVsyncSource( + const RefPtr& aVsyncSource) { + mVsyncSource = aVsyncSource; + if (!mVsyncSource) { + mVsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync(); + } + + if (mObservingVsync) { + mVsyncDispatcher->RemoveChildRefreshTimer(this); + } + mVsyncDispatcher = mVsyncSource->GetRefreshTimerVsyncDispatcher(); + if (mObservingVsync) { + mVsyncDispatcher->AddChildRefreshTimer(this); + } } bool VsyncParent::NotifyVsync(const VsyncEvent& aVsync) { + if (IsOnInitialThread()) { + DispatchVsyncEvent(aVsync); + return true; + } + // Called on hardware vsync thread. We should post to current ipc thread. - MOZ_ASSERT(!IsOnBackgroundThread()); nsCOMPtr vsyncEvent = NewRunnableMethod( - "layout::VsyncParent::DispatchVsyncEvent", this, + "dom::VsyncParent::DispatchVsyncEvent", this, &VsyncParent::DispatchVsyncEvent, aVsync); - MOZ_ALWAYS_SUCCEEDS( - mBackgroundThread->Dispatch(vsyncEvent, NS_DISPATCH_NORMAL)); + MOZ_ALWAYS_SUCCEEDS(mInitialThread->Dispatch(vsyncEvent, NS_DISPATCH_NORMAL)); return true; } void VsyncParent::DispatchVsyncEvent(const VsyncEvent& aVsync) { - AssertIsOnBackgroundThread(); + AssertIsOnInitialThread(); // If we call NotifyVsync() when we handle ActorDestroy() message, we might // still call DispatchVsyncEvent(). @@ -62,22 +59,13 @@ void VsyncParent::DispatchVsyncEvent(const VsyncEvent& aVsync) { // NotifyVsync(). We use mObservingVsync and mDestroyed flags to skip this // notification. if (mObservingVsync && !mDestroyed) { - Unused << SendNotify(aVsync); + TimeDuration vsyncRate = mVsyncSource->GetGlobalDisplay().GetVsyncRate(); + Unused << SendNotify(aVsync, vsyncRate.ToMilliseconds()); } } -mozilla::ipc::IPCResult VsyncParent::RecvRequestVsyncRate() { - AssertIsOnBackgroundThread(); - TimeDuration vsyncRate = gfxPlatform::GetPlatform() - ->GetHardwareVsync() - ->GetGlobalDisplay() - .GetVsyncRate(); - Unused << SendVsyncRate(vsyncRate.ToMilliseconds()); - return IPC_OK(); -} - mozilla::ipc::IPCResult VsyncParent::RecvObserve() { - AssertIsOnBackgroundThread(); + AssertIsOnInitialThread(); if (!mObservingVsync) { mVsyncDispatcher->AddChildRefreshTimer(this); mObservingVsync = true; @@ -87,7 +75,7 @@ mozilla::ipc::IPCResult VsyncParent::RecvObserve() { } mozilla::ipc::IPCResult VsyncParent::RecvUnobserve() { - AssertIsOnBackgroundThread(); + AssertIsOnInitialThread(); if (mObservingVsync) { mVsyncDispatcher->RemoveChildRefreshTimer(this); mObservingVsync = false; @@ -96,9 +84,9 @@ mozilla::ipc::IPCResult VsyncParent::RecvUnobserve() { return IPC_FAIL_NO_REASON(this); } -void VsyncParent::ActorDestroy(ActorDestroyReason aReason) { +void VsyncParent::ActorDestroy(ActorDestroyReason aActorDestroyReason) { MOZ_ASSERT(!mDestroyed); - AssertIsOnBackgroundThread(); + AssertIsOnInitialThread(); if (mObservingVsync) { mVsyncDispatcher->RemoveChildRefreshTimer(this); } @@ -106,5 +94,10 @@ void VsyncParent::ActorDestroy(ActorDestroyReason aReason) { mDestroyed = true; } -} // namespace layout -} // namespace mozilla +bool VsyncParent::IsOnInitialThread() { + return NS_GetCurrentThread() == mInitialThread; +} + +void VsyncParent::AssertIsOnInitialThread() { MOZ_ASSERT(IsOnInitialThread()); } + +} // namespace mozilla::dom diff --git a/layout/ipc/VsyncParent.h b/dom/ipc/VsyncParent.h similarity index 67% rename from layout/ipc/VsyncParent.h rename to dom/ipc/VsyncParent.h index 2d0885d81a7a..af452fd508ce 100644 --- a/layout/ipc/VsyncParent.h +++ b/dom/ipc/VsyncParent.h @@ -4,53 +4,51 @@ * 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_layout_ipc_VsyncParent_h -#define mozilla_layout_ipc_VsyncParent_h +#ifndef mozilla_dom_ipc_VsyncParent_h +#define mozilla_dom_ipc_VsyncParent_h -#include "mozilla/layout/PVsyncParent.h" +#include "mozilla/dom/PVsyncParent.h" #include "mozilla/VsyncDispatcher.h" #include "nsCOMPtr.h" #include "mozilla/RefPtr.h" +#include "VsyncSource.h" class nsIThread; -namespace mozilla { - -namespace ipc { -class BackgroundParentImpl; -} // namespace ipc - -namespace layout { +namespace mozilla::dom { // Use PBackground thread in the main process to send vsync notifications to // content process. This actor will be released when its parent protocol calls // DeallocPVsyncParent(). class VsyncParent final : public PVsyncParent, public VsyncObserver { - friend class mozilla::ipc::BackgroundParentImpl; friend class PVsyncParent; - private: - static already_AddRefed Create(); - + public: VsyncParent(); - virtual ~VsyncParent(); + void UpdateVsyncSource(const RefPtr& aVsyncSource); + + private: + virtual ~VsyncParent() = default; virtual bool NotifyVsync(const VsyncEvent& aVsync) override; - mozilla::ipc::IPCResult RecvRequestVsyncRate(); + virtual void ActorDestroy(ActorDestroyReason aActorDestroyReason) override; mozilla::ipc::IPCResult RecvObserve(); mozilla::ipc::IPCResult RecvUnobserve(); - virtual void ActorDestroy(ActorDestroyReason aActorDestroyReason) override; void DispatchVsyncEvent(const VsyncEvent& aVsync); + void UpdateVsyncRate(); + + bool IsOnInitialThread(); + void AssertIsOnInitialThread(); bool mObservingVsync; bool mDestroyed; - nsCOMPtr mBackgroundThread; + nsCOMPtr mInitialThread; + RefPtr mVsyncSource; RefPtr mVsyncDispatcher; }; -} // namespace layout -} // namespace mozilla +} // namespace mozilla::dom -#endif // mozilla_layout_ipc_VsyncParent_h +#endif // mozilla_dom_ipc_VsyncParent_h diff --git a/dom/ipc/moz.build b/dom/ipc/moz.build index 57d289b658ee..caa1bfc88eb5 100644 --- a/dom/ipc/moz.build +++ b/dom/ipc/moz.build @@ -71,6 +71,8 @@ EXPORTS.mozilla.dom += [ "URLClassifierChild.h", "URLClassifierParent.h", "UserActivationIPCUtils.h", + "VsyncChild.h", + "VsyncParent.h", "WindowGlobalActor.h", "WindowGlobalChild.h", "WindowGlobalParent.h", @@ -130,6 +132,8 @@ UNIFIED_SOURCES += [ SOURCES += [ "ContentChild.cpp", "ProcessHangMonitor.cpp", + "VsyncChild.cpp", + "VsyncParent.cpp", ] PREPROCESSED_IPDL_SOURCES += [ @@ -155,6 +159,7 @@ IPDL_SOURCES += [ "PURLClassifier.ipdl", "PURLClassifierInfo.ipdlh", "PURLClassifierLocal.ipdl", + "PVsync.ipdl", "PWindowGlobal.ipdl", "ServiceWorkerConfiguration.ipdlh", "WindowGlobalTypes.ipdlh", diff --git a/ipc/glue/BackgroundChildImpl.cpp b/ipc/glue/BackgroundChildImpl.cpp index deffc0aa168a..e10c90b5300e 100644 --- a/ipc/glue/BackgroundChildImpl.cpp +++ b/ipc/glue/BackgroundChildImpl.cpp @@ -46,11 +46,11 @@ #include "mozilla/dom/ServiceWorkerContainerChild.h" #include "mozilla/dom/ServiceWorkerManagerChild.h" #include "mozilla/dom/BrowserChild.h" +#include "mozilla/dom/VsyncChild.h" #include "mozilla/ipc/IPCStreamAlloc.h" #include "mozilla/ipc/PBackgroundTestChild.h" #include "mozilla/ipc/PChildToParentStreamChild.h" #include "mozilla/ipc/PParentToChildStreamChild.h" -#include "mozilla/layout/VsyncChild.h" #include "mozilla/net/HttpBackgroundChannelChild.h" #include "mozilla/net/PUDPSocketChild.h" #include "mozilla/dom/network/UDPSocketChild.h" @@ -408,8 +408,8 @@ bool BackgroundChildImpl::DeallocPFileDescriptorSetChild( return true; } -BackgroundChildImpl::PVsyncChild* BackgroundChildImpl::AllocPVsyncChild() { - RefPtr actor = new mozilla::layout::VsyncChild(); +dom::PVsyncChild* BackgroundChildImpl::AllocPVsyncChild() { + RefPtr actor = new dom::VsyncChild(); // There still has one ref-count after return, and it will be released in // DeallocPVsyncChild(). return actor.forget().take(); @@ -419,8 +419,8 @@ bool BackgroundChildImpl::DeallocPVsyncChild(PVsyncChild* aActor) { MOZ_ASSERT(aActor); // This actor already has one ref-count. Please check AllocPVsyncChild(). - RefPtr actor = - dont_AddRef(static_cast(aActor)); + RefPtr actor = + dont_AddRef(static_cast(aActor)); return true; } @@ -619,7 +619,7 @@ bool BackgroundChildImpl::DeallocPClientManagerChild( #ifdef EARLY_BETA_OR_EARLIER void BackgroundChildImpl::OnChannelReceivedMessage(const Message& aMsg) { - if (aMsg.type() == layout::PVsync::MessageType::Msg_Notify__ID) { + if (aMsg.type() == dom::PVsync::MessageType::Msg_Notify__ID) { // Not really necessary to look at the message payload, it will be // <0.5ms away from TimeStamp::Now() SchedulerGroup::MarkVsyncReceived(); diff --git a/ipc/glue/BackgroundParentImpl.cpp b/ipc/glue/BackgroundParentImpl.cpp index b866b2c457cc..17f08a349c06 100644 --- a/ipc/glue/BackgroundParentImpl.cpp +++ b/ipc/glue/BackgroundParentImpl.cpp @@ -55,6 +55,7 @@ #include "mozilla/dom/network/UDPSocketParent.h" #include "mozilla/dom/quota/ActorsParent.h" #include "mozilla/dom/simpledb/ActorsParent.h" +#include "mozilla/dom/VsyncParent.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/BackgroundUtils.h" #include "mozilla/ipc/Endpoint.h" @@ -64,7 +65,6 @@ #include "mozilla/ipc/PBackgroundTestParent.h" #include "mozilla/ipc/PChildToParentStreamParent.h" #include "mozilla/ipc/PParentToChildStreamParent.h" -#include "mozilla/layout/VsyncParent.h" #include "mozilla/media/MediaParent.h" #include "mozilla/net/BackgroundDataBridgeParent.h" #include "mozilla/net/HttpBackgroundChannelParent.h" @@ -717,8 +717,8 @@ BackgroundParentImpl::PVsyncParent* BackgroundParentImpl::AllocPVsyncParent() { AssertIsInMainOrSocketProcess(); AssertIsOnBackgroundThread(); - RefPtr actor = - mozilla::layout::VsyncParent::Create(); + RefPtr actor = new mozilla::dom::VsyncParent(); + actor->UpdateVsyncSource(nullptr); // There still has one ref-count after return, and it will be released in // DeallocPVsyncParent(). return actor.forget().take(); @@ -730,8 +730,8 @@ bool BackgroundParentImpl::DeallocPVsyncParent(PVsyncParent* aActor) { MOZ_ASSERT(aActor); // This actor already has one ref-count. Please check AllocPVsyncParent(). - RefPtr actor = - dont_AddRef(static_cast(aActor)); + RefPtr actor = + dont_AddRef(static_cast(aActor)); return true; } diff --git a/ipc/glue/BackgroundParentImpl.h b/ipc/glue/BackgroundParentImpl.h index 70a836553c3a..9c169ba81b8b 100644 --- a/ipc/glue/BackgroundParentImpl.h +++ b/ipc/glue/BackgroundParentImpl.h @@ -12,11 +12,6 @@ #include "mozilla/ipc/PBackgroundParent.h" namespace mozilla { - -namespace layout { -class VsyncParent; -} // namespace layout - namespace ipc { // Instances of this class should never be created directly. This class is meant diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index d3a27b70c82c..a2a677af96f7 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -692,9 +692,6 @@ nsresult nsPresContext::Init(nsDeviceContext* aDeviceContext) { if (!mRefreshDriver) { mRefreshDriver = new nsRefreshDriver(this); - if (XRE_IsContentProcess()) { - mRefreshDriver->InitializeTimer(); - } } } diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index 461e431d0032..bfce7ec5ca82 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -59,10 +59,12 @@ #include "nsViewManager.h" #include "GeckoProfiler.h" #include "nsNPAPIPluginInstance.h" +#include "mozilla/dom/BrowserChild.h" #include "mozilla/dom/CallbackDebuggerNotification.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/Performance.h" #include "mozilla/dom/Selection.h" +#include "mozilla/dom/VsyncChild.h" #include "mozilla/dom/WindowBinding.h" #include "mozilla/RestyleManager.h" #include "Layers.h" @@ -75,7 +77,6 @@ #include "BackgroundChild.h" #include "mozilla/ipc/PBackgroundChild.h" -#include "mozilla/layout/VsyncChild.h" #include "VsyncSource.h" #include "mozilla/VsyncDispatcher.h" #include "mozilla/Unused.h" @@ -246,26 +247,6 @@ class RefreshDriverTimer { TimeStamp MostRecentRefresh() const { return mLastFireTime; } - void SwapRefreshDrivers(RefreshDriverTimer* aNewTimer) { - MOZ_ASSERT(NS_IsMainThread()); - - for (nsRefreshDriver* driver : mContentRefreshDrivers) { - aNewTimer->AddRefreshDriver(driver); - driver->mActiveTimer = aNewTimer; - } - mContentRefreshDrivers.Clear(); - - for (nsRefreshDriver* driver : mRootRefreshDrivers) { - aNewTimer->AddRefreshDriver(driver); - driver->mActiveTimer = aNewTimer; - } - mRootRefreshDrivers.Clear(); - - aNewTimer->mLastFireTime = mLastFireTime; - - StopTimer(); - } - virtual TimeDuration GetTimerRate() = 0; TimeStamp GetIdleDeadlineHint(TimeStamp aDefault) { @@ -446,56 +427,48 @@ class SimpleTimerBasedRefreshDriverTimer : public RefreshDriverTimer { */ class VsyncRefreshDriverTimer : public RefreshDriverTimer { public: - VsyncRefreshDriverTimer() : mVsyncChild(nullptr) { + VsyncRefreshDriverTimer() + : mVsyncDispatcher(nullptr), + mVsyncChild(nullptr), + mVsyncRate(TimeDuration::Forever()) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); + mVsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync(); mVsyncObserver = new RefreshDriverVsyncObserver(this); - RefPtr vsyncSource = - gfxPlatform::GetPlatform()->GetHardwareVsync(); MOZ_ALWAYS_TRUE(mVsyncDispatcher = - vsyncSource->GetRefreshTimerVsyncDispatcher()); - mVsyncDispatcher->AddChildRefreshTimer(mVsyncObserver); - mVsyncRate = vsyncSource->GetGlobalDisplay().GetVsyncRate(); - } - - explicit VsyncRefreshDriverTimer(VsyncChild* aVsyncChild) - : mVsyncChild(aVsyncChild) { - MOZ_ASSERT(!XRE_IsParentProcess()); - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mVsyncChild); - mVsyncObserver = new RefreshDriverVsyncObserver(this); - mVsyncChild->SetVsyncObserver(mVsyncObserver); - mVsyncRate = mVsyncChild->GetVsyncRate(); + mVsyncSource->GetRefreshTimerVsyncDispatcher()); } // Constructor for when we have a local vsync source. As it is local, we do // not have to worry about it being re-inited by gfxPlatform on frame rate // change on the global source. explicit VsyncRefreshDriverTimer(const RefPtr& aVsyncSource) - : mVsyncChild(nullptr) { + : mVsyncSource(aVsyncSource), + mVsyncDispatcher(nullptr), + mVsyncChild(nullptr), + mVsyncRate(TimeDuration::Forever()) { MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); - mVsyncSource = aVsyncSource; mVsyncObserver = new RefreshDriverVsyncObserver(this); MOZ_ALWAYS_TRUE(mVsyncDispatcher = aVsyncSource->GetRefreshTimerVsyncDispatcher()); - mVsyncDispatcher->AddChildRefreshTimer(mVsyncObserver); - mVsyncRate = aVsyncSource->GetGlobalDisplay().GetVsyncRate(); + } + + explicit VsyncRefreshDriverTimer(const RefPtr& aVsyncChild) + : mVsyncSource(nullptr), + mVsyncDispatcher(nullptr), + mVsyncChild(aVsyncChild), + mVsyncRate(TimeDuration::Forever()) { + MOZ_ASSERT(XRE_IsContentProcess()); + MOZ_ASSERT(NS_IsMainThread()); + mVsyncObserver = new RefreshDriverVsyncObserver(this); } TimeDuration GetTimerRate() override { - if (mVsyncRate != TimeDuration::Forever()) { - return mVsyncRate; - } - - if (mVsyncChild) { - // VsyncChild::VsyncRate() is a simple getter for the cached - // hardware vsync rate. We depend on that - // VsyncChild::GetVsyncRate() being called in the constructor - // will result in a response with the actual vsync rate sooner - // or later. Until that happens VsyncChild::VsyncRate() returns - // TimeDuration::Forever() and we have to guess below. - mVsyncRate = mVsyncChild->VsyncRate(); + if (mVsyncSource) { + mVsyncRate = mVsyncSource->GetGlobalDisplay().GetVsyncRate(); + } else if (mVsyncChild) { + mVsyncRate = mVsyncChild->GetVsyncRate(); } // If hardware queries fail / are unsupported, we have to just guess. @@ -772,16 +745,11 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer { }; // RefreshDriverVsyncObserver ~VsyncRefreshDriverTimer() override { - if (XRE_IsParentProcess()) { + if (mVsyncDispatcher) { mVsyncDispatcher->RemoveChildRefreshTimer(mVsyncObserver); mVsyncDispatcher = nullptr; - } else { - // Since the PVsyncChild actors live through the life of the process, just - // send the unobserveVsync message to disable vsync event. We don't need - // to handle the cleanup stuff of this actor. PVsyncChild::ActorDestroy() - // will be called and clean up this actor. - Unused << mVsyncChild->SendUnobserve(); - mVsyncChild->SetVsyncObserver(nullptr); + } else if (mVsyncChild) { + mVsyncChild->RemoveChildRefreshTimer(mVsyncObserver); mVsyncChild = nullptr; } @@ -797,10 +765,10 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer { mLastFireTime = TimeStamp::Now(); - if (XRE_IsParentProcess()) { + if (mVsyncDispatcher) { mVsyncDispatcher->AddChildRefreshTimer(mVsyncObserver); - } else { - Unused << mVsyncChild->SendObserve(); + } else if (mVsyncChild) { + mVsyncChild->AddChildRefreshTimer(mVsyncObserver); mVsyncObserver->OnTimerStart(); } @@ -811,10 +779,10 @@ class VsyncRefreshDriverTimer : public RefreshDriverTimer { // Protect updates to `sActiveVsyncTimers`. MOZ_ASSERT(NS_IsMainThread()); - if (XRE_IsParentProcess()) { + if (mVsyncDispatcher) { mVsyncDispatcher->RemoveChildRefreshTimer(mVsyncObserver); - } else { - Unused << mVsyncChild->SendUnobserve(); + } else if (mVsyncChild) { + mVsyncChild->RemoveChildRefreshTimer(mVsyncObserver); } MOZ_ASSERT(sActiveVsyncTimers > 0); @@ -1007,33 +975,12 @@ class InactiveRefreshDriverTimer final } // namespace mozilla static StaticRefPtr sRegularRateTimer; +static nsTArray* sRegularRateTimerList; static StaticRefPtr sThrottledRateTimer; -static void CreateContentVsyncRefreshTimer(void*) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!XRE_IsParentProcess()); - - // Create the PVsync actor child for vsync-base refresh timer. - // PBackgroundChild is created synchronously. We will still use software - // timer before PVsync ready, and change to use hw timer when the connection - // is done. Please check nsRefreshDriver::PVsyncActorCreated(). - - PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread(); - if (NS_WARN_IF(!actorChild)) { - return; - } - - layout::PVsyncChild* actor = actorChild->SendPVsyncConstructor(); - if (NS_WARN_IF(!actor)) { - return; - } - - layout::VsyncChild* child = static_cast(actor); - nsRefreshDriver::PVsyncActorCreated(child); -} - void nsRefreshDriver::CreateVsyncRefreshTimer() { MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mOwnTimer); PodArrayZero(sJankLevels); @@ -1042,32 +989,29 @@ void nsRefreshDriver::CreateVsyncRefreshTimer() { } // If available, we fetch the widget-specific vsync source. - // - // NOTE(heycam): If we are initializing an nsRefreshDriver under - // nsPresContext::Init, then this GetRootWidget() call will fail, as the - // pres context does not yet have a pres shell. For now, null check the - // pres shell to avoid a console warning. nsPresContext* pc = GetPresContext(); - nsIWidget* widget = pc->GetPresShell() ? pc->GetRootWidget() : nullptr; + nsIWidget* widget = pc->GetRootWidget(); if (widget) { - RefPtr localVsyncSource = widget->GetVsyncSource(); - if (localVsyncSource) { + if (RefPtr localVsyncSource = widget->GetVsyncSource()) { mOwnTimer = new VsyncRefreshDriverTimer(localVsyncSource); + sRegularRateTimerList->AppendElement(mOwnTimer.get()); return; } + if (BrowserChild* browserChild = widget->GetOwningBrowserChild()) { + if (RefPtr localVsyncSource = browserChild->GetVsyncChild()) { + mOwnTimer = new VsyncRefreshDriverTimer(localVsyncSource); + sRegularRateTimerList->AppendElement(mOwnTimer.get()); + return; + } + } } - - if (XRE_IsParentProcess()) { + if (!sRegularRateTimer && XRE_IsParentProcess()) { // Make sure all vsync systems are ready. gfxPlatform::GetPlatform(); - // In parent process, we don't need to use ipc. We can create the - // VsyncRefreshDriverTimer directly. + // In parent process, we can create the VsyncRefreshDriverTimer directly. sRegularRateTimer = new VsyncRefreshDriverTimer(); - return; + sRegularRateTimerList->AppendElement(sRegularRateTimer); } - - // If this process is not created by NUWA, just create the vsync timer here. - CreateContentVsyncRefreshTimer(nullptr); } static uint32_t GetFirstFrameDelay(imgIRequest* req) { @@ -1085,8 +1029,11 @@ static uint32_t GetFirstFrameDelay(imgIRequest* req) { /* static */ void nsRefreshDriver::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); // clean up our timers sRegularRateTimer = nullptr; + delete sRegularRateTimerList; + sRegularRateTimerList = nullptr; sThrottledRateTimer = nullptr; } @@ -1142,25 +1089,20 @@ RefreshDriverTimer* nsRefreshDriver::ChooseTimer() { return sThrottledRateTimer; } - if (!sRegularRateTimer && !mOwnTimer) { - double rate = GetRegularTimerInterval(); - - // Try to use vsync-base refresh timer first for sRegularRateTimer. + if (!mOwnTimer) { CreateVsyncRefreshTimer(); - - if (mOwnTimer) { - return mOwnTimer.get(); - } - - if (!sRegularRateTimer) { - sRegularRateTimer = new StartupRefreshDriverTimer(rate); - } } if (mOwnTimer) { return mOwnTimer.get(); } + if (!sRegularRateTimer) { + double rate = GetRegularTimerInterval(); + sRegularRateTimer = new StartupRefreshDriverTimer(rate); + sRegularRateTimerList->AppendElement(sRegularRateTimer); + } + return sRegularRateTimer; } @@ -1196,6 +1138,9 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext) mNextThrottledFrameRequestTick = mMostRecentRefresh; mNextRecomputeVisibilityTick = mMostRecentRefresh; + if (!sRegularRateTimerList) { + sRegularRateTimerList = new nsTArray(); + } ++sRefreshDriverCount; } @@ -1213,6 +1158,9 @@ nsRefreshDriver::~nsRefreshDriver() { mRootRefresh->RemoveRefreshObserver(this, FlushType::Style); mRootRefresh = nullptr; } + if (mOwnTimer && sRegularRateTimerList) { + sRegularRateTimerList->RemoveElement(mOwnTimer.get()); + } } // Method for testing. See nsIDOMWindowUtils.advanceTimeAndRefresh @@ -2675,21 +2623,6 @@ void nsRefreshDriver::SetThrottled(bool aThrottled) { nsPresContext* nsRefreshDriver::GetPresContext() const { return mPresContext; } -/*static*/ -void nsRefreshDriver::PVsyncActorCreated(VsyncChild* aVsyncChild) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!XRE_IsParentProcess()); - RefPtr vsyncRefreshDriverTimer = - new VsyncRefreshDriverTimer(aVsyncChild); - - // If we are using software timer, swap current timer to - // VsyncRefreshDriverTimer. - if (sRegularRateTimer) { - sRegularRateTimer->SwapRefreshDrivers(vsyncRefreshDriverTimer); - } - sRegularRateTimer = std::move(vsyncRefreshDriverTimer); -} - void nsRefreshDriver::DoRefresh() { // Don't do a refresh unless we're in a state where we should be refreshing. if (!IsFrozen() && mPresContext && mActiveTimer) { @@ -2766,27 +2699,40 @@ TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!aDefault.IsNull()); - if (!sRegularRateTimer) { - return aDefault; - } - // For computing idleness of refresh drivers we only care about - // sRegularRateTimer, since we consider refresh drivers attached to + // sRegularRateTimerList, since we consider refresh drivers attached to // sThrottledRateTimer to be inactive. This implies that tasks // resulting from a tick on the sRegularRateTimer counts as being // busy but tasks resulting from a tick on sThrottledRateTimer // counts as being idle. - return sRegularRateTimer->GetIdleDeadlineHint(aDefault); + TimeStamp hint = aDefault; + if (sRegularRateTimerList) { + for (RefreshDriverTimer* timer : *sRegularRateTimerList) { + TimeStamp newHint = timer->GetIdleDeadlineHint(aDefault); + if (newHint > hint) { + hint = newHint; + } + } + } + return hint; } /* static */ Maybe nsRefreshDriver::GetNextTickHint() { MOZ_ASSERT(NS_IsMainThread()); - if (!sRegularRateTimer) { - return Nothing(); + Maybe hint = Nothing(); + if (sRegularRateTimerList) { + for (RefreshDriverTimer* timer : *sRegularRateTimerList) { + Maybe newHint = timer->GetNextTickHint(); + if ((newHint.isSome() && hint.isNothing()) || + (newHint.isSome() && hint.isSome() && + newHint.value() > hint.value())) { + hint = newHint; + } + } } - return sRegularRateTimer->GetNextTickHint(); + return hint; } void nsRefreshDriver::Disconnect() { diff --git a/layout/base/nsRefreshDriver.h b/layout/base/nsRefreshDriver.h index 67caea1b1d86..7eb4bce445e5 100644 --- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -43,10 +43,6 @@ class PresShell; class RefreshDriverTimer; class Runnable; -namespace layout { -class VsyncChild; -} // namespace layout - } // namespace mozilla class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator, @@ -298,14 +294,6 @@ class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator, */ nsPresContext* GetPresContext() const; - /** - * PBackgroundChild actor is created asynchronously in content process. - * We can't create vsync-based timers during PBackground startup. This - * function will be called when PBackgroundChild actor is created. Then we can - * do the pending vsync-based timer creation. - */ - static void PVsyncActorCreated(mozilla::layout::VsyncChild* aVsyncChild); - void CreateVsyncRefreshTimer(); #ifdef DEBUG diff --git a/layout/base/nsRefreshObservers.h b/layout/base/nsRefreshObservers.h index a30667dccbf1..d46a8ddeaeb9 100644 --- a/layout/base/nsRefreshObservers.h +++ b/layout/base/nsRefreshObservers.h @@ -23,9 +23,6 @@ class AnimationEventDispatcher; class PendingFullscreenEvent; class PresShell; class RefreshDriverTimer; -namespace layout { -class VsyncChild; -} } // namespace mozilla /** diff --git a/layout/ipc/moz.build b/layout/ipc/moz.build index 30b05f96c943..fcb6b477b132 100644 --- a/layout/ipc/moz.build +++ b/layout/ipc/moz.build @@ -9,23 +9,12 @@ with Files("**"): EXPORTS.mozilla.layout += [ "RemoteLayerTreeOwner.h", - "VsyncChild.h", - "VsyncParent.h", ] UNIFIED_SOURCES += [ "RemoteLayerTreeOwner.cpp", ] -SOURCES += [ - "VsyncChild.cpp", - "VsyncParent.cpp", -] - -IPDL_SOURCES = [ - "PVsync.ipdl", -] - include("/ipc/chromium/chromium-config.mozbuild") FINAL_LIBRARY = "xul"