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
;
19 CompositorVsyncDispatcher::CompositorVsyncDispatcher(
20 RefPtr
<VsyncDispatcher
> aVsyncDispatcher
)
21 : mVsyncDispatcher(std::move(aVsyncDispatcher
)),
22 mCompositorObserverLock("CompositorObserverLock"),
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
) {
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());
50 mVsyncDispatcher
->AddVsyncObserver(this);
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
61 MOZ_ASSERT(NS_IsMainThread() ||
62 CompositorThreadHolder::IsInCompositorThread());
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
);
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
) {
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();
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
)) {
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
);
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
);
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
);
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
);
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
);
238 void VsyncDispatcher::UpdateVsyncStatus() {
239 bool wasObservingVsync
= false;
240 bool needVsync
= false;
241 RefPtr
<gfx::VsyncSource
> vsyncSource
;
244 auto state
= mState
.Lock();
245 wasObservingVsync
= state
->mIsObservingVsync
;
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