DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (1aeaa33a64f9)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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>

#define AMT_LOG(...)
// #define AMT_LOG(...) printf_stderr("AMT: " __VA_ARGS__)

namespace mozilla {
namespace layers {

AnimationMetricsTracker::AnimationMetricsTracker() : mMaxLayerAreaAnimated(0) {}

AnimationMetricsTracker::~AnimationMetricsTracker() {}

void AnimationMetricsTracker::UpdateAnimationInProgress(
    AnimationProcessTypes aActive, uint64_t aLayerArea,
    TimeDuration aVsyncInterval) {
  bool inProgress = (aActive != AnimationProcessTypes::eNone);
  MOZ_ASSERT(inProgress || aLayerArea == 0);
  if (mCurrentAnimationStart && !inProgress) {
    AnimationEnded();
    mCurrentAnimationStart = TimeStamp();
    mMaxLayerAreaAnimated = 0;
  } else if (inProgress) {
    if (!mCurrentAnimationStart) {
      mCurrentAnimationStart = TimeStamp::Now();
      mMaxLayerAreaAnimated = aLayerArea;
      AnimationStarted();
    } else {
      mMaxLayerAreaAnimated = std::max(mMaxLayerAreaAnimated, aLayerArea);
    }
  }

  UpdateAnimationThroughput(
      "chrome",
      (aActive & AnimationProcessTypes::eChrome) !=
          AnimationProcessTypes::eNone,
      mChromeAnimation, aVsyncInterval,
      Telemetry::COMPOSITOR_ANIMATION_THROUGHPUT_CHROME,
      Telemetry::COMPOSITOR_ANIMATION_MAX_CONTIGUOUS_DROPS_CHROME);
  UpdateAnimationThroughput(
      "content",
      (aActive & AnimationProcessTypes::eContent) !=
          AnimationProcessTypes::eNone,
      mContentAnimation, aVsyncInterval,
      Telemetry::COMPOSITOR_ANIMATION_THROUGHPUT_CONTENT,
      Telemetry::COMPOSITOR_ANIMATION_MAX_CONTIGUOUS_DROPS_CONTENT);
}

void AnimationMetricsTracker::UpdateApzAnimationInProgress(
    bool aInProgress, TimeDuration aVsyncInterval) {
  UpdateAnimationThroughput(
      "apz", aInProgress, mApzAnimation, aVsyncInterval,
      Telemetry::COMPOSITOR_ANIMATION_THROUGHPUT_APZ,
      Telemetry::COMPOSITOR_ANIMATION_MAX_CONTIGUOUS_DROPS_APZ);
}

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, AnimationData& aAnimation,
    TimeDuration aVsyncInterval, Telemetry::HistogramID aThroughputHistogram,
    Telemetry::HistogramID aMaxDropsHistogram) {
  if (aInProgress && !aAnimation.mStart) {
    // the animation just started
    aAnimation.mStart = TimeStamp::Now();
    aAnimation.mLastFrameTime = aAnimation.mStart;
    aAnimation.mLongestFrame = TimeDuration();
    aAnimation.mFrameCount = 1;
    AMT_LOG("Compositor animation of type %s just started\n", aLabel);
  } else if (aInProgress && aAnimation.mStart) {
    // the animation continues
    aAnimation.mFrameCount++;
    TimeStamp now = TimeStamp::Now();
    aAnimation.mLongestFrame =
        std::max(aAnimation.mLongestFrame, now - aAnimation.mLastFrameTime);
    aAnimation.mLastFrameTime = now;
  } else if (!aInProgress && aAnimation.mStart) {
    // the animation just ended

    TimeStamp now = TimeStamp::Now();
    // Get the length and clear aAnimation.mStart before the early-returns below
    TimeDuration animationLength = now - aAnimation.mStart;
    aAnimation.mStart = 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,
            aAnimation.mFrameCount, expectedFrameCount);
    if (expectedFrameCount <= 0) {
      // Graceful handling of probably impossible thing, unless the clock
      // changes while running?
      // Note that we also skip the frames-dropped probe if this happens,
      // because we cannot be sure that the frame length measurements are valid.
      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 * aAnimation.mFrameCount / expectedFrameCount);
    Telemetry::Accumulate(aThroughputHistogram, frameHitRatio);
    AMT_LOG("Reported frameHitRatio %u\n", frameHitRatio);

    // Get the longest frame time (make sure to check the final frame as well)
    TimeDuration longestFrame =
        std::max(aAnimation.mLongestFrame, now - aAnimation.mLastFrameTime);
    // As above, we round to get the frame count. Additionally we subtract one
    // from the frame count to get the number of dropped frames.
    uint32_t framesDropped =
        std::lround(longestFrame.ToMilliseconds() / vsyncIntervalMs) - 1;
    AMT_LOG("Longest frame was %fms (%d drops)\n",
            longestFrame.ToMilliseconds(), framesDropped);
    Telemetry::Accumulate(aMaxDropsHistogram, framesDropped);
  }
}

}  // namespace layers
}  // namespace mozilla