[M108 Migration][VD] Avoid pending frame counter becoming negative
[platform/framework/web/chromium-efl.git] / cc / metrics / frame_info.cc
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.
4
5 #include "cc/metrics/frame_info.h"
6
7 #include <algorithm>
8
9 #include "base/check.h"
10 #include "build/build_config.h"
11
12 namespace cc {
13
14 namespace {
15
16 bool IsCompositorSmooth(FrameInfo::SmoothThread thread) {
17   return thread == FrameInfo::SmoothThread::kSmoothCompositor ||
18          thread == FrameInfo::SmoothThread::kSmoothBoth;
19 }
20
21 bool IsMainSmooth(FrameInfo::SmoothThread thread) {
22   return thread == FrameInfo::SmoothThread::kSmoothMain ||
23          thread == FrameInfo::SmoothThread::kSmoothBoth;
24 }
25
26 bool ValidateFinalStateIsForMainThread(FrameInfo::FrameFinalState state) {
27   switch (state) {
28     case FrameInfo::FrameFinalState::kPresentedPartialOldMain:
29     case FrameInfo::FrameFinalState::kPresentedPartialNewMain:
30       // Frames that contain main-thread update cannot have a 'partial update'
31       // state.
32       return false;
33
34     case FrameInfo::FrameFinalState::kPresentedAll:
35     case FrameInfo::FrameFinalState::kNoUpdateDesired:
36     case FrameInfo::FrameFinalState::kDropped:
37       return true;
38   }
39 }
40
41 }  // namespace
42
43 bool FrameInfo::IsDroppedAffectingSmoothness() const {
44   // If neither of the threads are expected to be smooth, then this frame cannot
45   // affect smoothness.
46   if (smooth_thread == SmoothThread::kSmoothNone)
47     return false;
48
49   return WasSmoothMainUpdateDropped() || WasSmoothCompositorUpdateDropped();
50 }
51
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.
56   if (was_merged)
57     return;
58   if (main_thread_response == MainThreadResponse::kIncluded &&
59       other.main_thread_response == MainThreadResponse::kIncluded) {
60     return;
61   }
62 #endif
63   DCHECK(!was_merged);
64   DCHECK(!other.was_merged);
65   DCHECK(Validate());
66   DCHECK(other.Validate());
67
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));
74
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;
84
85     compositor_update_was_dropped =
86         other.final_state == FrameFinalState::kDropped;
87   } else {
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));
93
94     main_update_was_dropped = other.final_state == FrameFinalState::kDropped;
95     compositor_update_was_dropped = final_state == FrameFinalState::kDropped;
96   }
97
98   was_merged = true;
99   main_thread_response = MainThreadResponse::kIncluded;
100
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;
109     } else {
110       DCHECK_EQ(other.scroll_thread, SmoothEffectDrivingThread::kUnknown);
111     }
112   }
113
114   if (other.has_missing_content)
115     has_missing_content = true;
116
117   if (other.final_state == FrameFinalState::kDropped)
118     final_state = FrameFinalState::kDropped;
119
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;
130   } else {
131     smooth_thread = SmoothThread::kSmoothNone;
132   }
133
134   total_latency = std::max(total_latency, other.total_latency);
135
136   // Validate the state after the merge.
137   DCHECK(Validate());
138 }
139
140 bool FrameInfo::Validate() const {
141   // If |scroll_thread| is set, then the |smooth_thread| must include that
142   // thread.
143   if (scroll_thread == SmoothEffectDrivingThread::kCompositor) {
144     DCHECK(IsCompositorSmooth(smooth_thread));
145   } else if (scroll_thread == SmoothEffectDrivingThread::kMain) {
146     DCHECK(IsMainSmooth(smooth_thread));
147   }
148
149   return true;
150 }
151
152 bool FrameInfo::WasSmoothCompositorUpdateDropped() const {
153   if (!IsCompositorSmooth(smooth_thread))
154     return false;
155
156   if (was_merged)
157     return compositor_update_was_dropped;
158   return final_state == FrameFinalState::kDropped;
159 }
160
161 bool FrameInfo::WasSmoothMainUpdateDropped() const {
162   if (!IsMainSmooth(smooth_thread))
163     return false;
164
165   if (was_merged)
166     return main_update_was_dropped;
167
168   switch (final_state) {
169     case FrameFinalState::kDropped:
170     case FrameFinalState::kPresentedPartialOldMain:
171       return true;
172
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
176       // dropped frame.
177       return false;
178
179     case FrameFinalState::kNoUpdateDesired:
180     case FrameFinalState::kPresentedAll:
181       return false;
182   }
183
184   return false;
185 }
186
187 bool FrameInfo::WasSmoothMainUpdateExpected() const {
188   return final_state != FrameFinalState::kNoUpdateDesired;
189 }
190
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();
202   }
203 }
204
205 }  // namespace cc