Bug 1169880 - Recompute image visibility on a timer if layout or style flushes have occurred. r=tn, a=jocheng
authorSeth Fowler <mark.seth.fowler@gmail.com>
Fri, 29 May 2015 23:50:51 -0700
changeset 238569 a4a5092bd193125cf81bdc38a4e7be37979a13b1
parent 238568 1f88b045ac6ccbb938da92f0f0f84fe2dee05dd5
child 238570 5b3f1796ddf64171475ab4c0e06604d19fa1fe8b
push id672
push userryanvm@gmail.com
push dateTue, 02 Jun 2015 17:36:08 +0000
treeherdermozilla-b2g37_v2_2@5b3f1796ddf6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn, jocheng
bugs1169880
milestone37.0
Bug 1169880 - Recompute image visibility on a timer if layout or style flushes have occurred. r=tn, a=jocheng
layout/base/nsRefreshDriver.cpp
layout/base/nsRefreshDriver.h
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -77,16 +77,17 @@ using namespace mozilla::layout;
 static PRLogModuleInfo *gLog = nullptr;
 #define LOG(...) PR_LOG(gLog, PR_LOG_NOTICE, (__VA_ARGS__))
 #else
 #define LOG(...) do { } while(0)
 #endif
 
 #define DEFAULT_FRAME_RATE 60
 #define DEFAULT_THROTTLED_FRAME_RATE 1
+#define DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS 1000
 // after 10 minutes, stop firing off inactive timers
 #define DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS 600
 
 namespace mozilla {
 
 /*
  * The base class for all global refresh driver timers.  It takes care
  * of managing the list of refresh drivers attached to them and
@@ -967,16 +968,27 @@ nsRefreshDriver::GetThrottledTimerInterv
 {
   int32_t rate = Preferences::GetInt("layout.throttled_frame_rate", -1);
   if (rate <= 0) {
     rate = DEFAULT_THROTTLED_FRAME_RATE;
   }
   return 1000.0 / rate;
 }
 
+/* static */ mozilla::TimeDuration
+nsRefreshDriver::GetMinRecomputeVisibilityInterval()
+{
+  int32_t interval =
+    Preferences::GetInt("layout.visibility.min-recompute-interval-ms", -1);
+  if (interval <= 0) {
+    interval = DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS;
+  }
+  return TimeDuration::FromMilliseconds(interval);
+}
+
 double
 nsRefreshDriver::GetRefreshTimerInterval() const
 {
   return mThrottled ? GetThrottledTimerInterval() : GetRegularTimerInterval();
 }
 
 RefreshDriverTimer*
 nsRefreshDriver::ChooseTimer() const
@@ -1011,27 +1023,30 @@ nsRefreshDriver::nsRefreshDriver(nsPresC
   : mActiveTimer(nullptr),
     mReflowCause(nullptr),
     mStyleCause(nullptr),
     mPresContext(aPresContext),
     mRootRefresh(nullptr),
     mPendingTransaction(0),
     mCompletedTransaction(0),
     mFreezeCount(0),
+    mMinRecomputeVisibilityInterval(GetMinRecomputeVisibilityInterval()),
     mThrottled(false),
+    mNeedToRecomputeVisibility(false),
     mTestControllingRefreshes(false),
     mViewManagerFlushIsPending(false),
     mRequestedHighPrecision(false),
     mInRefresh(false),
     mWaitingForTransaction(false),
     mSkippedPaints(false)
 {
   mMostRecentRefreshEpochTime = JS_Now();
   mMostRecentRefresh = TimeStamp::Now();
   mMostRecentTick = mMostRecentRefresh;
+  mNextRecomputeVisibilityTick = mMostRecentTick;
 }
 
 nsRefreshDriver::~nsRefreshDriver()
 {
   NS_ABORT_IF_FALSE(ObserverCount() == 0,
                     "observers should have unregistered");
   NS_ABORT_IF_FALSE(!mActiveTimer, "timer should be gone");
   
@@ -1616,16 +1631,18 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
           }
           NS_RELEASE(shell);
 
           if (docShell) {
             docShell->AddProfileTimelineMarker("Styles", TRACING_INTERVAL_END);
           }
         }
 
+        mNeedToRecomputeVisibility = true;
+
         if (tracingStyleFlush) {
           profiler_tracing("Paint", "Styles", TRACING_INTERVAL_END);
         }
       }
 
       if (!nsLayoutUtils::AreAsyncAnimationsEnabled()) {
         mPresContext->TickLastStyleUpdateForAllAnimations();
       }
