1 // Copyright 2011 The Chromium Authors. All rights reserved.
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/scheduler/scheduler_state_machine.h"
7 #include "base/logging.h"
8 #include "base/stringprintf.h"
12 SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings
& settings
)
13 : settings_(settings
),
14 commit_state_(COMMIT_STATE_IDLE
),
15 current_frame_number_(0),
16 last_frame_number_where_draw_was_called_(-1),
17 last_frame_number_where_tree_activation_attempted_(-1),
18 last_frame_number_where_check_for_completed_tile_uploads_called_(-1),
19 consecutive_failed_draws_(0),
20 maximum_number_of_failed_draws_before_draw_is_forced_(3),
22 swap_used_incomplete_tile_(false),
23 needs_forced_redraw_(false),
24 needs_forced_redraw_after_next_commit_(false),
26 needs_forced_commit_(false),
27 expect_immediate_begin_frame_for_main_thread_(false),
28 main_thread_needs_layer_textures_(false),
29 inside_begin_frame_(false),
33 has_pending_tree_(false),
34 draw_if_possible_failed_(false),
35 texture_state_(LAYER_TEXTURE_STATE_UNLOCKED
),
36 output_surface_state_(OUTPUT_SURFACE_LOST
),
37 did_create_and_initialize_first_output_surface_(false) {}
39 std::string
SchedulerStateMachine::ToString() {
41 base::StringAppendF(&str
,
42 "settings_.impl_side_painting = %d; ",
43 settings_
.impl_side_painting
);
44 base::StringAppendF(&str
, "commit_state_ = %d; ", commit_state_
);
46 &str
, "current_frame_number_ = %d; ", current_frame_number_
);
47 base::StringAppendF(&str
,
48 "last_frame_number_where_draw_was_called_ = %d; ",
49 last_frame_number_where_draw_was_called_
);
52 "last_frame_number_where_tree_activation_attempted_ = %d; ",
53 last_frame_number_where_tree_activation_attempted_
);
56 "last_frame_number_where_check_for_completed_tile_uploads_called_ = %d; ",
57 last_frame_number_where_check_for_completed_tile_uploads_called_
);
59 &str
, "consecutive_failed_draws_ = %d; ", consecutive_failed_draws_
);
62 "maximum_number_of_failed_draws_before_draw_is_forced_ = %d; ",
63 maximum_number_of_failed_draws_before_draw_is_forced_
);
64 base::StringAppendF(&str
, "needs_redraw_ = %d; ", needs_redraw_
);
66 &str
, "swap_used_incomplete_tile_ = %d; ", swap_used_incomplete_tile_
);
68 &str
, "needs_forced_redraw_ = %d; ", needs_forced_redraw_
);
69 base::StringAppendF(&str
,
70 "needs_forced_redraw_after_next_commit_ = %d; ",
71 needs_forced_redraw_after_next_commit_
);
72 base::StringAppendF(&str
, "needs_commit_ = %d; ", needs_commit_
);
74 &str
, "needs_forced_commit_ = %d; ", needs_forced_commit_
);
75 base::StringAppendF(&str
,
76 "expect_immediate_begin_frame_for_main_thread_ = %d; ",
77 expect_immediate_begin_frame_for_main_thread_
);
78 base::StringAppendF(&str
,
79 "main_thread_needs_layer_textures_ = %d; ",
80 main_thread_needs_layer_textures_
);
81 base::StringAppendF(&str
, "inside_begin_frame_ = %d; ",
83 base::StringAppendF(&str
, "visible_ = %d; ", visible_
);
84 base::StringAppendF(&str
, "can_start_ = %d; ", can_start_
);
85 base::StringAppendF(&str
, "can_draw_ = %d; ", can_draw_
);
87 &str
, "draw_if_possible_failed_ = %d; ", draw_if_possible_failed_
);
88 base::StringAppendF(&str
, "has_pending_tree_ = %d; ", has_pending_tree_
);
89 base::StringAppendF(&str
, "texture_state_ = %d; ", texture_state_
);
91 &str
, "output_surface_state_ = %d; ", output_surface_state_
);
95 bool SchedulerStateMachine::HasDrawnThisFrame() const {
96 return current_frame_number_
== last_frame_number_where_draw_was_called_
;
99 bool SchedulerStateMachine::HasAttemptedTreeActivationThisFrame() const {
100 return current_frame_number_
==
101 last_frame_number_where_tree_activation_attempted_
;
104 bool SchedulerStateMachine::HasCheckedForCompletedTileUploadsThisFrame() const {
105 return current_frame_number_
==
106 last_frame_number_where_check_for_completed_tile_uploads_called_
;
109 bool SchedulerStateMachine::DrawSuspendedUntilCommit() const {
114 if (texture_state_
== LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD
)
119 bool SchedulerStateMachine::ScheduledToDraw() const {
122 if (DrawSuspendedUntilCommit())
127 bool SchedulerStateMachine::ShouldDraw() const {
128 if (needs_forced_redraw_
)
131 if (!ScheduledToDraw())
133 if (!inside_begin_frame_
)
135 if (HasDrawnThisFrame())
137 if (output_surface_state_
!= OUTPUT_SURFACE_ACTIVE
)
142 bool SchedulerStateMachine::ShouldAttemptTreeActivation() const {
143 return has_pending_tree_
&& inside_begin_frame_
&&
144 !HasAttemptedTreeActivationThisFrame();
147 bool SchedulerStateMachine::ShouldCheckForCompletedTileUploads() const {
148 if (!settings_
.impl_side_painting
)
150 if (HasCheckedForCompletedTileUploadsThisFrame())
153 return ShouldAttemptTreeActivation() || ShouldDraw() ||
154 swap_used_incomplete_tile_
;
157 bool SchedulerStateMachine::ShouldAcquireLayerTexturesForMainThread() const {
158 if (!main_thread_needs_layer_textures_
)
160 if (texture_state_
== LAYER_TEXTURE_STATE_UNLOCKED
)
162 DCHECK_EQ(texture_state_
, LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD
);
163 // Transfer the lock from impl thread to main thread immediately if the
164 // impl thread is not even scheduled to draw. Guards against deadlocking.
165 if (!ScheduledToDraw())
167 if (!BeginFrameNeededByImplThread())
172 SchedulerStateMachine::Action
SchedulerStateMachine::NextAction() const {
173 if (ShouldAcquireLayerTexturesForMainThread())
174 return ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD
;
176 switch (commit_state_
) {
177 case COMMIT_STATE_IDLE
:
178 if (output_surface_state_
!= OUTPUT_SURFACE_ACTIVE
&&
179 needs_forced_redraw_
)
180 return ACTION_DRAW_FORCED
;
181 if (output_surface_state_
!= OUTPUT_SURFACE_ACTIVE
&&
182 needs_forced_commit_
)
183 // TODO(enne): Should probably drop the active tree on force commit.
184 return has_pending_tree_
? ACTION_NONE
185 : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD
;
186 if (output_surface_state_
== OUTPUT_SURFACE_LOST
&& can_start_
)
187 return ACTION_BEGIN_OUTPUT_SURFACE_CREATION
;
188 if (output_surface_state_
== OUTPUT_SURFACE_CREATING
)
190 if (ShouldCheckForCompletedTileUploads())
191 return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS
;
192 if (ShouldAttemptTreeActivation())
193 return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED
;
195 return needs_forced_redraw_
? ACTION_DRAW_FORCED
196 : ACTION_DRAW_IF_POSSIBLE
;
199 ((visible_
&& output_surface_state_
== OUTPUT_SURFACE_ACTIVE
)
200 || needs_forced_commit_
))
201 // TODO(enne): Should probably drop the active tree on force commit.
202 return has_pending_tree_
? ACTION_NONE
203 : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD
;
206 case COMMIT_STATE_FRAME_IN_PROGRESS
:
207 if (ShouldCheckForCompletedTileUploads())
208 return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS
;
209 if (ShouldAttemptTreeActivation())
210 return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED
;
212 return needs_forced_redraw_
? ACTION_DRAW_FORCED
213 : ACTION_DRAW_IF_POSSIBLE
;
217 case COMMIT_STATE_READY_TO_COMMIT
:
218 return ACTION_COMMIT
;
220 case COMMIT_STATE_WAITING_FOR_FIRST_DRAW
: {
221 if (ShouldCheckForCompletedTileUploads())
222 return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS
;
223 if (ShouldAttemptTreeActivation())
224 return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED
;
225 if (ShouldDraw() || output_surface_state_
== OUTPUT_SURFACE_LOST
) {
226 return needs_forced_redraw_
? ACTION_DRAW_FORCED
227 : ACTION_DRAW_IF_POSSIBLE
;
229 // COMMIT_STATE_WAITING_FOR_FIRST_DRAW wants to enforce a draw. If
230 // can_draw_ is false or textures are not available, proceed to the next
231 // step (similar as in COMMIT_STATE_IDLE).
232 bool can_commit
= visible_
|| needs_forced_commit_
;
233 if (needs_commit_
&& can_commit
&& DrawSuspendedUntilCommit())
234 return has_pending_tree_
? ACTION_NONE
235 : ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD
;
239 case COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW
:
240 if (ShouldCheckForCompletedTileUploads())
241 return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS
;
242 if (ShouldAttemptTreeActivation())
243 return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED
;
244 if (needs_forced_redraw_
)
245 return ACTION_DRAW_FORCED
;
252 void SchedulerStateMachine::UpdateState(Action action
) {
257 case ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS
:
258 last_frame_number_where_check_for_completed_tile_uploads_called_
=
259 current_frame_number_
;
262 case ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED
:
263 last_frame_number_where_tree_activation_attempted_
=
264 current_frame_number_
;
267 case ACTION_SEND_BEGIN_FRAME_TO_MAIN_THREAD
:
268 DCHECK(!has_pending_tree_
);
269 DCHECK(visible_
|| needs_forced_commit_
);
270 commit_state_
= COMMIT_STATE_FRAME_IN_PROGRESS
;
271 needs_commit_
= false;
272 needs_forced_commit_
= false;
276 if (expect_immediate_begin_frame_for_main_thread_
)
277 commit_state_
= COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW
;
279 commit_state_
= COMMIT_STATE_WAITING_FOR_FIRST_DRAW
;
280 // When impl-side painting, we draw on activation instead of on commit.
281 if (!settings_
.impl_side_painting
)
282 needs_redraw_
= true;
283 if (draw_if_possible_failed_
)
284 last_frame_number_where_draw_was_called_
= -1;
286 if (needs_forced_redraw_after_next_commit_
) {
287 needs_forced_redraw_after_next_commit_
= false;
288 needs_forced_redraw_
= true;
291 texture_state_
= LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD
;
294 case ACTION_DRAW_FORCED
:
295 case ACTION_DRAW_IF_POSSIBLE
:
296 needs_redraw_
= false;
297 needs_forced_redraw_
= false;
298 draw_if_possible_failed_
= false;
299 swap_used_incomplete_tile_
= false;
300 if (inside_begin_frame_
)
301 last_frame_number_where_draw_was_called_
= current_frame_number_
;
302 if (commit_state_
== COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW
) {
303 DCHECK(expect_immediate_begin_frame_for_main_thread_
);
304 commit_state_
= COMMIT_STATE_FRAME_IN_PROGRESS
;
305 expect_immediate_begin_frame_for_main_thread_
= false;
306 } else if (commit_state_
== COMMIT_STATE_WAITING_FOR_FIRST_DRAW
) {
307 commit_state_
= COMMIT_STATE_IDLE
;
309 if (texture_state_
== LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD
)
310 texture_state_
= LAYER_TEXTURE_STATE_UNLOCKED
;
313 case ACTION_BEGIN_OUTPUT_SURFACE_CREATION
:
314 DCHECK_EQ(commit_state_
, COMMIT_STATE_IDLE
);
315 DCHECK_EQ(output_surface_state_
, OUTPUT_SURFACE_LOST
);
316 output_surface_state_
= OUTPUT_SURFACE_CREATING
;
319 case ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD
:
320 texture_state_
= LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD
;
321 main_thread_needs_layer_textures_
= false;
322 if (commit_state_
!= COMMIT_STATE_FRAME_IN_PROGRESS
)
323 needs_commit_
= true;
328 void SchedulerStateMachine::SetMainThreadNeedsLayerTextures() {
329 DCHECK(!main_thread_needs_layer_textures_
);
330 DCHECK_NE(texture_state_
, LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD
);
331 main_thread_needs_layer_textures_
= true;
334 bool SchedulerStateMachine::BeginFrameNeededByImplThread() const {
335 // If we have a pending tree, need to keep getting notifications until
336 // the tree is ready to be swapped.
337 if (has_pending_tree_
)
340 // If we can't draw, don't tick until we are notified that we can draw again.
344 if (needs_forced_redraw_
)
347 if (visible_
&& swap_used_incomplete_tile_
)
350 return needs_redraw_
&& visible_
&&
351 output_surface_state_
== OUTPUT_SURFACE_ACTIVE
;
354 void SchedulerStateMachine::DidEnterBeginFrame() {
355 inside_begin_frame_
= true;
358 void SchedulerStateMachine::DidLeaveBeginFrame() {
359 current_frame_number_
++;
360 inside_begin_frame_
= false;
363 void SchedulerStateMachine::SetVisible(bool visible
) { visible_
= visible
; }
365 void SchedulerStateMachine::SetNeedsRedraw() { needs_redraw_
= true; }
367 void SchedulerStateMachine::DidSwapUseIncompleteTile() {
368 swap_used_incomplete_tile_
= true;
371 void SchedulerStateMachine::SetNeedsForcedRedraw() {
372 needs_forced_redraw_
= true;
375 void SchedulerStateMachine::DidDrawIfPossibleCompleted(bool success
) {
376 draw_if_possible_failed_
= !success
;
377 if (draw_if_possible_failed_
) {
378 needs_redraw_
= true;
379 needs_commit_
= true;
380 consecutive_failed_draws_
++;
381 if (settings_
.timeout_and_draw_when_animation_checkerboards
&&
382 consecutive_failed_draws_
>=
383 maximum_number_of_failed_draws_before_draw_is_forced_
) {
384 consecutive_failed_draws_
= 0;
385 // We need to force a draw, but it doesn't make sense to do this until
386 // we've committed and have new textures.
387 needs_forced_redraw_after_next_commit_
= true;
390 consecutive_failed_draws_
= 0;
394 void SchedulerStateMachine::SetNeedsCommit() { needs_commit_
= true; }
396 void SchedulerStateMachine::SetNeedsForcedCommit() {
397 needs_forced_commit_
= true;
398 expect_immediate_begin_frame_for_main_thread_
= true;
401 void SchedulerStateMachine::FinishCommit() {
402 DCHECK(commit_state_
== COMMIT_STATE_FRAME_IN_PROGRESS
||
403 (expect_immediate_begin_frame_for_main_thread_
&&
404 commit_state_
!= COMMIT_STATE_IDLE
))
406 commit_state_
= COMMIT_STATE_READY_TO_COMMIT
;
409 void SchedulerStateMachine::BeginFrameAbortedByMainThread() {
410 DCHECK_EQ(commit_state_
, COMMIT_STATE_FRAME_IN_PROGRESS
);
411 if (expect_immediate_begin_frame_for_main_thread_
) {
412 expect_immediate_begin_frame_for_main_thread_
= false;
414 commit_state_
= COMMIT_STATE_IDLE
;
419 void SchedulerStateMachine::DidLoseOutputSurface() {
420 if (output_surface_state_
== OUTPUT_SURFACE_LOST
||
421 output_surface_state_
== OUTPUT_SURFACE_CREATING
)
423 output_surface_state_
= OUTPUT_SURFACE_LOST
;
426 void SchedulerStateMachine::SetHasPendingTree(bool has_pending_tree
) {
427 has_pending_tree_
= has_pending_tree
;
430 void SchedulerStateMachine::SetCanDraw(bool can
) { can_draw_
= can
; }
432 void SchedulerStateMachine::DidCreateAndInitializeOutputSurface() {
433 DCHECK_EQ(output_surface_state_
, OUTPUT_SURFACE_CREATING
);
434 output_surface_state_
= OUTPUT_SURFACE_ACTIVE
;
436 if (did_create_and_initialize_first_output_surface_
) {
437 // TODO(boliu): See if we can remove this when impl-side painting is always
438 // on. Does anything on the main thread need to update after recreate?
439 needs_commit_
= true;
440 // If anything has requested a redraw, we don't want to actually draw
441 // when the output surface is restored until things have a chance to
442 // sort themselves out with a commit.
443 needs_redraw_
= false;
445 did_create_and_initialize_first_output_surface_
= true;
448 bool SchedulerStateMachine::HasInitializedOutputSurface() const {
449 return output_surface_state_
== OUTPUT_SURFACE_ACTIVE
;
452 void SchedulerStateMachine::SetMaximumNumberOfFailedDrawsBeforeDrawIsForced(
454 maximum_number_of_failed_draws_before_draw_is_forced_
= num_draws
;