Bug 1338347 - Record frame throughput ratios for compositor animations r=botond draft
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 20 Mar 2017 11:41:05 -0400
changeset 501572 92e634c0afc4dedf67440daabd8b070d7db4065e
parent 501571 4db7db2c0b17701d5fe3fc6098d32046f3ae8dd8
child 501573 81dc51357f2bd13a33296b317164233c28088a77
push id50031
push userkgupta@mozilla.com
push dateMon, 20 Mar 2017 15:42:18 +0000
reviewersbotond
bugs1338347
milestone55.0a1
Bug 1338347 - Record frame throughput ratios for compositor animations r=botond This augments the AnimationMetricsTracker to also track compositor animations triggered by chrome and content layers separately. During the animation, the tracker keeps a count of frames composited, and once the animation ends, it uses the wall-clock time and vsync interval to compute the expected number of composited frames. It then submits a ratio of actual/expected to telemetry. A score of 1000 (because the ratio is scaled up to an integer between 0 and 1000) indicates a perfect score with no frames dropped. Lower values are worse, and values significantly above 1000 indicate abnormal behaviour. Values may be slightly above 1000 due to rounding error or vsync jitter. MozReview-Commit-ID: 30Vw0j3dm9G
gfx/layers/composite/AnimationMetricsTracker.cpp
gfx/layers/composite/AnimationMetricsTracker.h
gfx/layers/composite/AsyncCompositionManager.cpp
--- a/gfx/layers/composite/AnimationMetricsTracker.cpp
+++ b/gfx/layers/composite/AnimationMetricsTracker.cpp
@@ -1,63 +1,139 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/AnimationMetricsTracker.h"
 
 #include <algorithm>
+#include <cmath>
 #include <inttypes.h>
-#include "mozilla/Telemetry.h"
 
 #define AMT_LOG(...)
 // #define AMT_LOG(...) printf_stderr("AMT: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
 AnimationMetricsTracker::AnimationMetricsTracker()
+  : mMaxLayerAreaAnimated(0)
+  , mChromeAnimationFrameCount(0)
+  , mContentAnimationFrameCount(0)
 {
 }
 
 AnimationMetricsTracker::~AnimationMetricsTracker()
 {
 }
 
 void
