Bug 1769547 - Do not MOZ_CRASH() on missing process r=nika
[gecko.git] / widget / VsyncDispatcher.cpp
blobcf8430fafacd3444d9b8ab73c695d3b5c69a4495
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 void VsyncDispatcher::NotifyVsync(const VsyncEvent& aVsync) {
129 if (++mVsyncSkipCounter < StaticPrefs::gfx_display_frame_rate_divisor()) {
130 return;
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);
173 { // Scope lock
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);
188 UpdateVsyncStatus();
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);
198 UpdateVsyncStatus();
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);
209 UpdateVsyncStatus();
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);
220 UpdateVsyncStatus();
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;
231 needVsync =
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