123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560 |
- // 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/check_op.h"
- #include "base/format_macros.h"
- #include "base/notreached.h"
- #include "base/trace_event/trace_event.h"
- #include "base/trace_event/traced_value.h"
- #include "base/values.h"
- namespace cc {
- namespace {
- // Surfaces and CompositorTimingHistory don't support more than 1 pending swap.
- const int kMaxPendingSubmitFrames = 1;
- } // namespace
- SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings)
- : settings_(settings) {}
- SchedulerStateMachine::~SchedulerStateMachine() = default;
- perfetto::protos::pbzero::ChromeCompositorStateMachine::MajorState::
- LayerTreeFrameSinkState
- SchedulerStateMachine::LayerTreeFrameSinkStateToProtozeroEnum(
- LayerTreeFrameSinkState state) {
- using pbzeroMajorState =
- perfetto::protos::pbzero::ChromeCompositorStateMachine::MajorState;
- switch (state) {
- case LayerTreeFrameSinkState::NONE:
- return pbzeroMajorState::LAYER_TREE_FRAME_NONE;
- case LayerTreeFrameSinkState::ACTIVE:
- return pbzeroMajorState::LAYER_TREE_FRAME_ACTIVE;
- case LayerTreeFrameSinkState::CREATING:
- return pbzeroMajorState::LAYER_TREE_FRAME_CREATING;
- case LayerTreeFrameSinkState::WAITING_FOR_FIRST_COMMIT:
- return pbzeroMajorState::LAYER_TREE_FRAME_WAITING_FOR_FIRST_COMMIT;
- case LayerTreeFrameSinkState::WAITING_FOR_FIRST_ACTIVATION:
- return pbzeroMajorState::LAYER_TREE_FRAME_WAITING_FOR_FIRST_ACTIVATION;
- }
- NOTREACHED();
- return pbzeroMajorState::LAYER_TREE_FRAME_UNSPECIFIED;
- }
- perfetto::protos::pbzero::ChromeCompositorStateMachine::MajorState::
- BeginImplFrameState
- SchedulerStateMachine::BeginImplFrameStateToProtozeroEnum(
- BeginImplFrameState state) {
- using pbzeroMajorState =
- perfetto::protos::pbzero::ChromeCompositorStateMachine::MajorState;
- switch (state) {
- case BeginImplFrameState::IDLE:
- return pbzeroMajorState::BEGIN_IMPL_FRAME_IDLE;
- case BeginImplFrameState::INSIDE_BEGIN_FRAME:
- return pbzeroMajorState::BEGIN_IMPL_FRAME_INSIDE_BEGIN_FRAME;
- case BeginImplFrameState::INSIDE_DEADLINE:
- return pbzeroMajorState::BEGIN_IMPL_FRAME_INSIDE_DEADLINE;
- }
- NOTREACHED();
- return pbzeroMajorState::BEGIN_IMPL_FRAME_UNSPECIFIED;
- }
- const char* SchedulerStateMachine::BeginImplFrameDeadlineModeToString(
- BeginImplFrameDeadlineMode mode) {
- switch (mode) {
- case BeginImplFrameDeadlineMode::NONE:
- return "BeginImplFrameDeadlineMode::NONE";
- case BeginImplFrameDeadlineMode::IMMEDIATE:
- return "BeginImplFrameDeadlineMode::IMMEDIATE";
- case BeginImplFrameDeadlineMode::REGULAR:
- return "BeginImplFrameDeadlineMode::REGULAR";
- case BeginImplFrameDeadlineMode::LATE:
- return "BeginImplFrameDeadlineMode::LATE";
- case BeginImplFrameDeadlineMode::BLOCKED:
- return "BeginImplFrameDeadlineMode::BLOCKED";
- }
- NOTREACHED();
- return "???";
- }
- perfetto::protos::pbzero::ChromeCompositorSchedulerState::
- BeginImplFrameDeadlineMode
- SchedulerStateMachine::BeginImplFrameDeadlineModeToProtozeroEnum(
- BeginImplFrameDeadlineMode mode) {
- using pbzeroSchedulerState =
- perfetto::protos::pbzero::ChromeCompositorSchedulerState;
- switch (mode) {
- case BeginImplFrameDeadlineMode::NONE:
- return pbzeroSchedulerState::DEADLINE_MODE_NONE;
- case BeginImplFrameDeadlineMode::IMMEDIATE:
- return pbzeroSchedulerState::DEADLINE_MODE_IMMEDIATE;
- case BeginImplFrameDeadlineMode::REGULAR:
- return pbzeroSchedulerState::DEADLINE_MODE_REGULAR;
- case BeginImplFrameDeadlineMode::LATE:
- return pbzeroSchedulerState::DEADLINE_MODE_LATE;
- case BeginImplFrameDeadlineMode::BLOCKED:
- return pbzeroSchedulerState::DEADLINE_MODE_BLOCKED;
- }
- NOTREACHED();
- return pbzeroSchedulerState::DEADLINE_MODE_UNSPECIFIED;
- }
- perfetto::protos::pbzero::ChromeCompositorStateMachine::MajorState::
- BeginMainFrameState
- SchedulerStateMachine::BeginMainFrameStateToProtozeroEnum(
- BeginMainFrameState state) {
- using pbzeroMajorState =
- perfetto::protos::pbzero::ChromeCompositorStateMachine::MajorState;
- switch (state) {
- case BeginMainFrameState::IDLE:
- return pbzeroMajorState::BEGIN_MAIN_FRAME_IDLE;
- case BeginMainFrameState::SENT:
- return pbzeroMajorState::BEGIN_MAIN_FRAME_SENT;
- case BeginMainFrameState::READY_TO_COMMIT:
- return pbzeroMajorState::BEGIN_MAIN_FRAME_READY_TO_COMMIT;
- }
- NOTREACHED();
- return pbzeroMajorState::BEGIN_MAIN_FRAME_UNSPECIFIED;
- }
- perfetto::protos::pbzero::ChromeCompositorStateMachine::MajorState::
- ForcedRedrawOnTimeoutState
- SchedulerStateMachine::ForcedRedrawOnTimeoutStateToProtozeroEnum(
- ForcedRedrawOnTimeoutState state) {
- using pbzeroMajorState =
- perfetto::protos::pbzero::ChromeCompositorStateMachine::MajorState;
- switch (state) {
- case ForcedRedrawOnTimeoutState::IDLE:
- return pbzeroMajorState::FORCED_REDRAW_IDLE;
- case ForcedRedrawOnTimeoutState::WAITING_FOR_COMMIT:
- return pbzeroMajorState::FORCED_REDRAW_WAITING_FOR_COMMIT;
- case ForcedRedrawOnTimeoutState::WAITING_FOR_ACTIVATION:
- return pbzeroMajorState::FORCED_REDRAW_WAITING_FOR_ACTIVATION;
- case ForcedRedrawOnTimeoutState::WAITING_FOR_DRAW:
- return pbzeroMajorState::FORCED_REDRAW_WAITING_FOR_DRAW;
- }
- NOTREACHED();
- return pbzeroMajorState::FORCED_REDRAW_UNSPECIFIED;
- }
- perfetto::protos::pbzero::ChromeCompositorStateMachine::MinorState::
- ScrollHandlerState
- ScrollHandlerStateToProtozeroEnum(ScrollHandlerState state) {
- using pbzeroMinorState =
- perfetto::protos::pbzero::ChromeCompositorStateMachine::MinorState;
- switch (state) {
- case ScrollHandlerState::SCROLL_AFFECTS_SCROLL_HANDLER:
- return pbzeroMinorState::SCROLL_AFFECTS_SCROLL_HANDLER;
- case ScrollHandlerState::SCROLL_DOES_NOT_AFFECT_SCROLL_HANDLER:
- return pbzeroMinorState::SCROLL_DOES_NOT_AFFECT_SCROLL_HANDLER;
- }
- NOTREACHED();
- return pbzeroMinorState::SCROLL_HANDLER_UNSPECIFIED;
- }
- perfetto::protos::pbzero::ChromeCompositorSchedulerAction
- SchedulerStateMachine::ActionToProtozeroEnum(Action action) {
- using pbzeroSchedulerAction =
- perfetto::protos::pbzero::ChromeCompositorSchedulerAction;
- switch (action) {
- case Action::NONE:
- return pbzeroSchedulerAction::CC_SCHEDULER_ACTION_NONE;
- case Action::SEND_BEGIN_MAIN_FRAME:
- return pbzeroSchedulerAction::CC_SCHEDULER_ACTION_SEND_BEGIN_MAIN_FRAME;
- case Action::COMMIT:
- case Action::POST_COMMIT:
- // TODO(szager): Add CC_SCHEDULER_ACTION_POST_COMMIT to perfetto
- return pbzeroSchedulerAction::CC_SCHEDULER_ACTION_COMMIT;
- case Action::ACTIVATE_SYNC_TREE:
- return pbzeroSchedulerAction::CC_SCHEDULER_ACTION_ACTIVATE_SYNC_TREE;
- case Action::DRAW_IF_POSSIBLE:
- return pbzeroSchedulerAction::CC_SCHEDULER_ACTION_DRAW_IF_POSSIBLE;
- case Action::DRAW_FORCED:
- return pbzeroSchedulerAction::CC_SCHEDULER_ACTION_DRAW_FORCED;
- case Action::DRAW_ABORT:
- return pbzeroSchedulerAction::CC_SCHEDULER_ACTION_DRAW_ABORT;
- case Action::BEGIN_LAYER_TREE_FRAME_SINK_CREATION:
- return pbzeroSchedulerAction::
- CC_SCHEDULER_ACTION_BEGIN_LAYER_TREE_FRAME_SINK_CREATION;
- case Action::PREPARE_TILES:
- return pbzeroSchedulerAction::CC_SCHEDULER_ACTION_PREPARE_TILES;
- case Action::INVALIDATE_LAYER_TREE_FRAME_SINK:
- return pbzeroSchedulerAction::
- CC_SCHEDULER_ACTION_INVALIDATE_LAYER_TREE_FRAME_SINK;
- case Action::PERFORM_IMPL_SIDE_INVALIDATION:
- return pbzeroSchedulerAction::
- CC_SCHEDULER_ACTION_PERFORM_IMPL_SIDE_INVALIDATION;
- case Action::NOTIFY_BEGIN_MAIN_FRAME_NOT_EXPECTED_UNTIL:
- return pbzeroSchedulerAction::
- CC_SCHEDULER_ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_EXPECTED_UNTIL;
- case Action::NOTIFY_BEGIN_MAIN_FRAME_NOT_EXPECTED_SOON:
- return pbzeroSchedulerAction::
- CC_SCHEDULER_ACTION_NOTIFY_BEGIN_MAIN_FRAME_NOT_EXPECTED_SOON;
- }
- NOTREACHED();
- return pbzeroSchedulerAction::CC_SCHEDULER_ACTION_UNSPECIFIED;
- }
- void SchedulerStateMachine::AsProtozeroInto(
- perfetto::protos::pbzero::ChromeCompositorStateMachine* state) const {
- auto* major_state = state->set_major_state();
- major_state->set_next_action(ActionToProtozeroEnum(NextAction()));
- major_state->set_begin_impl_frame_state(
- BeginImplFrameStateToProtozeroEnum(begin_impl_frame_state_));
- major_state->set_begin_main_frame_state(
- BeginMainFrameStateToProtozeroEnum(begin_main_frame_state_));
- major_state->set_layer_tree_frame_sink_state(
- LayerTreeFrameSinkStateToProtozeroEnum(layer_tree_frame_sink_state_));
- major_state->set_forced_redraw_state(
- ForcedRedrawOnTimeoutStateToProtozeroEnum(forced_redraw_state_));
- auto* minor_state = state->set_minor_state();
- minor_state->set_commit_count(commit_count_);
- minor_state->set_current_frame_number(current_frame_number_);
- minor_state->set_last_frame_number_submit_performed(
- last_frame_number_submit_performed_);
- minor_state->set_last_frame_number_draw_performed(
- last_frame_number_draw_performed_);
- minor_state->set_last_frame_number_begin_main_frame_sent(
- last_frame_number_begin_main_frame_sent_);
- minor_state->set_did_draw(did_draw_);
- minor_state->set_did_send_begin_main_frame_for_current_frame(
- did_send_begin_main_frame_for_current_frame_);
- minor_state->set_did_notify_begin_main_frame_not_expected_until(
- did_notify_begin_main_frame_not_expected_until_);
- minor_state->set_did_notify_begin_main_frame_not_expected_soon(
- did_notify_begin_main_frame_not_expected_soon_);
- minor_state->set_wants_begin_main_frame_not_expected(
- wants_begin_main_frame_not_expected_);
- minor_state->set_did_commit_during_frame(did_commit_during_frame_);
- minor_state->set_did_invalidate_layer_tree_frame_sink(
- did_invalidate_layer_tree_frame_sink_);
- minor_state->set_did_perform_impl_side_invalidaion(
- did_perform_impl_side_invalidation_);
- minor_state->set_did_prepare_tiles(did_prepare_tiles_);
- minor_state->set_consecutive_checkerboard_animations(
- consecutive_checkerboard_animations_);
- minor_state->set_pending_submit_frames(pending_submit_frames_);
- minor_state->set_submit_frames_with_current_layer_tree_frame_sink(
- submit_frames_with_current_layer_tree_frame_sink_);
- minor_state->set_needs_redraw(needs_redraw_);
- minor_state->set_needs_prepare_tiles(needs_prepare_tiles_);
- minor_state->set_needs_begin_main_frame(needs_begin_main_frame_);
- minor_state->set_needs_one_begin_impl_frame(needs_one_begin_impl_frame_);
- minor_state->set_visible(visible_);
- minor_state->set_begin_frame_source_paused(begin_frame_source_paused_);
- minor_state->set_can_draw(can_draw_);
- minor_state->set_resourceless_draw(resourceless_draw_);
- minor_state->set_has_pending_tree(has_pending_tree_);
- minor_state->set_pending_tree_is_ready_for_activation(
- pending_tree_is_ready_for_activation_);
- minor_state->set_active_tree_needs_first_draw(active_tree_needs_first_draw_);
- minor_state->set_active_tree_is_ready_to_draw(active_tree_is_ready_to_draw_);
- minor_state->set_did_create_and_initialize_first_layer_tree_frame_sink(
- did_create_and_initialize_first_layer_tree_frame_sink_);
- minor_state->set_tree_priority(TreePriorityToProtozeroEnum(tree_priority_));
- minor_state->set_scroll_handler_state(
- ScrollHandlerStateToProtozeroEnum(scroll_handler_state_));
- minor_state->set_critical_begin_main_frame_to_activate_is_fast(
- critical_begin_main_frame_to_activate_is_fast_);
- minor_state->set_main_thread_missed_last_deadline(
- main_thread_missed_last_deadline_);
- minor_state->set_video_needs_begin_frames(video_needs_begin_frames_);
- minor_state->set_defer_begin_main_frame(defer_begin_main_frame_);
- minor_state->set_last_commit_had_no_updates(last_commit_had_no_updates_);
- minor_state->set_did_draw_in_last_frame(did_attempt_draw_in_last_frame_);
- minor_state->set_did_submit_in_last_frame(did_submit_in_last_frame_);
- minor_state->set_needs_impl_side_invalidation(needs_impl_side_invalidation_);
- minor_state->set_current_pending_tree_is_impl_side(
- current_pending_tree_is_impl_side_);
- minor_state->set_previous_pending_tree_was_impl_side(
- previous_pending_tree_was_impl_side_);
- minor_state->set_processing_animation_worklets_for_active_tree(
- processing_animation_worklets_for_active_tree_);
- minor_state->set_processing_animation_worklets_for_pending_tree(
- processing_animation_worklets_for_pending_tree_);
- minor_state->set_processing_paint_worklets_for_pending_tree(
- processing_paint_worklets_for_pending_tree_);
- }
- bool SchedulerStateMachine::PendingDrawsShouldBeAborted() const {
- // Normally when |visible_| is false or |begin_frame_source_paused_| is true,
- // pending activations will be forced and draws will be aborted. However,
- // when the embedder is Android WebView, software draws could be scheduled by
- // the Android OS at any time and draws should not be aborted in this case.
- bool is_layer_tree_frame_sink_lost =
- (layer_tree_frame_sink_state_ == LayerTreeFrameSinkState::NONE);
- if (resourceless_draw_)
- return is_layer_tree_frame_sink_lost || !can_draw_;
- // These are all the cases where we normally cannot or do not want
- // to draw but, if |needs_redraw_| is true and we do not draw to
- // make forward progress, we might deadlock with the main
- // thread. This should be a superset of ShouldAbortCurrentFrame()
- // since activation of the pending tree is blocked by drawing of the
- // active tree and the main thread might be blocked on activation of
- // the most recent commit.
- return is_layer_tree_frame_sink_lost || !can_draw_ || !visible_ ||
- begin_frame_source_paused_;
- }
- bool SchedulerStateMachine::ShouldAbortCurrentFrame() const {
- // Abort the frame if there is no output surface to trigger our
- // activations, avoiding deadlock with the main thread.
- if (layer_tree_frame_sink_state_ == LayerTreeFrameSinkState::NONE)
- return true;
- // If we're not visible, we should just abort the frame. Since we
- // set RequiresHighResToDraw when becoming visible, we ensure that
- // we don't checkerboard until all visible resources are
- // done. Furthermore, if we do keep the pending tree around, when
- // becoming visible we might activate prematurely causing
- // RequiresHighResToDraw flag to be reset. In all cases, we can
- // simply activate on becoming invisible since we don't need to draw
- // the active tree when we're in this state.
- if (!visible_)
- return true;
- // Abort the frame when viz::BeginFrameSource is paused to avoid
- // deadlocking the main thread.
- if (begin_frame_source_paused_)
- return true;
- return false;
- }
- bool SchedulerStateMachine::ShouldBeginLayerTreeFrameSinkCreation() const {
- if (!visible_)
- return false;
- // We only want to start output surface initialization after the
- // previous commit is complete.
- if (begin_main_frame_state_ != BeginMainFrameState::IDLE) {
- return false;
- }
- // Make sure the BeginImplFrame from any previous LayerTreeFrameSinks
- // are complete before creating the new LayerTreeFrameSink.
- if (begin_impl_frame_state_ != BeginImplFrameState::IDLE)
- return false;
- // We want to clear the pipeline of any pending draws and activations
- // before starting output surface initialization. This allows us to avoid
- // weird corner cases where we abort draws or force activation while we
- // are initializing the output surface.
- if (active_tree_needs_first_draw_ || has_pending_tree_)
- return false;
- // We need to create the output surface if we don't have one and we haven't
- // started creating one yet.
- return layer_tree_frame_sink_state_ == LayerTreeFrameSinkState::NONE;
- }
- bool SchedulerStateMachine::ShouldDraw() const {
- // If we need to abort draws, we should do so ASAP since the draw could
- // be blocking other important actions (like output surface initialization),
- // from occurring. If we are waiting for the first draw, then perform the
- // aborted draw to keep things moving. If we are not waiting for the first
- // draw however, we don't want to abort for no reason.
- if (PendingDrawsShouldBeAborted())
- return active_tree_needs_first_draw_;
- // Do not draw more than once in the deadline. Aborted draws are ok because
- // those are effectively nops.
- if (did_draw_)
- return false;
- // Don't draw if an early check determined the frame does not have damage.
- if (skip_draw_)
- return false;
- // Don't draw if we are waiting on the first commit after a surface.
- if (layer_tree_frame_sink_state_ != LayerTreeFrameSinkState::ACTIVE)
- return false;
- // Do not queue too many draws.
- if (IsDrawThrottled())
- return false;
- // Except for the cases above, do not draw outside of the BeginImplFrame
- // deadline.
- if (begin_impl_frame_state_ != BeginImplFrameState::INSIDE_DEADLINE)
- return false;
- // Wait for ready to draw in full-pipeline mode or the browser compositor's
- // commit-to-active-tree mode.
- if ((settings_.wait_for_all_pipeline_stages_before_draw ||
- settings_.commit_to_active_tree) &&
- !active_tree_is_ready_to_draw_) {
- return false;
- }
- // Browser compositor commit steals any resources submitted in draw. Therefore
- // drawing while a commit is pending is wasteful.
- if (settings_.commit_to_active_tree && CommitPending())
- return false;
- // Only handle forced redraws due to timeouts on the regular deadline.
- if (forced_redraw_state_ == ForcedRedrawOnTimeoutState::WAITING_FOR_DRAW)
- return true;
- return needs_redraw_;
- }
- bool SchedulerStateMachine::ShouldActivateSyncTree() const {
- // There is nothing to activate.
- if (!has_pending_tree_)
- return false;
- // We should not activate a second tree before drawing the first one.
- // Even if we need to force activation of the pending tree, we should abort
- // drawing the active tree first. Relax this requirement for synchronous
- // compositor where scheduler does not control draw, and blocking commit
- // may lead to bad scheduling.
- if (!settings_.using_synchronous_renderer_compositor &&
- active_tree_needs_first_draw_) {
- return false;
- }
- // Delay pending tree activation until paint worklets have completed painting
- // the pending tree. This must occur before the |ShouldAbortCurrentFrame|
- // check as we cannot have an unpainted active tree.
- //
- // Note that paint worklets continue to paint when the page is not visible, so
- // any abort will eventually happen when they complete.
- if (processing_paint_worklets_for_pending_tree_)
- return false;
- if (ShouldAbortCurrentFrame())
- return true;
- // Delay pending tree activation until animation worklets have completed
- // their asynchronous updates to pick up initial values.
- if (processing_animation_worklets_for_pending_tree_)
- return false;
- // At this point, only activate if we are ready to activate.
- return pending_tree_is_ready_for_activation_;
- }
- bool SchedulerStateMachine::ShouldNotifyBeginMainFrameNotExpectedUntil() const {
- // This method returns true if most of the conditions for sending a
- // BeginMainFrame are met, but one is not actually requested. This gives the
- // main thread the chance to do something else.
- if (!wants_begin_main_frame_not_expected_)
- return false;
- // Don't notify if a BeginMainFrame has already been requested or is in
- // progress.
- if (needs_begin_main_frame_ ||
- begin_main_frame_state_ != BeginMainFrameState::IDLE)
- return false;
- // Only notify when we're visible.
- if (!visible_)
- return false;
- // There are no BeginImplFrames while viz::BeginFrameSource is paused, meaning
- // the scheduler should send SendBeginMainFrameNotExpectedSoon instead,
- // indicating a longer period of inactivity.
- if (begin_frame_source_paused_)
- return false;
- // If we've gone idle and have stopped getting BeginFrames, we should send
- // SendBeginMainFrameNotExpectedSoon instead.
- if (!BeginFrameNeeded() &&
- begin_impl_frame_state_ == BeginImplFrameState::IDLE) {
- return false;
- }
- // Do not notify that no BeginMainFrame was sent too many times in a single
- // frame.
- if (did_notify_begin_main_frame_not_expected_until_)
- return false;
- // Do not notify if a commit happened during this frame as the main thread
- // will already be active and does not need to be woken up to make further
- // actions. (This occurs if the main frame was scheduled but didn't complete
- // before the vsync deadline).
- if (did_commit_during_frame_)
- return false;
- return true;
- }
- bool SchedulerStateMachine::ShouldNotifyBeginMainFrameNotExpectedSoon() const {
- if (!wants_begin_main_frame_not_expected_)
- return false;
- // Don't notify if a BeginMainFrame has already been requested or is in
- // progress.
- if (needs_begin_main_frame_ ||
- begin_main_frame_state_ != BeginMainFrameState::IDLE) {
- return false;
- }
- // Only send this when we've stopped getting BeginFrames and have gone idle.
- if (BeginFrameNeeded() ||
- begin_impl_frame_state_ != BeginImplFrameState::IDLE) {
- return false;
- }
- // Do not notify that we're not expecting frames more than once per frame.
- if (did_notify_begin_main_frame_not_expected_soon_)
- return false;
- return true;
- }
- bool SchedulerStateMachine::CouldSendBeginMainFrame() const {
- if (!needs_begin_main_frame_)
- return false;
- // We can not perform commits if we are not visible.
- if (!visible_)
- return false;
- // There are no BeginImplFrames while viz::BeginFrameSource is paused,
- // so should also stop BeginMainFrames.
- if (begin_frame_source_paused_)
- return false;
- // Do not send begin main frame when it is deferred.
- if (defer_begin_main_frame_)
- return false;
- return true;
- }
- bool SchedulerStateMachine::ShouldSendBeginMainFrame() const {
- if (!CouldSendBeginMainFrame())
- return false;
- // Do not send more than one begin main frame in a begin frame.
- if (did_send_begin_main_frame_for_current_frame_)
- return false;
- // Only send BeginMainFrame when there isn't another commit pending already.
- // Other parts of the state machine indirectly defer the BeginMainFrame
- // by transitioning to WAITING commit states rather than going
- // immediately to IDLE.
- if (begin_main_frame_state_ != BeginMainFrameState::IDLE)
- return false;
- // MFBA is disabled and we are waiting for previous activation, or the current
- // pending tree is impl-side.
- bool can_send_main_frame_with_pending_tree =
- settings_.main_frame_before_activation_enabled ||
- current_pending_tree_is_impl_side_;
- if (has_pending_tree_ && !can_send_main_frame_with_pending_tree)
- return false;
- // We are waiting for previous frame to be drawn, submitted and acked.
- if (settings_.commit_to_active_tree &&
- (active_tree_needs_first_draw_ || IsDrawThrottled())) {
- return false;
- }
- // Don't send BeginMainFrame early if we are prioritizing the active tree
- // because of ImplLatencyTakesPriority.
- if (ImplLatencyTakesPriority() &&
- (has_pending_tree_ || active_tree_needs_first_draw_)) {
- return false;
- }
- // We should not send BeginMainFrame while we are in the idle state since we
- // might have new user input arriving soon. It's okay to send BeginMainFrame
- // for the synchronous compositor because the main thread is always high
- // latency in that case.
- // TODO(brianderson): Allow sending BeginMainFrame while idle when the main
- // thread isn't consuming user input for non-synchronous compositor.
- if (!settings_.using_synchronous_renderer_compositor &&
- begin_impl_frame_state_ == BeginImplFrameState::IDLE) {
- return false;
- }
- // We need a new commit for the forced redraw. This honors the
- // single commit per interval because the result will be swapped to screen.
- if (forced_redraw_state_ == ForcedRedrawOnTimeoutState::WAITING_FOR_COMMIT)
- return true;
- // We shouldn't normally accept commits if there isn't a LayerTreeFrameSink.
- if (!HasInitializedLayerTreeFrameSink())
- return false;
- // Throttle the BeginMainFrames on CompositorFrameAck unless we just
- // submitted a frame to potentially improve impl-thread latency over
- // main-thread throughput.
- // TODO(brianderson): Remove this restriction to improve throughput or
- // make it conditional on ImplLatencyTakesPriority.
- bool just_submitted_in_deadline =
- begin_impl_frame_state_ == BeginImplFrameState::INSIDE_DEADLINE &&
- did_submit_in_last_frame_;
- if (IsDrawThrottled() && !just_submitted_in_deadline)
- return false;
- return true;
- }
- bool SchedulerStateMachine::ShouldCommit() const {
- if (begin_main_frame_state_ != BeginMainFrameState::READY_TO_COMMIT)
- return false;
- // We must not finish the commit until the pending tree is free.
- if (has_pending_tree_) {
- DCHECK(settings_.main_frame_before_activation_enabled ||
- current_pending_tree_is_impl_side_);
- return false;
- }
- // If we only have an active tree, it is incorrect to replace it before we've
- // drawn it.
- DCHECK(!settings_.commit_to_active_tree || !active_tree_needs_first_draw_);
- // In browser compositor commit reclaims any resources submitted during draw.
- DCHECK(!settings_.commit_to_active_tree || !IsDrawThrottled());
- return true;
- }
- void SchedulerStateMachine::DidCommit() {
- DCHECK(!needs_post_commit_);
- needs_post_commit_ = true;
- }
- bool SchedulerStateMachine::ShouldRunPostCommit() const {
- return needs_post_commit_;
- }
- void SchedulerStateMachine::DidPostCommit() {
- DCHECK(needs_post_commit_);
- needs_post_commit_ = false;
- }
- bool SchedulerStateMachine::ShouldPrepareTiles() const {
- // In full-pipeline mode, we need to prepare tiles ASAP to ensure that we
- // don't get stuck.
- if (settings_.wait_for_all_pipeline_stages_before_draw)
- return needs_prepare_tiles_;
- // Do not prepare tiles if we've already done so in commit or impl side
- // invalidation.
- if (did_prepare_tiles_)
- return false;
- // Limiting to once per-frame is not enough, since we only want to prepare
- // tiles _after_ draws.
- if (begin_impl_frame_state_ != BeginImplFrameState::INSIDE_DEADLINE)
- return false;
- return needs_prepare_tiles_;
- }
- bool SchedulerStateMachine::ShouldInvalidateLayerTreeFrameSink() const {
- // Do not invalidate more than once per begin frame.
- if (did_invalidate_layer_tree_frame_sink_)
- return false;
- // Only the synchronous compositor requires invalidations.
- if (!settings_.using_synchronous_renderer_compositor)
- return false;
- // Invalidations are only performed inside a BeginFrame.
- if (begin_impl_frame_state_ != BeginImplFrameState::INSIDE_BEGIN_FRAME)
- return false;
- // Don't invalidate for draw if we cannnot draw.
- // TODO(sunnyps): needs_prepare_tiles_ is needed here because PrepareTiles is
- // called only inside the deadline / draw phase. We could remove this if we
- // allowed PrepareTiles to happen in OnBeginImplFrame.
- return (needs_redraw_ && !PendingDrawsShouldBeAborted()) ||
- needs_prepare_tiles_;
- }
- SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
- if (ShouldSendBeginMainFrame())
- return Action::SEND_BEGIN_MAIN_FRAME;
- if (ShouldRunPostCommit())
- return Action::POST_COMMIT;
- if (ShouldActivateSyncTree())
- return Action::ACTIVATE_SYNC_TREE;
- if (ShouldCommit())
- return Action::COMMIT;
- if (ShouldDraw()) {
- if (PendingDrawsShouldBeAborted())
- return Action::DRAW_ABORT;
- else if (forced_redraw_state_ ==
- ForcedRedrawOnTimeoutState::WAITING_FOR_DRAW)
- return Action::DRAW_FORCED;
- else
- return Action::DRAW_IF_POSSIBLE;
- }
- if (ShouldPerformImplSideInvalidation())
- return Action::PERFORM_IMPL_SIDE_INVALIDATION;
- if (ShouldPrepareTiles())
- return Action::PREPARE_TILES;
- if (ShouldInvalidateLayerTreeFrameSink())
- return Action::INVALIDATE_LAYER_TREE_FRAME_SINK;
- if (ShouldBeginLayerTreeFrameSinkCreation())
- return Action::BEGIN_LAYER_TREE_FRAME_SINK_CREATION;
- if (ShouldNotifyBeginMainFrameNotExpectedUntil())
- return Action::NOTIFY_BEGIN_MAIN_FRAME_NOT_EXPECTED_UNTIL;
- if (ShouldNotifyBeginMainFrameNotExpectedSoon())
- return Action::NOTIFY_BEGIN_MAIN_FRAME_NOT_EXPECTED_SOON;
- return Action::NONE;
- }
- bool SchedulerStateMachine::ShouldPerformImplSideInvalidation() const {
- if (begin_frame_is_animate_only_)
- return false;
- if (!needs_impl_side_invalidation_)
- return false;
- // Don't invalidate if we've already done so either from the scheduler or as
- // part of commit.
- if (did_perform_impl_side_invalidation_)
- return false;
- // No invalidations should be done outside the impl frame.
- if (begin_impl_frame_state_ == BeginImplFrameState::IDLE)
- return false;
- // We need to be able to create a pending tree to perform an invalidation.
- if (!CouldCreatePendingTree())
- return false;
- // Check if we should defer invalidating so we can merge these invalidations
- // with the main frame.
- if (ShouldDeferInvalidatingForMainFrame())
- return false;
- // If invalidations go to the active tree and we are waiting for the previous
- // frame to be drawn, submitted and acked.
- if (settings_.commit_to_active_tree &&
- (active_tree_needs_first_draw_ || IsDrawThrottled())) {
- return false;
- }
- return true;
- }
- bool SchedulerStateMachine::ShouldDeferInvalidatingForMainFrame() const {
- DCHECK_NE(begin_impl_frame_state_, BeginImplFrameState::IDLE);
- // If the main thread is ready to commit, the impl-side invalidations will be
- // merged with the incoming main frame.
- if (begin_main_frame_state_ == BeginMainFrameState::READY_TO_COMMIT)
- return true;
- // If commits are being aborted (which would be the common case for a
- // compositor scroll), don't defer the invalidation.
- if (last_frame_events_.commit_had_no_updates || last_commit_had_no_updates_)
- return false;
- // If we prefer to invalidate over waiting on the main frame, do the
- // invalidation now.
- if (!should_defer_invalidation_for_fast_main_frame_)
- return false;
- // If there is a request for a main frame, then this could either be a
- // request that we need to respond to in this impl frame or its possible the
- // request is for the next frame (a rAF issued at the beginning of the current
- // main frame). In either case, defer invalidating so we can merge it with the
- // main frame.
- if (needs_begin_main_frame_)
- return true;
- // If the main frame was already sent, wait for the main thread to respond.
- if (begin_main_frame_state_ == BeginMainFrameState::SENT)
- return true;
- // If the main thread committed during the last frame, i.e. it was not
- // aborted, then we might get another main frame request later in the impl
- // frame. This could be the case for a timer based animation running on the
- // main thread which doesn't align with our vsync. For such cases,
- // conservatively defer invalidating until the deadline.
- if (last_frame_events_.did_commit_during_frame)
- return true;
- // If the main thread is not requesting any frames, perform the invalidation
- // at the beginning of the impl frame.
- return false;
- }
- void SchedulerStateMachine::WillPerformImplSideInvalidation() {
- current_pending_tree_is_impl_side_ = true;
- WillPerformImplSideInvalidationInternal();
- }
- void SchedulerStateMachine::WillPerformImplSideInvalidationInternal() {
- DCHECK(needs_impl_side_invalidation_);
- DCHECK(!has_pending_tree_);
- needs_impl_side_invalidation_ = false;
- has_pending_tree_ = true;
- did_perform_impl_side_invalidation_ = true;
- pending_tree_needs_first_draw_on_activation_ =
- next_invalidation_needs_first_draw_on_activation_;
- next_invalidation_needs_first_draw_on_activation_ = false;
- // TODO(eseckler): Track impl-side invalidations for pending/active tree and
- // CompositorFrame freshness computation.
- }
- bool SchedulerStateMachine::CouldCreatePendingTree() const {
- // Can't create a new pending tree till the current one is activated.
- if (has_pending_tree_)
- return false;
- // Can't make frames while we're invisible.
- if (!visible_)
- return false;
- // If the viz::BeginFrameSource is paused, we will not be able to make any
- // impl frames.
- if (begin_frame_source_paused_)
- return false;
- // Don't create a pending tree till a frame sink is fully initialized. Check
- // for the ACTIVE state explicitly instead of calling
- // HasInitializedLayerTreeFrameSink() because that only checks if frame sink
- // has been recreated, but doesn't check if we're waiting for first commit or
- // activation.
- if (layer_tree_frame_sink_state_ != LayerTreeFrameSinkState::ACTIVE)
- return false;
- return true;
- }
- void SchedulerStateMachine::WillSendBeginMainFrame() {
- DCHECK(!has_pending_tree_ || settings_.main_frame_before_activation_enabled ||
- current_pending_tree_is_impl_side_);
- DCHECK(visible_);
- DCHECK(!begin_frame_source_paused_);
- DCHECK(!did_send_begin_main_frame_for_current_frame_);
- begin_main_frame_state_ = BeginMainFrameState::SENT;
- needs_begin_main_frame_ = false;
- did_send_begin_main_frame_for_current_frame_ = true;
- last_frame_number_begin_main_frame_sent_ = current_frame_number_;
- }
- void SchedulerStateMachine::WillNotifyBeginMainFrameNotExpectedUntil() {
- DCHECK(visible_);
- DCHECK(!begin_frame_source_paused_);
- DCHECK(BeginFrameNeeded() ||
- begin_impl_frame_state_ != BeginImplFrameState::IDLE);
- DCHECK(!did_notify_begin_main_frame_not_expected_until_);
- did_notify_begin_main_frame_not_expected_until_ = true;
- }
- void SchedulerStateMachine::WillNotifyBeginMainFrameNotExpectedSoon() {
- DCHECK(!BeginFrameNeeded());
- DCHECK(begin_impl_frame_state_ == BeginImplFrameState::IDLE);
- DCHECK(!did_notify_begin_main_frame_not_expected_soon_);
- did_notify_begin_main_frame_not_expected_soon_ = true;
- }
- void SchedulerStateMachine::WillCommit(bool commit_has_no_updates) {
- bool can_have_pending_tree =
- commit_has_no_updates &&
- (settings_.main_frame_before_activation_enabled ||
- current_pending_tree_is_impl_side_);
- DCHECK(!has_pending_tree_ || can_have_pending_tree);
- commit_count_++;
- last_commit_had_no_updates_ = commit_has_no_updates;
- begin_main_frame_state_ = BeginMainFrameState::IDLE;
- did_commit_during_frame_ = true;
- if (!commit_has_no_updates) {
- // If there was a commit, the impl-side invalidations will be merged with
- // it. We always fill the impl-side invalidation funnel here, even if no
- // request was currently pending, to defer creating another pending tree and
- // performing PrepareTiles until the next frame, in case the invalidation
- // request is received after the commit.
- if (needs_impl_side_invalidation_)
- WillPerformImplSideInvalidationInternal();
- did_perform_impl_side_invalidation_ = true;
- // We have a new pending tree.
- has_pending_tree_ = true;
- pending_tree_needs_first_draw_on_activation_ = true;
- pending_tree_is_ready_for_activation_ = false;
- if (!active_tree_needs_first_draw_ ||
- !settings_.wait_for_all_pipeline_stages_before_draw) {
- // Wait for the new pending tree to become ready to draw, which may happen
- // before or after activation (unless we're in full-pipeline mode and
- // need first draw to come through).
- active_tree_is_ready_to_draw_ = false;
- }
- aborted_begin_main_frame_count_ = 0;
- } else {
- aborted_begin_main_frame_count_++;
- }
- // Update state related to forced draws.
- if (forced_redraw_state_ == ForcedRedrawOnTimeoutState::WAITING_FOR_COMMIT) {
- forced_redraw_state_ =
- has_pending_tree_ ? ForcedRedrawOnTimeoutState::WAITING_FOR_ACTIVATION
- : ForcedRedrawOnTimeoutState::WAITING_FOR_DRAW;
- }
- // Update the output surface state.
- if (layer_tree_frame_sink_state_ ==
- LayerTreeFrameSinkState::WAITING_FOR_FIRST_COMMIT) {
- layer_tree_frame_sink_state_ =
- has_pending_tree_
- ? LayerTreeFrameSinkState::WAITING_FOR_FIRST_ACTIVATION
- : LayerTreeFrameSinkState::ACTIVE;
- }
- }
- void SchedulerStateMachine::WillActivate() {
- // We cannot activate the pending tree while paint worklets are still being
- // processed; the pending tree *must* be fully painted before it can ever be
- // activated because we cannot paint the active tree.
- DCHECK(!processing_paint_worklets_for_pending_tree_);
- if (layer_tree_frame_sink_state_ ==
- LayerTreeFrameSinkState::WAITING_FOR_FIRST_ACTIVATION)
- layer_tree_frame_sink_state_ = LayerTreeFrameSinkState::ACTIVE;
- if (forced_redraw_state_ ==
- ForcedRedrawOnTimeoutState::WAITING_FOR_ACTIVATION)
- forced_redraw_state_ = ForcedRedrawOnTimeoutState::WAITING_FOR_DRAW;
- has_pending_tree_ = false;
- pending_tree_is_ready_for_activation_ = false;
- active_tree_needs_first_draw_ = pending_tree_needs_first_draw_on_activation_;
- pending_tree_needs_first_draw_on_activation_ = false;
- needs_redraw_ = true;
- previous_pending_tree_was_impl_side_ = current_pending_tree_is_impl_side_;
- current_pending_tree_is_impl_side_ = false;
- }
- void SchedulerStateMachine::WillDrawInternal() {
- // If a new active tree is pending after the one we are about to draw,
- // the main thread is in a high latency mode.
- // main_thread_missed_last_deadline_ is here in addition to
- // OnBeginImplFrameIdle for cases where the scheduler aborts draws outside
- // of the deadline.
- main_thread_missed_last_deadline_ =
- CommitPending() ||
- (has_pending_tree_ && !current_pending_tree_is_impl_side_);
- // We need to reset needs_redraw_ before we draw since the
- // draw itself might request another draw.
- needs_redraw_ = false;
- did_draw_ = true;
- active_tree_needs_first_draw_ = false;
- last_frame_number_draw_performed_ = current_frame_number_;
- if (forced_redraw_state_ == ForcedRedrawOnTimeoutState::WAITING_FOR_DRAW)
- forced_redraw_state_ = ForcedRedrawOnTimeoutState::IDLE;
- }
- void SchedulerStateMachine::DidDrawInternal(DrawResult draw_result) {
- switch (draw_result) {
- case INVALID_RESULT:
- case DRAW_ABORTED_CANT_DRAW:
- NOTREACHED() << "Invalid return DrawResult:" << draw_result;
- break;
- case DRAW_ABORTED_DRAINING_PIPELINE:
- case DRAW_SUCCESS:
- consecutive_checkerboard_animations_ = 0;
- forced_redraw_state_ = ForcedRedrawOnTimeoutState::IDLE;
- break;
- case DRAW_ABORTED_CHECKERBOARD_ANIMATIONS:
- DCHECK(!did_submit_in_last_frame_);
- needs_begin_main_frame_ = true;
- needs_redraw_ = true;
- consecutive_checkerboard_animations_++;
- if (consecutive_checkerboard_animations_ >=
- settings_.maximum_number_of_failed_draws_before_draw_is_forced &&
- forced_redraw_state_ == ForcedRedrawOnTimeoutState::IDLE) {
- // We need to force a draw, but it doesn't make sense to do this until
- // we've committed and have new textures.
- forced_redraw_state_ = ForcedRedrawOnTimeoutState::WAITING_FOR_COMMIT;
- }
- break;
- case DRAW_ABORTED_MISSING_HIGH_RES_CONTENT:
- DCHECK(!did_submit_in_last_frame_);
- // It's not clear whether this missing content is because of missing
- // pictures (which requires a commit) or because of memory pressure
- // removing textures (which might not). To be safe, request a commit
- // anyway.
- needs_begin_main_frame_ = true;
- break;
- }
- }
- void SchedulerStateMachine::WillDraw() {
- DCHECK(!did_draw_);
- WillDrawInternal();
- // Set this to true to proactively request a new BeginFrame. We can't set this
- // in WillDrawInternal because AbortDraw calls WillDrawInternal but shouldn't
- // request another frame.
- did_attempt_draw_in_last_frame_ = true;
- }
- void SchedulerStateMachine::DidDraw(DrawResult draw_result) {
- draw_succeeded_in_last_frame_ = draw_result == DRAW_SUCCESS;
- DidDrawInternal(draw_result);
- }
- void SchedulerStateMachine::SetNeedsImplSideInvalidation(
- bool needs_first_draw_on_activation) {
- needs_impl_side_invalidation_ = true;
- next_invalidation_needs_first_draw_on_activation_ |=
- needs_first_draw_on_activation;
- }
- void SchedulerStateMachine::SetMainThreadWantsBeginMainFrameNotExpectedMessages(
- bool new_state) {
- wants_begin_main_frame_not_expected_ = new_state;
- }
- void SchedulerStateMachine::AbortDraw() {
- // Pretend like the draw was successful.
- // Note: We may abort at any time and cannot DCHECK that
- // we haven't drawn in or swapped in the last frame here.
- WillDrawInternal();
- DidDrawInternal(DRAW_ABORTED_DRAINING_PIPELINE);
- }
- void SchedulerStateMachine::WillPrepareTiles() {
- needs_prepare_tiles_ = false;
- }
- void SchedulerStateMachine::WillBeginLayerTreeFrameSinkCreation() {
- DCHECK_EQ(layer_tree_frame_sink_state_, LayerTreeFrameSinkState::NONE);
- layer_tree_frame_sink_state_ = LayerTreeFrameSinkState::CREATING;
- // The following DCHECKs make sure we are in the proper quiescent state.
- // The pipeline should be flushed entirely before we start output
- // surface creation to avoid complicated corner cases.
- DCHECK(begin_main_frame_state_ == BeginMainFrameState::IDLE);
- DCHECK(!has_pending_tree_);
- DCHECK(!active_tree_needs_first_draw_);
- }
- void SchedulerStateMachine::WillInvalidateLayerTreeFrameSink() {
- DCHECK(!did_invalidate_layer_tree_frame_sink_);
- did_invalidate_layer_tree_frame_sink_ = true;
- last_frame_number_invalidate_layer_tree_frame_sink_performed_ =
- current_frame_number_;
- }
- bool SchedulerStateMachine::BeginFrameNeededForVideo() const {
- return video_needs_begin_frames_;
- }
- bool SchedulerStateMachine::BeginFrameNeeded() const {
- // We can't handle BeginFrames when output surface isn't initialized.
- // TODO(brianderson): Support output surface creation inside a BeginFrame.
- if (!HasInitializedLayerTreeFrameSink())
- return false;
- // The propagation of the needsBeginFrame signal to viz is inherently racy
- // with issuing the next BeginFrame. In full-pipe mode, it is important we
- // don't miss a BeginFrame because our needsBeginFrames signal propagated to
- // viz too slowly. To avoid the race, we simply always request BeginFrames
- // from viz.
- if (settings_.wait_for_all_pipeline_stages_before_draw)
- return true;
- // If we are not visible, we don't need BeginFrame messages.
- if (!visible_)
- return false;
- return BeginFrameRequiredForAction() || BeginFrameNeededForVideo() ||
- ProactiveBeginFrameWanted();
- }
- void SchedulerStateMachine::SetVideoNeedsBeginFrames(
- bool video_needs_begin_frames) {
- video_needs_begin_frames_ = video_needs_begin_frames;
- }
- void SchedulerStateMachine::SetDeferBeginMainFrame(
- bool defer_begin_main_frame) {
- defer_begin_main_frame_ = defer_begin_main_frame;
- }
- // These are the cases where we require a BeginFrame message to make progress
- // on requested actions.
- bool SchedulerStateMachine::BeginFrameRequiredForAction() const {
- // The forced draw respects our normal draw scheduling, so we need to
- // request a BeginImplFrame for it.
- if (forced_redraw_state_ == ForcedRedrawOnTimeoutState::WAITING_FOR_DRAW)
- return true;
- return needs_redraw_ || needs_one_begin_impl_frame_ ||
- (needs_begin_main_frame_ && !defer_begin_main_frame_) ||
- needs_impl_side_invalidation_;
- }
- // These are cases where we are very likely want a BeginFrame message in the
- // near future. Proactively requesting the BeginImplFrame helps hide the round
- // trip latency of the SetNeedsBeginFrame request that has to go to the
- // Browser.
- // This includes things like drawing soon, but might not actually have a new
- // frame to draw when we receive the next BeginImplFrame.
- bool SchedulerStateMachine::ProactiveBeginFrameWanted() const {
- // Do not be proactive when invisible.
- if (!visible_)
- return false;
- // We should proactively request a BeginImplFrame if a commit is pending
- // because we will want to draw if the commit completes quickly. Do not
- // request frames when commits are disabled, because the frame requests will
- // not provide the needed commit (and will wake up the process when it could
- // stay idle).
- // TODO(schenney) crbug.com/805798 This will need to change. We do want to
- // issue BeginMainFrames even if commits are deferred if this is during page
- // load and we want to run lifecycle updates in preparation for the first
- // commit. We probably need another flag to indicate that we are
- // pre-rendering the page or in a page navigation state.
- if ((begin_main_frame_state_ != BeginMainFrameState::IDLE) &&
- !defer_begin_main_frame_)
- return true;
- // If the pending tree activates quickly, we'll want a BeginImplFrame soon
- // to draw the new active tree.
- if (has_pending_tree_)
- return true;
- // Changing priorities may allow us to activate (given the new priorities),
- // which may result in a new frame.
- if (needs_prepare_tiles_)
- return true;
- // If we just tried to draw, it's likely that we are going to produce another
- // frame soon. This helps avoid negative glitches in our SetNeedsBeginFrame
- // requests, which may propagate to the BeginImplFrame provider and get
- // sampled at an inopportune time, delaying the next BeginImplFrame.
- if (did_attempt_draw_in_last_frame_)
- return true;
- // If the last commit was aborted because of early out (no updates), we should
- // still want a begin frame in case there is a commit coming again.
- if (last_commit_had_no_updates_)
- return true;
- // If there is active interaction happening (e.g. scroll/pinch), then keep
- // reqeusting frames.
- if (tree_priority_ == SMOOTHNESS_TAKES_PRIORITY)
- return true;
- return false;
- }
- void SchedulerStateMachine::OnBeginImplFrame(const viz::BeginFrameId& frame_id,
- bool animate_only) {
- begin_impl_frame_state_ = BeginImplFrameState::INSIDE_BEGIN_FRAME;
- current_frame_number_++;
- begin_frame_is_animate_only_ = animate_only;
- // Cache the values from the previous impl frame before reseting them for this
- // frame.
- last_frame_events_.commit_had_no_updates = last_commit_had_no_updates_;
- last_frame_events_.did_commit_during_frame = did_commit_during_frame_;
- last_commit_had_no_updates_ = false;
- did_attempt_draw_in_last_frame_ = false;
- draw_succeeded_in_last_frame_ = false;
- did_submit_in_last_frame_ = false;
- needs_one_begin_impl_frame_ = false;
- did_notify_begin_main_frame_not_expected_until_ = false;
- did_notify_begin_main_frame_not_expected_soon_ = false;
- did_send_begin_main_frame_for_current_frame_ = false;
- did_commit_during_frame_ = false;
- did_invalidate_layer_tree_frame_sink_ = false;
- did_perform_impl_side_invalidation_ = false;
- }
- void SchedulerStateMachine::OnBeginImplFrameDeadline() {
- begin_impl_frame_state_ = BeginImplFrameState::INSIDE_DEADLINE;
- // Clear funnels for any actions we perform during the deadline.
- did_draw_ = false;
- }
- void SchedulerStateMachine::OnBeginImplFrameIdle() {
- begin_impl_frame_state_ = BeginImplFrameState::IDLE;
- // Count any prepare tiles that happens in commits in between frames. We want
- // to prevent a prepare tiles during the next frame's deadline in that case.
- // This also allows synchronous compositor to do one PrepareTiles per draw.
- // This is same as the old prepare tiles funnel behavior.
- did_prepare_tiles_ = false;
- // If a new or undrawn active tree is pending after the deadline,
- // then the main thread is in a high latency mode.
- main_thread_missed_last_deadline_ =
- CommitPending() || has_pending_tree_ || active_tree_needs_first_draw_;
- // If we're entering a state where we won't get BeginFrames set all the
- // funnels so that we don't perform any actions that we shouldn't.
- if (!BeginFrameNeeded())
- did_send_begin_main_frame_for_current_frame_ = true;
- }
- SchedulerStateMachine::BeginImplFrameDeadlineMode
- SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode() const {
- const bool outside_begin_frame =
- begin_impl_frame_state_ != BeginImplFrameState::INSIDE_BEGIN_FRAME;
- if (settings_.using_synchronous_renderer_compositor || outside_begin_frame) {
- // No deadline for synchronous compositor, or when outside the begin frame.
- return BeginImplFrameDeadlineMode::NONE;
- } else if (ShouldBlockDeadlineIndefinitely()) {
- // We do not want to wait for a deadline because we're waiting for full
- // pipeline to be flushed for headless.
- return BeginImplFrameDeadlineMode::BLOCKED;
- } else if (ShouldTriggerBeginImplFrameDeadlineImmediately()) {
- // We are ready to draw a new active tree immediately because there's no
- // commit expected or we're prioritizing active tree latency.
- return BeginImplFrameDeadlineMode::IMMEDIATE;
- } else if (needs_redraw_) {
- // We have an animation or fast input path on the impl thread that wants
- // to draw, so don't wait too long for a new active tree.
- return BeginImplFrameDeadlineMode::REGULAR;
- } else {
- // The impl thread doesn't have anything it wants to draw and we are just
- // waiting for a new active tree.
- return BeginImplFrameDeadlineMode::LATE;
- }
- }
- bool SchedulerStateMachine::ShouldTriggerBeginImplFrameDeadlineImmediately()
- const {
- // If we aborted the current frame we should end the deadline right now.
- if (ShouldAbortCurrentFrame() && !has_pending_tree_)
- return true;
- // Throttle the deadline on CompositorFrameAck since we wont draw and submit
- // anyway.
- if (IsDrawThrottled())
- return false;
- // Delay immediate draws when we have pending animation worklet updates to
- // give them time to produce output before we draw.
- if (processing_animation_worklets_for_active_tree_)
- return false;
- // In full-pipe mode, we just gave all pipeline stages a chance to contribute.
- // We shouldn't wait any longer in any case - even if there are no updates.
- if (settings_.wait_for_all_pipeline_stages_before_draw)
- return true;
- if (active_tree_needs_first_draw_)
- return true;
- if (!needs_redraw_)
- return false;
- // This is used to prioritize impl-thread draws when the main thread isn't
- // producing anything, e.g., after an aborted commit. We also check that we
- // don't have a pending tree -- otherwise we should give it a chance to
- // activate.
- // TODO(skyostil): Revisit this when we have more accurate deadline estimates.
- if (!CommitPending() && !has_pending_tree_)
- return true;
- // Prioritize impl-thread draws in ImplLatencyTakesPriority mode.
- if (ImplLatencyTakesPriority())
- return true;
- return false;
- }
- bool SchedulerStateMachine::ShouldBlockDeadlineIndefinitely() const {
- if (!settings_.wait_for_all_pipeline_stages_before_draw &&
- !settings_.commit_to_active_tree) {
- return false;
- }
- // Avoid blocking for any reason if we don't have a layer tree frame sink or
- // are invisible.
- if (layer_tree_frame_sink_state_ == LayerTreeFrameSinkState::NONE)
- return false;
- if (!visible_)
- return false;
- // Do not wait for main frame to be ready for commits if in full-pipe mode,
- // if we're deferring commits, as the main thread may be blocked on paused
- // virtual time, causing deadlock against external frame control.
- if (defer_begin_main_frame_ &&
- settings_.wait_for_all_pipeline_stages_before_draw) {
- return false;
- }
- // Wait for main frame if one is in progress or about to be started.
- if (ShouldSendBeginMainFrame())
- return true;
- // TODO(schenney): Is the right way to handle main frame without commit
- // to add a new begin_main_frame_state_?
- if (begin_main_frame_state_ != BeginMainFrameState::IDLE)
- return true;
- // Wait for tiles and activation.
- if (has_pending_tree_)
- return true;
- // Avoid blocking for draw when we can't draw. We block in the above cases
- // even if we cannot draw, because we may still be waiting for the first
- // active tree.
- if (!can_draw_)
- return false;
- // Wait for remaining tiles and draw.
- if (!active_tree_is_ready_to_draw_)
- return true;
- return false;
- }
- bool SchedulerStateMachine::IsDrawThrottled() const {
- return pending_submit_frames_ >= kMaxPendingSubmitFrames;
- }
- void SchedulerStateMachine::SetVisible(bool visible) {
- if (visible_ == visible)
- return;
- visible_ = visible;
- if (visible)
- main_thread_missed_last_deadline_ = false;
- did_prepare_tiles_ = false;
- }
- void SchedulerStateMachine::SetBeginFrameSourcePaused(bool paused) {
- begin_frame_source_paused_ = paused;
- }
- void SchedulerStateMachine::SetResourcelessSoftwareDraw(
- bool resourceless_draw) {
- resourceless_draw_ = resourceless_draw;
- }
- void SchedulerStateMachine::SetCanDraw(bool can_draw) {
- can_draw_ = can_draw;
- }
- void SchedulerStateMachine::SetSkipDraw(bool skip_draw) {
- skip_draw_ = skip_draw;
- }
- void SchedulerStateMachine::SetNeedsRedraw() {
- needs_redraw_ = true;
- }
- void SchedulerStateMachine::SetNeedsPrepareTiles() {
- if (!needs_prepare_tiles_) {
- TRACE_EVENT0("cc", "SchedulerStateMachine::SetNeedsPrepareTiles");
- needs_prepare_tiles_ = true;
- }
- }
- void SchedulerStateMachine::DidSubmitCompositorFrame() {
- TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("cc", "Scheduler:pending_submit_frames",
- TRACE_ID_LOCAL(this), "pending_frames",
- pending_submit_frames_);
- DCHECK_LT(pending_submit_frames_, kMaxPendingSubmitFrames);
- pending_submit_frames_++;
- submit_frames_with_current_layer_tree_frame_sink_++;
- did_submit_in_last_frame_ = true;
- last_frame_number_submit_performed_ = current_frame_number_;
- }
- void SchedulerStateMachine::DidReceiveCompositorFrameAck() {
- TRACE_EVENT_NESTABLE_ASYNC_END1("cc", "Scheduler:pending_submit_frames",
- TRACE_ID_LOCAL(this), "pending_frames",
- pending_submit_frames_);
- pending_submit_frames_--;
- }
- void SchedulerStateMachine::SetTreePrioritiesAndScrollState(
- TreePriority tree_priority,
- ScrollHandlerState scroll_handler_state) {
- tree_priority_ = tree_priority;
- scroll_handler_state_ = scroll_handler_state;
- }
- void SchedulerStateMachine::SetCriticalBeginMainFrameToActivateIsFast(
- bool is_fast) {
- critical_begin_main_frame_to_activate_is_fast_ = is_fast;
- }
- bool SchedulerStateMachine::ImplLatencyTakesPriority() const {
- // Attempt to synchronize with the main thread if it has a scroll listener
- // and is fast.
- if (ScrollHandlerState::SCROLL_AFFECTS_SCROLL_HANDLER ==
- scroll_handler_state_ &&
- critical_begin_main_frame_to_activate_is_fast_)
- return false;
- // Don't wait for the main thread if we are prioritizing smoothness.
- if (SMOOTHNESS_TAKES_PRIORITY == tree_priority_)
- return true;
- return false;
- }
- void SchedulerStateMachine::SetNeedsBeginMainFrame() {
- needs_begin_main_frame_ = true;
- }
- void SchedulerStateMachine::SetNeedsOneBeginImplFrame() {
- needs_one_begin_impl_frame_ = true;
- }
- void SchedulerStateMachine::NotifyReadyToCommit() {
- DCHECK_EQ(begin_main_frame_state_, BeginMainFrameState::SENT);
- begin_main_frame_state_ = BeginMainFrameState::READY_TO_COMMIT;
- // In commit_to_active_tree mode, commit should happen right after BeginFrame,
- // meaning when this function is called, next action should be commit.
- if (settings_.commit_to_active_tree)
- DCHECK(ShouldCommit());
- }
- void SchedulerStateMachine::BeginMainFrameAborted(CommitEarlyOutReason reason) {
- DCHECK_EQ(begin_main_frame_state_, BeginMainFrameState::SENT);
- // If the main thread aborted, it doesn't matter if the main thread missed
- // the last deadline since it didn't have an update anyway.
- main_thread_missed_last_deadline_ = false;
- switch (reason) {
- case CommitEarlyOutReason::ABORTED_NOT_VISIBLE:
- case CommitEarlyOutReason::ABORTED_DEFERRED_MAIN_FRAME_UPDATE:
- case CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT:
- // TODO(schenney) For ABORTED_DEFERRED_COMMIT we will need to do
- // something different because we have updated the main frame, but
- // we have not committed it. So we do not need a begin main frame
- // but we might need a commit.
- // We might have top split the compositor commit code from frame updates,
- // or track a pending commit separately from a pending main frame update.
- begin_main_frame_state_ = BeginMainFrameState::IDLE;
- SetNeedsBeginMainFrame();
- return;
- case CommitEarlyOutReason::FINISHED_NO_UPDATES:
- WillCommit(/*commit_had_no_updates=*/true);
- return;
- }
- }
- void SchedulerStateMachine::DidPrepareTiles() {
- needs_prepare_tiles_ = false;
- did_prepare_tiles_ = true;
- }
- void SchedulerStateMachine::DidLoseLayerTreeFrameSink() {
- if (layer_tree_frame_sink_state_ == LayerTreeFrameSinkState::NONE ||
- layer_tree_frame_sink_state_ == LayerTreeFrameSinkState::CREATING)
- return;
- layer_tree_frame_sink_state_ = LayerTreeFrameSinkState::NONE;
- needs_redraw_ = false;
- }
- bool SchedulerStateMachine::NotifyReadyToActivate() {
- // It is not valid for clients to try and activate the pending tree whilst
- // paint worklets are still being processed; the pending tree *must* be fully
- // painted before it can ever be activated (even if e.g. it is not visible),
- // because we cannot paint the active tree.
- DCHECK(!processing_paint_worklets_for_pending_tree_);
- if (!has_pending_tree_ || pending_tree_is_ready_for_activation_)
- return false;
- pending_tree_is_ready_for_activation_ = true;
- return true;
- }
- bool SchedulerStateMachine::IsReadyToActivate() {
- return pending_tree_is_ready_for_activation_;
- }
- void SchedulerStateMachine::NotifyReadyToDraw() {
- active_tree_is_ready_to_draw_ = true;
- }
- void SchedulerStateMachine::NotifyAnimationWorkletStateChange(
- AnimationWorkletState state,
- TreeType tree) {
- if (tree == TreeType::ACTIVE) {
- switch (state) {
- case AnimationWorkletState::PROCESSING:
- DCHECK_GE(processing_animation_worklets_for_active_tree_, 0);
- DCHECK_LE(processing_animation_worklets_for_active_tree_, 1);
- processing_animation_worklets_for_active_tree_++;
- break;
- case AnimationWorkletState::IDLE:
- DCHECK_LE(processing_animation_worklets_for_active_tree_, 2);
- DCHECK_GE(processing_animation_worklets_for_active_tree_, 1);
- processing_animation_worklets_for_active_tree_--;
- }
- } else {
- processing_animation_worklets_for_pending_tree_ =
- (state == AnimationWorkletState::PROCESSING);
- }
- }
- void SchedulerStateMachine::NotifyPaintWorkletStateChange(
- PaintWorkletState state) {
- bool processing_paint_worklets_for_pending_tree =
- (state == PaintWorkletState::PROCESSING);
- DCHECK_NE(processing_paint_worklets_for_pending_tree,
- processing_paint_worklets_for_pending_tree_);
- processing_paint_worklets_for_pending_tree_ =
- processing_paint_worklets_for_pending_tree;
- }
- void SchedulerStateMachine::DidCreateAndInitializeLayerTreeFrameSink() {
- DCHECK_EQ(layer_tree_frame_sink_state_, LayerTreeFrameSinkState::CREATING);
- layer_tree_frame_sink_state_ =
- LayerTreeFrameSinkState::WAITING_FOR_FIRST_COMMIT;
- if (did_create_and_initialize_first_layer_tree_frame_sink_) {
- // 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_begin_main_frame_ = true;
- }
- did_create_and_initialize_first_layer_tree_frame_sink_ = true;
- pending_submit_frames_ = 0;
- submit_frames_with_current_layer_tree_frame_sink_ = 0;
- main_thread_missed_last_deadline_ = false;
- }
- bool SchedulerStateMachine::HasInitializedLayerTreeFrameSink() const {
- switch (layer_tree_frame_sink_state_) {
- case LayerTreeFrameSinkState::NONE:
- case LayerTreeFrameSinkState::CREATING:
- return false;
- case LayerTreeFrameSinkState::ACTIVE:
- case LayerTreeFrameSinkState::WAITING_FOR_FIRST_COMMIT:
- case LayerTreeFrameSinkState::WAITING_FOR_FIRST_ACTIVATION:
- return true;
- }
- NOTREACHED();
- return false;
- }
- } // namespace cc
|