Fix Win8 metro startup crash from window switcher button
[chromium-blink-merge.git] / cc / scheduler / scheduler_state_machine.cc
blob01252dd7aff1c21a0330eb5c9598a567658a6eb1
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"
10 namespace cc {
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),
21 needs_redraw_(false),
22 swap_used_incomplete_tile_(false),
23 needs_forced_redraw_(false),
24 needs_forced_redraw_after_next_commit_(false),
25 needs_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),
30 visible_(false),
31 can_start_(false),
32 can_draw_(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() {
40 std::string str;
41 base::StringAppendF(&str,
42 "settings_.impl_side_painting = %d; ",
43 settings_.impl_side_painting);
44 base::StringAppendF(&str, "commit_state_ = %d; ", commit_state_);
45 base::StringAppendF(
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_);
50 base::StringAppendF(
51 &str,
52 "last_frame_number_where_tree_activation_attempted_ = %d; ",
53 last_frame_number_where_tree_activation_attempted_);
54 base::StringAppendF(
55 &str,
56 "last_frame_number_where_check_for_completed_tile_uploads_called_ = %d; ",
57 last_frame_number_where_check_for_completed_tile_uploads_called_);
58 base::StringAppendF(
59 &str, "consecutive_failed_draws_ = %d; ", consecutive_failed_draws_);
60 base::StringAppendF(
61 &str,
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_);
65 base::StringAppendF(
66 &str, "swap_used_incomplete_tile_ = %d; ", swap_used_incomplete_tile_);
67 base::StringAppendF(
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_);
73 base::StringAppendF(
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; ",
82 inside_begin_frame_);
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_);
86 base::StringAppendF(
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_);
90 base::StringAppendF(
91 &str, "output_surface_state_ = %d; ", output_surface_state_);
92 return str;
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 {
110 if (!can_draw_)
111 return true;
112 if (!visible_)
113 return true;
114 if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD)
115 return true;
116 return false;
119 bool SchedulerStateMachine::ScheduledToDraw() const {
120 if (!needs_redraw_)
121 return false;
122 if (DrawSuspendedUntilCommit())
123 return false;
124 return true;
127 bool SchedulerStateMachine::ShouldDraw() const {
128 if (needs_forced_redraw_)
129 return true;
131 if (!ScheduledToDraw())
132 return false;
133 if (!inside_begin_frame_)
134 return false;
135 if (HasDrawnThisFrame())
136 return false;
137 if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE)
138 return false;
139 return true;
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)
149 return false;
150 if (HasCheckedForCompletedTileUploadsThisFrame())
151 return false;
153 return ShouldAttemptTreeActivation() || ShouldDraw() ||
154 swap_used_incomplete_tile_;
157 bool SchedulerStateMachine::ShouldAcquireLayerTexturesForMainThread() const {
158 if (!main_thread_needs_layer_textures_)
159 return false;
160 if (texture_state_ == LAYER_TEXTURE_STATE_UNLOCKED)
161 return true;
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())
166 return true;
167 if (!BeginFrameNeededByImplThread())
168 return true;
169 return false;
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)
189 return ACTION_NONE;
190 if (ShouldCheckForCompletedTileUploads())
191 return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS;
192 if (ShouldAttemptTreeActivation())
193 return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
194 if (ShouldDraw()) {
195 return needs_forced_redraw_ ? ACTION_DRAW_FORCED
196 : ACTION_DRAW_IF_POSSIBLE;
198 if (needs_commit_ &&
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;
204 return ACTION_NONE;
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;
211 if (ShouldDraw()) {
212 return needs_forced_redraw_ ? ACTION_DRAW_FORCED
213 : ACTION_DRAW_IF_POSSIBLE;
215 return ACTION_NONE;
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;
236 return ACTION_NONE;
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;
246 return ACTION_NONE;
248 NOTREACHED();
249 return ACTION_NONE;
252 void SchedulerStateMachine::UpdateState(Action action) {
253 switch (action) {
254 case ACTION_NONE:
255 return;
257 case ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS:
258 last_frame_number_where_check_for_completed_tile_uploads_called_ =
259 current_frame_number_;
260 return;
262 case ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED:
263 last_frame_number_where_tree_activation_attempted_ =
264 current_frame_number_;
265 return;
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;
273 return;
275 case ACTION_COMMIT:
276 if (expect_immediate_begin_frame_for_main_thread_)
277 commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW;
278 else
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;
292 return;
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;
311 return;
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;
317 return;
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;
324 return;
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_)
338 return true;
340 // If we can't draw, don't tick until we are notified that we can draw again.
341 if (!can_draw_)
342 return false;
344 if (needs_forced_redraw_)
345 return true;
347 if (visible_ && swap_used_incomplete_tile_)
348 return true;
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;
389 } else {
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))
405 << ToString();
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;
413 } else {
414 commit_state_ = COMMIT_STATE_IDLE;
415 SetNeedsCommit();
419 void SchedulerStateMachine::DidLoseOutputSurface() {
420 if (output_surface_state_ == OUTPUT_SURFACE_LOST ||
421 output_surface_state_ == OUTPUT_SURFACE_CREATING)
422 return;
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(
453 int num_draws) {
454 maximum_number_of_failed_draws_before_draw_is_forced_ = num_draws;
457 } // namespace cc