1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/metrics/frame_info.h"
9 #include "base/check.h"
10 #include "build/build_config.h"
16 bool IsCompositorSmooth(FrameInfo::SmoothThread thread) {
17 return thread == FrameInfo::SmoothThread::kSmoothCompositor ||
18 thread == FrameInfo::SmoothThread::kSmoothBoth;
21 bool IsMainSmooth(FrameInfo::SmoothThread thread) {
22 return thread == FrameInfo::SmoothThread::kSmoothMain ||
23 thread == FrameInfo::SmoothThread::kSmoothBoth;
26 bool ValidateFinalStateIsForMainThread(FrameInfo::FrameFinalState state) {
28 case FrameInfo::FrameFinalState::kPresentedPartialOldMain:
29 case FrameInfo::FrameFinalState::kPresentedPartialNewMain:
30 // Frames that contain main-thread update cannot have a 'partial update'
34 case FrameInfo::FrameFinalState::kPresentedAll:
35 case FrameInfo::FrameFinalState::kNoUpdateDesired:
36 case FrameInfo::FrameFinalState::kDropped:
43 bool FrameInfo::IsDroppedAffectingSmoothness() const {
44 // If neither of the threads are expected to be smooth, then this frame cannot
46 if (smooth_thread == SmoothThread::kSmoothNone)
49 return WasSmoothMainUpdateDropped() || WasSmoothCompositorUpdateDropped();
52 void FrameInfo::MergeWith(const FrameInfo& other) {
53 #if BUILDFLAG(IS_ANDROID)
54 // TODO(1278168): on android-webview, multiple frames can be submitted against
55 // the same BeginFrameArgs. This can trip the DCHECK()s in this function.
58 if (main_thread_response == MainThreadResponse::kIncluded &&
59 other.main_thread_response == MainThreadResponse::kIncluded) {
64 DCHECK(!other.was_merged);
66 DCHECK(other.Validate());
68 if (main_thread_response == MainThreadResponse::kIncluded) {
69 // |this| includes the main-thread updates. Therefore:
70 // - |other| must not also include main-thread updates.
71 // - |this| must have a valid final-state.
72 DCHECK_EQ(MainThreadResponse::kMissing, other.main_thread_response);
73 DCHECK(ValidateFinalStateIsForMainThread(final_state));
75 // If the compositor-only update did not include any changes from the
76 // main-thread, then it did drop the main-thread update. Therefore, overall
77 // the main-thread update was dropped, even if the 'main thread update' is
78 // presented in a subsequent frame.
79 bool compositor_only_change_included_new_main =
80 other.final_state == FrameFinalState::kPresentedAll ||
81 other.final_state == FrameFinalState::kPresentedPartialNewMain;
82 main_update_was_dropped = final_state == FrameFinalState::kDropped ||
83 !compositor_only_change_included_new_main;
85 compositor_update_was_dropped =
86 other.final_state == FrameFinalState::kDropped;
88 // |this| does not include main-thread updates. Therefore:
89 // - |other| must include main-thread updates.
90 // - |other| must have a valid final-state.
91 DCHECK_EQ(MainThreadResponse::kIncluded, other.main_thread_response);
92 DCHECK(ValidateFinalStateIsForMainThread(other.final_state));
94 main_update_was_dropped = other.final_state == FrameFinalState::kDropped;
95 compositor_update_was_dropped = final_state == FrameFinalState::kDropped;
99 main_thread_response = MainThreadResponse::kIncluded;
101 // The |scroll_thread| information cannot change once the frame starts.
102 // However, if a frame did not have any scroll-events, or the scroll-events
103 // for the frame did not cause any visual updates, then |scroll_thread| is
104 // reset. Therefore, either |scroll_thread| should be the same for |this| and
105 // |other|, or one of them must be |kUnknown|.
106 if (scroll_thread != other.scroll_thread) {
107 if (scroll_thread == SmoothEffectDrivingThread::kUnknown) {
108 scroll_thread = other.scroll_thread;
110 DCHECK_EQ(other.scroll_thread, SmoothEffectDrivingThread::kUnknown);
114 if (other.has_missing_content)
115 has_missing_content = true;
117 if (other.final_state == FrameFinalState::kDropped)
118 final_state = FrameFinalState::kDropped;
120 const bool is_compositor_smooth = IsCompositorSmooth(smooth_thread) ||
121 IsCompositorSmooth(other.smooth_thread);
122 const bool is_main_smooth =
123 IsMainSmooth(smooth_thread) || IsMainSmooth(other.smooth_thread);
124 if (is_compositor_smooth && is_main_smooth) {
125 smooth_thread = SmoothThread::kSmoothBoth;
126 } else if (is_compositor_smooth) {
127 smooth_thread = SmoothThread::kSmoothCompositor;
128 } else if (is_main_smooth) {
129 smooth_thread = SmoothThread::kSmoothMain;
131 smooth_thread = SmoothThread::kSmoothNone;
134 total_latency = std::max(total_latency, other.total_latency);
136 // Validate the state after the merge.
140 bool FrameInfo::Validate() const {
141 // If |scroll_thread| is set, then the |smooth_thread| must include that
143 if (scroll_thread == SmoothEffectDrivingThread::kCompositor) {
144 DCHECK(IsCompositorSmooth(smooth_thread));
145 } else if (scroll_thread == SmoothEffectDrivingThread::kMain) {
146 DCHECK(IsMainSmooth(smooth_thread));
152 bool FrameInfo::WasSmoothCompositorUpdateDropped() const {
153 if (!IsCompositorSmooth(smooth_thread))
157 return compositor_update_was_dropped;
158 return final_state == FrameFinalState::kDropped;
161 bool FrameInfo::WasSmoothMainUpdateDropped() const {
162 if (!IsMainSmooth(smooth_thread))
166 return main_update_was_dropped;
168 switch (final_state) {
169 case FrameFinalState::kDropped:
170 case FrameFinalState::kPresentedPartialOldMain:
173 case FrameFinalState::kPresentedPartialNewMain:
174 // Although this frame dropped the main-thread updates for this particular
175 // frame, it did include new main-thread update. So do not treat this as a
179 case FrameFinalState::kNoUpdateDesired:
180 case FrameFinalState::kPresentedAll:
187 bool FrameInfo::WasSmoothMainUpdateExpected() const {
188 return final_state != FrameFinalState::kNoUpdateDesired;
191 bool FrameInfo::IsScrollPrioritizeFrameDropped() const {
192 // If any scroll is active the dropped frame for only the scrolling thread is
193 // reported. If no scroll is active then reports if dropped frames is
194 // affecting smoothness.
195 switch (scroll_thread) {
196 case SmoothEffectDrivingThread::kCompositor:
197 return WasSmoothCompositorUpdateDropped();
198 case SmoothEffectDrivingThread::kMain:
199 return WasSmoothMainUpdateDropped();
200 case SmoothEffectDrivingThread::kUnknown:
201 return IsDroppedAffectingSmoothness();