2014-10-21 22:40:54 +00:00
|
|
|
/* -*- 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/. */
|
|
|
|
|
2015-01-08 02:17:36 +00:00
|
|
|
#include "MainThreadUtils.h"
|
2014-10-21 22:40:54 +00:00
|
|
|
#include "VsyncDispatcher.h"
|
2014-12-18 16:30:06 +00:00
|
|
|
#include "VsyncSource.h"
|
2015-01-08 02:17:36 +00:00
|
|
|
#include "gfxPlatform.h"
|
2015-01-23 19:19:53 +00:00
|
|
|
#include "mozilla/layers/Compositor.h"
|
2016-03-22 18:08:38 +00:00
|
|
|
#include "mozilla/layers/CompositorBridgeParent.h"
|
2016-07-19 18:56:07 +00:00
|
|
|
#include "mozilla/layers/CompositorThread.h"
|
2022-05-11 17:15:51 +00:00
|
|
|
#include "mozilla/StaticPrefs_gfx.h"
|
2014-10-24 01:50:31 +00:00
|
|
|
|
2017-03-14 10:44:54 +00:00
|
|
|
using namespace mozilla::layers;
|
|
|
|
|
2014-10-21 22:40:54 +00:00
|
|
|
namespace mozilla {
|
|
|
|
|
2019-11-27 00:21:33 +00:00
|
|
|
CompositorVsyncDispatcher::CompositorVsyncDispatcher(
|
2022-05-05 02:15:14 +00:00
|
|
|
RefPtr<VsyncDispatcher> aVsyncDispatcher)
|
|
|
|
: mVsyncDispatcher(std::move(aVsyncDispatcher)),
|
2019-11-27 00:21:33 +00:00
|
|
|
mCompositorObserverLock("CompositorObserverLock"),
|
|
|
|
mDidShutdown(false) {
|
2014-12-18 16:30:06 +00:00
|
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
2015-01-08 02:17:36 +00:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2014-10-21 22:40:54 +00:00
|
|
|
}
|
|
|
|
|
2014-12-19 20:52:42 +00:00
|
|
|
CompositorVsyncDispatcher::~CompositorVsyncDispatcher() {
|
2015-01-08 02:17:36 +00:00
|
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
2014-10-24 01:50:31 +00:00
|
|
|
}
|
|
|
|
|
2018-12-07 23:27:28 +00:00
|
|
|
void CompositorVsyncDispatcher::NotifyVsync(const VsyncEvent& aVsync) {
|
2015-01-20 16:31:24 +00:00
|
|
|
// In vsync thread
|
2018-12-07 23:27:28 +00:00
|
|
|
layers::CompositorBridgeParent::PostInsertVsyncProfilerMarker(aVsync.mTime);
|
2014-11-18 21:28:42 +00:00
|
|
|
|
2014-12-18 16:30:06 +00:00
|
|
|
MutexAutoLock lock(mCompositorObserverLock);
|
2015-01-08 02:17:36 +00:00
|
|
|
if (mCompositorVsyncObserver) {
|
2018-12-07 23:27:28 +00:00
|
|
|
mCompositorVsyncObserver->NotifyVsync(aVsync);
|
2014-10-24 01:50:31 +00:00
|
|
|
}
|
2014-10-21 22:40:54 +00:00
|
|
|
}
|
|
|
|
|
2015-01-20 16:31:24 +00:00
|
|
|
void CompositorVsyncDispatcher::ObserveVsync(bool aEnable) {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
|
|
if (mDidShutdown) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aEnable) {
|
2022-05-05 02:15:14 +00:00
|
|
|
mVsyncDispatcher->AddVsyncObserver(this);
|
2015-01-20 16:31:24 +00:00
|
|
|
} else {
|
2022-05-05 02:15:14 +00:00
|
|
|
mVsyncDispatcher->RemoveVsyncObserver(this);
|
2015-01-20 16:31:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-19 20:52:42 +00:00
|
|
|
void CompositorVsyncDispatcher::SetCompositorVsyncObserver(
|
|
|
|
VsyncObserver* aVsyncObserver) {
|
2016-07-19 18:56:07 +00:00
|
|
|
// 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());
|
|
|
|
|
2015-01-23 19:19:53 +00:00
|
|
|
{ // scope lock
|
|
|
|
MutexAutoLock lock(mCompositorObserverLock);
|
|
|
|
mCompositorVsyncObserver = aVsyncObserver;
|
|
|
|
}
|
2015-01-20 16:31:24 +00:00
|
|
|
|
|
|
|
bool observeVsync = aVsyncObserver != nullptr;
|
2017-06-12 19:34:10 +00:00
|
|
|
nsCOMPtr<nsIRunnable> vsyncControl = NewRunnableMethod<bool>(
|
|
|
|
"CompositorVsyncDispatcher::ObserveVsync", this,
|
|
|
|
&CompositorVsyncDispatcher::ObserveVsync, observeVsync);
|
2015-01-20 16:31:24 +00:00
|
|
|
NS_DispatchToMainThread(vsyncControl);
|
2014-10-21 22:40:54 +00:00
|
|
|
}
|
|
|
|
|
2014-12-19 20:52:42 +00:00
|
|
|
void CompositorVsyncDispatcher::Shutdown() {
|
|
|
|
// Need to explicitly remove CompositorVsyncDispatcher when the nsBaseWidget
|
2014-12-18 16:30:06 +00:00
|
|
|
// shuts down. Otherwise, we would get dead vsync notifications between when
|
2016-03-22 18:08:38 +00:00
|
|
|
// the nsBaseWidget shuts down and the CompositorBridgeParent shuts down.
|
2014-12-18 16:30:06 +00:00
|
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
2020-03-16 23:24:39 +00:00
|
|
|
MOZ_ASSERT(!mDidShutdown);
|
2015-01-20 16:31:24 +00:00
|
|
|
ObserveVsync(false);
|
|
|
|
mDidShutdown = true;
|
|
|
|
{ // scope lock
|
|
|
|
MutexAutoLock lock(mCompositorObserverLock);
|
|
|
|
mCompositorVsyncObserver = nullptr;
|
|
|
|
}
|
2022-05-05 02:15:14 +00:00
|
|
|
mVsyncDispatcher = nullptr;
|
2014-10-21 22:40:54 +00:00
|
|
|
}
|
2015-01-08 02:17:36 +00:00
|
|
|
|
2022-05-05 02:15:13 +00:00
|
|
|
VsyncDispatcher::VsyncDispatcher(gfx::VsyncSource* aVsyncSource)
|
2022-05-05 02:15:18 +00:00
|
|
|
: mState(State(aVsyncSource), "VsyncDispatcher::mState") {
|
2020-02-27 17:39:15 +00:00
|
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
2015-01-08 02:17:36 +00:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
}
|
|
|
|
|
2022-05-05 02:15:13 +00:00
|
|
|
VsyncDispatcher::~VsyncDispatcher() {
|
2020-02-27 17:39:15 +00:00
|
|
|
MOZ_ASSERT(XRE_IsParentProcess());
|
2015-01-08 02:17:36 +00:00
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
}
|
|
|
|
|
2022-05-05 02:15:18 +00:00
|
|
|
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();
|
2019-11-27 00:21:33 +00:00
|
|
|
}
|
|
|
|
|
2022-11-07 19:30:49 +00:00
|
|
|
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)));
|
|
|
|
}
|
|
|
|
|
2022-05-05 02:15:13 +00:00
|
|
|
void VsyncDispatcher::NotifyVsync(const VsyncEvent& aVsync) {
|
2022-05-05 02:15:16 +00:00
|
|
|
nsTArray<RefPtr<VsyncObserver>> observers;
|
|
|
|
bool shouldDispatchToMainThread = false;
|
|
|
|
{
|
2022-07-19 18:51:46 +00:00
|
|
|
auto state = mState.Lock();
|
|
|
|
if (++state->mVsyncSkipCounter <
|
2022-11-07 19:30:49 +00:00
|
|
|
ComputeFrameRateDivisor(state->mCurrentVsyncSource)) {
|
2022-07-19 18:51:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
state->mVsyncSkipCounter = 0;
|
|
|
|
|
2022-05-05 02:15:16 +00:00
|
|
|
// 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);
|
|
|
|
}
|
2015-01-08 02:17:36 +00:00
|
|
|
|
2022-05-05 02:15:16 +00:00
|
|
|
for (const auto& observer : observers) {
|
2022-03-01 22:48:19 +00:00
|
|
|
observer->NotifyVsync(aVsync);
|
2015-01-08 02:17:36 +00:00
|
|
|
}
|
2022-05-05 02:15:16 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2015-01-08 02:17:36 +00:00
|
|
|
}
|
|
|
|
|
2022-05-05 02:15:13 +00:00
|
|
|
void VsyncDispatcher::AddVsyncObserver(VsyncObserver* aVsyncObserver) {
|
2022-03-01 22:48:19 +00:00
|
|
|
MOZ_ASSERT(aVsyncObserver);
|
|
|
|
{ // scope lock - called on PBackground thread or main thread
|
2022-05-05 02:15:16 +00:00
|
|
|
auto state = mState.Lock();
|
|
|
|
if (!state->mObservers.Contains(aVsyncObserver)) {
|
|
|
|
state->mObservers.AppendElement(aVsyncObserver);
|
2015-01-20 16:31:26 +00:00
|
|
|
}
|
2015-01-08 02:17:36 +00:00
|
|
|
}
|
2015-01-20 16:31:26 +00:00
|
|
|
|
|
|
|
UpdateVsyncStatus();
|
2015-01-08 02:17:36 +00:00
|
|
|
}
|
|
|
|
|
2022-05-05 02:15:13 +00:00
|
|
|
void VsyncDispatcher::RemoveVsyncObserver(VsyncObserver* aVsyncObserver) {
|
2022-03-01 22:48:19 +00:00
|
|
|
MOZ_ASSERT(aVsyncObserver);
|
|
|
|
{ // scope lock - called on PBackground thread or main thread
|
2022-05-05 02:15:16 +00:00
|
|
|
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);
|
2022-05-04 16:13:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
UpdateVsyncStatus();
|
|
|
|
}
|
|
|
|
|
2022-05-05 02:15:13 +00:00
|
|
|
void VsyncDispatcher::UpdateVsyncStatus() {
|
2022-05-05 02:15:18 +00:00
|
|
|
bool wasObservingVsync = false;
|
|
|
|
bool needVsync = false;
|
2023-04-01 08:31:12 +00:00
|
|
|
RefPtr<gfx::VsyncSource> vsyncSource;
|
2022-05-04 16:13:37 +00:00
|
|
|
|
2022-05-05 02:15:18 +00:00
|
|
|
{
|
|
|
|
auto state = mState.Lock();
|
|
|
|
wasObservingVsync = state->mIsObservingVsync;
|
|
|
|
needVsync =
|
|
|
|
!state->mObservers.IsEmpty() || !state->mMainThreadObservers.IsEmpty();
|
|
|
|
state->mIsObservingVsync = needVsync;
|
|
|
|
vsyncSource = state->mCurrentVsyncSource;
|
|
|
|
}
|
2022-05-04 16:13:37 +00:00
|
|
|
|
2022-05-05 02:15:18 +00:00
|
|
|
// 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);
|
|
|
|
}
|
2015-01-08 02:17:36 +00:00
|
|
|
}
|
|
|
|
|
2014-10-21 22:40:54 +00:00
|
|
|
} // namespace mozilla
|