/src/mozilla-central/gfx/layers/ipc/CompositorVsyncScheduler.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "mozilla/layers/CompositorVsyncScheduler.h" |
8 | | |
9 | | #include <stdio.h> // for fprintf, stdout |
10 | | #include <stdint.h> // for uint64_t |
11 | | #include "base/task.h" // for CancelableTask, etc |
12 | | #include "base/thread.h" // for Thread |
13 | | #include "gfxPlatform.h" // for gfxPlatform |
14 | | #ifdef MOZ_WIDGET_GTK |
15 | | #include "gfxPlatformGtk.h" // for gfxPlatform |
16 | | #endif |
17 | | #include "gfxPrefs.h" // for gfxPrefs |
18 | | #include "mozilla/AutoRestore.h" // for AutoRestore |
19 | | #include "mozilla/DebugOnly.h" // for DebugOnly |
20 | | #include "mozilla/gfx/2D.h" // for DrawTarget |
21 | | #include "mozilla/gfx/Point.h" // for IntSize |
22 | | #include "mozilla/gfx/Rect.h" // for IntSize |
23 | | #include "mozilla/layers/CompositorThread.h" |
24 | | #include "mozilla/layers/CompositorVsyncSchedulerOwner.h" |
25 | | #include "mozilla/mozalloc.h" // for operator new, etc |
26 | | #include "nsCOMPtr.h" // for already_AddRefed |
27 | | #include "nsDebug.h" // for NS_ASSERTION, etc |
28 | | #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc |
29 | | #include "nsIWidget.h" // for nsIWidget |
30 | | #include "nsThreadUtils.h" // for NS_IsMainThread |
31 | | #include "mozilla/Telemetry.h" |
32 | | #include "mozilla/VsyncDispatcher.h" |
33 | | #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) |
34 | | #include "VsyncSource.h" |
35 | | #endif |
36 | | #include "mozilla/widget/CompositorWidget.h" |
37 | | #include "VRManager.h" |
38 | | #include "VRThread.h" |
39 | | |
40 | | namespace mozilla { |
41 | | |
42 | | namespace layers { |
43 | | |
44 | | using namespace mozilla::gfx; |
45 | | using namespace std; |
46 | | |
47 | | CompositorVsyncScheduler::Observer::Observer(CompositorVsyncScheduler* aOwner) |
48 | | : mMutex("CompositorVsyncScheduler.Observer.Mutex") |
49 | | , mOwner(aOwner) |
50 | 0 | { |
51 | 0 | } |
52 | | |
53 | | CompositorVsyncScheduler::Observer::~Observer() |
54 | 0 | { |
55 | 0 | MOZ_ASSERT(!mOwner); |
56 | 0 | } |
57 | | |
58 | | bool |
59 | | CompositorVsyncScheduler::Observer::NotifyVsync(TimeStamp aVsyncTimestamp) |
60 | 0 | { |
61 | 0 | MutexAutoLock lock(mMutex); |
62 | 0 | if (!mOwner) { |
63 | 0 | return false; |
64 | 0 | } |
65 | 0 | return mOwner->NotifyVsync(aVsyncTimestamp); |
66 | 0 | } |
67 | | |
68 | | void |
69 | | CompositorVsyncScheduler::Observer::Destroy() |
70 | 0 | { |
71 | 0 | MutexAutoLock lock(mMutex); |
72 | 0 | mOwner = nullptr; |
73 | 0 | } |
74 | | |
75 | | CompositorVsyncScheduler::CompositorVsyncScheduler(CompositorVsyncSchedulerOwner* aVsyncSchedulerOwner, |
76 | | widget::CompositorWidget* aWidget) |
77 | | : mVsyncSchedulerOwner(aVsyncSchedulerOwner) |
78 | | , mLastCompose(TimeStamp::Now()) |
79 | | , mIsObservingVsync(false) |
80 | | , mNeedsComposite(0) |
81 | | , mVsyncNotificationsSkipped(0) |
82 | | , mWidget(aWidget) |
83 | | , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor") |
84 | | , mCurrentCompositeTask(nullptr) |
85 | | , mCurrentVRListenerTaskMonitor("CurrentVRTaskMonitor") |
86 | | , mCurrentVRListenerTask(nullptr) |
87 | 0 | { |
88 | 0 | mVsyncObserver = new Observer(this); |
89 | 0 |
|
90 | 0 | // mAsapScheduling is set on the main thread during init, |
91 | 0 | // but is only accessed after on the compositor thread. |
92 | 0 | mAsapScheduling = gfxPrefs::LayersCompositionFrameRate() == 0 || |
93 | 0 | gfxPlatform::IsInLayoutAsapMode() || |
94 | 0 | recordreplay::IsRecordingOrReplaying(); |
95 | 0 | } |
96 | | |
97 | | CompositorVsyncScheduler::~CompositorVsyncScheduler() |
98 | 0 | { |
99 | 0 | MOZ_ASSERT(!mIsObservingVsync); |
100 | 0 | MOZ_ASSERT(!mVsyncObserver); |
101 | 0 | // The CompositorVsyncDispatcher is cleaned up before this in the nsBaseWidget, which stops vsync listeners |
102 | 0 | mVsyncSchedulerOwner = nullptr; |
103 | 0 | } |
104 | | |
105 | | void |
106 | | CompositorVsyncScheduler::Destroy() |
107 | 0 | { |
108 | 0 | MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); |
109 | 0 |
|
110 | 0 | if (!mVsyncObserver) { |
111 | 0 | // Destroy was already called on this object. |
112 | 0 | return; |
113 | 0 | } |
114 | 0 | UnobserveVsync(); |
115 | 0 | mVsyncObserver->Destroy(); |
116 | 0 | mVsyncObserver = nullptr; |
117 | 0 |
|
118 | 0 | mNeedsComposite = 0; |
119 | 0 | CancelCurrentCompositeTask(); |
120 | 0 | } |
121 | | |
122 | | void |
123 | | CompositorVsyncScheduler::PostCompositeTask(TimeStamp aCompositeTimestamp) |
124 | 0 | { |
125 | 0 | MonitorAutoLock lock(mCurrentCompositeTaskMonitor); |
126 | 0 | if (mCurrentCompositeTask == nullptr && CompositorThreadHolder::Loop()) { |
127 | 0 | RefPtr<CancelableRunnable> task = NewCancelableRunnableMethod<TimeStamp>( |
128 | 0 | "layers::CompositorVsyncScheduler::Composite", |
129 | 0 | this, |
130 | 0 | &CompositorVsyncScheduler::Composite, |
131 | 0 | aCompositeTimestamp); |
132 | 0 | mCurrentCompositeTask = task; |
133 | 0 | ScheduleTask(task.forget()); |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | | void |
138 | | CompositorVsyncScheduler::PostVRTask(TimeStamp aTimestamp) |
139 | 0 | { |
140 | 0 | MonitorAutoLock lockVR(mCurrentVRListenerTaskMonitor); |
141 | 0 | if (mCurrentVRListenerTask == nullptr && VRListenerThreadHolder::Loop()) { |
142 | 0 | RefPtr<Runnable> task = NewRunnableMethod<TimeStamp>( |
143 | 0 | "layers::CompositorVsyncScheduler::DispatchVREvents", |
144 | 0 | this, |
145 | 0 | &CompositorVsyncScheduler::DispatchVREvents, |
146 | 0 | aTimestamp); |
147 | 0 | mCurrentVRListenerTask = task; |
148 | 0 | VRListenerThreadHolder::Loop()->PostDelayedTask(task.forget(), 0); |
149 | 0 | } |
150 | 0 | } |
151 | | |
152 | | void |
153 | | CompositorVsyncScheduler::ScheduleComposition() |
154 | 0 | { |
155 | 0 | MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); |
156 | 0 | if (!mVsyncObserver) { |
157 | 0 | // Destroy was already called on this object. |
158 | 0 | return; |
159 | 0 | } |
160 | 0 | |
161 | 0 | if (mAsapScheduling) { |
162 | 0 | // Used only for performance testing purposes, and when recording/replaying |
163 | 0 | // to ensure that graphics are up to date. |
164 | 0 | PostCompositeTask(TimeStamp::Now()); |
165 | | #ifdef MOZ_WIDGET_ANDROID |
166 | | } else if (mNeedsComposite >= 2 && mIsObservingVsync) { |
167 | | // uh-oh, we already requested a composite at least twice so far, and a |
168 | | // composite hasn't happened yet. It is possible that the vsync observation |
169 | | // is blocked on the main thread, so let's just composite ASAP and not |
170 | | // wait for the vsync. Note that this should only ever happen on Fennec |
171 | | // because there content runs in the same process as the compositor, and so |
172 | | // content can actually block the main thread in this process. |
173 | | PostCompositeTask(TimeStamp::Now()); |
174 | | #endif |
175 | 0 | } else { |
176 | 0 | mNeedsComposite++; |
177 | 0 | if (!mIsObservingVsync && mNeedsComposite) { |
178 | 0 | ObserveVsync(); |
179 | 0 | // Starting to observe vsync is an async operation that goes |
180 | 0 | // through the main thread of the UI process. It's possible that |
181 | 0 | // we're blocking there waiting on a composite, so schedule an initial |
182 | 0 | // one now to get things started. |
183 | 0 | PostCompositeTask(TimeStamp::Now()); |
184 | 0 | } |
185 | 0 | } |
186 | 0 | } |
187 | | |
188 | | bool |
189 | | CompositorVsyncScheduler::NotifyVsync(TimeStamp aVsyncTimestamp) |
190 | 0 | { |
191 | 0 | // Called from the vsync dispatch thread. When in the GPU Process, that's |
192 | 0 | // the same as the compositor thread. |
193 | 0 | MOZ_ASSERT_IF(XRE_IsParentProcess(), !CompositorThreadHolder::IsInCompositorThread()); |
194 | 0 | MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_GPU, CompositorThreadHolder::IsInCompositorThread()); |
195 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
196 | 0 | PostCompositeTask(aVsyncTimestamp); |
197 | 0 | PostVRTask(aVsyncTimestamp); |
198 | 0 | return true; |
199 | 0 | } |
200 | | |
201 | | void |
202 | | CompositorVsyncScheduler::CancelCurrentCompositeTask() |
203 | 0 | { |
204 | 0 | MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || NS_IsMainThread()); |
205 | 0 | MonitorAutoLock lock(mCurrentCompositeTaskMonitor); |
206 | 0 | if (mCurrentCompositeTask) { |
207 | 0 | mCurrentCompositeTask->Cancel(); |
208 | 0 | mCurrentCompositeTask = nullptr; |
209 | 0 | } |
210 | 0 | } |
211 | | |
212 | | void |
213 | | CompositorVsyncScheduler::Composite(TimeStamp aVsyncTimestamp) |
214 | 0 | { |
215 | 0 | MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); |
216 | 0 | MOZ_ASSERT(mVsyncSchedulerOwner); |
217 | 0 |
|
218 | 0 | { // scope lock |
219 | 0 | MonitorAutoLock lock(mCurrentCompositeTaskMonitor); |
220 | 0 | mCurrentCompositeTask = nullptr; |
221 | 0 | } |
222 | 0 |
|
223 | 0 | if (!mAsapScheduling) { |
224 | 0 | // Some early exit conditions if we're not in ASAP mode |
225 | 0 | if (aVsyncTimestamp < mLastCompose) { |
226 | 0 | // We can sometimes get vsync timestamps that are in the past |
227 | 0 | // compared to the last compose with force composites. |
228 | 0 | // In those cases, wait until the next vsync; |
229 | 0 | return; |
230 | 0 | } |
231 | 0 | |
232 | 0 | if (mVsyncSchedulerOwner->IsPendingComposite()) { |
233 | 0 | // If previous composite is still on going, finish it and wait for the |
234 | 0 | // next vsync. |
235 | 0 | mVsyncSchedulerOwner->FinishPendingComposite(); |
236 | 0 | return; |
237 | 0 | } |
238 | 0 | } |
239 | 0 | |
240 | 0 | if (mNeedsComposite || mAsapScheduling) { |
241 | 0 | mNeedsComposite = 0; |
242 | 0 | mLastCompose = aVsyncTimestamp; |
243 | 0 |
|
244 | 0 | // Tell the owner to do a composite |
245 | 0 | mVsyncSchedulerOwner->CompositeToTarget(nullptr, nullptr); |
246 | 0 |
|
247 | 0 | mVsyncNotificationsSkipped = 0; |
248 | 0 |
|
249 | 0 | TimeDuration compositeFrameTotal = TimeStamp::Now() - aVsyncTimestamp; |
250 | 0 | mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_FRAME_ROUNDTRIP_TIME, |
251 | 0 | compositeFrameTotal.ToMilliseconds()); |
252 | 0 | } else if (mVsyncNotificationsSkipped++ > gfxPrefs::CompositorUnobserveCount()) { |
253 | 0 | UnobserveVsync(); |
254 | 0 | } |
255 | 0 | } |
256 | | |
257 | | void |
258 | | CompositorVsyncScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect) |
259 | 0 | { |
260 | 0 | MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); |
261 | 0 |
|
262 | 0 | /** |
263 | 0 | * bug 1138502 - There are cases such as during long-running window resizing |
264 | 0 | * events where we receive many force-composites. We also continue to get |
265 | 0 | * vsync notifications. Because the force-composites trigger compositing and |
266 | 0 | * clear the mNeedsComposite counter, the vsync notifications will not need |
267 | 0 | * to do anything and so will increment the mVsyncNotificationsSkipped counter |
268 | 0 | * to indicate the vsync was ignored. If this happens enough times, we will |
269 | 0 | * disable listening for vsync entirely. On the next force-composite we will |
270 | 0 | * enable listening for vsync again, and continued force-composites and vsyncs |
271 | 0 | * will cause oscillation between observing vsync and not. |
272 | 0 | * On some platforms, enabling/disabling vsync is not free and this |
273 | 0 | * oscillating behavior causes a performance hit. In order to avoid this |
274 | 0 | * problem, we reset the mVsyncNotificationsSkipped counter to keep vsync |
275 | 0 | * enabled. |
276 | 0 | */ |
277 | 0 | mVsyncNotificationsSkipped = 0; |
278 | 0 |
|
279 | 0 | mLastCompose = TimeStamp::Now(); |
280 | 0 | MOZ_ASSERT(mVsyncSchedulerOwner); |
281 | 0 | mVsyncSchedulerOwner->CompositeToTarget(aTarget, aRect); |
282 | 0 | } |
283 | | |
284 | | bool |
285 | | CompositorVsyncScheduler::NeedsComposite() |
286 | 0 | { |
287 | 0 | MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); |
288 | 0 | return mNeedsComposite; |
289 | 0 | } |
290 | | |
291 | | bool |
292 | | CompositorVsyncScheduler::FlushPendingComposite() |
293 | 0 | { |
294 | 0 | MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); |
295 | 0 | if (mNeedsComposite) { |
296 | 0 | CancelCurrentCompositeTask(); |
297 | 0 | ForceComposeToTarget(nullptr, nullptr); |
298 | 0 | return true; |
299 | 0 | } |
300 | 0 | return false; |
301 | 0 | } |
302 | | |
303 | | void |
304 | | CompositorVsyncScheduler::ObserveVsync() |
305 | 0 | { |
306 | 0 | MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); |
307 | 0 | mWidget->ObserveVsync(mVsyncObserver); |
308 | 0 | mIsObservingVsync = true; |
309 | 0 | } |
310 | | |
311 | | void |
312 | | CompositorVsyncScheduler::UnobserveVsync() |
313 | 0 | { |
314 | 0 | MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); |
315 | 0 | mWidget->ObserveVsync(nullptr); |
316 | 0 | mIsObservingVsync = false; |
317 | 0 | } |
318 | | |
319 | | void |
320 | | CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp) |
321 | 0 | { |
322 | 0 | { |
323 | 0 | MonitorAutoLock lock(mCurrentVRListenerTaskMonitor); |
324 | 0 | mCurrentVRListenerTask = nullptr; |
325 | 0 | } |
326 | 0 | // This only allows to be called by CompositorVsyncScheduler::PostVRTask() |
327 | 0 | // When the process is going to shutdown, the runnable has chance to be executed |
328 | 0 | // by other threads, we only want it to be run at VRListenerThread. |
329 | 0 | if (!VRListenerThreadHolder::IsInVRListenerThread()) { |
330 | 0 | return; |
331 | 0 | } |
332 | 0 | |
333 | 0 | VRManager* vm = VRManager::Get(); |
334 | 0 | vm->NotifyVsync(aVsyncTimestamp); |
335 | 0 | } |
336 | | |
337 | | void |
338 | | CompositorVsyncScheduler::ScheduleTask(already_AddRefed<CancelableRunnable> aTask) |
339 | 0 | { |
340 | 0 | MOZ_ASSERT(CompositorThreadHolder::Loop()); |
341 | 0 | CompositorThreadHolder::Loop()->PostDelayedTask(std::move(aTask), 0); |
342 | 0 | } |
343 | | |
344 | | const TimeStamp& |
345 | | CompositorVsyncScheduler::GetLastComposeTime() const |
346 | 0 | { |
347 | 0 | MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); |
348 | 0 | return mLastCompose; |
349 | 0 | } |
350 | | |
351 | | void |
352 | | CompositorVsyncScheduler::UpdateLastComposeTime() |
353 | 0 | { |
354 | 0 | MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); |
355 | 0 | mLastCompose = TimeStamp::Now(); |
356 | 0 | } |
357 | | |
358 | | } // namespace layers |
359 | | } // namespace mozilla |