| Index: cc/surfaces/display_scheduler.cc
|
| diff --git a/cc/surfaces/display_scheduler.cc b/cc/surfaces/display_scheduler.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e46031fc5455a312d865d742cb6847f1f7da83f2
|
| --- /dev/null
|
| +++ b/cc/surfaces/display_scheduler.cc
|
| @@ -0,0 +1,273 @@
|
| +// Copyright 2015 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/surfaces/display_scheduler.h"
|
| +
|
| +#include <vector>
|
| +
|
| +#include "base/stl_util.h"
|
| +#include "base/trace_event/trace_event.h"
|
| +#include "cc/output/output_surface.h"
|
| +#include "ui/gfx/frame_time.h"
|
| +
|
| +namespace cc {
|
| +
|
| +DisplayScheduler::DisplayScheduler(
|
| + DisplaySchedulerClient* client,
|
| + BeginFrameSource* begin_frame_source,
|
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner,
|
| + int max_pending_swaps)
|
| + : client_(client),
|
| + begin_frame_source_(begin_frame_source),
|
| + task_runner_(task_runner),
|
| + output_surface_lost_(false),
|
| + root_surface_resources_locked_(true),
|
| + inside_begin_frame_deadline_interval_(false),
|
| + needs_draw_(false),
|
| + entire_display_damaged_(false),
|
| + all_active_child_surfaces_ready_to_draw_(false),
|
| + pending_swaps_(0),
|
| + max_pending_swaps_(max_pending_swaps),
|
| + root_surface_damaged_(false),
|
| + expect_damage_from_root_surface_(false),
|
| + weak_ptr_factory_(this) {
|
| + begin_frame_source_->AddObserver(this);
|
| + begin_frame_deadline_closure_ = base::Bind(
|
| + &DisplayScheduler::OnBeginFrameDeadline, weak_ptr_factory_.GetWeakPtr());
|
| +}
|
| +
|
| +DisplayScheduler::~DisplayScheduler() {
|
| + begin_frame_source_->RemoveObserver(this);
|
| +}
|
| +
|
| +// If we try to draw when the root surface resources are locked, the
|
| +// draw will fail.
|
| +void DisplayScheduler::SetRootSurfaceResourcesLocked(bool locked) {
|
| + root_surface_resources_locked_ = locked;
|
| + ScheduleBeginFrameDeadline();
|
| +}
|
| +
|
| +// Notification that there was a resize or the root surface changed and
|
| +// that we should just draw immediately.
|
| +void DisplayScheduler::EntireDisplayDamaged(SurfaceId root_surface_id) {
|
| + TRACE_EVENT0("cc", "DisplayScheduler::EntireDisplayDamaged");
|
| + needs_draw_ = true;
|
| + entire_display_damaged_ = true;
|
| + root_surface_id_ = root_surface_id;
|
| +
|
| + begin_frame_source_->SetNeedsBeginFrames(!output_surface_lost_);
|
| + ScheduleBeginFrameDeadline();
|
| +}
|
| +
|
| +// Indicates that there was damage to one of the surfaces.
|
| +// Has some logic to wait for multiple active surfaces before
|
| +// triggering the deadline.
|
| +void DisplayScheduler::SurfaceDamaged(SurfaceId surface_id) {
|
| + TRACE_EVENT1("cc", "DisplayScheduler::SurfaceDamaged", "surface_id",
|
| + surface_id.id);
|
| +
|
| + needs_draw_ = true;
|
| +
|
| + if (surface_id == root_surface_id_) {
|
| + root_surface_damaged_ = true;
|
| + } else {
|
| + child_surface_ids_damaged_.insert(surface_id);
|
| +
|
| + // TODO(mithro): Use hints from SetNeedsBeginFrames and SwapAborts.
|
| + all_active_child_surfaces_ready_to_draw_ = base::STLIncludes(
|
| + child_surface_ids_damaged_, child_surface_ids_to_expect_damage_from_);
|
| + }
|
| +
|
| + begin_frame_source_->SetNeedsBeginFrames(!output_surface_lost_);
|
| + ScheduleBeginFrameDeadline();
|
| +}
|
| +
|
| +void DisplayScheduler::OutputSurfaceLost() {
|
| + TRACE_EVENT0("cc", "DisplayScheduler::OutputSurfaceLost");
|
| + output_surface_lost_ = true;
|
| + begin_frame_source_->SetNeedsBeginFrames(false);
|
| + ScheduleBeginFrameDeadline();
|
| +}
|
| +
|
| +void DisplayScheduler::DrawAndSwap() {
|
| + TRACE_EVENT0("cc", "DisplayScheduler::DrawAndSwap");
|
| + DCHECK_LT(pending_swaps_, max_pending_swaps_);
|
| + DCHECK(!output_surface_lost_);
|
| +
|
| + bool success = client_->DrawAndSwap();
|
| + if (!success)
|
| + return;
|
| +
|
| + needs_draw_ = false;
|
| + entire_display_damaged_ = false;
|
| + all_active_child_surfaces_ready_to_draw_ = false;
|
| +
|
| + expect_damage_from_root_surface_ = root_surface_damaged_;
|
| + root_surface_damaged_ = false;
|
| +
|
| + child_surface_ids_to_expect_damage_from_ =
|
| + base::STLSetIntersection<std::vector<SurfaceId>>(
|
| + child_surface_ids_damaged_, child_surface_ids_damaged_prev_);
|
| +
|
| + child_surface_ids_damaged_prev_.swap(child_surface_ids_damaged_);
|
| + child_surface_ids_damaged_.clear();
|
| +}
|
| +
|
| +bool DisplayScheduler::OnBeginFrameMixInDelegate(const BeginFrameArgs& args) {
|
| + base::TimeTicks now = gfx::FrameTime::Now();
|
| + TRACE_EVENT2("cc", "DisplayScheduler::BeginFrame", "args", args.AsValue(),
|
| + "now", now);
|
| +
|
| + // Only service missed BeginFrames if they haven't already expired.
|
| + base::TimeTicks adjusted_deadline =
|
| + args.deadline - BeginFrameArgs::DefaultEstimatedParentDrawTime();
|
| + if (args.type == BeginFrameArgs::MISSED && now > adjusted_deadline)
|
| + return false;
|
| +
|
| + // If we get another BeginFrame before the previous deadline,
|
| + // synchronously trigger the previous deadline before progressing.
|
| + if (inside_begin_frame_deadline_interval_) {
|
| + OnBeginFrameDeadline();
|
| + }
|
| +
|
| + // Schedule the deadline.
|
| + current_begin_frame_args_ = args;
|
| + current_begin_frame_args_.deadline = adjusted_deadline;
|
| + inside_begin_frame_deadline_interval_ = true;
|
| + ScheduleBeginFrameDeadline();
|
| +
|
| + return true;
|
| +}
|
| +
|
| +base::TimeTicks DisplayScheduler::DesiredBeginFrameDeadlineTime() {
|
| + if (output_surface_lost_) {
|
| + TRACE_EVENT_INSTANT0("cc", "Lost output surface", TRACE_EVENT_SCOPE_THREAD);
|
| + return base::TimeTicks();
|
| + }
|
| +
|
| + if (pending_swaps_ >= max_pending_swaps_) {
|
| + TRACE_EVENT_INSTANT0("cc", "Swap throttled", TRACE_EVENT_SCOPE_THREAD);
|
| + return base::TimeTicks();
|
| + }
|
| +
|
| + if (!needs_draw_) {
|
| + TRACE_EVENT_INSTANT0("cc", "No damage yet", TRACE_EVENT_SCOPE_THREAD);
|
| + return current_begin_frame_args_.frame_time +
|
| + current_begin_frame_args_.interval;
|
| + }
|
| +
|
| + if (root_surface_resources_locked_) {
|
| + TRACE_EVENT_INSTANT0("cc", "Root surface resources locked",
|
| + TRACE_EVENT_SCOPE_THREAD);
|
| + return current_begin_frame_args_.frame_time +
|
| + current_begin_frame_args_.interval;
|
| + }
|
| +
|
| + // TODO(mithro): Be smarter about resize deadlines.
|
| + if (entire_display_damaged_) {
|
| + TRACE_EVENT_INSTANT0("cc", "Entire display damaged",
|
| + TRACE_EVENT_SCOPE_THREAD);
|
| + return base::TimeTicks();
|
| + }
|
| +
|
| + bool root_ready_to_draw =
|
| + !expect_damage_from_root_surface_ || root_surface_damaged_;
|
| +
|
| + if (all_active_child_surfaces_ready_to_draw_ && root_ready_to_draw) {
|
| + TRACE_EVENT_INSTANT0("cc", "All active surfaces ready",
|
| + TRACE_EVENT_SCOPE_THREAD);
|
| + return base::TimeTicks();
|
| + }
|
| +
|
| + // Use an earlier deadline if we are only waiting for the root surface
|
| + // in case our expect_damage_from_root_surface heuristic is incorrect.
|
| + // TODO(mithro): Replace this with SetNeedsBeginFrame and SwapAbort
|
| + // logic.
|
| + if (all_active_child_surfaces_ready_to_draw_ &&
|
| + expect_damage_from_root_surface_) {
|
| + TRACE_EVENT_INSTANT0("cc", "Waiting for damage from root surface",
|
| + TRACE_EVENT_SCOPE_THREAD);
|
| + // This adjusts the deadline by DefaultEstimatedParentDrawTime for
|
| + // a second time. The first one represented the Surfaces draw to display
|
| + // latency. This one represents root surface commit+raster+draw latency.
|
| + // We treat the root surface differently since it lives on the same thread
|
| + // as Surfaces and waiting for it too long may push out the Surfaces draw.
|
| + // If we also assume the root surface is fast to start a commit after the
|
| + // beginning of a frame, it'll have a chance to lock its resources, which
|
| + // will cause us to wait for it to unlock its resources above.
|
| + // TODO(mithro): Replace hard coded estimates.
|
| + return current_begin_frame_args_.deadline -
|
| + BeginFrameArgs::DefaultEstimatedParentDrawTime();
|
| + }
|
| +
|
| + TRACE_EVENT_INSTANT0("cc", "More damage expected soon",
|
| + TRACE_EVENT_SCOPE_THREAD);
|
| + return current_begin_frame_args_.deadline;
|
| +}
|
| +
|
| +void DisplayScheduler::ScheduleBeginFrameDeadline() {
|
| + TRACE_EVENT0("cc", "DisplayScheduler::ScheduleBeginFrameDeadline");
|
| +
|
| + // We need to wait for the next BeginFrame before scheduling a deadline.
|
| + if (!inside_begin_frame_deadline_interval_) {
|
| + TRACE_EVENT_INSTANT0("cc", "Waiting for next BeginFrame",
|
| + TRACE_EVENT_SCOPE_THREAD);
|
| + DCHECK(begin_frame_deadline_task_.IsCancelled());
|
| + return;
|
| + }
|
| +
|
| + // Determine the deadline we want to use.
|
| + base::TimeTicks desired_deadline = DesiredBeginFrameDeadlineTime();
|
| +
|
| + // Avoid re-scheduling the deadline if it's already correctly scheduled.
|
| + if (!begin_frame_deadline_task_.IsCancelled() &&
|
| + desired_deadline == begin_frame_deadline_task_time_) {
|
| + TRACE_EVENT_INSTANT0("cc", "Using existing deadline",
|
| + TRACE_EVENT_SCOPE_THREAD);
|
| + return;
|
| + }
|
| +
|
| + // Schedule the deadline.
|
| + begin_frame_deadline_task_time_ = desired_deadline;
|
| + begin_frame_deadline_task_.Cancel();
|
| + begin_frame_deadline_task_.Reset(begin_frame_deadline_closure_);
|
| +
|
| + base::TimeDelta delta =
|
| + std::max(base::TimeDelta(), desired_deadline - gfx::FrameTime::Now());
|
| + task_runner_->PostDelayedTask(FROM_HERE,
|
| + begin_frame_deadline_task_.callback(), delta);
|
| + TRACE_EVENT2("cc", "Using new deadline", "delta", delta.ToInternalValue(),
|
| + "desired_deadline", desired_deadline);
|
| +}
|
| +
|
| +void DisplayScheduler::OnBeginFrameDeadline() {
|
| + TRACE_EVENT0("cc", "DisplayScheduler::OnBeginFrameDeadline");
|
| + inside_begin_frame_deadline_interval_ = false;
|
| + begin_frame_deadline_task_.Cancel();
|
| + begin_frame_deadline_task_time_ = base::TimeTicks();
|
| +
|
| + if (needs_draw_ && !output_surface_lost_) {
|
| + if (pending_swaps_ < max_pending_swaps_ && !root_surface_resources_locked_)
|
| + DrawAndSwap();
|
| + } else {
|
| + begin_frame_source_->SetNeedsBeginFrames(false);
|
| + }
|
| +
|
| + begin_frame_source_->DidFinishFrame(0);
|
| +}
|
| +
|
| +void DisplayScheduler::DidSwapBuffers() {
|
| + pending_swaps_++;
|
| + TRACE_EVENT1("cc", "DisplayScheduler::DidSwapBuffers", "pending_frames",
|
| + pending_swaps_);
|
| +}
|
| +
|
| +void DisplayScheduler::DidSwapBuffersComplete() {
|
| + pending_swaps_--;
|
| + TRACE_EVENT1("cc", "DisplayScheduler::DidSwapBuffersComplete",
|
| + "pending_frames", pending_swaps_);
|
| + ScheduleBeginFrameDeadline();
|
| +}
|
| +
|
| +} // namespace cc
|
|
|