[go: nahoru, domu]

blob: d1cf24e9658e14e12c8a5cf9f15e564d07b93fac [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/metrics/frame_sequence_tracker.h"
#include <utility>
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/quads/compositor_frame_metadata.h"
#include "ui/gfx/presentation_feedback.h"
namespace cc {
const char* FrameSequenceTracker::GetFrameSequenceTrackerTypeName(
FrameSequenceTrackerType type) {
switch (type) {
case FrameSequenceTrackerType::kCompositorAnimation:
return "CompositorAnimation";
case FrameSequenceTrackerType::kMainThreadAnimation:
return "MainThreadAnimation";
case FrameSequenceTrackerType::kPinchZoom:
return "PinchZoom";
case FrameSequenceTrackerType::kRAF:
return "RAF";
case FrameSequenceTrackerType::kTouchScroll:
return "TouchScroll";
case FrameSequenceTrackerType::kVideo:
return "Video";
case FrameSequenceTrackerType::kWheelScroll:
return "WheelScroll";
case FrameSequenceTrackerType::kScrollbarScroll:
return "ScrollbarScroll";
case FrameSequenceTrackerType::kCustom:
return "Custom";
case FrameSequenceTrackerType::kCanvasAnimation:
return "CanvasAnimation";
case FrameSequenceTrackerType::kJSAnimation:
return "JSAnimation";
case FrameSequenceTrackerType::kSETMainThreadAnimation:
return "SETMainThreadAnimation";
case FrameSequenceTrackerType::kSETCompositorAnimation:
return "SETCompositorAnimation";
case FrameSequenceTrackerType::kMaxType:
return "";
}
}
FrameSequenceTracker::FrameSequenceTracker(FrameSequenceTrackerType type)
: custom_sequence_id_(-1),
metrics_(std::make_unique<FrameSequenceMetrics>(type)) {
DCHECK_LT(type, FrameSequenceTrackerType::kMaxType);
DCHECK(type != FrameSequenceTrackerType::kCustom);
}
FrameSequenceTracker::FrameSequenceTracker(
int custom_sequence_id,
FrameSequenceMetrics::CustomReporter custom_reporter)
: custom_sequence_id_(custom_sequence_id),
metrics_(std::make_unique<FrameSequenceMetrics>(
FrameSequenceTrackerType::kCustom)) {
DCHECK_GT(custom_sequence_id_, 0);
metrics_->SetCustomReporter(std::move(custom_reporter));
}
FrameSequenceTracker::~FrameSequenceTracker() {
CleanUp();
}
void FrameSequenceTracker::ScheduleTerminate() {
// If the last frame has ended and there is no frame awaiting presentation,
// then it is ready to terminate.
if (!is_inside_frame_ && last_ended_frame_id_.sequence_number <=
last_sorted_frame_id_.sequence_number) {
termination_status_ = TerminationStatus::kReadyForTermination;
} else {
termination_status_ = TerminationStatus::kScheduledForTermination;
}
}
void FrameSequenceTracker::ReportBeginImplFrame(
const viz::BeginFrameArgs& args) {
if (termination_status_ != TerminationStatus::kActive)
return;
if (ShouldIgnoreBeginFrameSource(args.frame_id.source_id))
return;
is_inside_frame_ = true;
ResetAllStateIfPaused();
begin_impl_frame_data_.previous_source = args.frame_id.source_id;
begin_impl_frame_data_.previous_sequence = args.frame_id.sequence_number;
if (!first_begin_frame_args_.IsValid()) {
first_begin_frame_args_ = args;
}
}
void FrameSequenceTracker::ReportFrameEnd(
const viz::BeginFrameArgs& args,
const viz::BeginFrameArgs& main_args) {
DCHECK_NE(termination_status_, TerminationStatus::kReadyForTermination);
if (ShouldIgnoreBeginFrameSource(args.frame_id.source_id)) {
return;
}
bool should_ignore_sequence =
args.frame_id.sequence_number != begin_impl_frame_data_.previous_sequence;
ResetAllStateIfPaused();
is_inside_frame_ = false;
if (should_ignore_sequence) {
return;
}
if (termination_status_ == TerminationStatus::kActive) {
last_ended_frame_id_ = args.frame_id;
}
}
void FrameSequenceTracker::PauseFrameProduction() {
// The states need to be reset, so that the tracker ignores the vsyncs until
// the next received begin-frame. However, defer doing that until the frame
// ends (or a new frame starts), so that in case a frame is in-progress,
// subsequent notifications for that frame can be handled correctly.
reset_all_state_ = true;
}
bool FrameSequenceTracker::ShouldIgnoreBeginFrameSource(
uint64_t source_id) const {
if (begin_impl_frame_data_.previous_source == 0)
return source_id == viz::BeginFrameArgs::kManualSourceId;
return source_id != begin_impl_frame_data_.previous_source;
}
void FrameSequenceTracker::ResetAllStateIfPaused() {
// TODO(crbug.com/1256879): With FrameSequenceMetrics handling
// FrameInfo::FrameFinalState::kNoUpdateDesired we likely do not need this
// anymore.
if (!reset_all_state_) {
return;
}
begin_impl_frame_data_ = {};
last_ended_frame_id_ = viz::BeginFrameId();
reset_all_state_ = false;
}
bool FrameSequenceTracker::ShouldReportMetricsNow(
const viz::BeginFrameArgs& args) const {
return metrics_->HasEnoughDataForReporting() &&
first_begin_frame_args_.IsValid() &&
args.frame_time - first_begin_frame_args_.frame_time >=
time_delta_to_report_;
}
std::unique_ptr<FrameSequenceMetrics> FrameSequenceTracker::TakeMetrics() {
return std::move(metrics_);
}
void FrameSequenceTracker::CleanUp() {
if (metrics_)
metrics_->ReportLeftoverData();
}
void FrameSequenceTracker::AddSortedFrame(const viz::BeginFrameArgs& args,
const FrameInfo& frame_info) {
// We will begin receiving sorted frames upon creation. Ignore those that are
// from before the `first_begin_frame_args_` as they are not a part of this
// sequence.
if (!first_begin_frame_args_.IsValid() ||
args.frame_id.sequence_number <
first_begin_frame_args_.frame_id.sequence_number) {
return;
}
// Do not process any frames once we are terminated.
if (termination_status_ == TerminationStatus::kReadyForTermination) {
return;
}
last_sorted_frame_id_ = args.frame_id;
// For trackers that scheduled for termination, only proceed to update
// metrics for the frame if it is before the last ended frame.
if (termination_status_ == TerminationStatus::kScheduledForTermination) {
if (!last_ended_frame_id_.IsSequenceValid() ||
last_ended_frame_id_.source_id != args.frame_id.source_id) {
// Frame source changed so we will no longer receive updates for frames
// we submitted to the old source.
termination_status_ = TerminationStatus::kReadyForTermination;
return;
}
// We only terminate when the content is no longer dropped.
if (frame_info.final_state != FrameInfo::FrameFinalState::kDropped) {
if (last_ended_frame_id_.sequence_number <
args.frame_id.sequence_number) {
termination_status_ = TerminationStatus::kReadyForTermination;
return;
} else if (last_ended_frame_id_.sequence_number ==
args.frame_id.sequence_number) {
// We still report the final `sequence_number`, but need to mark for
// termination.
termination_status_ = TerminationStatus::kReadyForTermination;
}
}
}
// Don't count drops from after the sequence has begun termination.
if (frame_info.final_state == FrameInfo::FrameFinalState::kDropped &&
last_ended_frame_id_.sequence_number < args.frame_id.sequence_number) {
return;
}
if (metrics_)
metrics_->AddSortedFrame(args, frame_info);
}
} // namespace cc