Bug 1338347 - Record frame throughput ratios for compositor animations r=botond
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 20 Mar 2017 11:41:05 -0400
changeset 552137 6bb6ad363f65d95247fc6930f188b265792d5064
parent 552136 59f8c2560e6f96fdc70748540898b7b17ac207de
child 552138 02b734210c9c14c91ccd324074786a0ce68bd30a
push id51252
push userhikezoe@mozilla.com
push dateTue, 28 Mar 2017 02:10:27 +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