-AnimationMetricsTracker::UpdateAnimationInProgress(bool aInProgress,
-                                                   uint64_t aLayerArea)
+AnimationMetricsTracker::UpdateAnimationInProgress(AnimationProcessTypes aActive,
+                                                   uint64_t aLayerArea,
+                                                   TimeDuration aVsyncInterval)
 {
-  MOZ_ASSERT(aInProgress || aLayerArea == 0);
-  if (mCurrentAnimationStart && !aInProgress) {
+  bool inProgress = (aActive != AnimationProcessTypes::eNone);
+  MOZ_ASSERT(inProgress || aLayerArea == 0);
+  if (mCurrentAnimationStart && !inProgress) {
     AnimationEnded();
     mCurrentAnimationStart = TimeStamp();
     mMaxLayerAreaAnimated = 0;
-  } else if (aInProgress) {
+  } else if (inProgress) {
     if (!mCurrentAnimationStart) {
       mCurrentAnimationStart = TimeStamp::Now();
       mMaxLayerAreaAnimated = aLayerArea;
       AnimationStarted();
     } else {
       mMaxLayerAreaAnimated = std::max(mMaxLayerAreaAnimated, aLayerArea);
     }
   }
+
+  UpdateAnimationThroughput("chrome",
+                            (aActive & AnimationProcessTypes::eChrome) != AnimationProcessTypes::eNone,
+                            mChromeAnimationStart,
+                            mChromeAnimationFrameCount,
+                            aVsyncInterval,
+                            Telemetry::COMPOSITOR_ANIMATION_THROUGHPUT_CHROME);
+  UpdateAnimationThroughput("content",
+                            (aActive & AnimationProcessTypes::eContent) != AnimationProcessTypes::eNone,
+                            mContentAnimationStart,
+                            mContentAnimationFrameCount,
+                            aVsyncInterval,
+                            Telemetry::COMPOSITOR_ANIMATION_THROUGHPUT_CONTENT);
 }
 
 void
 AnimationMetricsTracker::AnimationStarted()
 {
 }
 
 void
 AnimationMetricsTracker::AnimationEnded()
 {
   MOZ_ASSERT(mCurrentAnimationStart);
+
   Telemetry::AccumulateTimeDelta(Telemetry::COMPOSITOR_ANIMATION_DURATION, mCurrentAnimationStart);
   Telemetry::Accumulate(Telemetry::COMPOSITOR_ANIMATION_MAX_LAYER_AREA, mMaxLayerAreaAnimated);
   AMT_LOG("Ended animation; duration: %f ms, area: %" PRIu64 "\n",
     (TimeStamp::Now() - mCurrentAnimationStart).ToMilliseconds(),
     mMaxLayerAreaAnimated);
 }
 
+void
+AnimationMetricsTracker::UpdateAnimationThroughput(const char* aLabel,
+                                                   bool aInProgress,
+                                                   TimeStamp& aStartTime,
+                                                   uint32_t& aFrameCount,
+                                                   TimeDuration aVsyncInterval,
+                                                   Telemetry::HistogramID aHistogram)
+{
+  if (aInProgress && !aStartTime) {
+    // the animation just started
+    aStartTime = TimeStamp::Now();
+    aFrameCount = 1;
+    AMT_LOG("Compositor animation of type %s just started\n", aLabel);
+  } else if (aInProgress && aStartTime) {
+    // the animation continues
+    aFrameCount++;
+  } else if (!aInProgress && aStartTime) {
+    // the animation just ended
+
+    // Get the length and clear aStartTime before the early-returns below
+    TimeDuration animationLength = TimeStamp::Now() - aStartTime;
+    aStartTime = TimeStamp();
+
+    if (aVsyncInterval == TimeDuration::Forever()) {
+      AMT_LOG("Invalid vsync interval: forever\n");
+      return;
+    }
+    double vsyncIntervalMs = aVsyncInterval.ToMilliseconds();
+    if (vsyncIntervalMs < 1.0f) {
+      // Guard to avoid division by zero or other crazy results below
+      AMT_LOG("Invalid vsync interval: %fms\n", vsyncIntervalMs);
+      return;
+    }
+
+    // We round the expectedFrameCount because it's a count and should be an
+    // integer. The animationLength might not be an exact vsync multiple because
+    // it's taken during the composition process and the amount of work done
+    // between the vsync signal and the Timestamp::Now() call may vary slightly
+    // from one composite to another.
+    uint32_t expectedFrameCount = std::lround(animationLength.ToMilliseconds() / vsyncIntervalMs);
+    AMT_LOG("Type %s ran for %fms (interval: %fms), %u frames (expected: %u)\n",
+        aLabel, animationLength.ToMilliseconds(), vsyncIntervalMs, aFrameCount,
+        expectedFrameCount);
+    if (expectedFrameCount <= 0) {
+      // Graceful handling of probably impossible thing, unless the clock
+      // changes while running?
+      return;
+    }
+
+    // Scale up by 1000 because telemetry takes ints, truncate intentionally
+    // to avoid artificial inflation of the result.
+    uint32_t frameHitRatio = (uint32_t)(1000.0f * aFrameCount / expectedFrameCount);
+    Telemetry::Accumulate(aHistogram, frameHitRatio);
+    AMT_LOG("Reported frameHitRatio %u\n", frameHitRatio);
+  }
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/composite/AnimationMetricsTracker.h
+++ b/gfx/layers/composite/AnimationMetricsTracker.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_AnimationMetricsTracker_h
 #define mozilla_layers_AnimationMetricsTracker_h
 
+#include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/TypedEnumBits.h"
 
 namespace mozilla {
 namespace layers {
 
 enum class AnimationProcessTypes {
   eNone = 0x0,
@@ -25,25 +26,47 @@ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(An
  */
 class AnimationMetricsTracker {
 public:
   AnimationMetricsTracker();
   ~AnimationMetricsTracker();
 
   /**
    * This function should be called per composite, to inform the metrics
-   * tracker if any animation is in progress, and if so, what area is
-   * being animated. The aLayerArea is in Layer pixels squared.
+   * tracker which processes have active animations. If there is are animations
+   * in progress, the sum of their areas should also be provided, along with
+   * the vsync interval.
    */
-  void UpdateAnimationInProgress(bool aInProgress, uint64_t aLayerArea);
+  void UpdateAnimationInProgress(AnimationProcessTypes aActive, uint64_t aLayerArea,
+                                 TimeDuration aVsyncInterval);
 
 private:
   void AnimationStarted();
   void AnimationEnded();
+  void UpdateAnimationThroughput(const char* aLabel,
+                                 bool aInProgress,
+                                 TimeStamp& aStartTime,
+                                 uint32_t& aFrameCount,
+                                 TimeDuration aVsyncInterval,
+                                 Telemetry::HistogramID aHistogram);
 
+  // The start time of the current compositor animation. This just tracks
+  // whether the compositor is running an animation, without regard to which
+  // process the animation is coming from.
   TimeStamp mCurrentAnimationStart;
+  // The max area (in layer pixels) that the current compositor animation
+  // has touched on any given animation frame.
   uint64_t mMaxLayerAreaAnimated;
+
+  // The start time of the current chrome-process animation.
+  TimeStamp mChromeAnimationStart;
+  // The number of frames composited for the current chrome-process animation.
+  uint32_t mChromeAnimationFrameCount;
+  // The start time of the current content-process animation.
+  TimeStamp mContentAnimationStart;
+  // The number of frames composited for the current content-process animation.
+  uint32_t mContentAnimationFrameCount;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_AnimationMetricsTracker_h
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -1362,17 +1362,17 @@ AsyncCompositionManager::TransformShadow
     SampleAnimations(root,
                      storage,
                      !mPreviousFrameTimeStamp.IsNull() ?
                        mPreviousFrameTimeStamp : aCurrentFrame,
                      &layerAreaAnimated);
   bool wantNextFrame = (animationProcess != AnimationProcessTypes::eNone);
 
   mAnimationMetricsTracker.UpdateAnimationInProgress(
-    wantNextFrame, layerAreaAnimated);
+    animationProcess, layerAreaAnimated, aVsyncRate);
 
   if (!wantNextFrame) {
     // Clean up the CompositorAnimationStorage because
     // there are no active animations running
     storage->Clear();
   }
 
   // Reset the previous time stamp if we don't already have any running