@@ -1661,23 +1678,36 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
           // ready promise if it needs to.
           nsPresContext* presContext = shell->GetPresContext();
           if (presContext) {
             presContext->NotifyFontFaceSetOnRefresh();
           }
           NS_RELEASE(shell);
         }
 
+        mNeedToRecomputeVisibility = true;
+
         if (tracingLayoutFlush) {
           profiler_tracing("Paint", "Reflow", TRACING_INTERVAL_END);
         }
       }
     }
   }
 
+  // Recompute image visibility if it's necessary and enough time has passed
+  // since the last time we did it.
+  if (mNeedToRecomputeVisibility && !mThrottled &&
+      aNowTime >= mNextRecomputeVisibilityTick &&
+      !presShell->IsPaintingSuppressed()) {
+    mNextRecomputeVisibilityTick = aNowTime + mMinRecomputeVisibilityInterval;
+    mNeedToRecomputeVisibility = false;
+
+    presShell->ScheduleImageVisibilityUpdate();
+  }
+
   /*
    * Perform notification to imgIRequests subscribed to listen
    * for refresh events.
    */
 
   ImageRequestParameters parms = {aNowTime, previousRefresh, &mRequests};
 
   mStartTable.EnumerateRead(nsRefreshDriver::StartTableRefresh, &parms);
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -332,16 +332,18 @@ private:
   ObserverArray& ArrayFor(mozFlushType aFlushType);
   // Trigger a refresh immediately, if haven't been disconnected or frozen.
   void DoRefresh();
 
   double GetRefreshTimerInterval() const;
   double GetRegularTimerInterval(bool *outIsDefault = nullptr) const;
   double GetThrottledTimerInterval() const;
 
+  static mozilla::TimeDuration GetMinRecomputeVisibilityInterval();
+
   bool HaveFrameRequestCallbacks() const {
     return mFrameRequestCallbackDocs.Length() != 0;
   }
 
   void FinishedWaitingForTransaction();
 
   mozilla::RefreshDriverTimer* ChooseTimer() const;
   mozilla::RefreshDriverTimer* mActiveTimer;
@@ -355,17 +357,25 @@ private:
   nsRefPtr<nsRefreshDriver> mRootRefresh;
 
   // The most recently allocated transaction id.
   uint64_t mPendingTransaction;
   // The most recently completed transaction id.
   uint64_t mCompletedTransaction;
 
   uint32_t mFreezeCount;
+
+  // How long we wait, at a minimum, before recomputing image visibility
+  // information. This is a minimum because, regardless of this interval, we
+  // only recompute visibility when we've seen a layout or style flush since the
+  // last time we did it.
+  const mozilla::TimeDuration mMinRecomputeVisibilityInterval;
+
   bool mThrottled;
+  bool mNeedToRecomputeVisibility;
   bool mTestControllingRefreshes;
   bool mViewManagerFlushIsPending;
   bool mRequestedHighPrecision;
   bool mInRefresh;
 
   // True if the refresh driver is suspended waiting for transaction
   // id's to be returned and shouldn't do any work during Tick().
   bool mWaitingForTransaction;
@@ -373,16 +383,17 @@ private:
   // we should schedule a new Tick immediately when resumed instead
   // of waiting until the next interval.
   bool mSkippedPaints;
 
   int64_t mMostRecentRefreshEpochTime;
   mozilla::TimeStamp mMostRecentRefresh;
   mozilla::TimeStamp mMostRecentTick;
   mozilla::TimeStamp mTickStart;
+  mozilla::TimeStamp mNextRecomputeVisibilityTick;
 
   // separate arrays for each flush type we support
   ObserverArray mObservers[3];
   RequestTable mRequests;
   ImageStartTable mStartTable;
 
   nsAutoTArray<nsIPresShell*, 16> mStyleFlushObservers;
   nsAutoTArray<nsIPresShell*, 16> mLayoutFlushObservers;