Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
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 : /*
8 : * Code to notify things that animate before a refresh, at an appropriate
9 : * refresh rate. (Perhaps temporary, until replaced by compositor.)
10 : */
11 :
12 : #ifndef nsRefreshDriver_h_
13 : #define nsRefreshDriver_h_
14 :
15 : #include "mozilla/FlushType.h"
16 : #include "mozilla/TimeStamp.h"
17 : #include "mozilla/Vector.h"
18 : #include "mozilla/WeakPtr.h"
19 : #include "nsTObserverArray.h"
20 : #include "nsTArray.h"
21 : #include "nsTHashtable.h"
22 : #include "nsTObserverArray.h"
23 : #include "nsClassHashtable.h"
24 : #include "nsHashKeys.h"
25 : #include "mozilla/Attributes.h"
26 : #include "mozilla/Maybe.h"
27 : #include "GeckoProfiler.h"
28 : #include "mozilla/layers/TransactionIdAllocator.h"
29 :
30 : class nsPresContext;
31 : class nsIPresShell;
32 : class nsIDocument;
33 : class imgIRequest;
34 : class nsIDOMEvent;
35 : class nsINode;
36 : class nsIRunnable;
37 :
38 : namespace mozilla {
39 : class RefreshDriverTimer;
40 : namespace layout {
41 : class VsyncChild;
42 : } // namespace layout
43 : } // namespace mozilla
44 :
45 : /**
46 : * An abstract base class to be implemented by callers wanting to be
47 : * notified at refresh times. When nothing needs to be painted, callers
48 : * may not be notified.
49 : */
50 73 : class nsARefreshObserver {
51 : public:
52 : // AddRef and Release signatures that match nsISupports. Implementors
53 : // must implement reference counting, and those that do implement
54 : // nsISupports will already have methods with the correct signature.
55 : //
56 : // The refresh driver does NOT hold references to refresh observers
57 : // except while it is notifying them.
58 : NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
59 :
60 : virtual void WillRefresh(mozilla::TimeStamp aTime) = 0;
61 : };
62 :
63 : /**
64 : * An abstract base class to be implemented by callers wanting to be notified
65 : * that a refresh has occurred. Callers must ensure an observer is removed
66 : * before it is destroyed.
67 : */
68 0 : class nsAPostRefreshObserver {
69 : public:
70 : virtual void DidRefresh() = 0;
71 : };
72 :
73 : class nsRefreshDriver final : public mozilla::layers::TransactionIdAllocator,
74 : public nsARefreshObserver
75 : {
76 : public:
77 : explicit nsRefreshDriver(nsPresContext *aPresContext);
78 : ~nsRefreshDriver();
79 :
80 : /**
81 : * Methods for testing, exposed via nsIDOMWindowUtils. See
82 : * nsIDOMWindowUtils.advanceTimeAndRefresh for description.
83 : */
84 : void AdvanceTimeAndRefresh(int64_t aMilliseconds);
85 : void RestoreNormalRefresh();
86 : void DoTick();
87 75 : bool IsTestControllingRefreshesEnabled() const
88 : {
89 75 : return mTestControllingRefreshes;
90 : }
91 :
92 : /**
93 : * Return the time of the most recent refresh. This is intended to be
94 : * used by callers who want to start an animation now and want to know
95 : * what time to consider the start of the animation. (This helps
96 : * ensure that multiple animations started during the same event off
97 : * the main event loop have the same start time.)
98 : */
99 : mozilla::TimeStamp MostRecentRefresh() const;
100 : /**
101 : * Same thing, but in microseconds since the epoch.
102 : */
103 : int64_t MostRecentRefreshEpochTime() const;
104 :
105 : /**
106 : * Add / remove refresh observers. Returns whether the operation
107 : * succeeded.
108 : *
109 : * The flush type affects:
110 : * + the order in which the observers are notified (lowest flush
111 : * type to highest, in order registered)
112 : * + (in the future) which observers are suppressed when the display
113 : * doesn't require current position data or isn't currently
114 : * painting, and, correspondingly, which get notified when there
115 : * is a flush during such suppression
116 : * and it must be FlushType::Style, FlushType::Layout, or FlushType::Display.
117 : *
118 : * The refresh driver does NOT own a reference to these observers;
119 : * they must remove themselves before they are destroyed.
120 : *
121 : * The observer will be called even if there is no other activity.
122 : */
123 : bool AddRefreshObserver(nsARefreshObserver *aObserver,
124 : mozilla::FlushType aFlushType);
125 : bool RemoveRefreshObserver(nsARefreshObserver *aObserver,
126 : mozilla::FlushType aFlushType);
127 :
128 : /**
129 : * Add an observer that will be called after each refresh. The caller
130 : * must remove the observer before it is deleted. This does not trigger
131 : * refresh driver ticks.
132 : */
133 : void AddPostRefreshObserver(nsAPostRefreshObserver *aObserver);
134 : void RemovePostRefreshObserver(nsAPostRefreshObserver *aObserver);
135 :
136 : /**
137 : * Add/Remove imgIRequest versions of observers.
138 : *
139 : * These are used for hooking into the refresh driver for
140 : * controlling animated images.
141 : *
142 : * @note The refresh driver owns a reference to these listeners.
143 : *
144 : * @note Technically, imgIRequest objects are not nsARefreshObservers, but
145 : * for controlling animated image repaint events, we subscribe the
146 : * imgIRequests to the nsRefreshDriver for notification of paint events.
147 : *
148 : * @returns whether the operation succeeded, or void in the case of removal.
149 : */
150 : bool AddImageRequest(imgIRequest* aRequest);
151 : void RemoveImageRequest(imgIRequest* aRequest);
152 :
153 : /**
154 : * Add / remove presshells that we should flush style and layout on
155 : */
156 38 : bool AddStyleFlushObserver(nsIPresShell* aShell) {
157 38 : NS_ASSERTION(!mStyleFlushObservers.Contains(aShell),
158 : "Double-adding style flush observer");
159 : // We only get the cause for the first observer each frame because capturing
160 : // a stack is expensive. This is still useful if (1) you're trying to remove
161 : // all flushes for a particial frame or (2) the costly flush is triggered
162 : // near the call site where the first observer is triggered.
163 38 : if (!mStyleCause) {
164 38 : mStyleCause = profiler_get_backtrace();
165 : }
166 38 : bool appended = mStyleFlushObservers.AppendElement(aShell) != nullptr;
167 38 : EnsureTimerStarted();
168 :
169 38 : return appended;
170 : }
171 4 : void RemoveStyleFlushObserver(nsIPresShell* aShell) {
172 4 : mStyleFlushObservers.RemoveElement(aShell);
173 4 : }
174 45 : bool AddLayoutFlushObserver(nsIPresShell* aShell) {
175 45 : NS_ASSERTION(!IsLayoutFlushObserver(aShell),
176 : "Double-adding layout flush observer");
177 : // We only get the cause for the first observer each frame because capturing
178 : // a stack is expensive. This is still useful if (1) you're trying to remove
179 : // all flushes for a particial frame or (2) the costly flush is triggered
180 : // near the call site where the first observer is triggered.
181 45 : if (!mReflowCause) {
182 45 : mReflowCause = profiler_get_backtrace();
183 : }
184 45 : bool appended = mLayoutFlushObservers.AppendElement(aShell) != nullptr;
185 45 : EnsureTimerStarted();
186 45 : return appended;
187 : }
188 4 : void RemoveLayoutFlushObserver(nsIPresShell* aShell) {
189 4 : mLayoutFlushObservers.RemoveElement(aShell);
190 4 : }
191 342 : bool IsLayoutFlushObserver(nsIPresShell* aShell) {
192 342 : return mLayoutFlushObservers.Contains(aShell);
193 : }
194 :
195 : /**
196 : * "Early Runner" runnables will be called as the first step when refresh
197 : * driver tick is triggered. Runners shouldn't keep other objects alive,
198 : * since it isn't guaranteed they will ever get called.
199 : */
200 2 : void AddEarlyRunner(nsIRunnable* aRunnable)
201 : {
202 2 : mEarlyRunners.AppendElement(aRunnable);
203 2 : EnsureTimerStarted();
204 2 : }
205 :
206 : /**
207 : * Remember whether our presshell's view manager needs a flush
208 : */
209 : void ScheduleViewManagerFlush();
210 39 : void RevokeViewManagerFlush() {
211 39 : mViewManagerFlushIsPending = false;
212 39 : }
213 0 : bool ViewManagerFlushIsPending() {
214 0 : return mViewManagerFlushIsPending;
215 : }
216 :
217 : /**
218 : * Add a document for which we have FrameRequestCallbacks
219 : */
220 : void ScheduleFrameRequestCallbacks(nsIDocument* aDocument);
221 :
222 : /**
223 : * Remove a document for which we have FrameRequestCallbacks
224 : */
225 : void RevokeFrameRequestCallbacks(nsIDocument* aDocument);
226 :
227 : /**
228 : * Queue a new event to dispatch in next tick before the style flush
229 : */
230 : void ScheduleEventDispatch(nsINode* aTarget, nsIDOMEvent* aEvent);
231 :
232 : /**
233 : * Cancel all pending events scheduled by ScheduleEventDispatch which
234 : * targets any node in aDocument.
235 : */
236 : void CancelPendingEvents(nsIDocument* aDocument);
237 :
238 : /**
239 : * Schedule a frame visibility update "soon", subject to the heuristics and
240 : * throttling we apply to visibility updates.
241 : */
242 0 : void ScheduleFrameVisibilityUpdate() { mNeedToRecomputeVisibility = true; }
243 :
244 : /**
245 : * Tell the refresh driver that it is done driving refreshes and
246 : * should stop its timer and forget about its pres context. This may
247 : * be called from within a refresh.
248 : */
249 : void Disconnect();
250 :
251 171 : bool IsFrozen() { return mFreezeCount > 0; }
252 :
253 : /**
254 : * Freeze the refresh driver. It should stop delivering future
255 : * refreshes until thawed. Note that the number of calls to Freeze() must
256 : * match the number of calls to Thaw() in order for the refresh driver to
257 : * be un-frozen.
258 : */
259 : void Freeze();
260 :
261 : /**
262 : * Thaw the refresh driver. If the number of calls to Freeze() matches the
263 : * number of calls to this function, the refresh driver should start
264 : * delivering refreshes again.
265 : */
266 : void Thaw();
267 :
268 : /**
269 : * Throttle or unthrottle the refresh driver. This is done if the
270 : * corresponding presshell is hidden or shown.
271 : */
272 : void SetThrottled(bool aThrottled);
273 :
274 : /**
275 : * Return the prescontext we were initialized with
276 : */
277 81 : nsPresContext* GetPresContext() const { return mPresContext; }
278 :
279 : /**
280 : * PBackgroundChild actor is created asynchronously in content process.
281 : * We can't create vsync-based timers during PBackground startup. This
282 : * function will be called when PBackgroundChild actor is created. Then we can
283 : * do the pending vsync-based timer creation.
284 : */
285 : static void PVsyncActorCreated(mozilla::layout::VsyncChild* aVsyncChild);
286 :
287 : #ifdef DEBUG
288 : /**
289 : * Check whether the given observer is an observer for the given flush type
290 : */
291 : bool IsRefreshObserver(nsARefreshObserver *aObserver,
292 : mozilla::FlushType aFlushType);
293 : #endif
294 :
295 : /**
296 : * Default interval the refresh driver uses, in ms.
297 : */
298 : static int32_t DefaultInterval();
299 :
300 20 : bool IsInRefresh() { return mInRefresh; }
301 :
302 0 : void SetIsResizeSuppressed() { mResizeSuppressed = true; }
303 56 : bool IsResizeSuppressed() const { return mResizeSuppressed; }
304 :
305 : /**
306 : * The latest value of process-wide jank levels.
307 : *
308 : * For each i, sJankLevels[i] counts the number of times delivery of
309 : * vsync to the main thread has been delayed by at least 2^i
310 : * ms. This data structure has been designed to make it easy to
311 : * determine how much jank has taken place between two instants in
312 : * time.
313 : *
314 : * Return `false` if `aJank` needs to be grown to accomodate the
315 : * data but we didn't have enough memory.
316 : */
317 : static bool GetJankLevels(mozilla::Vector<uint64_t>& aJank);
318 :
319 : // mozilla::layers::TransactionIdAllocator
320 : uint64_t GetTransactionId(bool aThrottle) override;
321 : uint64_t LastTransactionId() const override;
322 : void NotifyTransactionCompleted(uint64_t aTransactionId) override;
323 : void RevokeTransactionId(uint64_t aTransactionId) override;
324 : void ClearPendingTransactions() override;
325 : void ResetInitialTransactionId(uint64_t aTransactionId) override;
326 : mozilla::TimeStamp GetTransactionStart() override;
327 :
328 : bool IsWaitingForPaint(mozilla::TimeStamp aTime);
329 :
330 : // nsARefreshObserver
331 109 : NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override { return TransactionIdAllocator::AddRef(); }
332 81 : NS_IMETHOD_(MozExternalRefCountType) Release(void) override { return TransactionIdAllocator::Release(); }
333 : virtual void WillRefresh(mozilla::TimeStamp aTime) override;
334 :
335 : /**
336 : * Compute the time when the currently active refresh driver timer
337 : * will start its next tick.
338 : *
339 : * Expects a non-null default value that is the upper bound of the
340 : * expected deadline. If the next expected deadline is later than
341 : * the default value, the default value is returned.
342 : *
343 : * If we're animating and we have skipped paints a time in the past
344 : * is returned.
345 : */
346 : static mozilla::TimeStamp GetIdleDeadlineHint(mozilla::TimeStamp aDefault);
347 :
348 : static void DispatchIdleRunnableAfterTick(nsIRunnable* aRunnable,
349 : uint32_t aDelay);
350 : static void CancelIdleRunnable(nsIRunnable* aRunnable);
351 :
352 0 : bool SkippedPaints() const
353 : {
354 0 : return mSkippedPaints;
355 : }
356 :
357 : private:
358 : typedef nsTObserverArray<nsARefreshObserver*> ObserverArray;
359 : typedef nsTHashtable<nsISupportsHashKey> RequestTable;
360 0 : struct ImageStartData {
361 2 : ImageStartData()
362 2 : {
363 2 : }
364 :
365 : mozilla::Maybe<mozilla::TimeStamp> mStartTime;
366 : RequestTable mEntries;
367 : };
368 : typedef nsClassHashtable<nsUint32HashKey, ImageStartData> ImageStartTable;
369 :
370 : void DispatchPendingEvents();
371 : void DispatchAnimationEvents();
372 : void RunFrameRequestCallbacks(mozilla::TimeStamp aNowTime);
373 : void Tick(int64_t aNowEpoch, mozilla::TimeStamp aNowTime);
374 :
375 : enum EnsureTimerStartedFlags {
376 : eNone = 0,
377 : eForceAdjustTimer = 1 << 0,
378 : eAllowTimeToGoBackwards = 1 << 1,
379 : eNeverAdjustTimer = 1 << 2,
380 : };
381 : void EnsureTimerStarted(EnsureTimerStartedFlags aFlags = eNone);
382 : void StopTimer();
383 :
384 : uint32_t ObserverCount() const;
385 : uint32_t ImageRequestCount() const;
386 : ObserverArray& ArrayFor(mozilla::FlushType aFlushType);
387 : // Trigger a refresh immediately, if haven't been disconnected or frozen.
388 : void DoRefresh();
389 :
390 : double GetRefreshTimerInterval() const;
391 : double GetRegularTimerInterval(bool *outIsDefault = nullptr) const;
392 : static double GetThrottledTimerInterval();
393 :
394 : static mozilla::TimeDuration GetMinRecomputeVisibilityInterval();
395 :
396 : bool HaveFrameRequestCallbacks() const {
397 : return mFrameRequestCallbackDocs.Length() != 0;
398 : }
399 :
400 : void FinishedWaitingForTransaction();
401 :
402 : mozilla::RefreshDriverTimer* ChooseTimer() const;
403 : mozilla::RefreshDriverTimer* mActiveTimer;
404 :
405 : UniqueProfilerBacktrace mReflowCause;
406 : UniqueProfilerBacktrace mStyleCause;
407 :
408 : // nsPresContext passed in constructor and unset in Disconnect.
409 : mozilla::WeakPtr<nsPresContext> mPresContext;
410 :
411 : RefPtr<nsRefreshDriver> mRootRefresh;
412 :
413 : // The most recently allocated transaction id.
414 : uint64_t mPendingTransaction;
415 : // The most recently completed transaction id.
416 : uint64_t mCompletedTransaction;
417 :
418 : uint32_t mFreezeCount;
419 :
420 : // How long we wait between ticks for throttled (which generally means
421 : // non-visible) documents registered with a non-throttled refresh driver.
422 : const mozilla::TimeDuration mThrottledFrameRequestInterval;
423 :
424 : // How long we wait, at a minimum, before recomputing approximate frame
425 : // visibility information. This is a minimum because, regardless of this
426 : // interval, we only recompute visibility when we've seen a layout or style
427 : // flush since the last time we did it.
428 : const mozilla::TimeDuration mMinRecomputeVisibilityInterval;
429 :
430 : bool mThrottled;
431 : bool mNeedToRecomputeVisibility;
432 : bool mTestControllingRefreshes;
433 : bool mViewManagerFlushIsPending;
434 : bool mInRefresh;
435 :
436 : // True if the refresh driver is suspended waiting for transaction
437 : // id's to be returned and shouldn't do any work during Tick().
438 : bool mWaitingForTransaction;
439 : // True if Tick() was skipped because of mWaitingForTransaction and
440 : // we should schedule a new Tick immediately when resumed instead
441 : // of waiting until the next interval.
442 : bool mSkippedPaints;
443 :
444 : // True if view managers should delay any resize request until the
445 : // next tick by the refresh driver. This flag will be reset at the
446 : // start of every tick.
447 : bool mResizeSuppressed;
448 :
449 : int64_t mMostRecentRefreshEpochTime;
450 : // Number of seconds that the refresh driver is blocked waiting for a compositor
451 : // transaction to be completed before we append a note to the gfx critical log.
452 : // The number is doubled every time the threshold is hit.
453 : uint64_t mWarningThreshold;
454 : mozilla::TimeStamp mMostRecentRefresh;
455 : mozilla::TimeStamp mMostRecentTick;
456 : mozilla::TimeStamp mTickStart;
457 : mozilla::TimeStamp mNextThrottledFrameRequestTick;
458 : mozilla::TimeStamp mNextRecomputeVisibilityTick;
459 :
460 : // separate arrays for each flush type we support
461 : ObserverArray mObservers[3];
462 : RequestTable mRequests;
463 : ImageStartTable mStartTable;
464 : AutoTArray<nsCOMPtr<nsIRunnable>, 16> mEarlyRunners;
465 :
466 0 : struct PendingEvent {
467 : nsCOMPtr<nsINode> mTarget;
468 : nsCOMPtr<nsIDOMEvent> mEvent;
469 : };
470 :
471 : AutoTArray<nsIPresShell*, 16> mStyleFlushObservers;
472 : AutoTArray<nsIPresShell*, 16> mLayoutFlushObservers;
473 : // nsTArray on purpose, because we want to be able to swap.
474 : nsTArray<nsIDocument*> mFrameRequestCallbackDocs;
475 : nsTArray<nsIDocument*> mThrottledFrameRequestCallbackDocs;
476 : nsTObserverArray<nsAPostRefreshObserver*> mPostRefreshObservers;
477 : nsTArray<PendingEvent> mPendingEvents;
478 :
479 : void BeginRefreshingImages(RequestTable& aEntries,
480 : mozilla::TimeStamp aDesired);
481 :
482 : friend class mozilla::RefreshDriverTimer;
483 :
484 : static void Shutdown();
485 :
486 : // `true` if we are currently in jank-critical mode.
487 : //
488 : // In jank-critical mode, any iteration of the event loop that takes
489 : // more than 16ms to compute will cause an ongoing animation to miss
490 : // frames.
491 : //
492 : // For simplicity, the current implementation assumes that we are
493 : // in jank-critical mode if and only if the vsync driver has at least
494 : // one observer.
495 : static bool IsJankCritical();
496 : };
497 :
498 : #endif /* !defined(nsRefreshDriver_h_) */
|