[go: nahoru, domu]

blob: af7c289f5a967bac9ca187c122fa05cba2b3480d [file] [log] [blame]
// Copyright 2020 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_DROPPED_FRAME_COUNTER_H_
#define CC_METRICS_DROPPED_FRAME_COUNTER_H_
#include <stddef.h>
#include <map>
#include <queue>
#include <utility>
#include <vector>
#include <optional>
#include "base/containers/ring_buffer.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "cc/cc_export.h"
#include "cc/metrics/frame_info.h"
#include "cc/metrics/frame_sorter.h"
#include "cc/metrics/ukm_smoothness_data.h"
namespace cc {
class TotalFrameCounter;
// This class maintains a counter for produced/dropped frames, and can be used
// to estimate the recent throughput.
class CC_EXPORT DroppedFrameCounter {
public:
enum FrameState {
kFrameStateDropped,
kFrameStatePartial,
kFrameStateComplete
};
enum SmoothnessStrategy {
kDefaultStrategy, // All threads and interactions are considered equal.
kScrollFocusedStrategy, // Scroll interactions has the highest priority.
kMainFocusedStrategy, // Reports dropped frames with main thread updates.
kCompositorFocusedStrategy, // Reports dropped frames with compositor
// thread updates.
kStrategyCount
};
class CC_EXPORT SlidingWindowHistogram {
public:
void AddPercentDroppedFrame(double percent_dropped_frame, size_t count = 1);
uint32_t GetPercentDroppedFramePercentile(double percentile) const;
double GetPercentDroppedFrameVariance() const;
std::vector<double> GetPercentDroppedFrameBuckets() const;
void Clear();
std::ostream& Dump(std::ostream& stream) const;
uint32_t total_count() const { return total_count_; }
private:
uint32_t histogram_bins_[101] = {0};
uint32_t smoothness_buckets_[7] = {0};
uint32_t total_count_ = 0;
};
DroppedFrameCounter();
~DroppedFrameCounter();
DroppedFrameCounter(const DroppedFrameCounter&) = delete;
DroppedFrameCounter& operator=(const DroppedFrameCounter&) = delete;
size_t frame_history_size() const { return ring_buffer_.BufferSize(); }
size_t total_frames() const { return total_frames_; }
size_t total_dropped() const { return total_dropped_; }
size_t total_partial() const { return total_partial_; }
size_t total_smoothness_dropped() const { return total_smoothness_dropped_; }
uint32_t GetAverageThroughput() const;
double GetMostRecentAverageSmoothness() const;
double GetMostRecent95PercentileSmoothness() const;
using SortedFrameCallback =
base::RepeatingCallback<void(const viz::BeginFrameArgs& args,
const FrameInfo&)>;
void SetSortedFrameCallback(SortedFrameCallback callback);
typedef base::RingBuffer<FrameState, 180> RingBufferType;
RingBufferType::Iterator Begin() const { return ring_buffer_.Begin(); }
// `End()` points to the last `FrameState`, not past it.
RingBufferType::Iterator End() const { return ring_buffer_.End(); }
void AddGoodFrame();
void AddPartialFrame();
void AddDroppedFrame();
void ReportFrames();
void ReportFramesOnEveryFrameForUI();
void OnBeginFrame(const viz::BeginFrameArgs& args);
void OnEndFrame(const viz::BeginFrameArgs& args, const FrameInfo& frame_info);
void SetUkmSmoothnessDestination(UkmSmoothnessDataShared* smoothness_data);
void OnFcpReceived();
// Reset is used on navigation, which resets frame statistics as well as
// frame sorter.
void Reset();
// ResetPendingFrames is used when we need to keep track of frame statistics,
// but should no longer wait for the pending frames (e.g. connection to
// gpu-process was reset, or the page became invisible, etc.). The pending
// frames are not considered to be dropped.
void ResetPendingFrames(base::TimeTicks timestamp);
// Enable dropped frame report for ui::Compositor..
void EnableReporForUI();
void set_total_counter(TotalFrameCounter* total_counter) {
total_counter_ = total_counter;
}
void SetTimeFcpReceivedForTesting(base::TimeTicks time_fcp_received) {
DCHECK(fcp_received_);
time_fcp_received_ = time_fcp_received;
}
double sliding_window_max_percent_dropped() const {
return sliding_window_max_percent_dropped_;
}
std::optional<double> max_percent_dropped_After_1_sec() const {
return sliding_window_max_percent_dropped_After_1_sec_;
}
std::optional<double> max_percent_dropped_After_2_sec() const {
return sliding_window_max_percent_dropped_After_2_sec_;
}
std::optional<double> max_percent_dropped_After_5_sec() const {
return sliding_window_max_percent_dropped_After_5_sec_;
}
uint32_t SlidingWindow95PercentilePercentDropped(
SmoothnessStrategy strategy) const {
DCHECK_GT(SmoothnessStrategy::kStrategyCount, strategy);
return sliding_window_histogram_[strategy].GetPercentDroppedFramePercentile(
0.95);
}
uint32_t SlidingWindowMedianPercentDropped(
SmoothnessStrategy strategy) const {
DCHECK_GT(SmoothnessStrategy::kStrategyCount, strategy);
return sliding_window_histogram_[strategy].GetPercentDroppedFramePercentile(
0.5);
}
double SlidingWindowPercentDroppedVariance(
SmoothnessStrategy strategy) const {
DCHECK_GT(SmoothnessStrategy::kStrategyCount, strategy);
return sliding_window_histogram_[strategy].GetPercentDroppedFrameVariance();
}
const SlidingWindowHistogram* GetSlidingWindowHistogram(
SmoothnessStrategy strategy) const {
DCHECK_GT(SmoothnessStrategy::kStrategyCount, strategy);
return &sliding_window_histogram_[strategy];
}
double sliding_window_current_percent_dropped() const {
return sliding_window_current_percent_dropped_.value_or(0);
}
private:
void NotifyFrameResult(const viz::BeginFrameArgs& args,
const FrameInfo& frame_info);
base::TimeDelta ComputeCurrentWindowSize() const;
void PopSlidingWindow();
void UpdateMaxPercentDroppedFrame(double percent_dropped_frame);
// Adds count to dropped_frame_count_in_window_ of each strategy.
void UpdateDroppedFrameCountInWindow(const FrameInfo& frame_info, int count);
base::TimeDelta sliding_window_interval_;
std::queue<std::pair<const viz::BeginFrameArgs, FrameInfo>> sliding_window_;
uint32_t dropped_frame_count_in_window_[SmoothnessStrategy::kStrategyCount] =
{0};
double total_frames_in_window_ = 60.0;
SlidingWindowHistogram
sliding_window_histogram_[SmoothnessStrategy::kStrategyCount];
base::TimeTicks latest_sliding_window_start_;
base::TimeDelta latest_sliding_window_interval_;
RingBufferType ring_buffer_;
size_t total_frames_ = 0;
size_t total_partial_ = 0;
size_t total_dropped_ = 0;
size_t total_smoothness_dropped_ = 0;
bool fcp_received_ = false;
double sliding_window_max_percent_dropped_ = 0;
std::optional<double> sliding_window_max_percent_dropped_After_1_sec_;
std::optional<double> sliding_window_max_percent_dropped_After_2_sec_;
std::optional<double> sliding_window_max_percent_dropped_After_5_sec_;
base::TimeTicks time_fcp_received_;
raw_ptr<UkmSmoothnessDataShared> ukm_smoothness_data_ = nullptr;
FrameSorter frame_sorter_;
raw_ptr<TotalFrameCounter> total_counter_ = nullptr;
struct {
double max_window = 0;
double p95_window = 0;
} last_reported_metrics_;
std::optional<SortedFrameCallback> sorted_frame_callback_;
bool report_for_ui_ = false;
std::optional<double> sliding_window_current_percent_dropped_;
// Sets to true on a newly dropped frame and stays true as long as the frames
// that follow are dropped. Reset when a frame is presented. It is used to
// generate asynchronous trace events that cover the duration of consecutive
// dropped frames
bool in_dropping_ = false;
};
CC_EXPORT std::ostream& operator<<(
std::ostream&,
const DroppedFrameCounter::SlidingWindowHistogram&);
} // namespace cc
#endif // CC_METRICS_DROPPED_FRAME_COUNTER_H_