gecko-dev/widget/VsyncDispatcher.cpp
Kenny Levinsen 1c5650fe48 Bug 1542808 - Implement widget-local VsyncSource for Wayland windows. r=stransky,lsalzman
Lets Wayland sessions run vsync off wayland surface frame callbacks by creating
an interface for widgets to return a local VsyncSource, if applicable.

This interface is currently used for the compositor, and for refresh drivers
in the parent process. It is not yet used for vsync in content processes.

Differential Revision: https://phabricator.services.mozilla.com/D28430

--HG--
extra : moz-landing-system : lando
2019-11-27 00:21:33 +00:00

186 lines
5.7 KiB
C++

/* -*- 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 "MainThreadUtils.h"
#include "VsyncDispatcher.h"
#include "VsyncSource.h"
#include "gfxPlatform.h"
#include "mozilla/layers/Compositor.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/layers/CompositorThread.h"
using namespace mozilla::layers;
namespace mozilla {
CompositorVsyncDispatcher::CompositorVsyncDispatcher()
: mVsyncSource(gfxPlatform::GetPlatform()->GetHardwareVsync()),
mCompositorObserverLock("CompositorObserverLock"),
mDidShutdown(false) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
}
CompositorVsyncDispatcher::CompositorVsyncDispatcher(
RefPtr<gfx::VsyncSource> aVsyncSource)
: mVsyncSource(std::move(aVsyncSource)),
mCompositorObserverLock("CompositorObserverLock"),
mDidShutdown(false) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
}
CompositorVsyncDispatcher::~CompositorVsyncDispatcher() {
MOZ_ASSERT(XRE_IsParentProcess());
// We auto remove this vsync dispatcher from the vsync source in the
// nsBaseWidget
}
void CompositorVsyncDispatcher::NotifyVsync(const VsyncEvent& aVsync) {
// In vsync thread
layers::CompositorBridgeParent::PostInsertVsyncProfilerMarker(aVsync.mTime);
MutexAutoLock lock(mCompositorObserverLock);
if (mCompositorVsyncObserver) {
mCompositorVsyncObserver->NotifyVsync(aVsync);
}
}
void CompositorVsyncDispatcher::ObserveVsync(bool aEnable) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_IsParentProcess());
if (mDidShutdown) {
return;
}
if (aEnable) {
mVsyncSource->AddCompositorVsyncDispatcher(this);
} else {
mVsyncSource->RemoveCompositorVsyncDispatcher(this);
}
}
void CompositorVsyncDispatcher::SetCompositorVsyncObserver(
VsyncObserver* aVsyncObserver) {
// When remote compositing or running gtests, vsync observation is
// initiated on the main thread. Otherwise, it is initiated from the
// compositor thread.
MOZ_ASSERT(NS_IsMainThread() ||
CompositorThreadHolder::IsInCompositorThread());
{ // scope lock
MutexAutoLock lock(mCompositorObserverLock);
mCompositorVsyncObserver = aVsyncObserver;
}
bool observeVsync = aVsyncObserver != nullptr;
nsCOMPtr<nsIRunnable> vsyncControl = NewRunnableMethod<bool>(
"CompositorVsyncDispatcher::ObserveVsync", this,
&CompositorVsyncDispatcher::ObserveVsync, observeVsync);
NS_DispatchToMainThread(vsyncControl);
}
void CompositorVsyncDispatcher::Shutdown() {
// Need to explicitly remove CompositorVsyncDispatcher when the nsBaseWidget
// shuts down. Otherwise, we would get dead vsync notifications between when
// the nsBaseWidget shuts down and the CompositorBridgeParent shuts down.
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
ObserveVsync(false);
mDidShutdown = true;
{ // scope lock
MutexAutoLock lock(mCompositorObserverLock);
mCompositorVsyncObserver = nullptr;
}
}
RefreshTimerVsyncDispatcher::RefreshTimerVsyncDispatcher(
gfx::VsyncSource::Display* aDisplay)
: mDisplay(aDisplay),
mDisplayLock("RefreshTimerVsyncDispatcherDisplayLock"),
mRefreshTimersLock("RefreshTimers lock") {
MOZ_ASSERT(XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying());
MOZ_ASSERT(NS_IsMainThread());
}
RefreshTimerVsyncDispatcher::~RefreshTimerVsyncDispatcher() {
MOZ_ASSERT(XRE_IsParentProcess() || recordreplay::IsRecordingOrReplaying());
MOZ_ASSERT(NS_IsMainThread());
}
void RefreshTimerVsyncDispatcher::MoveToDisplay(
gfx::VsyncSource::Display* aDisplay) {
MutexAutoLock lock(mDisplayLock);
mDisplay = aDisplay;
}
void RefreshTimerVsyncDispatcher::NotifyVsync(const VsyncEvent& aVsync) {
MutexAutoLock lock(mRefreshTimersLock);
for (size_t i = 0; i < mChildRefreshTimers.Length(); i++) {
mChildRefreshTimers[i]->NotifyVsync(aVsync);
}
if (mParentRefreshTimer) {
mParentRefreshTimer->NotifyVsync(aVsync);
}
}
void RefreshTimerVsyncDispatcher::SetParentRefreshTimer(
VsyncObserver* aVsyncObserver) {
MOZ_ASSERT(NS_IsMainThread());
{ // lock scope because UpdateVsyncStatus runs on main thread and will
// deadlock
MutexAutoLock lock(mRefreshTimersLock);
mParentRefreshTimer = aVsyncObserver;
}
UpdateVsyncStatus();
}
void RefreshTimerVsyncDispatcher::AddChildRefreshTimer(
VsyncObserver* aVsyncObserver) {
{ // scope lock - called on pbackground thread
MutexAutoLock lock(mRefreshTimersLock);
MOZ_ASSERT(aVsyncObserver);
if (!mChildRefreshTimers.Contains(aVsyncObserver)) {
mChildRefreshTimers.AppendElement(aVsyncObserver);
}
}
UpdateVsyncStatus();
}
void RefreshTimerVsyncDispatcher::RemoveChildRefreshTimer(
VsyncObserver* aVsyncObserver) {
{ // scope lock - called on pbackground thread
MutexAutoLock lock(mRefreshTimersLock);
MOZ_ASSERT(aVsyncObserver);
mChildRefreshTimers.RemoveElement(aVsyncObserver);
}
UpdateVsyncStatus();
}
void RefreshTimerVsyncDispatcher::UpdateVsyncStatus() {
if (!NS_IsMainThread()) {
NS_DispatchToMainThread(NewRunnableMethod(
"RefreshTimerVsyncDispatcher::UpdateVsyncStatus", this,
&RefreshTimerVsyncDispatcher::UpdateVsyncStatus));
return;
}
MutexAutoLock lock(mDisplayLock);
mDisplay->NotifyRefreshTimerVsyncStatus(NeedsVsync());
}
bool RefreshTimerVsyncDispatcher::NeedsVsync() {
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mRefreshTimersLock);
return (mParentRefreshTimer != nullptr) || !mChildRefreshTimers.IsEmpty();
}
} // namespace mozilla