blob: 63ba4149a169400480d36ffef9885402ce341439 [file] [log] [blame]
// Copyright 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/scheduler/scheduler_state_machine.h"
#include "base/logging.h"
#include "base/stringprintf.h"
namespace cc {
SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings)
: settings_(settings),
commit_state_(COMMIT_STATE_IDLE),
current_frame_number_(0),
last_frame_number_where_draw_was_called_(-1),
last_frame_number_where_tree_activation_attempted_(-1),
last_frame_number_where_check_for_completed_tile_uploads_called_(-1),
consecutive_failed_draws_(0),
maximum_number_of_failed_draws_before_draw_is_forced_(3),
needs_redraw_(false),
swap_used_incomplete_tile_(false),
needs_forced_redraw_(false),
needs_forced_redraw_after_next_commit_(false),
needs_commit_(false),
needs_forced_commit_(false),
expect_immediate_begin_frame_(false),
main_thread_needs_layer_textures_(false),
inside_vsync_(false),
visible_(false),
can_start_(false),
can_draw_(false),
has_pending_tree_(false),
draw_if_possible_failed_(false),
texture_state_(LAYER_TEXTURE_STATE_UNLOCKED),
output_surface_state_(OUTPUT_SURFACE_LOST),
did_create_and_initialize_first_output_surface_(false) {}
std::string SchedulerStateMachine::ToString() {
std::string str;
base::StringAppendF(&str,
"settings_.impl_side_painting = %d; ",
settings_.impl_side_painting);
base::StringAppendF(&str, "commit_state_ = %d; ", commit_state_);
base::StringAppendF(
&str, "current_frame_number_ = %d; ", current_frame_number_);
base::StringAppendF(&str,
"last_frame_number_where_draw_was_called_ = %d; ",
last_frame_number_where_draw_was_called_);
base::StringAppendF(
&str,
"last_frame_number_where_tree_activation_attempted_ = %d; ",
last_frame_number_where_tree_activation_attempted_);
base::StringAppendF(
&str,
"last_frame_number_where_check_for_completed_tile_uploads_called_ = %d; ",
last_frame_number_where_check_for_completed_tile_uploads_called_);
base::StringAppendF(
&str, "consecutive_failed_draws_ = %d; ", consecutive_failed_draws_);
base::StringAppendF(
&str,
"maximum_number_of_failed_draws_before_draw_is_forced_ = %d; ",
maximum_number_of_failed_draws_before_draw_is_forced_);
base::StringAppendF(&str, "needs_redraw_ = %d; ", needs_redraw_);
base::StringAppendF(
&str, "swap_used_incomplete_tile_ = %d; ", swap_used_incomplete_tile_);
base::StringAppendF(
&str, "needs_forced_redraw_ = %d; ", needs_forced_redraw_);
base::StringAppendF(&str,
"needs_forced_redraw_after_next_commit_ = %d; ",
needs_forced_redraw_after_next_commit_);
base::StringAppendF(&str, "needs_commit_ = %d; ", needs_commit_);
base::StringAppendF(
&str, "needs_forced_commit_ = %d; ", needs_forced_commit_);
base::StringAppendF(&str,
"expect_immediate_begin_frame_ = %d; ",
expect_immediate_begin_frame_);
base::StringAppendF(&str,
"main_thread_needs_layer_textures_ = %d; ",
main_thread_needs_layer_textures_);
base::StringAppendF(&str, "inside_vsync_ = %d; ", inside_vsync_);
base::StringAppendF(&str, "visible_ = %d; ", visible_);
base::StringAppendF(&str, "can_start_ = %d; ", can_start_);
base::StringAppendF(&str, "can_draw_ = %d; ", can_draw_);
base::StringAppendF(
&str, "draw_if_possible_failed_ = %d; ", draw_if_possible_failed_);
base::StringAppendF(&str, "has_pending_tree_ = %d; ", has_pending_tree_);
base::StringAppendF(&str, "texture_state_ = %d; ", texture_state_);
base::StringAppendF(
&str, "output_surface_state_ = %d; ", output_surface_state_);
return str;
}
bool SchedulerStateMachine::HasDrawnThisFrame() const {
return current_frame_number_ == last_frame_number_where_draw_was_called_;
}
bool SchedulerStateMachine::HasAttemptedTreeActivationThisFrame() const {
return current_frame_number_ ==
last_frame_number_where_tree_activation_attempted_;
}
bool SchedulerStateMachine::HasCheckedForCompletedTileUploadsThisFrame() const {
return current_frame_number_ ==
last_frame_number_where_check_for_completed_tile_uploads_called_;
}
bool SchedulerStateMachine::DrawSuspendedUntilCommit() const {
if (!can_draw_)
return true;
if (!visible_)
return true;
if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD)
return true;
return false;
}
bool SchedulerStateMachine::ScheduledToDraw() const {
if (!needs_redraw_)
return false;
if (DrawSuspendedUntilCommit())
return false;
return true;
}
bool SchedulerStateMachine::ShouldDraw() const {
if (needs_forced_redraw_)
return true;
if (!ScheduledToDraw())
return false;
if (!inside_vsync_)
return false;
if (HasDrawnThisFrame())
return false;
if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE)
return false;
return true;
}
bool SchedulerStateMachine::ShouldAttemptTreeActivation() const {
return has_pending_tree_ && inside_vsync_ &&
!HasAttemptedTreeActivationThisFrame();
}
bool SchedulerStateMachine::ShouldCheckForCompletedTileUploads() const {
if (!settings_.impl_side_painting)
return false;
if (HasCheckedForCompletedTileUploadsThisFrame())
return false;
return ShouldAttemptTreeActivation() || ShouldDraw() ||
swap_used_incomplete_tile_;
}
bool SchedulerStateMachine::ShouldAcquireLayerTexturesForMainThread() const {
if (!main_thread_needs_layer_textures_)
return false;
if (texture_state_ == LAYER_TEXTURE_STATE_UNLOCKED)
return true;
DCHECK_EQ(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD);
// Transfer the lock from impl thread to main thread immediately if the
// impl thread is not even scheduled to draw. Guards against deadlocking.
if (!ScheduledToDraw())
return true;
if (!VSyncCallbackNeeded())
return true;
return false;
}
SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
if (ShouldAcquireLayerTexturesForMainThread())
return ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD;
switch (commit_state_) {
case COMMIT_STATE_IDLE:
if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE &&
needs_forced_redraw_)
return ACTION_DRAW_FORCED;
if (output_surface_state_ != OUTPUT_SURFACE_ACTIVE &&
needs_forced_commit_)
// TODO(enne): Should probably drop the active tree on force commit.
return has_pending_tree_ ? ACTION_NONE : ACTION_BEGIN_FRAME;
if (output_surface_state_ == OUTPUT_SURFACE_LOST && can_start_)
return ACTION_BEGIN_OUTPUT_SURFACE_CREATION;
if (output_surface_state_ == OUTPUT_SURFACE_CREATING)
return ACTION_NONE;
if (ShouldCheckForCompletedTileUploads())
return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS;
if (ShouldAttemptTreeActivation())
return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
if (ShouldDraw()) {
return needs_forced_redraw_ ? ACTION_DRAW_FORCED
: ACTION_DRAW_IF_POSSIBLE;
}
if (needs_commit_ &&
((visible_ && output_surface_state_ == OUTPUT_SURFACE_ACTIVE)
|| needs_forced_commit_))
// TODO(enne): Should probably drop the active tree on force commit.
return has_pending_tree_ ? ACTION_NONE : ACTION_BEGIN_FRAME;
return ACTION_NONE;
case COMMIT_STATE_FRAME_IN_PROGRESS:
if (ShouldCheckForCompletedTileUploads())
return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS;
if (ShouldAttemptTreeActivation())
return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
if (ShouldDraw()) {
return needs_forced_redraw_ ? ACTION_DRAW_FORCED
: ACTION_DRAW_IF_POSSIBLE;
}
return ACTION_NONE;
case COMMIT_STATE_READY_TO_COMMIT:
return ACTION_COMMIT;
case COMMIT_STATE_WAITING_FOR_FIRST_DRAW: {
if (ShouldCheckForCompletedTileUploads())
return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS;
if (ShouldAttemptTreeActivation())
return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
if (ShouldDraw() || output_surface_state_ == OUTPUT_SURFACE_LOST) {
return needs_forced_redraw_ ? ACTION_DRAW_FORCED
: ACTION_DRAW_IF_POSSIBLE;
}
// COMMIT_STATE_WAITING_FOR_FIRST_DRAW wants to enforce a draw. If
// can_draw_ is false or textures are not available, proceed to the next
// step (similar as in COMMIT_STATE_IDLE).
bool can_commit = visible_ || needs_forced_commit_;
if (needs_commit_ && can_commit && DrawSuspendedUntilCommit())
return has_pending_tree_ ? ACTION_NONE : ACTION_BEGIN_FRAME;
return ACTION_NONE;
}
case COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW:
if (ShouldCheckForCompletedTileUploads())
return ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS;
if (ShouldAttemptTreeActivation())
return ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED;
if (needs_forced_redraw_)
return ACTION_DRAW_FORCED;
return ACTION_NONE;
}
NOTREACHED();
return ACTION_NONE;
}
void SchedulerStateMachine::UpdateState(Action action) {
switch (action) {
case ACTION_NONE:
return;
case ACTION_CHECK_FOR_COMPLETED_TILE_UPLOADS:
last_frame_number_where_check_for_completed_tile_uploads_called_ =
current_frame_number_;
return;
case ACTION_ACTIVATE_PENDING_TREE_IF_NEEDED:
last_frame_number_where_tree_activation_attempted_ =
current_frame_number_;
return;
case ACTION_BEGIN_FRAME:
DCHECK(!has_pending_tree_);
DCHECK(visible_ || needs_forced_commit_);
commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS;
needs_commit_ = false;
needs_forced_commit_ = false;
return;
case ACTION_COMMIT:
if (expect_immediate_begin_frame_)
commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW;
else
commit_state_ = COMMIT_STATE_WAITING_FOR_FIRST_DRAW;
// When impl-side painting, we draw on activation instead of on commit.
if (!settings_.impl_side_painting)
needs_redraw_ = true;
if (draw_if_possible_failed_)
last_frame_number_where_draw_was_called_ = -1;
if (needs_forced_redraw_after_next_commit_) {
needs_forced_redraw_after_next_commit_ = false;
needs_forced_redraw_ = true;
}
texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD;
return;
case ACTION_DRAW_FORCED:
case ACTION_DRAW_IF_POSSIBLE:
needs_redraw_ = false;
needs_forced_redraw_ = false;
draw_if_possible_failed_ = false;
swap_used_incomplete_tile_ = false;
if (inside_vsync_)
last_frame_number_where_draw_was_called_ = current_frame_number_;
if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_FORCED_DRAW) {
DCHECK(expect_immediate_begin_frame_);
commit_state_ = COMMIT_STATE_FRAME_IN_PROGRESS;
expect_immediate_begin_frame_ = false;
} else if (commit_state_ == COMMIT_STATE_WAITING_FOR_FIRST_DRAW) {
commit_state_ = COMMIT_STATE_IDLE;
}
if (texture_state_ == LAYER_TEXTURE_STATE_ACQUIRED_BY_IMPL_THREAD)
texture_state_ = LAYER_TEXTURE_STATE_UNLOCKED;
return;
case ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
DCHECK_EQ(commit_state_, COMMIT_STATE_IDLE);
DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_LOST);
output_surface_state_ = OUTPUT_SURFACE_CREATING;
return;
case ACTION_ACQUIRE_LAYER_TEXTURES_FOR_MAIN_THREAD:
texture_state_ = LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD;
main_thread_needs_layer_textures_ = false;
if (commit_state_ != COMMIT_STATE_FRAME_IN_PROGRESS)
needs_commit_ = true;
return;
}
}
void SchedulerStateMachine::SetMainThreadNeedsLayerTextures() {
DCHECK(!main_thread_needs_layer_textures_);
DCHECK_NE(texture_state_, LAYER_TEXTURE_STATE_ACQUIRED_BY_MAIN_THREAD);
main_thread_needs_layer_textures_ = true;
}
bool SchedulerStateMachine::VSyncCallbackNeeded() const {
// If we have a pending tree, need to keep getting notifications until
// the tree is ready to be swapped.
if (has_pending_tree_)
return true;
// If we can't draw, don't tick until we are notified that we can draw again.
if (!can_draw_)
return false;
if (needs_forced_redraw_)
return true;
if (visible_ && swap_used_incomplete_tile_)
return true;
return needs_redraw_ && visible_ &&
output_surface_state_ == OUTPUT_SURFACE_ACTIVE;
}
void SchedulerStateMachine::DidEnterVSync() { inside_vsync_ = true; }
void SchedulerStateMachine::DidLeaveVSync() {
current_frame_number_++;
inside_vsync_ = false;
}
void SchedulerStateMachine::SetVisible(bool visible) { visible_ = visible; }
void SchedulerStateMachine::SetNeedsRedraw() { needs_redraw_ = true; }
void SchedulerStateMachine::DidSwapUseIncompleteTile() {
swap_used_incomplete_tile_ = true;
}
void SchedulerStateMachine::SetNeedsForcedRedraw() {
needs_forced_redraw_ = true;
}
void SchedulerStateMachine::DidDrawIfPossibleCompleted(bool success) {
draw_if_possible_failed_ = !success;
if (draw_if_possible_failed_) {
needs_redraw_ = true;
needs_commit_ = true;
consecutive_failed_draws_++;
if (settings_.timeout_and_draw_when_animation_checkerboards &&
consecutive_failed_draws_ >=
maximum_number_of_failed_draws_before_draw_is_forced_) {
consecutive_failed_draws_ = 0;
// We need to force a draw, but it doesn't make sense to do this until
// we've committed and have new textures.
needs_forced_redraw_after_next_commit_ = true;
}
} else {
consecutive_failed_draws_ = 0;
}
}
void SchedulerStateMachine::SetNeedsCommit() { needs_commit_ = true; }
void SchedulerStateMachine::SetNeedsForcedCommit() {
needs_forced_commit_ = true;
expect_immediate_begin_frame_ = true;
}
void SchedulerStateMachine::BeginFrameComplete() {
DCHECK(commit_state_ == COMMIT_STATE_FRAME_IN_PROGRESS ||
(expect_immediate_begin_frame_ && commit_state_ != COMMIT_STATE_IDLE))
<< ToString();
commit_state_ = COMMIT_STATE_READY_TO_COMMIT;
}
void SchedulerStateMachine::BeginFrameAborted() {
DCHECK_EQ(commit_state_, COMMIT_STATE_FRAME_IN_PROGRESS);
if (expect_immediate_begin_frame_) {
expect_immediate_begin_frame_ = false;
} else {
commit_state_ = COMMIT_STATE_IDLE;
SetNeedsCommit();
}
}
void SchedulerStateMachine::DidLoseOutputSurface() {
if (output_surface_state_ == OUTPUT_SURFACE_LOST ||
output_surface_state_ == OUTPUT_SURFACE_CREATING)
return;
output_surface_state_ = OUTPUT_SURFACE_LOST;
}
void SchedulerStateMachine::SetHasPendingTree(bool has_pending_tree) {
has_pending_tree_ = has_pending_tree;
}
void SchedulerStateMachine::SetCanDraw(bool can) { can_draw_ = can; }
void SchedulerStateMachine::DidCreateAndInitializeOutputSurface() {
DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_CREATING);
output_surface_state_ = OUTPUT_SURFACE_ACTIVE;
if (did_create_and_initialize_first_output_surface_) {
// TODO(boliu): See if we can remove this when impl-side painting is always
// on. Does anything on the main thread need to update after recreate?
needs_commit_ = true;
// If anything has requested a redraw, we don't want to actually draw
// when the output surface is restored until things have a chance to
// sort themselves out with a commit.
needs_redraw_ = false;
}
did_create_and_initialize_first_output_surface_ = true;
}
bool SchedulerStateMachine::HasInitializedOutputSurface() const {
return output_surface_state_ == OUTPUT_SURFACE_ACTIVE;
}
void SchedulerStateMachine::SetMaximumNumberOfFailedDrawsBeforeDrawIsForced(
int num_draws) {
maximum_number_of_failed_draws_before_draw_is_forced_ = num_draws;
}
} // namespace cc