// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CC_METRICS_FRAME_SEQUENCE_TRACKER_H_ #define CC_METRICS_FRAME_SEQUENCE_TRACKER_H_ #include #include #include "base/containers/circular_deque.h" #include "base/containers/flat_set.h" #include "cc/cc_export.h" #include "cc/metrics/frame_sequence_metrics.h" namespace gfx { struct PresentationFeedback; } namespace viz { struct BeginFrameAck; struct BeginFrameArgs; struct BeginFrameId; } // namespace viz namespace cc { class ThroughputUkmReporter; // Tracks a sequence of frames to determine the throughput. It tracks this by // tracking the vsync sequence-numbers (from |BeginFrameArgs::sequence_number|), // and the presentation-timestamps (from |gfx::PresentationFeedback|). It also // tracks which frames were expected to include update from the main-thread, and // which presented frames did include updates from the main-thread. // This object should be created through // FrameSequenceTrackerCollection::CreateTracker() API. class CC_EXPORT FrameSequenceTracker { public: enum class TerminationStatus { kActive, kScheduledForTermination, kReadyForTermination, }; static const char* GetFrameSequenceTrackerTypeName( FrameSequenceTrackerType type); ~FrameSequenceTracker(); FrameSequenceTracker(const FrameSequenceTracker&) = delete; FrameSequenceTracker& operator=(const FrameSequenceTracker&) = delete; // Notifies the tracker when the compositor thread starts to process a // BeginFrameArgs. void ReportBeginImplFrame(const viz::BeginFrameArgs& args); // Notifies the tracker when a BeginFrameArgs is dispatched to the main // thread. void ReportBeginMainFrame(const viz::BeginFrameArgs& args); void ReportMainFrameProcessed(const viz::BeginFrameArgs& args); // Notifies the tracker when the compositor submits a CompositorFrame. // |origin_args| represents the BeginFrameArgs that triggered the update from // the main-thread. void ReportSubmitFrame(uint32_t frame_token, bool has_missing_content, const viz::BeginFrameAck& ack, const viz::BeginFrameArgs& origin_args); void ReportFrameEnd(const viz::BeginFrameArgs& args, const viz::BeginFrameArgs& main_args); // Notifies the tracker of the presentation-feedback of a previously submitted // CompositorFrame with |frame_token|. void ReportFramePresented(uint32_t frame_token, const gfx::PresentationFeedback& feedback); // Notifies the tracker that a CompositorFrame is not going to be submitted // for a particular BeginFrameArgs because it did not cause any damage (visual // change). Note that if a begin-main-frame was dispatched, then a separate // call to |ReportMainFrameCausedNoDamage()| is made to notify that the // main-thread did not cause any damage/updates. void ReportImplFrameCausedNoDamage(const viz::BeginFrameAck& ack); // Notifies the tracker that a |BeginFrameArgs| either was not dispatched to // the main-thread (because it did not ask for it), or that a |BeginFrameArgs| // that was dispatched to the main-thread did not cause any updates/damage. void ReportMainFrameCausedNoDamage(const viz::BeginFrameArgs& args); // Notifies that frame production has currently paused. This is typically used // for interactive frame-sequences, e.g. during touch-scroll. void PauseFrameProduction(); TerminationStatus termination_status() const { return termination_status_; } // Returns true if we should ask this tracker to report its throughput data. bool ShouldReportMetricsNow(const viz::BeginFrameArgs& args) const; FrameSequenceMetrics* metrics() { return metrics_.get(); } FrameSequenceTrackerType type() const { return metrics_->type(); } int custom_sequence_id() const { return custom_sequence_id_; } std::unique_ptr TakeMetrics(); // Called by the destructor of FrameSequenceTrackerCollection, asking its // |metrics_| to report. void CleanUp(); private: friend class FrameSequenceTrackerCollection; friend class FrameSequenceTrackerTest; // Constructs a tracker for a typed sequence other than kCustom. FrameSequenceTracker(FrameSequenceTrackerType type, ThroughputUkmReporter* throughput_ukm_reporter); // Constructs a tracker for a kCustom typed sequence. FrameSequenceTracker(int custom_sequence_id, FrameSequenceMetrics::CustomReporter custom_reporter); FrameSequenceMetrics::ThroughputData& impl_throughput() { return metrics_->impl_throughput(); } FrameSequenceMetrics::ThroughputData& main_throughput() { return metrics_->main_throughput(); } void ScheduleTerminate(); struct TrackedFrameData { // Represents the |BeginFrameArgs::source_id| and // |BeginFrameArgs::sequence_number| fields of the last processed // BeginFrameArgs. uint64_t previous_source = 0; uint64_t previous_sequence = 0; // The difference in |BeginFrameArgs::sequence_number| fields of the last // two processed BeginFrameArgs. uint32_t previous_sequence_delta = 0; }; struct CheckerboardingData { CheckerboardingData(); ~CheckerboardingData(); // Tracks whether the last presented frame had checkerboarding. This is used // to track how many vsyncs showed frames with checkerboarding. bool last_frame_had_checkerboarding = false; base::TimeTicks last_frame_timestamp; // A list of frame-tokens that had checkerboarding. base::circular_deque frames; }; void UpdateTrackedFrameData(TrackedFrameData* frame_data, uint64_t source_id, uint64_t sequence_number, uint64_t throttled_frame_count); bool ShouldIgnoreBeginFrameSource(uint64_t source_id) const; bool ShouldIgnoreSequence(uint64_t sequence_number) const; const int custom_sequence_id_; TerminationStatus termination_status_ = TerminationStatus::kActive; TrackedFrameData begin_impl_frame_data_; TrackedFrameData begin_main_frame_data_; std::unique_ptr metrics_; CheckerboardingData checkerboarding_; // Tracks the list of frame-tokens for compositor-frames that included new // updates from the main-thread, whose presentation-feedback have not been // received yet. When the presentation-feedback for a frame is received, the // corresponding frame-token is removed from this collection. base::circular_deque main_frames_; // Keeps track of the sequence-number of the first received begin-main-frame. // This is used to ignore submitted frames that include updates from earlier // begin-main-frames. uint64_t first_received_main_sequence_ = 0; // Keeps track of the first submitted compositor-frame. This is used to ignore // reports from frames that were submitted before this tracker had been // created. uint32_t first_submitted_frame_ = 0; // Keeps track of the latest submitted compositor-frame, so that it can // determine when it has received presentation-feedback for submitted frames. // This is used to decide when to terminate this FrameSequenceTracker object. uint32_t last_submitted_frame_ = 0; // Keeps track of the begin-main-frame that needs to be processed next. uint64_t awaiting_main_response_sequence_ = 0; // Keeps track of the last sequence-number that produced a frame from the // main-thread. uint64_t last_submitted_main_sequence_ = 0; // Keeps track of the last sequence-number that produced a frame that did not // have any damage from the main-thread. uint64_t last_no_main_damage_sequence_ = 0; // The time when this tracker is created, or the time when it was previously // scheduled to report histogram. base::TimeTicks first_frame_timestamp_; // Tracks the presentation timestamp of the previous frame. base::TimeTicks last_frame_presentation_timestamp_; // Keeps track of whether the impl-frame being processed did not have any // damage from the compositor (i.e. 'impl damage'). bool frame_had_no_compositor_damage_ = false; // Keeps track of whether a CompositorFrame is submitted during the frame. bool compositor_frame_submitted_ = false; bool submitted_frame_had_new_main_content_ = false; // Keeps track of whether the frame-states should be reset. bool reset_all_state_ = false; // A frame that is ignored at ReportSubmitFrame should never be presented. // TODO(xidachen): this should not be necessary. Some webview tests seem to // present a frame even if it is ignored by ReportSubmitFrame. base::flat_set ignored_frame_tokens_; // Report the throughput metrics every 5 seconds. const base::TimeDelta time_delta_to_report_ = base::TimeDelta::FromSeconds(5); uint64_t last_started_impl_sequence_ = 0; uint64_t last_processed_impl_sequence_ = 0; uint64_t last_processed_main_sequence_ = 0; uint64_t last_processed_main_sequence_latency_ = 0; // Handle off-screen main damage case. In this case, the sequence is typically // like: b(1)B(0,1)E(1)n(1)e(1)b(2)n(2)e(2)...b(10)E(2)B(10,10)n(10)e(10). // Note that between two 'E's, all the impl frames caused no damage, and // no main frames were submitted or caused no damage. bool had_impl_frame_submitted_between_commits_ = false; uint64_t previous_begin_main_sequence_ = 0; // TODO(xidachen): remove this one. uint64_t current_begin_main_sequence_ = 0; // True when an impl-impl is not ended. A tracker is ready for termination // only when the last impl-frame is ended (ReportFrameEnd). bool is_inside_frame_ = false; #if DCHECK_IS_ON() // This stringstream represents a sequence of frame reporting activities on // the current tracker. Each letter can be one of the following: // {'B', 'N', 'b', 'n', 'S', 'P'}, where // 'B' = ReportBeginMainFrame(), 'N' = ReportMainFrameCausedNoDamage(), // 'b' = ReportBeginImplFrame(), 'n' = ReportMainFrameCausedNoDamage(), // 'S' = ReportSubmitFrame() and 'P' = ReportFramePresented(). // Note that |frame_sequence_trace_| is only defined and populated // when DCHECK is on. std::stringstream frame_sequence_trace_; // |frame_sequence_trace_| can be very long, in some cases we just need a // substring of it. This var tells us how many chars can be ignored from the // beginning of that debug string. unsigned ignored_trace_char_count_ = 0; // If ReportBeginImplFrame is never called on a arg, then ReportBeginMainFrame // should ignore that arg. base::flat_set impl_frames_; #endif }; } // namespace cc #endif // CC_METRICS_FRAME_SEQUENCE_TRACKER_H_