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