scheduler_state_machine.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
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.
4
5#include "cc/scheduler/scheduler_state_machine.h"
6
7#include "base/logging.h"
8#include "base/stringprintf.h"
9
10namespace cc {
11
12SchedulerStateMachine::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_(false),
28      main_thread_needs_layer_textures_(false),
29      inside_vsync_(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) {}
38
39std::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_ = %d; ",
77                      expect_immediate_begin_frame_);
78  base::StringAppendF(&str,
79                      "main_thread_needs_layer_textures_ = %d; ",
80                      main_thread_needs_layer_textures_);
81  base::StringAppendF(&str, "inside_vsync_ = %d; ", inside_vsync_);
82  base::StringAppendF(&str, "visible_ = %d; ", visible_);
83  base::StringAppendF(&str, "can_start_ = %d; ", can_start_);
84  base::StringAppendF(&str, "can_draw_ = %d; ", can_draw_);
85  base::StringAppendF(
86      &str, "draw_if_possible_failed_ = %d; ", draw_if_possible_failed_);
87  base::StringAppendF(&str, "has_pending_tree_ = %d; ", has_pending_tree_);
88  base::StringAppendF(&str, "texture_state_ = %d; ", texture_state_);
89  base::StringAppendF(
90      &str, "output_surface_state_ = %d; ", output_surface_state_);
91  return str;
92}
93
94bool SchedulerStateMachine::HasDrawnThisFrame() const {
95  return current_frame_number_ == last_frame_number_where_draw_was_called_;
96}
97
98bool SchedulerStateMachine::HasAttemptedTreeActivationThisFrame() const {
99  return current_frame_number_ ==
100         last_frame_number_where_tree_activation_attempted_;
101}
102
103bool SchedulerStateMachine::HasCheckedForCompletedTileUploadsThisFrame() const {
104  return current_frame_number_ ==
105         last_frame_number_where_check_for_completed_tile_uploads_called_;
106}
107
108bool SchedulerStateMachine::DrawSuspendedUntilCommit() const {
109  if (!can_draw_)
110    return true;
111  if (!visible_)
112    return true;
113  if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD)
114    return true;
115  return false;
116}
117
118bool SchedulerStateMachine::ScheduledToDraw() const {
119  if (!needs_redraw_)
120    return false;
121  if (DrawSuspendedUntilCommit())
122    return false;
123  return true;
124}
125
126bool SchedulerStateMachine::ShouldDraw() const {
127  if (needs_forced_redraw_)
128    return true;
129
130  if (!ScheduledToDraw())
131    return false;
132  if (!inside_vsync_)
133    return false;
134  if (HasDrawnThisFrame())
135    return false;
136  if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE)
137    return false;
138  return true;
139}
140
141bool SchedulerStateMachine::ShouldAttemptTreeActivation() const {
142  return has_pending_tree_ && inside_vsync_ &&
143         !HasAttemptedTreeActivationThisFrame();
144}
145
146bool SchedulerStateMachine::ShouldCheckForCompletedTileUploads() const {
147  if (!settings_.impl_side_painting)
148    return false;
149  if (HasCheckedForCompletedTileUploadsThisFrame())
150    return false;
151
152  return ShouldAttemptTreeActivation() || ShouldDraw() ||
153         swap_used_incomplete_tile_;
154}
155
156bool SchedulerStateMachine::ShouldAcquireLayerTexturesForMainThread() const {
157  if (!main_thread_needs_layer_textures_)
158    return false;
159  if (texture_state_ == LAYER_TEXTURE_STATE_UNLOCKED)
160    return true;
161  DCHECK_EQ(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD);
162  // Transfer the lock from impl thread to main thread immediately if the
163  // impl thread is not even scheduled to draw. Guards against deadlocking.
164  if (!ScheduledToDraw())
165    return true;
166  if (!VSyncCallbackNeeded())
167    return true;
168  return false;
169}
170
171SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
172  if (ShouldAcquireLayerTexturesForMainThread())
173    return ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD;
174
175  switch (commit_state_) {
176    case COMMIT_STATE_IDLE:
177      if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE &&
178          needs_forced_redraw_)
179        return ACTION_DRAW_FORCED;
180      if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE &&
181          needs_forced_commit_)
182        // TODO(enne): Should probably drop the active tree on force commit.
183        return has_pending_tree_ ? ACTION_NONE : ACTION_BEGIN_FRAME;
184      if (output_surface_state_ == OUTPUT_SURFACE_LOST && can_start_)
185        return ACTION_BEGIN_OUTPUT_SURFACE_CREATION;
186      if (output_surface_state_ == OUTPUT_SURFACE_CREATING)
187        return ACTION_NONE;
188      if (ShouldCheckForCompletedTileUploads())
189        return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS;
190      if (ShouldAttemptTreeActivation())
191        return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
192      if (ShouldDraw()) {
193        return needs_forced_redraw_ ? ACTION_DRAW_FORCED
194                                    : ACTION_DRAW_IF_POSSIBLE;
195      }
196      if (needs_commit_ &&
197          ((visible_ && output_surface_state_ == OUTPUT_SURFACE_ACTIVE)
198           || needs_forced_commit_))
199        // TODO(enne): Should probably drop the active tree on force commit.
200        return has_pending_tree_ ? ACTION_NONE : ACTION_BEGIN_FRAME;
201      return ACTION_NONE;
202
203    case COMMIT_STATE_FRAME_IN_PROGRESS:
204      if (ShouldCheckForCompletedTileUploads())
205        return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS;
206      if (ShouldAttemptTreeActivation())
207        return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
208      if (ShouldDraw()) {
209        return needs_forced_redraw_ ? ACTION_DRAW_FORCED
210                                    : ACTION_DRAW_IF_POSSIBLE;
211      }
212      return ACTION_NONE;
213
214    case COMMIT_STATE_READY_TO_COMMIT:
215      return ACTION_COMMIT;
216
217    case COMMIT_STATE_WAITING_FOR_FIRST_DRAW: {
218      if (ShouldCheckForCompletedTileUploads())
219        return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS;
220      if (ShouldAttemptTreeActivation())
221        return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
222      if (ShouldDraw() || output_surface_state_ == OUTPUT_SURFACE_LOST) {
223        return needs_forced_redraw_ ? ACTION_DRAW_FORCED
224                                    : ACTION_DRAW_IF_POSSIBLE;
225      }
226      // COMMIT_STATE_WAITING_FOR_FIRST_DRAW wants to enforce a draw. If
227      // can_draw_ is false or textures are not available, proceed to the next
228      // step (similar as in COMMIT_STATE_IDLE).
229      bool can_commit = visible_ || needs_forced_commit_;
230      if (needs_commit_ && can_commit && DrawSuspendedUntilCommit())
231        return has_pending_tree_ ? ACTION_NONE : ACTION_BEGIN_FRAME;
232      return ACTION_NONE;
233    }
234
235    case COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW:
236      if (ShouldCheckForCompletedTileUploads())
237        return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS;
238      if (ShouldAttemptTreeActivation())
239        return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
240      if (needs_forced_redraw_)
241        return ACTION_DRAW_FORCED;
242      return ACTION_NONE;
243  }
244  NOTREACHED();
245  return ACTION_NONE;
246}
247
248void SchedulerStateMachine::UpdateState(Action action) {
249  switch (action) {
250    case ACTION_NONE:
251      return;
252
253    case ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS:
254      last_frame_number_where_check_for_completed_tile_uploads_called_ =
255          current_frame_number_;
256      return;
257
258    case ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED:
259      last_frame_number_where_tree_activation_attempted_ =
260          current_frame_number_;
261      return;
262
263    case ACTION_BEGIN_FRAME:
264      DCHECK(!has_pending_tree_);
265      DCHECK(visible_ || needs_forced_commit_);
266      commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS;
267      needs_commit_ = false;
268      needs_forced_commit_ = false;
269      return;
270
271    case ACTION_COMMIT:
272      if (expect_immediate_begin_frame_)
273        commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW;
274      else
275        commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
276      // When impl-side painting, we draw on activation instead of on commit.
277      if (!settings_.impl_side_painting)
278        needs_redraw_ = true;
279      if (draw_if_possible_failed_)
280        last_frame_number_where_draw_was_called_ = -1;
281
282      if (needs_forced_redraw_after_next_commit_) {
283        needs_forced_redraw_after_next_commit_ = false;
284        needs_forced_redraw_ = true;
285      }
286
287      texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD;
288      return;
289
290    case ACTION_DRAW_FORCED:
291    case ACTION_DRAW_IF_POSSIBLE:
292      needs_redraw_ = false;
293      needs_forced_redraw_ = false;
294      draw_if_possible_failed_ = false;
295      swap_used_incomplete_tile_ = false;
296      if (inside_vsync_)
297        last_frame_number_where_draw_was_called_ = current_frame_number_;
298      if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW) {
299        DCHECK(expect_immediate_begin_frame_);
300        commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS;
301        expect_immediate_begin_frame_ = false;
302      } else if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW) {
303        commit_state_ = COMMIT_STATE_IDLE;
304      }
305      if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD)
306        texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED;
307      return;
308
309    case ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
310      DCHECK_EQ(commit_state_, COMMIT_STATE_IDLE);
311      DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_LOST);
312      output_surface_state_ = OUTPUT_SURFACE_CREATING;
313      return;
314
315    case ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD:
316      texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD;
317      main_thread_needs_layer_textures_ = false;
318      if (commit_state_ != COMMIT_STATE_FRAME_IN_PROGRESS)
319        needs_commit_ = true;
320      return;
321  }
322}
323
324void SchedulerStateMachine::SetMainThreadNeedsLayerTextures() {
325  DCHECK(!main_thread_needs_layer_textures_);
326  DCHECK_NE(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD);
327  main_thread_needs_layer_textures_ = true;
328}
329
330bool SchedulerStateMachine::VSyncCallbackNeeded() const {
331  // If we have a pending tree, need to keep getting notifications until
332  // the tree is ready to be swapped.
333  if (has_pending_tree_)
334    return true;
335
336  // If we can't draw, don't tick until we are notified that we can draw again.
337  if (!can_draw_)
338    return false;
339
340  if (needs_forced_redraw_)
341    return true;
342
343  if (visible_ && swap_used_incomplete_tile_)
344    return true;
345
346  return needs_redraw_ && visible_ &&
347         output_surface_state_ == OUTPUT_SURFACE_ACTIVE;
348}
349
350void SchedulerStateMachine::DidEnterVSync() { inside_vsync_ = true; }
351
352void SchedulerStateMachine::DidLeaveVSync() {
353  current_frame_number_++;
354  inside_vsync_ = false;
355}
356
357void SchedulerStateMachine::SetVisible(bool visible) { visible_ = visible; }
358
359void SchedulerStateMachine::SetNeedsRedraw() { needs_redraw_ = true; }
360
361void SchedulerStateMachine::DidSwapUseIncompleteTile() {
362  swap_used_incomplete_tile_ = true;
363}
364
365void SchedulerStateMachine::SetNeedsForcedRedraw() {
366  needs_forced_redraw_ = true;
367}
368
369void SchedulerStateMachine::DidDrawIfPossibleCompleted(bool success) {
370  draw_if_possible_failed_ = !success;
371  if (draw_if_possible_failed_) {
372    needs_redraw_ = true;
373    needs_commit_ = true;
374    consecutive_failed_draws_++;
375    if (settings_.timeout_and_draw_when_animation_checkerboards &&
376        consecutive_failed_draws_ >=
377        maximum_number_of_failed_draws_before_draw_is_forced_) {
378      consecutive_failed_draws_ = 0;
379      // We need to force a draw, but it doesn't make sense to do this until
380      // we've committed and have new textures.
381      needs_forced_redraw_after_next_commit_ = true;
382    }
383  } else {
384    consecutive_failed_draws_ = 0;
385  }
386}
387
388void SchedulerStateMachine::SetNeedsCommit() { needs_commit_ = true; }
389
390void SchedulerStateMachine::SetNeedsForcedCommit() {
391  needs_forced_commit_ = true;
392  expect_immediate_begin_frame_ = true;
393}
394
395void SchedulerStateMachine::BeginFrameComplete() {
396  DCHECK(commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS ||
397         (expect_immediate_begin_frame_ && commit_state_ != COMMIT_STATE_IDLE))
398      << ToString();
399  commit_state_ = COMMIT_STATE_READY_TO_COMMIT;
400}
401
402void SchedulerStateMachine::BeginFrameAborted() {
403  DCHECK_EQ(commit_state_, COMMIT_STATE_FRAME_IN_PROGRESS);
404  if (expect_immediate_begin_frame_) {
405    expect_immediate_begin_frame_ = false;
406  } else {
407    commit_state_ = COMMIT_STATE_IDLE;
408    SetNeedsCommit();
409  }
410}
411
412void SchedulerStateMachine::DidLoseOutputSurface() {
413  if (output_surface_state_ == OUTPUT_SURFACE_LOST ||
414      output_surface_state_ == OUTPUT_SURFACE_CREATING)
415    return;
416  output_surface_state_ = OUTPUT_SURFACE_LOST;
417}
418
419void SchedulerStateMachine::SetHasPendingTree(bool has_pending_tree) {
420  has_pending_tree_ = has_pending_tree;
421}
422
423void SchedulerStateMachine::SetCanDraw(bool can) { can_draw_ = can; }
424
425void SchedulerStateMachine::DidCreateAndInitializeOutputSurface() {
426  DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_CREATING);
427  output_surface_state_ = OUTPUT_SURFACE_ACTIVE;
428
429  if (did_create_and_initialize_first_output_surface_) {
430    // TODO(boliu): See if we can remove this when impl-side painting is always
431    // on. Does anything on the main thread need to update after recreate?
432    needs_commit_ = true;
433    // If anything has requested a redraw, we don't want to actually draw
434    // when the output surface is restored until things have a chance to
435    // sort themselves out with a commit.
436    needs_redraw_ = false;
437  }
438  did_create_and_initialize_first_output_surface_ = true;
439}
440
441bool SchedulerStateMachine::HasInitializedOutputSurface() const {
442  return output_surface_state_ == OUTPUT_SURFACE_ACTIVE;
443}
444
445void SchedulerStateMachine::SetMaximumNumberOfFailedDrawsBeforeDrawIsForced(
446    int num_draws) {
447  maximum_number_of_failed_draws_before_draw_is_forced_ = num_draws;
448}
449
450}  // namespace cc
451