Bug 1800044 [wpt PR 36907] - Fix same URL navigation test, a=testonly
[gecko.git] / widget / VsyncDispatcher.cpp
blobe83ff077cdb3988ea3250cec5428438a257eb17b
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "MainThreadUtils.h"
7 #include "VsyncDispatcher.h"
8 #include "VsyncSource.h"
9 #include "gfxPlatform.h"
10 #include "mozilla/layers/Compositor.h"
11 #include "mozilla/layers/CompositorBridgeParent.h"
12 #include "mozilla/layers/CompositorThread.h"
13 #include "mozilla/StaticPrefs_gfx.h"
15 using namespace mozilla::layers;
17 namespace mozilla {
19 CompositorVsyncDispatcher::CompositorVsyncDispatcher(
20 RefPtr<VsyncDispatcher> aVsyncDispatcher)
21 : mVsyncDispatcher(std::move(aVsyncDispatcher)),
22 mCompositorObserverLock("CompositorObserverLock"),
23 mDidShutdown(false) {
24 MOZ_ASSERT(XRE_IsParentProcess());
25 MOZ_ASSERT(NS_IsMainThread());
28 CompositorVsyncDispatcher::~CompositorVsyncDispatcher() {
29 MOZ_ASSERT(XRE_IsParentProcess());
32 void CompositorVsyncDispatcher::NotifyVsync(const VsyncEvent& aVsync) {
33 // In vsync thread
34 layers::CompositorBridgeParent::PostInsertVsyncProfilerMarker(aVsync.mTime);
36 MutexAutoLock lock(mCompositorObserverLock);
37 if (mCompositorVsyncObserver) {
38 mCompositorVsyncObserver->NotifyVsync(aVsync);
42 void CompositorVsyncDispatcher::ObserveVsync(bool aEnable) {
43 MOZ_ASSERT(NS_IsMainThread());
44 MOZ_ASSERT(XRE_IsParentProcess());
45 if (mDidShutdown) {
46 return;
49 if (aEnable) {
50 mVsyncDispatcher->AddVsyncObserver(this);
51 } else {
52 mVsyncDispatcher->RemoveVsyncObserver(this);
56 void CompositorVsyncDispatcher::SetCompositorVsyncObserver(
57 VsyncObserver* aVsyncObserver) {
58 // When remote compositing or running gtests, vsync observation is
59 // initiated on the main thread. Otherwise, it is initiated from the
60 // compositor thread.
61 MOZ_ASSERT(NS_IsMainThread() ||
62 CompositorThreadHolder::IsInCompositorThread());
64 { // scope lock
65 MutexAutoLock lock(mCompositorObserverLock);
66 mCompositorVsyncObserver = aVsyncObserver;
69 bool observeVsync = aVsyncObserver != nullptr;
70 nsCOMPtr<nsIRunnable> vsyncControl = NewRunnableMethod<bool>(
71 "CompositorVsyncDispatcher::ObserveVsync", this,
72 &CompositorVsyncDispatcher::ObserveVsync, observeVsync);
73 NS_DispatchToMainThread(vsyncControl);
76 void CompositorVsyncDispatcher::Shutdown() {
77 // Need to explicitly remove CompositorVsyncDispatcher when the nsBaseWidget
78 // shuts down. Otherwise, we would get dead vsync notifications between when
79 // the nsBaseWidget shuts down and the CompositorBridgeParent shuts down.
80 MOZ_ASSERT(XRE_IsParentProcess());
81 MOZ_ASSERT(NS_IsMainThread());
82 MOZ_ASSERT(!mDidShutdown);
83 ObserveVsync(false);
84 mDidShutdown = true;
85 { // scope lock
86 MutexAutoLock lock(mCompositorObserverLock);
87 mCompositorVsyncObserver = nullptr;
89 mVsyncDispatcher = nullptr;
92 VsyncDispatcher::VsyncDispatcher(gfx::VsyncSource* aVsyncSource)
93 : mState(State(aVsyncSource), "VsyncDispatcher::mState") {
94 MOZ_ASSERT(XRE_IsParentProcess());
95 MOZ_ASSERT(NS_IsMainThread());
98 VsyncDispatcher::~VsyncDispatcher() {
99 MOZ_ASSERT(XRE_IsParentProcess());
100 MOZ_ASSERT(NS_IsMainThread());
103 void VsyncDispatcher::SetVsyncSource(gfx::VsyncSource* aVsyncSource) {
104 MOZ_RELEASE_ASSERT(aVsyncSource);
106 auto state = mState.Lock();
107 if (aVsyncSource == state->mCurrentVsyncSource) {
108 return;
111 if (state->mIsObservingVsync) {
112 state->mCurrentVsyncSource->RemoveVsyncDispatcher(this);
113 aVsyncSource->AddVsyncDispatcher(this);
115 state->mCurrentVsyncSource = aVsyncSource;
118 RefPtr<gfx::VsyncSource> VsyncDispatcher::GetCurrentVsyncSource() {
119 auto state = mState.Lock();
120 return state->mCurrentVsyncSource;
123 TimeDuration VsyncDispatcher::GetVsyncRate() {
124 auto state = mState.Lock();
125 return state->mCurrentVsyncSource->GetVsyncRate();
128 static int32_t ComputeFrameRateDivisor(gfx::VsyncSource* aCurrentVsyncSource) {
129 int32_t maxRate = StaticPrefs::gfx_display_max_frame_rate();
130 if (maxRate == 0) {
131 return StaticPrefs::gfx_display_frame_rate_divisor();
134 // Compute the frame rate divisor based on max frame rates.
135 double frameDuration = aCurrentVsyncSource->GetVsyncRate().ToMilliseconds();
137 // Respect the pref gfx.display.frame-rate-divisor if larger.
138 return std::max(StaticPrefs::gfx_display_frame_rate_divisor(),
139 int32_t(floor(1000.0 / frameDuration / maxRate)));
142 void VsyncDispatcher::NotifyVsync(const VsyncEvent& aVsync) {
143 nsTArray<RefPtr<VsyncObserver>> observers;
144 bool shouldDispatchToMainThread = false;
146 auto state = mState.Lock();
147 if (++state->mVsyncSkipCounter <
148 ComputeFrameRateDivisor(state->mCurrentVsyncSource)) {
149 return;
151 state->mVsyncSkipCounter = 0;
153 // Copy out the observers so that we don't keep the mutex
154 // locked while notifying vsync.
155 observers = state->mObservers.Clone();
156 shouldDispatchToMainThread = !state->mMainThreadObservers.IsEmpty() &&
157 (state->mLastVsyncIdSentToMainThread ==
158 state->mLastMainThreadProcessedVsyncId);
161 for (const auto& observer : observers) {
162 observer->NotifyVsync(aVsync);
165 if (shouldDispatchToMainThread) {
166 auto state = mState.Lock();
167 state->mLastVsyncIdSentToMainThread = aVsync.mId;
168 NS_DispatchToMainThread(NewRunnableMethod<VsyncEvent>(
169 "VsyncDispatcher::NotifyMainThreadObservers", this,
170 &VsyncDispatcher::NotifyMainThreadObservers, aVsync));
174 void VsyncDispatcher::NotifyMainThreadObservers(VsyncEvent aEvent) {
175 MOZ_ASSERT(NS_IsMainThread());
176 nsTArray<RefPtr<VsyncObserver>> observers;
178 // Copy out the main thread observers so that we don't keep the mutex
179 // locked while notifying vsync.
180 auto state = mState.Lock();
181 observers.AppendElements(state->mMainThreadObservers);
184 for (const auto& observer : observers) {
185 observer->NotifyVsync(aEvent);
188 { // Scope lock
189 auto state = mState.Lock();
190 state->mLastMainThreadProcessedVsyncId = aEvent.mId;
194 void VsyncDispatcher::AddVsyncObserver(VsyncObserver* aVsyncObserver) {
195 MOZ_ASSERT(aVsyncObserver);
196 { // scope lock - called on PBackground thread or main thread
197 auto state = mState.Lock();
198 if (!state->mObservers.Contains(aVsyncObserver)) {
199 state->mObservers.AppendElement(aVsyncObserver);
203 UpdateVsyncStatus();
206 void VsyncDispatcher::RemoveVsyncObserver(VsyncObserver* aVsyncObserver) {
207 MOZ_ASSERT(aVsyncObserver);
208 { // scope lock - called on PBackground thread or main thread
209 auto state = mState.Lock();
210 state->mObservers.RemoveElement(aVsyncObserver);
213 UpdateVsyncStatus();
216 void VsyncDispatcher::AddMainThreadObserver(VsyncObserver* aObserver) {
217 MOZ_ASSERT(NS_IsMainThread());
218 MOZ_ASSERT(aObserver);
220 auto state = mState.Lock();
221 state->mMainThreadObservers.AppendElement(aObserver);
224 UpdateVsyncStatus();
227 void VsyncDispatcher::RemoveMainThreadObserver(VsyncObserver* aObserver) {
228 MOZ_ASSERT(NS_IsMainThread());
229 MOZ_ASSERT(aObserver);
231 auto state = mState.Lock();
232 state->mMainThreadObservers.RemoveElement(aObserver);
235 UpdateVsyncStatus();
238 void VsyncDispatcher::UpdateVsyncStatus() {
239 bool wasObservingVsync = false;
240 bool needVsync = false;
241 RefPtr<VsyncSource> vsyncSource;
244 auto state = mState.Lock();
245 wasObservingVsync = state->mIsObservingVsync;
246 needVsync =
247 !state->mObservers.IsEmpty() || !state->mMainThreadObservers.IsEmpty();
248 state->mIsObservingVsync = needVsync;
249 vsyncSource = state->mCurrentVsyncSource;
252 // Call Add/RemoveVsyncDispatcher outside the lock, because it can re-enter
253 // into VsyncDispatcher::NotifyVsync.
254 if (needVsync && !wasObservingVsync) {
255 vsyncSource->AddVsyncDispatcher(this);
256 } else if (!needVsync && wasObservingVsync) {
257 vsyncSource->RemoveVsyncDispatcher(this);
261 } // namespace mozilla