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_DECISION_QUEUE_H_
#define CC_METRICS_SCROLL_JANK_V4_DECISION_QUEUE_H_

#include <memory>
#include <optional>
#include <vector>

#include "base/time/time.h"
#include "cc/cc_export.h"
#include "cc/metrics/scroll_jank_v4_decider.h"
#include "cc/metrics/scroll_jank_v4_frame.h"
#include "cc/metrics/scroll_jank_v4_frame_stage.h"
#include "cc/metrics/scroll_jank_v4_result.h"

namespace cc {

// Class responsible for deciding whether a frame containing one or more scroll
// updates was janky or not according to the scroll jank v4 metric. In order to
// work correctly, it must be informed about each frame that contained one or
// more scroll updates in chronological order.
//
// Scroll updates (and subsequently frames) can be categorized according to the
// following criteria:
//
//   1. Whether the scroll update cause a frame update and changed the scroll
//      offset: damaging vs. non-damaging scroll updates. See
//      `ScrollJankV4Result::is_damaging_frame` for
//      the definition of non-damaging scroll updates and frames.
//   2. Whether the scroll update originated from hardware/OS: real vs.
//      synthetic scroll updates. See `ScrollJankV4FrameStage::ScrollUpdates`
//      for the definition of synthetic scroll updates and frames.
//
// To avoid false positives, the decider must be informed about all four types
// of scroll updates and frames that occur within a scroll (damaging real,
// non-damaging real, damaging synthetic, non-damaging synthetic).
//
// See
// https://docs.google.com/document/d/1AaBvTIf8i-c-WTKkjaL4vyhQMkSdynxo3XEiwpofdeA
// for more details about the scroll jank v4 metric.
class CC_EXPORT ScrollJankV4DecisionQueue {
 public:
  class CC_EXPORT ResultConsumer {
   public:
    virtual ~ResultConsumer();
    virtual void OnFrameResult(
        const ScrollJankV4FrameStage::ScrollUpdates& updates,
        const ScrollJankV4Frame::ScrollDamage& damage,
        const ScrollJankV4Frame::BeginFrameArgsForScrollJank& args,
        const ScrollJankV4Result& result) = 0;
    virtual void OnScrollStarted() = 0;
    virtual void OnScrollEnded() = 0;
  };

  explicit ScrollJankV4DecisionQueue(
      std::unique_ptr<ResultConsumer> result_consumer);
  ~ScrollJankV4DecisionQueue();
  ScrollJankV4DecisionQueue(const ScrollJankV4DecisionQueue&) = delete;
  ScrollJankV4DecisionQueue(ScrollJankV4DecisionQueue&&) = delete;

  // Processes a frame which contains scroll updates to decide whether it was
  // janky.
  //
  // If the frame is malformed in any way (e.g. it has an earlier presentation
  // time than the previous frame provided to the decider), this method false.
  // returns without invoking the queue's `ResultConsumer`. Otherwise, this
  // method returns true and provides the jank result to the queue's
  // `ResultConsumer`. Depending on whether there is enough information to make
  // a decision, the method will provide the jank result to the
  // `ResultConsumer`:
  //
  //   * either immediately before returning,
  //   * or at a later point when it receives the necessary information (e.g.
  //     when this method is called later with information about a subsequent
  //     frame or when the scroll ends).
  //
  // Either way, this method guarantees that the queue's `ResultConsumer` will
  // be invoked with jank results in the same order as the frames were provided
  // to this method. For example, if the caller does the following (with valid
  // arguments):
  //
  // ```
  // queue.ProcessFrameWithScrollUpdates(updates1, damage1, args1);
  // queue.ProcessFrameWithScrollUpdates(updates2, damage2, args2);
  // ```
  //
  // this method guarantees to invoke the `ResultConsumer` with the jank results
  // for (`updates1`, `damage1`, `args1`) before invoking it with the jank
  // results for (`updates2`, `damage2`, `args2`).
  bool ProcessFrameWithScrollUpdates(
      const ScrollJankV4FrameStage::ScrollUpdates& updates,
      const ScrollJankV4Frame::ScrollDamage& damage,
      const ScrollJankV4Frame::BeginFrameArgsForScrollJank& args);

  void OnScrollStarted();
  void OnScrollEnded();

 private:
  bool AcceptFrameIfValidAndChronological(
      const ScrollJankV4FrameStage::ScrollUpdates& updates,
      const ScrollJankV4Frame::ScrollDamage& damage,
      const ScrollJankV4Frame::BeginFrameArgsForScrollJank& args);

  void FlushDeferredSyntheticFrames(
      bool future_real_frame_is_fast_scroll_or_sufficiently_fast_fling);

  ScrollJankV4Decider decider_;

  std::unique_ptr<ResultConsumer> result_consumer_;

  // Begin frame and presentation timestamps of the most recent valid frame
  // provided to `ProcessFrameWithScrollUpdates()`. The timestamps increase
  // monotonically with each new valid frame.
  base::TimeTicks last_provided_valid_begin_frame_ts_ = base::TimeTicks::Min();
  base::TimeTicks last_provided_valid_presentation_ts_ = base::TimeTicks::Min();

  // Synthetic frames provided to `ProcessFrameWithScrollUpdates()` which the
  // decider hasn't yet decided whether they're janky or not. They're waiting to
  // hear if they're in the middle of a fast scroll.
  //
  // The vector is sorted in chronological order (i.e. new synthetic frames are
  // appended to the end of the vector). If this vector is non-empty, then
  // `last_provided_valid_begin_frame_ts_` is equal to the begin frame timestamp
  // of the last frame in this vector
  // (`deferred_synthetic_frames_.rbegin()->args.frame_time`). Furthermore, if
  // this vector contains any damaging frames,
  // `last_provided_valid_presentation_ts_` is equal to the presentation time of
  // the last damaging frame in this vector.
  struct DeferredSyntheticFrame {
    ScrollJankV4FrameStage::ScrollUpdates::Synthetic synthetic_updates;
    ScrollJankV4Frame::ScrollDamage damage;
    ScrollJankV4Frame::BeginFrameArgsForScrollJank args;
  };
  std::vector<DeferredSyntheticFrame> deferred_synthetic_frames_;
};

}  // namespace cc

#endif  // CC_METRICS_SCROLL_JANK_V4_DECISION_QUEUE_H_