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. When using PVsync, limit updates to once in every
250ms in order to minimize overhead while still updating fast.

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.

Differential Revision: https://phabricator.services.mozilla.com/D93173
This commit is contained in:
Robert Mader 2020-11-24 23:47:54 +00:00
parent c3096d1e25
commit d2fe090741
19 changed files with 282 additions and 305 deletions

View File

@ -14,6 +14,7 @@
#include <algorithm>
#include <utility>
#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<VsyncChild*>(actor);
} else
#endif
{
PBackgroundChild* actorChild =
BackgroundChild::GetOrCreateForCurrentThread();
mVsyncChild = static_cast<VsyncChild*>(actorChild->SendPVsyncConstructor());
}
}
void BrowserChild::NotifyTabContextUpdated() {
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
MOZ_ASSERT(docShell);
@ -2143,6 +2166,23 @@ bool BrowserChild::DeallocPFilePickerChild(PFilePickerChild* actor) {
return true;
}
PVsyncChild* BrowserChild::AllocPVsyncChild() {
RefPtr<dom::VsyncChild> 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<VsyncChild> actor = dont_AddRef(static_cast<VsyncChild*>(aActor));
return true;
}
RefPtr<VsyncChild> BrowserChild::GetVsyncChild() { return mVsyncChild; }
mozilla::ipc::IPCResult BrowserChild::RecvActivateFrameEvent(
const nsString& aType, const bool& capture) {
nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());

View File

@ -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<VsyncChild> 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<VsyncChild> mVsyncChild;
bool mIgnoreKeyPressEvent;
RefPtr<APZEventState> mAPZEventState;
SetAllowedTouchBehaviorCallback mSetAllowedTouchBehaviorCallback;

View File

@ -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,28 @@ IPCResult BrowserParent::RecvNewWindowGlobal(
return IPC_OK();
}
PVsyncParent* BrowserParent::AllocPVsyncParent() {
MOZ_ASSERT(!mVsyncParent);
mVsyncParent = new VsyncParent();
UpdateVsyncParentVsyncSource();
return mVsyncParent.get();
}
bool BrowserParent::DeallocPVsyncParent(PVsyncParent* aActor) {
mVsyncParent = nullptr;
return true;
}
void BrowserParent::UpdateVsyncParentVsyncSource() {
if (!mVsyncParent) {
return;
}
if (nsCOMPtr<nsIWidget> 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) {

View File

@ -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<nsString> mVerifyDropLinks;
RefPtr<VsyncParent> mVsyncParent;
#ifdef DEBUG
int32_t mActiveSupressDisplayportCount = 0;
#endif

View File

@ -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.

View File

@ -4,22 +4,23 @@
* 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.
@ -35,10 +36,9 @@ parent:
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

View File

@ -10,17 +10,18 @@
#include "mozilla/VsyncDispatcher.h"
#include "nsThreadUtils.h"
namespace mozilla::layout {
namespace mozilla::dom {
VsyncChild::VsyncChild()
: mObservingVsync(false),
mIsShutdown(false),
mVsyncRate(TimeDuration::Forever()) {
: mIsShutdown(false),
mVsyncRate(TimeDuration::Forever()),
lastVsyncRateTime(TimeStamp()) {
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,12 +39,42 @@ 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) {
@ -51,30 +82,26 @@ mozilla::ipc::IPCResult VsyncChild::RecvNotify(const VsyncEvent& aVsync) {
MOZ_ASSERT(!mIsShutdown);
SchedulerGroup::MarkVsyncRan();
if (mObservingVsync && mObserver) {
mObserver->NotifyVsync(aVsync);
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()) {
// Throttle vsync rate requests to avoid unnecessary IPC
if (lastVsyncRateTime.IsNull() ||
(TimeStamp::Now() - lastVsyncRateTime).ToMilliseconds() > 250) {
PVsyncChild::SendRequestVsyncRate();
lastVsyncRateTime = TimeStamp::Now();
}
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

View File

@ -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,44 +25,30 @@ 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);
// Bind a VsyncObserver into VsyncChild after ipc channel connected.
void SetVsyncObserver(VsyncObserver* aVsyncObserver);
// GetVsyncRate is a getter for mVsyncRate which sends a requests to
// VsyncParent to retreive the hardware vsync rate if mVsyncRate
// hasn't already been set.
TimeDuration GetVsyncRate();
// VsyncRate is a getter for mVsyncRate which always returns
// mVsyncRate directly, potentially returning
// 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);
virtual void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
bool mObservingVsync;
bool mIsShutdown;
// The content side vsync observer.
RefPtr<VsyncObserver> mObserver;
TimeDuration mVsyncRate;
TimeStamp lastVsyncRateTime;
nsTObserverArray<VsyncObserver*> mObservers;
};
} // namespace layout
} // namespace dom
} // namespace mozilla
#endif // mozilla_layout_ipc_VsyncChild_h
#endif // mozilla_dom_ipc_VsyncChild_h

View File

@ -10,51 +10,49 @@
#include "BackgroundParentImpl.h"
#include "gfxPlatform.h"
#include "mozilla/Unused.h"
#include "nsIThread.h"
#include "nsThreadUtils.h"
#include "VsyncSource.h"
namespace mozilla {
using namespace ipc;
namespace layout {
/*static*/
already_AddRefed<VsyncParent> VsyncParent::Create() {
AssertIsOnBackgroundThread();
RefPtr<gfx::VsyncSource> vsyncSource =
gfxPlatform::GetPlatform()->GetHardwareVsync();
RefPtr<VsyncParent> 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<gfx::VsyncSource>& 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<nsIRunnable> vsyncEvent = NewRunnableMethod<VsyncEvent>(
"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().
@ -67,17 +65,14 @@ void VsyncParent::DispatchVsyncEvent(const VsyncEvent& aVsync) {
}
mozilla::ipc::IPCResult VsyncParent::RecvRequestVsyncRate() {
AssertIsOnBackgroundThread();
TimeDuration vsyncRate = gfxPlatform::GetPlatform()
->GetHardwareVsync()
->GetGlobalDisplay()
.GetVsyncRate();
AssertIsOnInitialThread();
TimeDuration vsyncRate = mVsyncSource->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 +82,7 @@ mozilla::ipc::IPCResult VsyncParent::RecvObserve() {
}
mozilla::ipc::IPCResult VsyncParent::RecvUnobserve() {
AssertIsOnBackgroundThread();
AssertIsOnInitialThread();
if (mObservingVsync) {
mVsyncDispatcher->RemoveChildRefreshTimer(this);
mObservingVsync = false;
@ -96,9 +91,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 +101,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

View File

@ -4,36 +4,31 @@
* 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<VsyncParent> Create();
public:
VsyncParent();
virtual ~VsyncParent();
void UpdateVsyncSource(const RefPtr<gfx::VsyncSource>& aVsyncSource);
private:
virtual ~VsyncParent() = default;
virtual bool NotifyVsync(const VsyncEvent& aVsync) override;
mozilla::ipc::IPCResult RecvRequestVsyncRate();
@ -44,13 +39,16 @@ class VsyncParent final : public PVsyncParent, public VsyncObserver {
void DispatchVsyncEvent(const VsyncEvent& aVsync);
bool IsOnInitialThread();
void AssertIsOnInitialThread();
bool mObservingVsync;
bool mDestroyed;
nsCOMPtr<nsIThread> mBackgroundThread;
nsCOMPtr<nsIThread> mInitialThread;
RefPtr<gfx::VsyncSource> mVsyncSource;
RefPtr<RefreshTimerVsyncDispatcher> mVsyncDispatcher;
};
} // namespace layout
} // namespace mozilla
} // namespace mozilla::dom
#endif // mozilla_layout_ipc_VsyncParent_h
#endif // mozilla_dom_ipc_VsyncParent_h

View File

@ -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",

View File

@ -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<mozilla::layout::VsyncChild> actor = new mozilla::layout::VsyncChild();
dom::PVsyncChild* BackgroundChildImpl::AllocPVsyncChild() {
RefPtr<dom::VsyncChild> 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<mozilla::layout::VsyncChild> actor =
dont_AddRef(static_cast<mozilla::layout::VsyncChild*>(aActor));
RefPtr<dom::VsyncChild> actor =
dont_AddRef(static_cast<dom::VsyncChild*>(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();

View File

@ -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<mozilla::layout::VsyncParent> actor =
mozilla::layout::VsyncParent::Create();
RefPtr<mozilla::dom::VsyncParent> 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<mozilla::layout::VsyncParent> actor =
dont_AddRef(static_cast<mozilla::layout::VsyncParent*>(aActor));
RefPtr<mozilla::dom::VsyncParent> actor =
dont_AddRef(static_cast<mozilla::dom::VsyncParent*>(aActor));
return true;
}

View File

@ -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

View File

@ -692,9 +692,6 @@ nsresult nsPresContext::Init(nsDeviceContext* aDeviceContext) {
if (!mRefreshDriver) {
mRefreshDriver = new nsRefreshDriver(this);
if (XRE_IsContentProcess()) {
mRefreshDriver->InitializeTimer();
}
}
}

View File

@ -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,45 @@ class SimpleTimerBasedRefreshDriverTimer : public RefreshDriverTimer {
*/
class VsyncRefreshDriverTimer : public RefreshDriverTimer {
public:
VsyncRefreshDriverTimer() : mVsyncChild(nullptr) {
VsyncRefreshDriverTimer()
: mVsyncChild(nullptr), mVsyncRate(TimeDuration::Forever()) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
mVsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync();
mVsyncObserver = new RefreshDriverVsyncObserver(this);
RefPtr<mozilla::gfx::VsyncSource> 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<gfx::VsyncSource>& aVsyncSource)
: mVsyncChild(nullptr) {
: mVsyncSource(aVsyncSource),
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<VsyncChild>& 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 +742,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 +762,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 +776,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);
@ -1006,34 +971,12 @@ class InactiveRefreshDriverTimer final
} // namespace mozilla
static StaticRefPtr<RefreshDriverTimer> sRegularRateTimer;
static nsTArray<RefreshDriverTimer*> sRegularRateTimers;
static StaticRefPtr<InactiveRefreshDriverTimer> 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<layout::VsyncChild*>(actor);
nsRefreshDriver::PVsyncActorCreated(child);
}
void nsRefreshDriver::CreateVsyncRefreshTimer() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mOwnTimer);
PodArrayZero(sJankLevels);
@ -1042,32 +985,22 @@ 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;
if (widget) {
RefPtr<gfx::VsyncSource> localVsyncSource = widget->GetVsyncSource();
if (localVsyncSource) {
if (RefPtr<gfx::VsyncSource> localVsyncSource = widget->GetVsyncSource()) {
mOwnTimer = new VsyncRefreshDriverTimer(localVsyncSource);
return;
} else if (BrowserChild* browserChild = widget->GetOwningBrowserChild()) {
if (RefPtr<VsyncChild> localVsyncSource = browserChild->GetVsyncChild()) {
mOwnTimer = new VsyncRefreshDriverTimer(localVsyncSource);
}
}
}
if (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.
sRegularRateTimer = new VsyncRefreshDriverTimer();
return;
if (!mOwnTimer) {
mOwnTimer = new StartupRefreshDriverTimer(GetRegularTimerInterval());
}
// If this process is not created by NUWA, just create the vsync timer here.
CreateContentVsyncRefreshTimer(nullptr);
sRegularRateTimers.AppendElement(mOwnTimer.get());
}
static uint32_t GetFirstFrameDelay(imgIRequest* req) {
@ -1086,7 +1019,7 @@ static uint32_t GetFirstFrameDelay(imgIRequest* req) {
/* static */
void nsRefreshDriver::Shutdown() {
// clean up our timers
sRegularRateTimer = nullptr;
sRegularRateTimers.Clear();
sThrottledRateTimer = nullptr;
}
@ -1142,26 +1075,10 @@ 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();
}
return sRegularRateTimer;
return mOwnTimer.get();
}
nsRefreshDriver::nsRefreshDriver(nsPresContext* aPresContext)
@ -1213,6 +1130,9 @@ nsRefreshDriver::~nsRefreshDriver() {
mRootRefresh->RemoveRefreshObserver(this, FlushType::Style);
mRootRefresh = nullptr;
}
if (mOwnTimer) {
sRegularRateTimers.RemoveElement(mOwnTimer.get());
}
}
// Method for testing. See nsIDOMWindowUtils.advanceTimeAndRefresh
@ -2675,21 +2595,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<RefreshDriverTimer> 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 +2671,27 @@ TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aDefault.IsNull());
if (!sRegularRateTimer) {
if (sRegularRateTimers.IsEmpty()) {
return aDefault;
}
// For computing idleness of refresh drivers we only care about
// sRegularRateTimer, since we consider refresh drivers attached to
// sRegularRateTimers, 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);
return sRegularRateTimers.ElementAt(0)->GetIdleDeadlineHint(aDefault);
}
/* static */
Maybe<TimeStamp> nsRefreshDriver::GetNextTickHint() {
MOZ_ASSERT(NS_IsMainThread());
if (!sRegularRateTimer) {
if (sRegularRateTimers.IsEmpty()) {
return Nothing();
}
return sRegularRateTimer->GetNextTickHint();
return sRegularRateTimers.ElementAt(0)->GetNextTickHint();
}
void nsRefreshDriver::Disconnect() {

View File

@ -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

View File

@ -23,9 +23,6 @@ class AnimationEventDispatcher;
class PendingFullscreenEvent;
class PresShell;
class RefreshDriverTimer;
namespace layout {
class VsyncChild;
}
} // namespace mozilla
/**

View File

@ -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"