forked from mirrors/gecko-dev
Backed out changeset 9de3ed24d3a0 (bug 1825336) Backed out changeset aef787728f19 (bug 1825335) Backed out changeset a04c341244c1 (bug 1825333) Backed out changeset e3ad15f762ba (bug 1825332) Backed out changeset eed23da92a27 (bug 1825331) Backed out changeset 8213bb54376e (bug 1825330) Backed out changeset 747ec5ac4994 (bug 1825329) Backed out changeset e91ff431f92d (bug 1825328) Backed out changeset 59c18d13768b (bug 1825327) Backed out changeset 538096d99e49 (bug 1825325) Backed out changeset c76eb9d9b095 (bug 1825324) Backed out changeset 8b81410eb686 (bug 1824557)
261 lines
7.9 KiB
C++
261 lines
7.9 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"
|
|
#include "mozilla/StaticPrefs_gfx.h"
|
|
|
|
using namespace mozilla::layers;
|
|
|
|
namespace mozilla {
|
|
|
|
CompositorVsyncDispatcher::CompositorVsyncDispatcher(
|
|
RefPtr<VsyncDispatcher> aVsyncDispatcher)
|
|
: mVsyncDispatcher(std::move(aVsyncDispatcher)),
|
|
mCompositorObserverLock("CompositorObserverLock"),
|
|
mDidShutdown(false) {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
}
|
|
|
|
CompositorVsyncDispatcher::~CompositorVsyncDispatcher() {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
}
|
|
|
|
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) {
|
|
mVsyncDispatcher->AddVsyncObserver(this);
|
|
} else {
|
|
mVsyncDispatcher->RemoveVsyncObserver(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());
|
|
MOZ_ASSERT(!mDidShutdown);
|
|
ObserveVsync(false);
|
|
mDidShutdown = true;
|
|
{ // scope lock
|
|
MutexAutoLock lock(mCompositorObserverLock);
|
|
mCompositorVsyncObserver = nullptr;
|
|
}
|
|
mVsyncDispatcher = nullptr;
|
|
}
|
|
|
|
VsyncDispatcher::VsyncDispatcher(gfx::VsyncSource* aVsyncSource)
|
|
: mState(State(aVsyncSource), "VsyncDispatcher::mState") {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
}
|
|
|
|
VsyncDispatcher::~VsyncDispatcher() {
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
}
|
|
|
|
void VsyncDispatcher::SetVsyncSource(gfx::VsyncSource* aVsyncSource) {
|
|
MOZ_RELEASE_ASSERT(aVsyncSource);
|
|
|
|
auto state = mState.Lock();
|
|
if (aVsyncSource == state->mCurrentVsyncSource) {
|
|
return;
|
|
}
|
|
|
|
if (state->mIsObservingVsync) {
|
|
state->mCurrentVsyncSource->RemoveVsyncDispatcher(this);
|
|
aVsyncSource->AddVsyncDispatcher(this);
|
|
}
|
|
state->mCurrentVsyncSource = aVsyncSource;
|
|
}
|
|
|
|
RefPtr<gfx::VsyncSource> VsyncDispatcher::GetCurrentVsyncSource() {
|
|
auto state = mState.Lock();
|
|
return state->mCurrentVsyncSource;
|
|
}
|
|
|
|
TimeDuration VsyncDispatcher::GetVsyncRate() {
|
|
auto state = mState.Lock();
|
|
return state->mCurrentVsyncSource->GetVsyncRate();
|
|
}
|
|
|
|
static int32_t ComputeFrameRateDivisor(gfx::VsyncSource* aCurrentVsyncSource) {
|
|
int32_t maxRate = StaticPrefs::gfx_display_max_frame_rate();
|
|
if (maxRate == 0) {
|
|
return StaticPrefs::gfx_display_frame_rate_divisor();
|
|
}
|
|
|
|
// Compute the frame rate divisor based on max frame rates.
|
|
double frameDuration = aCurrentVsyncSource->GetVsyncRate().ToMilliseconds();
|
|
|
|
// Respect the pref gfx.display.frame-rate-divisor if larger.
|
|
return std::max(StaticPrefs::gfx_display_frame_rate_divisor(),
|
|
int32_t(floor(1000.0 / frameDuration / maxRate)));
|
|
}
|
|
|
|
void VsyncDispatcher::NotifyVsync(const VsyncEvent& aVsync) {
|
|
nsTArray<RefPtr<VsyncObserver>> observers;
|
|
bool shouldDispatchToMainThread = false;
|
|
{
|
|
auto state = mState.Lock();
|
|
if (++state->mVsyncSkipCounter <
|
|
ComputeFrameRateDivisor(state->mCurrentVsyncSource)) {
|
|
return;
|
|
}
|
|
state->mVsyncSkipCounter = 0;
|
|
|
|
// Copy out the observers so that we don't keep the mutex
|
|
// locked while notifying vsync.
|
|
observers = state->mObservers.Clone();
|
|
shouldDispatchToMainThread = !state->mMainThreadObservers.IsEmpty() &&
|
|
(state->mLastVsyncIdSentToMainThread ==
|
|
state->mLastMainThreadProcessedVsyncId);
|
|
}
|
|
|
|
for (const auto& observer : observers) {
|
|
observer->NotifyVsync(aVsync);
|
|
}
|
|
|
|
if (shouldDispatchToMainThread) {
|
|
auto state = mState.Lock();
|
|
state->mLastVsyncIdSentToMainThread = aVsync.mId;
|
|
NS_DispatchToMainThread(NewRunnableMethod<VsyncEvent>(
|
|
"VsyncDispatcher::NotifyMainThreadObservers", this,
|
|
&VsyncDispatcher::NotifyMainThreadObservers, aVsync));
|
|
}
|
|
}
|
|
|
|
void VsyncDispatcher::NotifyMainThreadObservers(VsyncEvent aEvent) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
nsTArray<RefPtr<VsyncObserver>> observers;
|
|
{
|
|
// Copy out the main thread observers so that we don't keep the mutex
|
|
// locked while notifying vsync.
|
|
auto state = mState.Lock();
|
|
observers.AppendElements(state->mMainThreadObservers);
|
|
}
|
|
|
|
for (const auto& observer : observers) {
|
|
observer->NotifyVsync(aEvent);
|
|
}
|
|
|
|
{ // Scope lock
|
|
auto state = mState.Lock();
|
|
state->mLastMainThreadProcessedVsyncId = aEvent.mId;
|
|
}
|
|
}
|
|
|
|
void VsyncDispatcher::AddVsyncObserver(VsyncObserver* aVsyncObserver) {
|
|
MOZ_ASSERT(aVsyncObserver);
|
|
{ // scope lock - called on PBackground thread or main thread
|
|
auto state = mState.Lock();
|
|
if (!state->mObservers.Contains(aVsyncObserver)) {
|
|
state->mObservers.AppendElement(aVsyncObserver);
|
|
}
|
|
}
|
|
|
|
UpdateVsyncStatus();
|
|
}
|
|
|
|
void VsyncDispatcher::RemoveVsyncObserver(VsyncObserver* aVsyncObserver) {
|
|
MOZ_ASSERT(aVsyncObserver);
|
|
{ // scope lock - called on PBackground thread or main thread
|
|
auto state = mState.Lock();
|
|
state->mObservers.RemoveElement(aVsyncObserver);
|
|
}
|
|
|
|
UpdateVsyncStatus();
|
|
}
|
|
|
|
void VsyncDispatcher::AddMainThreadObserver(VsyncObserver* aObserver) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aObserver);
|
|
{
|
|
auto state = mState.Lock();
|
|
state->mMainThreadObservers.AppendElement(aObserver);
|
|
}
|
|
|
|
UpdateVsyncStatus();
|
|
}
|
|
|
|
void VsyncDispatcher::RemoveMainThreadObserver(VsyncObserver* aObserver) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aObserver);
|
|
{
|
|
auto state = mState.Lock();
|
|
state->mMainThreadObservers.RemoveElement(aObserver);
|
|
}
|
|
|
|
UpdateVsyncStatus();
|
|
}
|
|
|
|
void VsyncDispatcher::UpdateVsyncStatus() {
|
|
bool wasObservingVsync = false;
|
|
bool needVsync = false;
|
|
RefPtr<VsyncSource> vsyncSource;
|
|
|
|
{
|
|
auto state = mState.Lock();
|
|
wasObservingVsync = state->mIsObservingVsync;
|
|
needVsync =
|
|
!state->mObservers.IsEmpty() || !state->mMainThreadObservers.IsEmpty();
|
|
state->mIsObservingVsync = needVsync;
|
|
vsyncSource = state->mCurrentVsyncSource;
|
|
}
|
|
|
|
// Call Add/RemoveVsyncDispatcher outside the lock, because it can re-enter
|
|
// into VsyncDispatcher::NotifyVsync.
|
|
if (needVsync && !wasObservingVsync) {
|
|
vsyncSource->AddVsyncDispatcher(this);
|
|
} else if (!needVsync && wasObservingVsync) {
|
|
vsyncSource->RemoveVsyncDispatcher(this);
|
|
}
|
|
}
|
|
|
|
} // namespace mozilla
|