1 /* -*- Mode: C++; tab-width: 20; 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 "VsyncSource.h"
7 #include "nsThreadUtils.h"
8 #include "nsXULAppAPI.h"
9 #include "mozilla/VsyncDispatcher.h"
10 #include "MainThreadUtils.h"
15 void VsyncSource::EnableCompositorVsyncDispatcher(
16 CompositorVsyncDispatcher
* aCompositorVsyncDispatcher
) {
17 MOZ_ASSERT(XRE_IsParentProcess());
18 MOZ_ASSERT(NS_IsMainThread());
19 // Just use the global display until we have enough information to get the
20 // corresponding display for compositor.
21 GetGlobalDisplay().EnableCompositorVsyncDispatcher(
22 aCompositorVsyncDispatcher
);
25 void VsyncSource::DisableCompositorVsyncDispatcher(
26 CompositorVsyncDispatcher
* aCompositorVsyncDispatcher
) {
27 MOZ_ASSERT(XRE_IsParentProcess());
28 MOZ_ASSERT(NS_IsMainThread());
29 // See also EnableCompositorVsyncDispatcher().
30 GetGlobalDisplay().DisableCompositorVsyncDispatcher(
31 aCompositorVsyncDispatcher
);
34 void VsyncSource::RegisterCompositorVsyncDispatcher(
35 CompositorVsyncDispatcher
* aCompositorVsyncDispatcher
) {
36 MOZ_ASSERT(XRE_IsParentProcess());
37 MOZ_ASSERT(NS_IsMainThread());
38 GetGlobalDisplay().RegisterCompositorVsyncDispatcher(
39 aCompositorVsyncDispatcher
);
42 void VsyncSource::DeregisterCompositorVsyncDispatcher(
43 CompositorVsyncDispatcher
* aCompositorVsyncDispatcher
) {
44 MOZ_ASSERT(XRE_IsParentProcess());
45 MOZ_ASSERT(NS_IsMainThread());
46 GetGlobalDisplay().DeregisterCompositorVsyncDispatcher(
47 aCompositorVsyncDispatcher
);
50 void VsyncSource::AddGenericObserver(VsyncObserver
* aObserver
) {
51 MOZ_ASSERT(XRE_IsParentProcess());
52 MOZ_ASSERT(NS_IsMainThread());
54 GetGlobalDisplay().AddGenericObserver(aObserver
);
57 void VsyncSource::RemoveGenericObserver(VsyncObserver
* aObserver
) {
58 MOZ_ASSERT(XRE_IsParentProcess());
59 MOZ_ASSERT(NS_IsMainThread());
61 GetGlobalDisplay().RemoveGenericObserver(aObserver
);
64 void VsyncSource::MoveListenersToNewSource(
65 const RefPtr
<VsyncSource
>& aNewSource
) {
66 GetGlobalDisplay().MoveListenersToNewSource(aNewSource
);
69 RefPtr
<RefreshTimerVsyncDispatcher
>
70 VsyncSource::GetRefreshTimerVsyncDispatcher() {
71 MOZ_ASSERT(XRE_IsParentProcess());
72 // See also AddCompositorVsyncDispatcher().
73 return GetGlobalDisplay().GetRefreshTimerVsyncDispatcher();
76 VsyncSource::Display::Display()
77 : mDispatcherLock("display dispatcher lock"),
78 mRefreshTimerNeedsVsync(false),
79 mHasGenericObservers(false) {
80 MOZ_ASSERT(NS_IsMainThread());
81 mRefreshTimerVsyncDispatcher
= new RefreshTimerVsyncDispatcher(this);
84 VsyncSource::Display::~Display() {
85 MOZ_ASSERT(NS_IsMainThread());
86 MutexAutoLock
lock(mDispatcherLock
);
87 mRefreshTimerVsyncDispatcher
= nullptr;
88 MOZ_ASSERT(mRegisteredCompositorVsyncDispatchers
.Length() == 0);
89 MOZ_ASSERT(mEnabledCompositorVsyncDispatchers
.Length() == 0);
92 void VsyncSource::Display::NotifyVsync(const TimeStamp
& aVsyncTimestamp
,
93 const TimeStamp
& aOutputTimestamp
) {
94 // Called on the vsync thread
95 MutexAutoLock
lock(mDispatcherLock
);
97 // mRefreshTimerVsyncDispatcher might be null here if MoveListenersToNewSource
98 // was called concurrently with this function and won the race to acquire
99 // mDispatcherLock. In this case the new VsyncSource that is replacing this
100 // one will handle notifications from now on, so we can abort.
101 if (!mRefreshTimerVsyncDispatcher
) {
105 // If the task posted to the main thread from the last NotifyVsync call
106 // hasn't been processed yet, then don't send another one. Otherwise we might
107 // end up flooding the main thread.
108 bool dispatchToMainThread
=
109 mHasGenericObservers
&&
110 (mLastVsyncIdSentToMainThread
== mLastMainThreadProcessedVsyncId
);
112 mVsyncId
= mVsyncId
.Next();
113 const VsyncEvent
event(mVsyncId
, aVsyncTimestamp
, aOutputTimestamp
);
115 for (size_t i
= 0; i
< mEnabledCompositorVsyncDispatchers
.Length(); i
++) {
116 mEnabledCompositorVsyncDispatchers
[i
]->NotifyVsync(event
);
119 mRefreshTimerVsyncDispatcher
->NotifyVsync(event
);
121 if (dispatchToMainThread
) {
122 mLastVsyncIdSentToMainThread
= mVsyncId
;
123 NS_DispatchToMainThread(NewRunnableMethod
<VsyncEvent
>(
124 "VsyncSource::Display::NotifyGenericObservers", this,
125 &VsyncSource::Display::NotifyGenericObservers
, event
));
129 void VsyncSource::Display::NotifyGenericObservers(VsyncEvent aEvent
) {
130 MOZ_ASSERT(NS_IsMainThread());
131 for (size_t i
= 0; i
< mGenericObservers
.Length(); i
++) {
132 mGenericObservers
[i
]->NotifyVsync(aEvent
);
136 MutexAutoLock
lock(mDispatcherLock
);
137 mLastMainThreadProcessedVsyncId
= aEvent
.mId
;
141 TimeDuration
VsyncSource::Display::GetVsyncRate() {
142 // If hardware queries fail / are unsupported, we have to just guess.
143 return TimeDuration::FromMilliseconds(1000.0 / 60.0);
146 void VsyncSource::Display::RegisterCompositorVsyncDispatcher(
147 CompositorVsyncDispatcher
* aCompositorVsyncDispatcher
) {
148 MOZ_ASSERT(NS_IsMainThread());
149 MOZ_ASSERT(aCompositorVsyncDispatcher
);
151 MutexAutoLock
lock(mDispatcherLock
);
152 mRegisteredCompositorVsyncDispatchers
.AppendElement(
153 aCompositorVsyncDispatcher
);
157 void VsyncSource::Display::DeregisterCompositorVsyncDispatcher(
158 CompositorVsyncDispatcher
* aCompositorVsyncDispatcher
) {
159 MOZ_ASSERT(NS_IsMainThread());
160 MOZ_ASSERT(aCompositorVsyncDispatcher
);
162 MutexAutoLock
lock(mDispatcherLock
);
163 mRegisteredCompositorVsyncDispatchers
.RemoveElement(
164 aCompositorVsyncDispatcher
);
168 void VsyncSource::Display::EnableCompositorVsyncDispatcher(
169 CompositorVsyncDispatcher
* aCompositorVsyncDispatcher
) {
170 MOZ_ASSERT(NS_IsMainThread());
171 MOZ_ASSERT(aCompositorVsyncDispatcher
);
173 MutexAutoLock
lock(mDispatcherLock
);
174 if (!mEnabledCompositorVsyncDispatchers
.Contains(
175 aCompositorVsyncDispatcher
)) {
176 mEnabledCompositorVsyncDispatchers
.AppendElement(
177 aCompositorVsyncDispatcher
);
183 void VsyncSource::Display::DisableCompositorVsyncDispatcher(
184 CompositorVsyncDispatcher
* aCompositorVsyncDispatcher
) {
185 MOZ_ASSERT(NS_IsMainThread());
186 MOZ_ASSERT(aCompositorVsyncDispatcher
);
188 MutexAutoLock
lock(mDispatcherLock
);
189 if (mEnabledCompositorVsyncDispatchers
.Contains(
190 aCompositorVsyncDispatcher
)) {
191 mEnabledCompositorVsyncDispatchers
.RemoveElement(
192 aCompositorVsyncDispatcher
);
198 void VsyncSource::Display::AddGenericObserver(VsyncObserver
* aObserver
) {
199 MOZ_ASSERT(NS_IsMainThread());
200 MOZ_ASSERT(aObserver
);
201 mGenericObservers
.AppendElement(aObserver
);
206 void VsyncSource::Display::RemoveGenericObserver(VsyncObserver
* aObserver
) {
207 MOZ_ASSERT(NS_IsMainThread());
208 MOZ_ASSERT(aObserver
);
209 mGenericObservers
.RemoveElement(aObserver
);
214 void VsyncSource::Display::MoveListenersToNewSource(
215 const RefPtr
<VsyncSource
>& aNewSource
) {
216 MOZ_ASSERT(NS_IsMainThread());
217 VsyncSource::Display
& aNewDisplay
= aNewSource
->GetGlobalDisplay();
218 MutexAutoLock
lock(mDispatcherLock
);
219 MutexAutoLock
newLock(aNewDisplay
.mDispatcherLock
);
220 aNewDisplay
.mRegisteredCompositorVsyncDispatchers
.AppendElements(
221 std::move(mRegisteredCompositorVsyncDispatchers
));
222 aNewDisplay
.mEnabledCompositorVsyncDispatchers
.AppendElements(
223 std::move(mEnabledCompositorVsyncDispatchers
));
224 aNewDisplay
.mGenericObservers
.AppendElements(std::move(mGenericObservers
));
227 i
< aNewDisplay
.mRegisteredCompositorVsyncDispatchers
.Length(); i
++) {
228 aNewDisplay
.mRegisteredCompositorVsyncDispatchers
[i
]->MoveToSource(
232 aNewDisplay
.mRefreshTimerVsyncDispatcher
= mRefreshTimerVsyncDispatcher
;
233 mRefreshTimerVsyncDispatcher
->MoveToDisplay(&aNewDisplay
);
234 mRefreshTimerVsyncDispatcher
= nullptr;
237 void VsyncSource::Display::NotifyRefreshTimerVsyncStatus(bool aEnable
) {
238 MOZ_ASSERT(NS_IsMainThread());
239 mRefreshTimerNeedsVsync
= aEnable
;
243 void VsyncSource::Display::UpdateVsyncStatus() {
244 MOZ_ASSERT(NS_IsMainThread());
245 // WARNING: This function SHOULD NOT BE CALLED WHILE HOLDING LOCKS
246 // NotifyVsync grabs a lock to dispatch vsync events
247 // When disabling vsync, we wait for the underlying thread to stop on some
248 // platforms We can deadlock if we wait for the underlying vsync thread to
249 // stop while the vsync thread is in NotifyVsync.
250 bool enableVsync
= false;
252 MutexAutoLock
lock(mDispatcherLock
);
253 enableVsync
= !mEnabledCompositorVsyncDispatchers
.IsEmpty() ||
254 mRefreshTimerNeedsVsync
|| !mGenericObservers
.IsEmpty();
255 mHasGenericObservers
= !mGenericObservers
.IsEmpty();
264 if (IsVsyncEnabled() != enableVsync
) {
265 NS_WARNING("Vsync status did not change.");
269 RefPtr
<RefreshTimerVsyncDispatcher
>
270 VsyncSource::Display::GetRefreshTimerVsyncDispatcher() {
271 return mRefreshTimerVsyncDispatcher
;
274 void VsyncSource::Shutdown() { GetGlobalDisplay().Shutdown(); }
277 } // namespace mozilla