#include "cc/metrics/scroll_jank_v4_decision_queue.h"
#include <utility>
#include <variant>
#include "base/check.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 "third_party/abseil-cpp/absl/functional/overload.h"
namespace cc {
namespace {
using ScrollDamage = ScrollJankV4Frame::ScrollDamage;
using DamagingFrame = ScrollJankV4Frame::DamagingFrame;
using ScrollUpdates = ScrollJankV4FrameStage::ScrollUpdates;
}
ScrollJankV4DecisionQueue::ResultConsumer::~ResultConsumer() = default;
ScrollJankV4DecisionQueue::ScrollJankV4DecisionQueue(
std::unique_ptr<ResultConsumer> result_consumer)
: result_consumer_(std::move(result_consumer)) {}
ScrollJankV4DecisionQueue::~ScrollJankV4DecisionQueue() {
FlushDeferredSyntheticFrames(
false);
}
bool ScrollJankV4DecisionQueue::ProcessFrameWithScrollUpdates(
const ScrollUpdates& updates,
const ScrollDamage& damage,
const ScrollJankV4Frame::BeginFrameArgsForScrollJank& args) {
CHECK(!updates.real().has_value() || updates.real()->has_inertial_input ||
updates.real()->max_abs_inertial_raw_delta_pixels == 0);
if (!AcceptFrameIfValidAndChronological(updates, damage, args)) {
return false;
}
if (!updates.real().has_value()) {
deferred_synthetic_frames_.emplace_back(*updates.synthetic(), damage, args);
return true;
}
const ScrollUpdates::Real& real_updates = *updates.real();
bool future_real_frame_is_fast_scroll_or_sufficiently_fast_fling =
ScrollJankV4Decider::IsFastScroll(real_updates) ||
ScrollJankV4Decider::IsSufficientlyFastFling(real_updates);
FlushDeferredSyntheticFrames(
future_real_frame_is_fast_scroll_or_sufficiently_fast_fling);
auto result =
decider_.DecideJankForFrameWithRealScrollUpdates(updates, damage, args);
result_consumer_->OnFrameResult(updates, damage, args, result);
return true;
}
void ScrollJankV4DecisionQueue::OnScrollStarted() {
FlushDeferredSyntheticFrames(
false);
decider_.OnScrollStarted();
result_consumer_->OnScrollStarted();
}
void ScrollJankV4DecisionQueue::OnScrollEnded() {
FlushDeferredSyntheticFrames(
false);
decider_.OnScrollEnded();
result_consumer_->OnScrollEnded();
}
bool ScrollJankV4DecisionQueue::AcceptFrameIfValidAndChronological(
const ScrollJankV4FrameStage::ScrollUpdates& updates,
const ScrollJankV4Frame::ScrollDamage& damage,
const ScrollJankV4Frame::BeginFrameArgsForScrollJank& args) {
if (!ScrollJankV4Decider::IsValidFrame(updates, damage, args)) {
return false;
}
if (args.frame_time <= last_provided_valid_begin_frame_ts_) {
return false;
}
const DamagingFrame* damaging_frame = std::get_if<DamagingFrame>(&damage);
if (damaging_frame &&
damaging_frame->presentation_ts <= last_provided_valid_presentation_ts_) {
return false;
}
last_provided_valid_begin_frame_ts_ = args.frame_time;
if (damaging_frame) {
last_provided_valid_presentation_ts_ = damaging_frame->presentation_ts;
}
return true;
}
void ScrollJankV4DecisionQueue::FlushDeferredSyntheticFrames(
bool future_real_frame_is_fast_scroll_or_sufficiently_fast_fling) {
for (const auto& [synthetic_updates, damage, args] :
deferred_synthetic_frames_) {
ScrollUpdates updates =
ScrollUpdates( std::nullopt, synthetic_updates);
auto result = decider_.DecideJankForFrameWithSyntheticScrollUpdatesOnly(
updates, damage, args,
future_real_frame_is_fast_scroll_or_sufficiently_fast_fling);
result_consumer_->OnFrameResult(updates, damage, args, result);
}
deferred_synthetic_frames_.clear();
}
}