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 void VsyncDispatcher::NotifyVsync(const VsyncEvent
& aVsync
) {
129 if (++mVsyncSkipCounter
< StaticPrefs::gfx_display_frame_rate_divisor()) {
132 mVsyncSkipCounter
= 0;
134 nsTArray
<RefPtr
<VsyncObserver
>> observers
;
135 bool shouldDispatchToMainThread
= false;
137 // Copy out the observers so that we don't keep the mutex
138 // locked while notifying vsync.
139 auto state
= mState
.Lock();
140 observers
= state
->mObservers
.Clone();
141 shouldDispatchToMainThread
= !state
->mMainThreadObservers
.IsEmpty() &&
142 (state
->mLastVsyncIdSentToMainThread
==
143 state
->mLastMainThreadProcessedVsyncId
);
146 for (const auto& observer
: observers
) {
147 observer
->NotifyVsync(aVsync
);
150 if (shouldDispatchToMainThread
) {
151 auto state
= mState
.Lock();
152 state
->mLastVsyncIdSentToMainThread
= aVsync
.mId
;
153 NS_DispatchToMainThread(NewRunnableMethod
<VsyncEvent
>(
154 "VsyncDispatcher::NotifyMainThreadObservers", this,
155 &VsyncDispatcher::NotifyMainThreadObservers
, aVsync
));
159 void VsyncDispatcher::NotifyMainThreadObservers(VsyncEvent aEvent
) {
160 MOZ_ASSERT(NS_IsMainThread());
161 nsTArray
<RefPtr
<VsyncObserver
>> observers
;
163 // Copy out the main thread observers so that we don't keep the mutex
164 // locked while notifying vsync.
165 auto state
= mState
.Lock();
166 observers
.AppendElements(state
->mMainThreadObservers
);
169 for (const auto& observer
: observers
) {
170 observer
->NotifyVsync(aEvent
);
174 auto state
= mState
.Lock();
175 state
->mLastMainThreadProcessedVsyncId
= aEvent
.mId
;
179 void VsyncDispatcher::AddVsyncObserver(VsyncObserver
* aVsyncObserver
) {
180 MOZ_ASSERT(aVsyncObserver
);
181 { // scope lock - called on PBackground thread or main thread
182 auto state
= mState
.Lock();
183 if (!state
->mObservers
.Contains(aVsyncObserver
)) {
184 state
->mObservers
.AppendElement(aVsyncObserver
);
191 void VsyncDispatcher::RemoveVsyncObserver(VsyncObserver
* aVsyncObserver
) {
192 MOZ_ASSERT(aVsyncObserver
);
193 { // scope lock - called on PBackground thread or main thread
194 auto state
= mState
.Lock();
195 state
->mObservers
.RemoveElement(aVsyncObserver
);
201 void VsyncDispatcher::AddMainThreadObserver(VsyncObserver
* aObserver
) {
202 MOZ_ASSERT(NS_IsMainThread());
203 MOZ_ASSERT(aObserver
);
205 auto state
= mState
.Lock();
206 state
->mMainThreadObservers
.AppendElement(aObserver
);
212 void VsyncDispatcher::RemoveMainThreadObserver(VsyncObserver
* aObserver
) {
213 MOZ_ASSERT(NS_IsMainThread());
214 MOZ_ASSERT(aObserver
);
216 auto state
= mState
.Lock();
217 state
->mMainThreadObservers
.RemoveElement(aObserver
);
223 void VsyncDispatcher::UpdateVsyncStatus() {
224 bool wasObservingVsync
= false;
225 bool needVsync
= false;
226 RefPtr
<VsyncSource
> vsyncSource
;
229 auto state
= mState
.Lock();
230 wasObservingVsync
= state
->mIsObservingVsync
;
232 !state
->mObservers
.IsEmpty() || !state
->mMainThreadObservers
.IsEmpty();
233 state
->mIsObservingVsync
= needVsync
;
234 vsyncSource
= state
->mCurrentVsyncSource
;
237 // Call Add/RemoveVsyncDispatcher outside the lock, because it can re-enter
238 // into VsyncDispatcher::NotifyVsync.
239 if (needVsync
&& !wasObservingVsync
) {
240 vsyncSource
->AddVsyncDispatcher(this);
241 } else if (!needVsync
&& wasObservingVsync
) {
242 vsyncSource
->RemoveVsyncDispatcher(this);
246 } // namespace mozilla