910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CC_METRICS_SCROLL_JANK_V4_RESULT_H_
#define CC_METRICS_SCROLL_JANK_V4_RESULT_H_

#include <array>
#include <optional>
#include <variant>

#include "base/time/time.h"

namespace cc {

// Reason why Chrome's scroll jank v4 metric marked a scroll update as janky. A
// single scroll update can be janky for more than one reason. See
// https://docs.google.com/document/d/1AaBvTIf8i-c-WTKkjaL4vyhQMkSdynxo3XEiwpofdeA
// for more details.
// LINT.IfChange(JankReason)
enum class JankReason {
  // Chrome's input→frame delivery slowed down to the point that it missed one
  // or more VSyncs.
  kMissedVsyncDueToDeceleratingInputFrameDelivery,
  kMinValue = kMissedVsyncDueToDeceleratingInputFrameDelivery,

  // Chrome missed one or more VSyncs in the middle of a fast regular scroll.
  kMissedVsyncDuringFastScroll,

  // Chrome missed one or more VSyncs during the transition from a fast regular
  // scroll to a fling.
  kMissedVsyncAtStartOfFling,

  // Chrome missed one or more VSyncs in the middle of a fling.
  kMissedVsyncDuringFling,
  kMaxValue = kMissedVsyncDuringFling,
};
// LINT.ThenChange(//base/tracing/protos/chrome_track_event.proto:JankReason,//tools/metrics/histograms/metadata/event/histograms.xml:ScrollJankReasonV4)

template <typename T>
using JankReasonArray =
    std::array<T, static_cast<size_t>(JankReason::kMaxValue) + 1>;

// Result of the Scroll Jank V4 Metric for a scroll update. See
// https://docs.google.com/document/d/1AaBvTIf8i-c-WTKkjaL4vyhQMkSdynxo3XEiwpofdeA
// and the Event.ScrollJank.DelayedFramesPercentage4.FixedWindow histogram's
// documentation for more information.
//
// To simplify the documentation below, we use "this frame" to refer to the
// frame in which the scroll update was presented.
struct ScrollJankV4Result {
  static constexpr int kMaxVsyncsSincePreviousFrame = 1000000;
  static constexpr int kMaxMissedVsyncs = kMaxVsyncsSincePreviousFrame - 1;

  // Number of VSyncs that that Chrome missed before presenting the scroll
  // update for each reason. If at least one value is greater than zero, this
  // frame was delayed and thus the scroll update is considered janky. Each
  // value is guaranteed to be at most `kMaxMissedVsyncs`.
  JankReasonArray<int> missed_vsyncs_per_reason = {};

  // How many VSyncs were between (A) this frame and (B) the previous frame.
  // If this value is greater than one, then Chrome potentially missed one or
  // more VSyncs (i.e. might have been able to present this scroll update
  // earlier). Guaranteed to be at most `kMaxVsyncsSincePreviousFrame`. Empty if
  // this frame is the first frame in a scroll.
  std::optional<int> vsyncs_since_previous_frame = std::nullopt;

  // The running delivery cut-off based on frames preceding this frame. See
  // `ScrollJankDroppedFrameTracker::running_delivery_cutoff_` for more
  // information. Empty if ANY of the following holds:
  //
  //   * This frame is the first frame in a scroll.
  //   * All frames since the beginning of the scroll up to and including the
  //     previous frame have been non-damaging or synthetic.
  //   * The most recent janky frame was non-damaging or synthetic and all
  //     frames since then up to and including the previous frame have been
  //     non-damaging or synthetic.
  std::optional<base::TimeDelta> running_delivery_cutoff = std::nullopt;

  // The running delivery cut-off adjusted for this frame. See
  // `ScrollJankDroppedFrameTracker::CalculateMissedVsyncsPerReasonV4()` for
  // more information. Empty if ANY of the following holds:
  //
  //   * This frame is the first frame in a scroll.
  //   * This frame is non-damaging or synthetic.
  //   * All frames since the beginning of the scroll up to and including the
  //     previous frame have been non-damaging or synthetic.
  //   * The most recent janky frame was non-damaging or synthetic and all
  //     frames since then up to and including the previous frame have been
  //     non-damaging or synthetic.
  //   * `vsyncs_since_previous_frame` is equal to one.
  std::optional<base::TimeDelta> adjusted_delivery_cutoff = std::nullopt;

  // The delivery cut-off of this frame. See
  // `ScrollJankDroppedFrameTracker::ReportLatestPresentationDataV4()` for
  // more information. Empty if this frame is non-damaging or synthetic.
  std::optional<base::TimeDelta> current_delivery_cutoff = std::nullopt;

  // The input generation timestamp of the first scroll update in the frame.
  //
  //   * If this frame contains ONLY REAL scroll updates, it's the actual input
  //     generation timestamp of the earliest scroll update.
  //   * If this frame contains ONLY SYNTHETIC scroll updates, it's an
  //     extrapolated input generation timestamp based on the input generation
  //     → begin frame duration of the most recent real scroll update.
  //   * If this frame contains BOTH real and synthetic scroll updates, it's
  //     the earlier input generation timestamp of the two.
  //
  // The extrapolated timestamp for a frame which contains only synthetic scroll
  // updates is empty if ANY of the following holds:
  //
  //   * This frame is janky and synthetic.
  //   * All frames since the beginning of the scroll up to and including this
  //     frame have been synthetic.
  //   * The most recent janky frame was synthetic and all frames since then up
  //     to and including the this frame have been synthetic.
  struct RealFirstScrollUpdate {
    base::TimeTicks actual_input_generation_ts;
  };
  struct SyntheticFirstScrollUpdate {
    std::optional<base::TimeTicks> extrapolated_input_generation_ts;
  };
  using FirstScrollUpdate =
      std::variant<RealFirstScrollUpdate, SyntheticFirstScrollUpdate>;
  FirstScrollUpdate first_scroll_update =
      SyntheticFirstScrollUpdate(std::nullopt);

  // The presentation timestamp of the frame.
  //
  //   * If this frame is DAMAGING, it's the actual presentation timestamp.
  //   * If this frame is NON-DAMAGING, it's an extrapolated timestamp based on
  //     the begin frame → presentation duration of the most recent damaging
  //     frame.
  //
  // The extrapolated timestamp for a non-damaging frame is empty if ANY of the
  // following holds:
  //
  //   * This frame is janky and non-damaging.
  //   * All frames since the beginning of the scroll up to and including this
  //     frame have been non-damaging.
  //   * The most recent janky frame was non-damaging and all frames since then
  //     up to and including the this frame have been non-damaging.
  struct DamagingPresentation {
    base::TimeTicks actual_presentation_ts;
  };
  struct NonDamagingPresentation {
    std::optional<base::TimeTicks> extrapolated_presentation_ts;
  };
  using Presentation =
      std::variant<DamagingPresentation, NonDamagingPresentation>;
  Presentation presentation = NonDamagingPresentation(std::nullopt);
};

}  // namespace cc

#endif  // CC_METRICS_SCROLL_JANK_V4_RESULT_H_