summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsunnyps <sunnyps@chromium.org>2015-04-01 19:31:52 -0700
committerCommit bot <commit-bot@chromium.org>2015-04-02 02:32:30 +0000
commit5333e1468ef3af0099769d4ec6a4c74e9e603766 (patch)
treecc0a818ac1139c606f134ed85f80501e056cb7be
parent4242790a39d4b37ea27995e43189983ad469542b (diff)
downloadchromium_src-5333e1468ef3af0099769d4ec6a4c74e9e603766.zip
chromium_src-5333e1468ef3af0099769d4ec6a4c74e9e603766.tar.gz
chromium_src-5333e1468ef3af0099769d4ec6a4c74e9e603766.tar.bz2
cc: Make scheduling be driven by vsync for android webview.
This CL makes android webview use the similar mechanisms for scheduling as other platforms instead of using special polling code in the scheduler. Design Doc: https://docs.google.com/a/chromium.org/document/d/1w5UiuA2uZcAiU9-1Y23bxXaStUThkAK8wHI4ULqho2Y/edit# BUG=439275 Committed: https://crrev.com/bf27da634790bb6eecf4b89f278cfd55c5e5d1f3 Cr-Commit-Position: refs/heads/master@{#323361} Review URL: https://codereview.chromium.org/817603002 Cr-Commit-Position: refs/heads/master@{#323410}
-rw-r--r--android_webview/browser/browser_view_renderer.cc118
-rw-r--r--android_webview/browser/browser_view_renderer.h22
-rw-r--r--android_webview/browser/browser_view_renderer_client.h2
-rw-r--r--android_webview/browser/browser_view_renderer_unittest.cc41
-rw-r--r--cc/output/begin_frame_args.cc2
-rw-r--r--cc/output/begin_frame_args.h1
-rw-r--r--cc/output/output_surface.h6
-rw-r--r--cc/output/output_surface_client.h2
-rw-r--r--cc/scheduler/scheduler.cc163
-rw-r--r--cc/scheduler/scheduler.h12
-rw-r--r--cc/scheduler/scheduler_state_machine.cc251
-rw-r--r--cc/scheduler/scheduler_state_machine.h43
-rw-r--r--cc/scheduler/scheduler_unittest.cc166
-rw-r--r--cc/surfaces/display.cc4
-rw-r--r--cc/surfaces/display.h1
-rw-r--r--cc/test/fake_layer_tree_host_impl_client.h1
-rw-r--r--cc/test/fake_output_surface.h1
-rw-r--r--cc/test/fake_output_surface_client.h1
-rw-r--r--cc/test/layer_tree_test.cc5
-rw-r--r--cc/test/layer_tree_test.h1
-rw-r--r--cc/trees/layer_tree_host_impl.cc4
-rw-r--r--cc/trees/layer_tree_host_impl.h4
-rw-r--r--cc/trees/layer_tree_host_impl_unittest.cc1
-rw-r--r--cc/trees/layer_tree_host_unittest.cc16
-rw-r--r--cc/trees/single_thread_proxy.cc10
-rw-r--r--cc/trees/single_thread_proxy.h3
-rw-r--r--cc/trees/thread_proxy.cc11
-rw-r--r--cc/trees/thread_proxy.h2
-rw-r--r--content/browser/android/in_process/synchronous_compositor_external_begin_frame_source.cc10
-rw-r--r--content/browser/android/in_process/synchronous_compositor_external_begin_frame_source.h3
-rw-r--r--content/browser/android/in_process/synchronous_compositor_impl.cc69
-rw-r--r--content/browser/android/in_process/synchronous_compositor_impl.h15
-rw-r--r--content/browser/android/in_process/synchronous_compositor_output_surface.cc28
-rw-r--r--content/browser/android/in_process/synchronous_compositor_output_surface.h14
-rw-r--r--content/browser/renderer_host/render_widget_host_view_android.cc51
-rw-r--r--content/public/browser/android/synchronous_compositor.h5
-rw-r--r--content/public/browser/android/synchronous_compositor_client.h5
-rw-r--r--content/public/test/test_synchronous_compositor_android.h1
38 files changed, 650 insertions, 445 deletions
diff --git a/android_webview/browser/browser_view_renderer.cc b/android_webview/browser/browser_view_renderer.cc
index 6b4ee92..928a62b7 100644
--- a/android_webview/browser/browser_view_renderer.cc
+++ b/android_webview/browser/browser_view_renderer.cc
@@ -98,8 +98,6 @@ BrowserViewRenderer::BrowserViewRenderer(
on_new_picture_enable_(false),
clear_view_(false),
offscreen_pre_raster_(false),
- compositor_needs_continuous_invalidate_(false),
- block_invalidates_(false),
fallback_tick_pending_(false) {
}
@@ -198,6 +196,7 @@ bool BrowserViewRenderer::CanOnDraw() {
bool BrowserViewRenderer::OnDrawHardware() {
TRACE_EVENT0("android_webview", "BrowserViewRenderer::OnDrawHardware");
+
shared_renderer_state_.InitializeHardwareDrawIfNeededOnUI();
if (!CanOnDraw()) {
@@ -220,6 +219,8 @@ bool BrowserViewRenderer::OnDrawHardware() {
}
bool BrowserViewRenderer::CompositeHw() {
+ CancelFallbackTick();
+
ReturnResourceFromParent();
compositor_->SetMemoryPolicy(CalculateDesiredMemoryPolicy());
@@ -263,7 +264,6 @@ bool BrowserViewRenderer::CompositeHw() {
transform_for_tile_priority, offscreen_pre_raster_,
parent_draw_constraints.is_layer));
- DidComposite();
// Uncommitted frame can happen with consecutive fallback ticks.
ReturnUnusedResource(shared_renderer_state_.PassUncommittedFrameOnUI());
shared_renderer_state_.SetCompositorFrameOnUI(child_frame.Pass());
@@ -271,7 +271,7 @@ bool BrowserViewRenderer::CompositeHw() {
}
void BrowserViewRenderer::UpdateParentDrawConstraints() {
- EnsureContinuousInvalidation(true);
+ PostInvalidateWithFallback();
ParentCompositorDrawConstraints parent_draw_constraints =
shared_renderer_state_.GetParentDrawConstraintsOnUI();
client_->ParentDrawConstraintsUpdated(parent_draw_constraints);
@@ -342,7 +342,7 @@ void BrowserViewRenderer::ClearView() {
clear_view_ = true;
// Always invalidate ignoring the compositor to actually clear the webview.
- EnsureContinuousInvalidation(true);
+ PostInvalidateWithFallback();
}
void BrowserViewRenderer::SetOffscreenPreRaster(bool enable) {
@@ -357,7 +357,7 @@ void BrowserViewRenderer::SetIsPaused(bool paused) {
"paused",
paused);
is_paused_ = paused;
- EnsureContinuousInvalidation(false);
+ UpdateCompositorIsActive();
}
void BrowserViewRenderer::SetViewVisibility(bool view_visible) {
@@ -376,7 +376,7 @@ void BrowserViewRenderer::SetWindowVisibility(bool window_visible) {
"window_visible",
window_visible);
window_visible_ = window_visible;
- EnsureContinuousInvalidation(false);
+ UpdateCompositorIsActive();
}
void BrowserViewRenderer::OnSizeChanged(int width, int height) {
@@ -399,6 +399,7 @@ void BrowserViewRenderer::OnAttachedToWindow(int width, int height) {
height);
attached_to_window_ = true;
size_.SetSize(width, height);
+ UpdateCompositorIsActive();
}
void BrowserViewRenderer::OnDetachedFromWindow() {
@@ -406,6 +407,7 @@ void BrowserViewRenderer::OnDetachedFromWindow() {
shared_renderer_state_.ReleaseHardwareDrawIfNeededOnUI();
attached_to_window_ = false;
DCHECK(!hardware_enabled_);
+ UpdateCompositorIsActive();
}
void BrowserViewRenderer::ReleaseHardware() {
@@ -437,6 +439,7 @@ void BrowserViewRenderer::DidInitializeCompositor(
DCHECK(compositor);
DCHECK(!compositor_);
compositor_ = compositor;
+ UpdateCompositorIsActive();
}
void BrowserViewRenderer::DidDestroyCompositor(
@@ -446,20 +449,6 @@ void BrowserViewRenderer::DidDestroyCompositor(
compositor_ = NULL;
}
-void BrowserViewRenderer::SetContinuousInvalidate(bool invalidate) {
- if (compositor_needs_continuous_invalidate_ == invalidate)
- return;
-
- TRACE_EVENT_INSTANT1("android_webview",
- "BrowserViewRenderer::SetContinuousInvalidate",
- TRACE_EVENT_SCOPE_THREAD,
- "invalidate",
- invalidate);
- compositor_needs_continuous_invalidate_ = invalidate;
-
- EnsureContinuousInvalidation(false);
-}
-
void BrowserViewRenderer::SetDipScale(float dip_scale) {
dip_scale_ = dip_scale;
CHECK_GT(dip_scale_, 0.f);
@@ -627,13 +616,13 @@ void BrowserViewRenderer::DidOverscroll(gfx::Vector2dF accumulated_overscroll,
client_->DidOverscroll(rounded_overscroll_delta);
}
-void BrowserViewRenderer::EnsureContinuousInvalidation(bool force_invalidate) {
- // This method should be called again when any of these conditions change.
- bool need_invalidate =
- compositor_needs_continuous_invalidate_ || force_invalidate;
- if (!need_invalidate || block_invalidates_)
- return;
+void BrowserViewRenderer::PostInvalidate() {
+ TRACE_EVENT_INSTANT0("android_webview", "BrowserViewRenderer::PostInvalidate",
+ TRACE_EVENT_SCOPE_THREAD);
+ PostInvalidateWithFallback();
+}
+void BrowserViewRenderer::PostInvalidateWithFallback() {
// Always call view invalidate. We rely the Android framework to ignore the
// invalidate when it's not needed such as when view is not visible.
client_->PostInvalidate();
@@ -646,63 +635,49 @@ void BrowserViewRenderer::EnsureContinuousInvalidation(bool force_invalidate) {
// "on-screen" but that updates are not needed when in the background.
bool throttle_fallback_tick =
(is_paused_ && !clear_view_) || (attached_to_window_ && !window_visible_);
- if (throttle_fallback_tick)
- return;
- block_invalidates_ = compositor_needs_continuous_invalidate_;
- if (fallback_tick_pending_)
+ if (throttle_fallback_tick || fallback_tick_pending_)
return;
- // Unretained here is safe because the callbacks are cancelled when
- // they are destroyed.
+ DCHECK(post_fallback_tick_.IsCancelled());
+ DCHECK(fallback_tick_fired_.IsCancelled());
+
post_fallback_tick_.Reset(base::Bind(&BrowserViewRenderer::PostFallbackTick,
base::Unretained(this)));
+ ui_task_runner_->PostTask(FROM_HERE, post_fallback_tick_.callback());
+ fallback_tick_pending_ = true;
+}
+
+void BrowserViewRenderer::CancelFallbackTick() {
+ post_fallback_tick_.Cancel();
fallback_tick_fired_.Cancel();
fallback_tick_pending_ = false;
-
- // No need to reschedule fallback tick if compositor does not need to be
- // ticked. This can happen if this is reached because force_invalidate is
- // true.
- if (compositor_needs_continuous_invalidate_) {
- fallback_tick_pending_ = true;
- ui_task_runner_->PostTask(FROM_HERE, post_fallback_tick_.callback());
- }
}
void BrowserViewRenderer::PostFallbackTick() {
DCHECK(fallback_tick_fired_.IsCancelled());
+ TRACE_EVENT0("android_webview", "BrowserViewRenderer::PostFallbackTick");
+ post_fallback_tick_.Cancel();
fallback_tick_fired_.Reset(base::Bind(&BrowserViewRenderer::FallbackTickFired,
base::Unretained(this)));
- if (compositor_needs_continuous_invalidate_) {
- ui_task_runner_->PostDelayedTask(
- FROM_HERE,
- fallback_tick_fired_.callback(),
- base::TimeDelta::FromMilliseconds(kFallbackTickTimeoutInMilliseconds));
- } else {
- // Pretend we just composited to unblock further invalidates.
- DidComposite();
- }
+ ui_task_runner_->PostDelayedTask(
+ FROM_HERE, fallback_tick_fired_.callback(),
+ base::TimeDelta::FromMilliseconds(kFallbackTickTimeoutInMilliseconds));
}
void BrowserViewRenderer::FallbackTickFired() {
- TRACE_EVENT1("android_webview",
- "BrowserViewRenderer::FallbackTickFired",
- "compositor_needs_continuous_invalidate_",
- compositor_needs_continuous_invalidate_);
-
+ TRACE_EVENT0("android_webview", "BrowserViewRenderer::FallbackTickFired");
// This should only be called if OnDraw or DrawGL did not come in time, which
- // means block_invalidates_ must still be true.
- DCHECK(block_invalidates_);
+ // means fallback_tick_pending_ must still be true.
+ DCHECK(fallback_tick_pending_);
+ fallback_tick_fired_.Cancel();
fallback_tick_pending_ = false;
- if (compositor_needs_continuous_invalidate_ && compositor_) {
+ if (compositor_) {
if (hardware_enabled_) {
CompositeHw();
} else {
ForceFakeCompositeSW();
}
- } else {
- // Pretend we just composited to unblock further invalidates.
- DidComposite();
}
}
@@ -717,18 +692,15 @@ void BrowserViewRenderer::ForceFakeCompositeSW() {
bool BrowserViewRenderer::CompositeSW(SkCanvas* canvas) {
DCHECK(compositor_);
+ CancelFallbackTick();
ReturnResourceFromParent();
- bool result = compositor_->DemandDrawSw(canvas);
- DidComposite();
- return result;
+ return compositor_->DemandDrawSw(canvas);
}
-void BrowserViewRenderer::DidComposite() {
- block_invalidates_ = false;
- post_fallback_tick_.Cancel();
- fallback_tick_fired_.Cancel();
- fallback_tick_pending_ = false;
- EnsureContinuousInvalidation(false);
+void BrowserViewRenderer::UpdateCompositorIsActive() {
+ if (compositor_)
+ compositor_->SetIsActive(!is_paused_ &&
+ (!attached_to_window_ || window_visible_));
}
std::string BrowserViewRenderer::ToString() const {
@@ -738,10 +710,8 @@ std::string BrowserViewRenderer::ToString() const {
base::StringAppendF(&str, "window_visible: %d ", window_visible_);
base::StringAppendF(&str, "dip_scale: %f ", dip_scale_);
base::StringAppendF(&str, "page_scale_factor: %f ", page_scale_factor_);
- base::StringAppendF(&str,
- "compositor_needs_continuous_invalidate: %d ",
- compositor_needs_continuous_invalidate_);
- base::StringAppendF(&str, "block_invalidates: %d ", block_invalidates_);
+ base::StringAppendF(&str, "fallback_tick_pending: %d ",
+ fallback_tick_pending_);
base::StringAppendF(&str, "view size: %s ", size_.ToString().c_str());
base::StringAppendF(&str, "attached_to_window: %d ", attached_to_window_);
base::StringAppendF(&str,
diff --git a/android_webview/browser/browser_view_renderer.h b/android_webview/browser/browser_view_renderer.h
index 843529b..308abd4 100644
--- a/android_webview/browser/browser_view_renderer.h
+++ b/android_webview/browser/browser_view_renderer.h
@@ -96,7 +96,7 @@ class BrowserViewRenderer : public content::SynchronousCompositorClient {
content::SynchronousCompositor* compositor) override;
void DidDestroyCompositor(
content::SynchronousCompositor* compositor) override;
- void SetContinuousInvalidate(bool invalidate) override;
+ void PostInvalidate() override;
void DidUpdateContent() override;
gfx::Vector2dF GetTotalRootLayerScrollOffset() override;
void UpdateRootLayerState(const gfx::Vector2dF& total_scroll_offset_dip,
@@ -116,12 +116,13 @@ class BrowserViewRenderer : public content::SynchronousCompositorClient {
private:
void SetTotalRootLayerScrollOffset(gfx::Vector2dF new_value_dip);
bool CanOnDraw();
- // Checks the continuous invalidate and block invalidate state, and schedule
- // invalidates appropriately. If |force_invalidate| is true, then send a view
- // invalidate regardless of compositor expectation.
- void EnsureContinuousInvalidation(bool force_invalidate);
+ // Posts an invalidate with fallback tick. All invalidates posted while an
+ // invalidate is pending will be posted as a single invalidate after the
+ // pending invalidate is done.
+ void PostInvalidateWithFallback();
+ void CancelFallbackTick();
+ void UpdateCompositorIsActive();
bool CompositeSW(SkCanvas* canvas);
- void DidComposite();
scoped_refptr<base::trace_event::ConvertableToTraceFormat>
RootLayerStateAsValue(const gfx::Vector2dF& total_scroll_offset_dip,
const gfx::SizeF& scrollable_size_dip);
@@ -145,6 +146,7 @@ class BrowserViewRenderer : public content::SynchronousCompositorClient {
gfx::Vector2d max_scroll_offset() const;
size_t CalculateDesiredMemoryPolicy();
+
// For debug tracing or logging. Return the string representation of this
// view renderer's state.
std::string ToString() const;
@@ -170,14 +172,6 @@ class BrowserViewRenderer : public content::SynchronousCompositorClient {
gfx::Vector2d last_on_draw_scroll_offset_;
gfx::Rect last_on_draw_global_visible_rect_;
- // When true, we should continuously invalidate and keep drawing, for example
- // to drive animation. This value is set by the compositor and should always
- // reflect the expectation of the compositor and not be reused for other
- // states.
- bool compositor_needs_continuous_invalidate_;
-
- // Used to block additional invalidates while one is already pending.
- bool block_invalidates_;
base::CancelableClosure post_fallback_tick_;
base::CancelableClosure fallback_tick_fired_;
diff --git a/android_webview/browser/browser_view_renderer_client.h b/android_webview/browser/browser_view_renderer_client.h
index 4c2f36f..c4bf8f2 100644
--- a/android_webview/browser/browser_view_renderer_client.h
+++ b/android_webview/browser/browser_view_renderer_client.h
@@ -26,6 +26,8 @@ class BrowserViewRendererClient {
virtual void OnNewPicture() = 0;
// Called to trigger view invalidations.
+ // This calls postInvalidateOnAnimation if outside of a vsync, otherwise it
+ // calls invalidate.
virtual void PostInvalidate() = 0;
// Call postInvalidateOnAnimation for invalidations. This is only used to
diff --git a/android_webview/browser/browser_view_renderer_unittest.cc b/android_webview/browser/browser_view_renderer_unittest.cc
index e4c6eac..634e1f3 100644
--- a/android_webview/browser/browser_view_renderer_unittest.cc
+++ b/android_webview/browser/browser_view_renderer_unittest.cc
@@ -9,13 +9,7 @@
namespace android_webview {
class SmokeTest : public RenderingTest {
- void StartTest() override {
- browser_view_renderer_->SetContinuousInvalidate(true);
- }
-
- void WillOnDraw() override {
- browser_view_renderer_->SetContinuousInvalidate(false);
- }
+ void StartTest() override { browser_view_renderer_->PostInvalidate(); }
void DidDrawOnRT(SharedRendererState* functor) override {
EndTest();
@@ -26,25 +20,20 @@ RENDERING_TEST_F(SmokeTest);
class ClearViewTest : public RenderingTest {
public:
- ClearViewTest() : on_draw_count_(0u) {}
+ ClearViewTest() : on_draw_count_(0) {}
void StartTest() override {
- browser_view_renderer_->SetContinuousInvalidate(true);
+ browser_view_renderer_->PostInvalidate();
browser_view_renderer_->ClearView();
}
- void WillOnDraw() override {
- on_draw_count_++;
- if (on_draw_count_ == 2u) {
- browser_view_renderer_->SetContinuousInvalidate(false);
- }
- }
-
void DidOnDraw(bool success) override {
- if (on_draw_count_ == 1u) {
+ on_draw_count_++;
+ if (on_draw_count_ == 1) {
// First OnDraw should be skipped due to ClearView.
EXPECT_FALSE(success);
browser_view_renderer_->DidUpdateContent(); // Unset ClearView.
+ browser_view_renderer_->PostInvalidate();
} else {
// Following OnDraws should succeed.
EXPECT_TRUE(success);
@@ -55,25 +44,23 @@ class ClearViewTest : public RenderingTest {
EndTest();
}
private:
- size_t on_draw_count_;
+ int on_draw_count_;
};
RENDERING_TEST_F(ClearViewTest);
class TestAnimateInAndOutOfScreen : public RenderingTest {
public:
- TestAnimateInAndOutOfScreen()
- : on_draw_count_(0u), draw_gl_count_on_rt_(0u) {}
+ TestAnimateInAndOutOfScreen() : on_draw_count_(0), draw_gl_count_on_rt_(0) {}
void StartTest() override {
new_constraints_ = ParentCompositorDrawConstraints(
false, gfx::Transform(), gfx::Rect(window_->surface_size()));
new_constraints_.transform.Scale(2.0, 2.0);
- browser_view_renderer_->SetContinuousInvalidate(true);
+ browser_view_renderer_->PostInvalidate();
}
void WillOnDraw() override {
- browser_view_renderer_->SetContinuousInvalidate(false);
// Step 0: A single onDraw on screen. The parent draw constraints
// of the BVR will updated to be the initial constraints.
// Step 1: A single onDrraw off screen. The parent draw constraints of the
@@ -81,7 +68,7 @@ class TestAnimateInAndOutOfScreen : public RenderingTest {
// Step 2: This onDraw is to introduce the DrawGL that animates the
// webview onto the screen on render thread. End the test when the parent
// draw constraints of BVR is updated to initial constraints.
- if (on_draw_count_ == 1u || on_draw_count_ == 2u)
+ if (on_draw_count_ == 1 || on_draw_count_ == 2)
browser_view_renderer_->PrepareToDraw(gfx::Vector2d(), gfx::Rect());
}
@@ -92,7 +79,7 @@ class TestAnimateInAndOutOfScreen : public RenderingTest {
bool WillDrawOnRT(SharedRendererState* functor,
AwDrawGLInfo* draw_info) override {
- if (draw_gl_count_on_rt_ == 1u) {
+ if (draw_gl_count_on_rt_ == 1) {
draw_gl_count_on_rt_++;
ui_proxy_->PostTask(FROM_HERE, base::Bind(&RenderingTest::PostInvalidate,
base::Unretained(this)));
@@ -104,7 +91,7 @@ class TestAnimateInAndOutOfScreen : public RenderingTest {
draw_info->is_layer = false;
gfx::Transform transform;
- if (draw_gl_count_on_rt_ == 0u)
+ if (draw_gl_count_on_rt_ == 0)
transform = new_constraints_.transform;
transform.matrix().asColMajorf(draw_info->transform);
@@ -145,8 +132,8 @@ class TestAnimateInAndOutOfScreen : public RenderingTest {
}
private:
- size_t on_draw_count_;
- size_t draw_gl_count_on_rt_;
+ int on_draw_count_;
+ int draw_gl_count_on_rt_;
ParentCompositorDrawConstraints initial_constraints_;
ParentCompositorDrawConstraints new_constraints_;
};
diff --git a/cc/output/begin_frame_args.cc b/cc/output/begin_frame_args.cc
index 3663125..7a77be2 100644
--- a/cc/output/begin_frame_args.cc
+++ b/cc/output/begin_frame_args.cc
@@ -15,8 +15,6 @@ const char* BeginFrameArgs::TypeToString(BeginFrameArgsType type) {
return "INVALID";
case BeginFrameArgs::NORMAL:
return "NORMAL";
- case BeginFrameArgs::SYNCHRONOUS:
- return "SYNCHRONOUS";
case BeginFrameArgs::MISSED:
return "MISSED";
case BeginFrameArgs::BEGIN_FRAME_ARGS_TYPE_MAX:
diff --git a/cc/output/begin_frame_args.h b/cc/output/begin_frame_args.h
index 10f7f28..c22f7fd 100644
--- a/cc/output/begin_frame_args.h
+++ b/cc/output/begin_frame_args.h
@@ -40,7 +40,6 @@ struct CC_EXPORT BeginFrameArgs {
enum BeginFrameArgsType {
INVALID,
NORMAL,
- SYNCHRONOUS,
MISSED,
// Not a real type, but used by the IPC system. Should always remain the
// *last* value in this enum.
diff --git a/cc/output/output_surface.h b/cc/output/output_surface.h
index 3e3b085..b72f7fd 100644
--- a/cc/output/output_surface.h
+++ b/cc/output/output_surface.h
@@ -151,6 +151,12 @@ class CC_EXPORT OutputSurface {
void DidLoseOutputSurface();
void SetMemoryPolicy(const ManagedMemoryPolicy& policy);
+ // Support for a pull-model where draws are requested by the output surface.
+ //
+ // OutputSurface::Invalidate is called by the compositor to notify that
+ // there's new content.
+ virtual void Invalidate() {}
+
protected:
OutputSurfaceClient* client_;
diff --git a/cc/output/output_surface_client.h b/cc/output/output_surface_client.h
index 60750b4..03eba10 100644
--- a/cc/output/output_surface_client.h
+++ b/cc/output/output_surface_client.h
@@ -49,6 +49,8 @@ class CC_EXPORT OutputSurfaceClient {
// valid for the lifetime of the OutputSurfaceClient or until unregisted --
// use SetTreeActivationCallback(base::Closure()) to unregister it.
virtual void SetTreeActivationCallback(const base::Closure& callback) = 0;
+ // This allows the output surface to ask it's client for a draw.
+ virtual void OnDraw() = 0;
protected:
virtual ~OutputSurfaceClient() {}
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 37e7c53..3525c55 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -94,6 +94,8 @@ Scheduler::Scheduler(
client_(client),
layer_tree_host_id_(layer_tree_host_id),
task_runner_(task_runner),
+ begin_impl_frame_deadline_mode_(
+ SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE),
state_machine_(scheduler_settings),
inside_process_scheduled_actions_(false),
inside_action_(SchedulerStateMachine::ACTION_NONE),
@@ -109,8 +111,6 @@ Scheduler::Scheduler(
base::Bind(&Scheduler::BeginRetroFrame, weak_factory_.GetWeakPtr());
begin_impl_frame_deadline_closure_ = base::Bind(
&Scheduler::OnBeginImplFrameDeadline, weak_factory_.GetWeakPtr());
- poll_for_draw_triggers_closure_ = base::Bind(
- &Scheduler::PollForAnticipatedDrawTriggers, weak_factory_.GetWeakPtr());
advance_commit_state_closure_ = base::Bind(
&Scheduler::PollToAdvanceCommitState, weak_factory_.GetWeakPtr());
@@ -331,37 +331,15 @@ void Scheduler::SetupNextBeginFrameIfNeeded() {
// We may need to poll when we can't rely on BeginFrame to advance certain
// state or to avoid deadlock.
void Scheduler::SetupPollingMechanisms() {
- bool needs_advance_commit_state_timer = false;
- // Setup PollForAnticipatedDrawTriggers if we need to monitor state but
- // aren't expecting any more BeginFrames. This should only be needed by
- // the synchronous compositor when BeginFrameNeeded is false.
- if (state_machine_.ShouldPollForAnticipatedDrawTriggers()) {
- DCHECK(!state_machine_.SupportsProactiveBeginFrame());
- if (poll_for_draw_triggers_task_.IsCancelled()) {
- poll_for_draw_triggers_task_.Reset(poll_for_draw_triggers_closure_);
- base::TimeDelta delay = begin_impl_frame_args_.IsValid()
- ? begin_impl_frame_args_.interval
- : BeginFrameArgs::DefaultInterval();
- task_runner_->PostDelayedTask(
- FROM_HERE, poll_for_draw_triggers_task_.callback(), delay);
- }
- } else {
- poll_for_draw_triggers_task_.Cancel();
-
- // At this point we'd prefer to advance through the commit flow by
- // drawing a frame, however it's possible that the frame rate controller
- // will not give us a BeginFrame until the commit completes. See
- // crbug.com/317430 for an example of a swap ack being held on commit. Thus
- // we set a repeating timer to poll on ProcessScheduledActions until we
- // successfully reach BeginFrame. Synchronous compositor does not use
- // frame rate controller or have the circular wait in the bug.
- if (IsBeginMainFrameSentOrStarted() &&
- !settings_.using_synchronous_renderer_compositor) {
- needs_advance_commit_state_timer = true;
- }
- }
-
- if (needs_advance_commit_state_timer) {
+ // At this point we'd prefer to advance through the commit flow by
+ // drawing a frame, however it's possible that the frame rate controller
+ // will not give us a BeginFrame until the commit completes. See
+ // crbug.com/317430 for an example of a swap ack being held on commit. Thus
+ // we set a repeating timer to poll on ProcessScheduledActions until we
+ // successfully reach BeginFrame. Synchronous compositor does not use
+ // frame rate controller or have the circular wait in the bug.
+ if (IsBeginMainFrameSentOrStarted() &&
+ !settings_.using_synchronous_renderer_compositor) {
if (advance_commit_state_task_.IsCancelled() &&
begin_impl_frame_args_.IsValid()) {
// Since we'd rather get a BeginImplFrame by the normal mechanism, we
@@ -400,6 +378,11 @@ bool Scheduler::OnBeginFrameMixInDelegate(const BeginFrameArgs& args) {
BeginFrameArgs adjusted_args(args);
adjusted_args.deadline -= EstimatedParentDrawTime();
+ if (settings_.using_synchronous_renderer_compositor) {
+ BeginImplFrameSynchronous(adjusted_args);
+ return true;
+ }
+
// We have just called SetNeedsBeginFrame(true) and the BeginFrameSource has
// sent us the last BeginFrame we have missed. As we might not be able to
// actually make rendering for this call, handle it like a "retro frame".
@@ -410,17 +393,12 @@ bool Scheduler::OnBeginFrameMixInDelegate(const BeginFrameArgs& args) {
return true;
}
- bool should_defer_begin_frame;
- if (settings_.using_synchronous_renderer_compositor) {
- should_defer_begin_frame = false;
- } else {
- should_defer_begin_frame =
- !begin_retro_frame_args_.empty() ||
- !begin_retro_frame_task_.IsCancelled() ||
- !frame_source_->NeedsBeginFrames() ||
- (state_machine_.begin_impl_frame_state() !=
- SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE);
- }
+ bool should_defer_begin_frame =
+ !begin_retro_frame_args_.empty() ||
+ !begin_retro_frame_task_.IsCancelled() ||
+ !frame_source_->NeedsBeginFrames() ||
+ (state_machine_.begin_impl_frame_state() !=
+ SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE);
if (should_defer_begin_frame) {
begin_retro_frame_args_.push_back(adjusted_args);
@@ -428,7 +406,7 @@ bool Scheduler::OnBeginFrameMixInDelegate(const BeginFrameArgs& args) {
"cc", "Scheduler::BeginFrame deferred", TRACE_EVENT_SCOPE_THREAD);
// Queuing the frame counts as "using it", so we need to return true.
} else {
- BeginImplFrame(adjusted_args);
+ BeginImplFrameWithDeadline(adjusted_args);
}
return true;
}
@@ -444,6 +422,19 @@ void Scheduler::SetAuthoritativeVSyncInterval(const base::TimeDelta& interval) {
vsync_observer_->OnUpdateVSyncParameters(last_vsync_timebase_, interval);
}
+void Scheduler::OnDrawForOutputSurface() {
+ DCHECK(settings_.using_synchronous_renderer_compositor);
+ DCHECK_EQ(state_machine_.begin_impl_frame_state(),
+ SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE);
+ DCHECK(!BeginImplFrameDeadlinePending());
+
+ state_machine_.OnBeginImplFrameDeadline();
+ ProcessScheduledActions();
+
+ state_machine_.OnBeginImplFrameIdle();
+ ProcessScheduledActions();
+}
+
// BeginRetroFrame is called for BeginFrames that we've deferred because
// the scheduler was in the middle of processing a previous BeginFrame.
void Scheduler::BeginRetroFrame() {
@@ -485,7 +476,7 @@ void Scheduler::BeginRetroFrame() {
} else {
BeginFrameArgs front = begin_retro_frame_args_.front();
begin_retro_frame_args_.pop_front();
- BeginImplFrame(front);
+ BeginImplFrameWithDeadline(front);
}
}
@@ -517,6 +508,27 @@ void Scheduler::PostBeginRetroFrameIfNeeded() {
task_runner_->PostTask(FROM_HERE, begin_retro_frame_task_.callback());
}
+void Scheduler::BeginImplFrameWithDeadline(const BeginFrameArgs& args) {
+ BeginImplFrame(args);
+
+ // The deadline will be scheduled in ProcessScheduledActions.
+ state_machine_.OnBeginImplFrameDeadlinePending();
+ ProcessScheduledActions();
+}
+
+void Scheduler::BeginImplFrameSynchronous(const BeginFrameArgs& args) {
+ BeginImplFrame(args);
+ FinishImplFrame();
+}
+
+void Scheduler::FinishImplFrame() {
+ state_machine_.OnBeginImplFrameIdle();
+ ProcessScheduledActions();
+
+ client_->DidBeginImplFrameDeadline();
+ frame_source_->DidFinishFrame(begin_retro_frame_args_.size());
+}
+
// BeginImplFrame starts a compositor frame that will wait up until a deadline
// for a BeginMainFrame+activation to complete before it times out and draws
// any asynchronous animation and scroll/pinch updates.
@@ -534,6 +546,7 @@ void Scheduler::BeginImplFrame(const BeginFrameArgs& args) {
main_thread_is_in_high_latency_mode);
DCHECK_EQ(state_machine_.begin_impl_frame_state(),
SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_IDLE);
+ DCHECK(!BeginImplFrameDeadlinePending());
DCHECK(state_machine_.HasInitializedOutputSurface());
advance_commit_state_task_.Cancel();
@@ -552,19 +565,6 @@ void Scheduler::BeginImplFrame(const BeginFrameArgs& args) {
client_->WillBeginImplFrame(begin_impl_frame_args_);
ProcessScheduledActions();
-
- state_machine_.OnBeginImplFrameDeadlinePending();
-
- if (settings_.using_synchronous_renderer_compositor) {
- // The synchronous renderer compositor has to make its GL calls
- // within this call.
- // TODO(brianderson): Have the OutputSurface initiate the deadline tasks
- // so the synchronous renderer compositor can take advantage of splitting
- // up the BeginImplFrame and deadline as well.
- OnBeginImplFrameDeadline();
- } else {
- ScheduleBeginImplFrameDeadline();
- }
}
void Scheduler::ScheduleBeginImplFrameDeadline() {
@@ -579,6 +579,9 @@ void Scheduler::ScheduleBeginImplFrameDeadline() {
base::TimeTicks deadline;
switch (begin_impl_frame_deadline_mode_) {
+ case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE:
+ // No deadline.
+ return;
case SchedulerStateMachine::BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE:
// We are ready to draw a new active tree immediately.
// We don't use Now() here because it's somewhat expensive to call.
@@ -597,17 +600,17 @@ void Scheduler::ScheduleBeginImplFrameDeadline() {
break;
}
- TRACE_EVENT1(
- "cc", "Scheduler::ScheduleBeginImplFrameDeadline", "deadline", deadline);
+ TRACE_EVENT2("cc", "Scheduler::ScheduleBeginImplFrameDeadline", "mode",
+ SchedulerStateMachine::BeginImplFrameDeadlineModeToString(
+ begin_impl_frame_deadline_mode_),
+ "deadline", deadline);
- base::TimeDelta delta = deadline - Now();
- if (delta <= base::TimeDelta())
- delta = base::TimeDelta();
+ base::TimeDelta delta = std::max(deadline - Now(), base::TimeDelta());
task_runner_->PostDelayedTask(
FROM_HERE, begin_impl_frame_deadline_task_.callback(), delta);
}
-void Scheduler::RescheduleBeginImplFrameDeadlineIfNeeded() {
+void Scheduler::ScheduleBeginImplFrameDeadlineIfNeeded() {
if (settings_.using_synchronous_renderer_compositor)
return;
@@ -615,9 +618,12 @@ void Scheduler::RescheduleBeginImplFrameDeadlineIfNeeded() {
SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME)
return;
- if (begin_impl_frame_deadline_mode_ !=
- state_machine_.CurrentBeginImplFrameDeadlineMode())
- ScheduleBeginImplFrameDeadline();
+ if (begin_impl_frame_deadline_mode_ ==
+ state_machine_.CurrentBeginImplFrameDeadlineMode() &&
+ BeginImplFrameDeadlinePending())
+ return;
+
+ ScheduleBeginImplFrameDeadline();
}
void Scheduler::OnBeginImplFrameDeadline() {
@@ -637,20 +643,9 @@ void Scheduler::OnBeginImplFrameDeadline() {
"461509 Scheduler::OnBeginImplFrameDeadline1"));
state_machine_.OnBeginImplFrameDeadline();
ProcessScheduledActions();
- state_machine_.OnBeginImplFrameIdle();
- ProcessScheduledActions();
-
- client_->DidBeginImplFrameDeadline();
- frame_source_->DidFinishFrame(begin_retro_frame_args_.size());
+ FinishImplFrame();
}
-void Scheduler::PollForAnticipatedDrawTriggers() {
- TRACE_EVENT0("cc", "Scheduler::PollForAnticipatedDrawTriggers");
- poll_for_draw_triggers_task_.Cancel();
- state_machine_.DidEnterPollForAnticipatedDrawTriggers();
- ProcessScheduledActions();
- state_machine_.DidLeavePollForAnticipatedDrawTriggers();
-}
void Scheduler::PollToAdvanceCommitState() {
TRACE_EVENT0("cc", "Scheduler::PollToAdvanceCommitState");
@@ -735,6 +730,10 @@ void Scheduler::ProcessScheduledActions() {
case SchedulerStateMachine::ACTION_PREPARE_TILES:
client_->ScheduledActionPrepareTiles();
break;
+ case SchedulerStateMachine::ACTION_INVALIDATE_OUTPUT_SURFACE: {
+ client_->ScheduledActionInvalidateOutputSurface();
+ break;
+ }
}
} while (action != SchedulerStateMachine::ACTION_NONE);
@@ -742,7 +741,7 @@ void Scheduler::ProcessScheduledActions() {
client_->DidAnticipatedDrawTimeChange(AnticipatedDrawTime());
- RescheduleBeginImplFrameDeadlineIfNeeded();
+ ScheduleBeginImplFrameDeadlineIfNeeded();
SetupNextBeginFrameIfNeeded();
}
@@ -783,8 +782,6 @@ void Scheduler::AsValueInto(base::trace_event::TracedValue* state) const {
!begin_retro_frame_task_.IsCancelled());
state->SetBoolean("begin_impl_frame_deadline_task_",
!begin_impl_frame_deadline_task_.IsCancelled());
- state->SetBoolean("poll_for_draw_triggers_task_",
- !poll_for_draw_triggers_task_.IsCancelled());
state->SetBoolean("advance_commit_state_task_",
!advance_commit_state_task_.IsCancelled());
state->BeginDictionary("begin_impl_frame_args");
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index 3a4af5a..4b039de 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -41,10 +41,12 @@ class SchedulerClient {
virtual void ScheduledActionActivateSyncTree() = 0;
virtual void ScheduledActionBeginOutputSurfaceCreation() = 0;
virtual void ScheduledActionPrepareTiles() = 0;
+ virtual void ScheduledActionInvalidateOutputSurface() = 0;
virtual void DidAnticipatedDrawTimeChange(base::TimeTicks time) = 0;
virtual base::TimeDelta DrawDurationEstimate() = 0;
virtual base::TimeDelta BeginMainFrameToCommitDurationEstimate() = 0;
virtual base::TimeDelta CommitToActivateDurationEstimate() = 0;
+ // TODO(sunnyps): Rename DidBeginImplFrameDeadline to DidFinishImplFrame.
virtual void DidBeginImplFrameDeadline() = 0;
virtual void SendBeginFramesToChildren(const BeginFrameArgs& args) = 0;
virtual void SendBeginMainFrameNotExpectedSoon() = 0;
@@ -95,6 +97,8 @@ class CC_EXPORT Scheduler : public BeginFrameObserverMixIn {
// BeginFrameObserverMixin
bool OnBeginFrameMixInDelegate(const BeginFrameArgs& args) override;
+ void OnDrawForOutputSurface();
+
const SchedulerSettings& settings() const { return settings_; }
void CommitVSyncParameters(base::TimeTicks timebase,
@@ -210,11 +214,9 @@ class CC_EXPORT Scheduler : public BeginFrameObserverMixIn {
base::Closure begin_retro_frame_closure_;
base::Closure begin_impl_frame_deadline_closure_;
- base::Closure poll_for_draw_triggers_closure_;
base::Closure advance_commit_state_closure_;
base::CancelableClosure begin_retro_frame_task_;
base::CancelableClosure begin_impl_frame_deadline_task_;
- base::CancelableClosure poll_for_draw_triggers_task_;
base::CancelableClosure advance_commit_state_task_;
SchedulerStateMachine state_machine_;
@@ -223,7 +225,7 @@ class CC_EXPORT Scheduler : public BeginFrameObserverMixIn {
private:
void ScheduleBeginImplFrameDeadline();
- void RescheduleBeginImplFrameDeadlineIfNeeded();
+ void ScheduleBeginImplFrameDeadlineIfNeeded();
void SetupNextBeginFrameIfNeeded();
void PostBeginRetroFrameIfNeeded();
void SetupPollingMechanisms();
@@ -233,9 +235,11 @@ class CC_EXPORT Scheduler : public BeginFrameObserverMixIn {
void AdvanceCommitStateIfPossible();
bool IsBeginMainFrameSentOrStarted() const;
void BeginRetroFrame();
+ void BeginImplFrameWithDeadline(const BeginFrameArgs& args);
+ void BeginImplFrameSynchronous(const BeginFrameArgs& args);
void BeginImplFrame(const BeginFrameArgs& args);
+ void FinishImplFrame();
void OnBeginImplFrameDeadline();
- void PollForAnticipatedDrawTriggers();
void PollToAdvanceCommitState();
void UpdateActiveFrameSource();
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
index 715d92f..cc1a564 100644
--- a/cc/scheduler/scheduler_state_machine.cc
+++ b/cc/scheduler/scheduler_state_machine.cc
@@ -26,10 +26,11 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings)
last_frame_number_swap_performed_(-1),
last_frame_number_swap_requested_(-1),
last_frame_number_begin_main_frame_sent_(-1),
+ last_frame_number_invalidate_output_surface_performed_(-1),
animate_funnel_(false),
- perform_swap_funnel_(false),
request_swap_funnel_(false),
send_begin_main_frame_funnel_(false),
+ invalidate_output_surface_funnel_(false),
prepare_tiles_funnel_(0),
consecutive_checkerboard_animations_(0),
max_pending_swaps_(1),
@@ -38,7 +39,6 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings)
needs_animate_(false),
needs_prepare_tiles_(false),
needs_commit_(false),
- inside_poll_for_anticipated_draw_triggers_(false),
visible_(false),
can_start_(false),
can_draw_(false),
@@ -52,7 +52,9 @@ SchedulerStateMachine::SchedulerStateMachine(const SchedulerSettings& settings)
continuous_painting_(false),
children_need_begin_frames_(false),
defer_commits_(false),
- last_commit_had_no_updates_(false) {
+ last_commit_had_no_updates_(false),
+ did_request_swap_in_last_frame_(false),
+ did_perform_swap_in_last_draw_(false) {
}
const char* SchedulerStateMachine::OutputSurfaceStateToString(
@@ -89,6 +91,22 @@ const char* SchedulerStateMachine::BeginImplFrameStateToString(
return "???";
}
+const char* SchedulerStateMachine::BeginImplFrameDeadlineModeToString(
+ BeginImplFrameDeadlineMode mode) {
+ switch (mode) {
+ case BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE:
+ return "BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE";
+ case BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE:
+ return "BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE";
+ case BEGIN_IMPL_FRAME_DEADLINE_MODE_REGULAR:
+ return "BEGIN_IMPL_FRAME_DEADLINE_MODE_REGULAR";
+ case BEGIN_IMPL_FRAME_DEADLINE_MODE_LATE:
+ return "BEGIN_IMPL_FRAME_DEADLINE_MODE_LATE";
+ }
+ NOTREACHED();
+ return "???";
+}
+
const char* SchedulerStateMachine::CommitStateToString(CommitState state) {
switch (state) {
case COMMIT_STATE_IDLE:
@@ -146,6 +164,8 @@ const char* SchedulerStateMachine::ActionToString(Action action) {
return "ACTION_BEGIN_OUTPUT_SURFACE_CREATION";
case ACTION_PREPARE_TILES:
return "ACTION_PREPARE_TILES";
+ case ACTION_INVALIDATE_OUTPUT_SURFACE:
+ return "ACTION_INVALIDATE_OUTPUT_SURFACE";
}
NOTREACHED();
return "???";
@@ -184,11 +204,12 @@ void SchedulerStateMachine::AsValueInto(
state->SetInteger("last_frame_number_begin_main_frame_sent",
last_frame_number_begin_main_frame_sent_);
state->SetBoolean("funnel: animate_funnel", animate_funnel_);
- state->SetBoolean("funnel: perform_swap_funnel", perform_swap_funnel_);
state->SetBoolean("funnel: request_swap_funnel", request_swap_funnel_);
state->SetBoolean("funnel: send_begin_main_frame_funnel",
send_begin_main_frame_funnel_);
state->SetInteger("funnel: prepare_tiles_funnel", prepare_tiles_funnel_);
+ state->SetBoolean("funnel: invalidate_output_surface_funnel",
+ invalidate_output_surface_funnel_);
state->SetInteger("consecutive_checkerboard_animations",
consecutive_checkerboard_animations_);
state->SetInteger("max_pending_swaps_", max_pending_swaps_);
@@ -218,26 +239,14 @@ void SchedulerStateMachine::AsValueInto(
state->SetBoolean("continuous_painting", continuous_painting_);
state->SetBoolean("children_need_begin_frames", children_need_begin_frames_);
state->SetBoolean("defer_commits", defer_commits_);
+ state->SetBoolean("last_commit_had_no_updates", last_commit_had_no_updates_);
+ state->SetBoolean("did_request_swap_in_last_frame",
+ did_request_swap_in_last_frame_);
+ state->SetBoolean("did_perform_swap_in_last_draw",
+ did_perform_swap_in_last_draw_);
state->EndDictionary();
}
-void SchedulerStateMachine::AdvanceCurrentFrameNumber() {
- current_frame_number_++;
-
- animate_funnel_ = false;
- perform_swap_funnel_ = false;
- request_swap_funnel_ = false;
- send_begin_main_frame_funnel_ = false;
-
- // "Drain" the PrepareTiles funnel.
- if (prepare_tiles_funnel_ > 0)
- prepare_tiles_funnel_--;
-
- skip_begin_main_frame_to_reduce_latency_ =
- skip_next_begin_main_frame_to_reduce_latency_;
- skip_next_begin_main_frame_to_reduce_latency_ = false;
-}
-
bool SchedulerStateMachine::PendingDrawsShouldBeAborted() const {
// 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,
@@ -427,7 +436,7 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const {
// TODO(brianderson): Remove this restriction to improve throughput.
bool just_swapped_in_deadline =
begin_impl_frame_state_ == BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE &&
- perform_swap_funnel_;
+ did_perform_swap_in_last_draw_;
if (pending_swaps_ >= max_pending_swaps_ && !just_swapped_in_deadline)
return false;
@@ -462,14 +471,32 @@ bool SchedulerStateMachine::ShouldPrepareTiles() const {
return false;
// Limiting to once per-frame is not enough, since we only want to
- // prepare tiles _after_ draws. Polling for draw triggers and
- // begin-frame are mutually exclusive, so we limit to these two cases.
- if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE &&
- !inside_poll_for_anticipated_draw_triggers_)
+ // prepare tiles _after_ draws.
+ if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE)
return false;
+
return needs_prepare_tiles_;
}
+bool SchedulerStateMachine::ShouldInvalidateOutputSurface() const {
+ // Do not invalidate too many times in a frame.
+ if (invalidate_output_surface_funnel_)
+ 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_ != BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING)
+ return false;
+
+ // 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_ || needs_prepare_tiles_;
+}
+
SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
if (ShouldActivatePendingTree())
return ACTION_ACTIVATE_SYNC_TREE;
@@ -489,6 +516,8 @@ SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
return ACTION_PREPARE_TILES;
if (ShouldSendBeginMainFrame())
return ACTION_SEND_BEGIN_MAIN_FRAME;
+ if (ShouldInvalidateOutputSurface())
+ return ACTION_INVALIDATE_OUTPUT_SURFACE;
if (ShouldBeginOutputSurfaceCreation())
return ACTION_BEGIN_OUTPUT_SURFACE_CREATION;
return ACTION_NONE;
@@ -504,25 +533,11 @@ void SchedulerStateMachine::UpdateState(Action action) {
return;
case ACTION_ANIMATE:
- DCHECK(!animate_funnel_);
- last_frame_number_animate_performed_ = current_frame_number_;
- animate_funnel_ = true;
- needs_animate_ = false;
- // TODO(skyostil): Instead of assuming this, require the client to tell
- // us.
- SetNeedsRedraw();
+ UpdateStateOnAnimate();
return;
case ACTION_SEND_BEGIN_MAIN_FRAME:
- DCHECK(!has_pending_tree_ ||
- settings_.main_frame_before_activation_enabled);
- DCHECK(visible_);
- DCHECK(!send_begin_main_frame_funnel_);
- commit_state_ = COMMIT_STATE_BEGIN_MAIN_FRAME_SENT;
- needs_commit_ = false;
- send_begin_main_frame_funnel_ = true;
- last_frame_number_begin_main_frame_sent_ =
- current_frame_number_;
+ UpdateStateOnSendBeginMainFrame();
return;
case ACTION_COMMIT: {
@@ -545,26 +560,42 @@ void SchedulerStateMachine::UpdateState(Action action) {
}
case ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
- DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_LOST);
- output_surface_state_ = OUTPUT_SURFACE_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_EQ(commit_state_, COMMIT_STATE_IDLE);
- DCHECK(!has_pending_tree_);
- DCHECK(!active_tree_needs_first_draw_);
+ UpdateStateOnBeginOutputSurfaceCreation();
return;
case ACTION_PREPARE_TILES:
UpdateStateOnPrepareTiles();
return;
+
+ case ACTION_INVALIDATE_OUTPUT_SURFACE:
+ UpdateStateOnInvalidateOutputSurface();
+ return;
}
}
+void SchedulerStateMachine::UpdateStateOnAnimate() {
+ DCHECK(!animate_funnel_);
+ last_frame_number_animate_performed_ = current_frame_number_;
+ animate_funnel_ = true;
+ needs_animate_ = false;
+ // TODO(skyostil): Instead of assuming this, require the client to tell us.
+ SetNeedsRedraw();
+}
+
+void SchedulerStateMachine::UpdateStateOnSendBeginMainFrame() {
+ DCHECK(!has_pending_tree_ || settings_.main_frame_before_activation_enabled);
+ DCHECK(visible_);
+ DCHECK(!send_begin_main_frame_funnel_);
+ commit_state_ = COMMIT_STATE_BEGIN_MAIN_FRAME_SENT;
+ needs_commit_ = false;
+ send_begin_main_frame_funnel_ = true;
+ last_frame_number_begin_main_frame_sent_ = current_frame_number_;
+}
+
void SchedulerStateMachine::UpdateStateOnCommit(bool commit_has_no_updates) {
commit_count_++;
+ // Animate after commit even if we've already animated.
if (!commit_has_no_updates)
animate_funnel_ = false;
@@ -649,6 +680,7 @@ void SchedulerStateMachine::UpdateStateOnDraw(bool did_request_swap) {
if (did_request_swap) {
DCHECK(!request_swap_funnel_);
request_swap_funnel_ = true;
+ did_request_swap_in_last_frame_ = true;
last_frame_number_swap_requested_ = current_frame_number_;
}
}
@@ -657,6 +689,25 @@ void SchedulerStateMachine::UpdateStateOnPrepareTiles() {
needs_prepare_tiles_ = false;
}
+void SchedulerStateMachine::UpdateStateOnBeginOutputSurfaceCreation() {
+ DCHECK_EQ(output_surface_state_, OUTPUT_SURFACE_LOST);
+ output_surface_state_ = OUTPUT_SURFACE_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_EQ(commit_state_, COMMIT_STATE_IDLE);
+ DCHECK(!has_pending_tree_);
+ DCHECK(!active_tree_needs_first_draw_);
+}
+
+void SchedulerStateMachine::UpdateStateOnInvalidateOutputSurface() {
+ DCHECK(!invalidate_output_surface_funnel_);
+ invalidate_output_surface_funnel_ = true;
+ last_frame_number_invalidate_output_surface_performed_ =
+ current_frame_number_;
+}
+
void SchedulerStateMachine::SetSkipNextBeginMainFrameToReduceLatency() {
TRACE_EVENT_INSTANT0("cc",
"Scheduler: SkipNextBeginMainFrameToReduceLatency",
@@ -665,10 +716,7 @@ void SchedulerStateMachine::SetSkipNextBeginMainFrameToReduceLatency() {
}
bool SchedulerStateMachine::BeginFrameNeededForChildren() const {
- if (HasInitializedOutputSurface())
- return children_need_begin_frames_;
-
- return false;
+ return children_need_begin_frames_;
}
bool SchedulerStateMachine::BeginFrameNeeded() const {
@@ -676,44 +724,8 @@ bool SchedulerStateMachine::BeginFrameNeeded() const {
// TODO(brianderson): Support output surface creation inside a BeginFrame.
if (!HasInitializedOutputSurface())
return false;
-
- if (SupportsProactiveBeginFrame()) {
- return (BeginFrameNeededToAnimateOrDraw() ||
- BeginFrameNeededForChildren() ||
- ProactiveBeginFrameWanted());
- }
-
- // Proactive BeginFrames are bad for the synchronous compositor because we
- // have to draw when we get the BeginFrame and could end up drawing many
- // duplicate frames if our new frame isn't ready in time.
- // To poll for state with the synchronous compositor without having to draw,
- // we rely on ShouldPollForAnticipatedDrawTriggers instead.
- // Synchronous compositor doesn't have a browser.
- DCHECK(!children_need_begin_frames_);
- return BeginFrameNeededToAnimateOrDraw();
-}
-
-bool SchedulerStateMachine::ShouldPollForAnticipatedDrawTriggers() const {
- // ShouldPollForAnticipatedDrawTriggers is what we use in place of
- // ProactiveBeginFrameWanted when we are using the synchronous
- // compositor.
- if (!SupportsProactiveBeginFrame()) {
- return !BeginFrameNeededToAnimateOrDraw() && ProactiveBeginFrameWanted();
- }
-
- // Non synchronous compositors should rely on
- // ProactiveBeginFrameWanted to poll for state instead.
- return false;
-}
-
-// Note: If SupportsProactiveBeginFrame is false, the scheduler should poll
-// for changes in it's draw state so it can request a BeginFrame when it's
-// actually ready.
-bool SchedulerStateMachine::SupportsProactiveBeginFrame() const {
- // It is undesirable to proactively request BeginFrames if we are
- // using a synchronous compositor because we *must* draw for every
- // BeginFrame, which could cause duplicate draws.
- return !settings_.using_synchronous_renderer_compositor;
+ return (BeginFrameNeededToAnimateOrDraw() || BeginFrameNeededForChildren() ||
+ ProactiveBeginFrameWanted());
}
void SchedulerStateMachine::SetChildrenNeedBeginFrames(
@@ -776,7 +788,7 @@ bool SchedulerStateMachine::ProactiveBeginFrameWanted() const {
// SetNeedsBeginFrame requests, which may propagate to the BeginImplFrame
// provider and get sampled at an inopportune time, delaying the next
// BeginImplFrame.
- if (request_swap_funnel_)
+ if (did_request_swap_in_last_frame_)
return true;
// If the last commit was aborted because of early out (no updates), we should
@@ -788,35 +800,49 @@ bool SchedulerStateMachine::ProactiveBeginFrameWanted() const {
}
void SchedulerStateMachine::OnBeginImplFrame() {
- AdvanceCurrentFrameNumber();
- DCHECK_EQ(begin_impl_frame_state_, BEGIN_IMPL_FRAME_STATE_IDLE)
- << AsValue()->ToString();
begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING;
+ current_frame_number_++;
+
last_commit_had_no_updates_ = false;
+ did_request_swap_in_last_frame_ = false;
+
+ // Clear funnels for any actions we perform during the frame.
+ animate_funnel_ = false;
+ send_begin_main_frame_funnel_ = false;
+ invalidate_output_surface_funnel_ = false;
+
+ // "Drain" the PrepareTiles funnel.
+ if (prepare_tiles_funnel_ > 0)
+ prepare_tiles_funnel_--;
+
+ skip_begin_main_frame_to_reduce_latency_ =
+ skip_next_begin_main_frame_to_reduce_latency_;
+ skip_next_begin_main_frame_to_reduce_latency_ = false;
}
void SchedulerStateMachine::OnBeginImplFrameDeadlinePending() {
- DCHECK_EQ(begin_impl_frame_state_,
- BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING)
- << AsValue()->ToString();
begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME;
}
void SchedulerStateMachine::OnBeginImplFrameDeadline() {
- DCHECK_EQ(begin_impl_frame_state_, BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME)
- << AsValue()->ToString();
begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE;
+
+ did_perform_swap_in_last_draw_ = false;
+
+ // Clear funnels for any actions we perform during the deadline.
+ request_swap_funnel_ = false;
}
void SchedulerStateMachine::OnBeginImplFrameIdle() {
- DCHECK_EQ(begin_impl_frame_state_, BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE)
- << AsValue()->ToString();
begin_impl_frame_state_ = BEGIN_IMPL_FRAME_STATE_IDLE;
}
SchedulerStateMachine::BeginImplFrameDeadlineMode
SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode() const {
- if (ShouldTriggerBeginImplFrameDeadlineImmediately()) {
+ if (settings_.using_synchronous_renderer_compositor) {
+ // No deadline for synchronous compositor.
+ return BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE;
+ } else if (ShouldTriggerBeginImplFrameDeadlineImmediately()) {
return BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE;
} else if (needs_redraw_ && pending_swaps_ < max_pending_swaps_) {
// We have an animation or fast input path on the impl thread that wants
@@ -834,7 +860,6 @@ SchedulerStateMachine::CurrentBeginImplFrameDeadlineMode() const {
bool SchedulerStateMachine::ShouldTriggerBeginImplFrameDeadlineImmediately()
const {
// TODO(brianderson): This should take into account multiple commit sources.
-
if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME)
return false;
@@ -900,7 +925,7 @@ bool SchedulerStateMachine::MainThreadIsInHighLatencyMode() const {
// Even if there's a new active tree to draw at the deadline or we've just
// swapped it, it may have been triggered by a previous BeginImplFrame, in
// which case the main thread is in a high latency mode.
- return (active_tree_needs_first_draw_ || perform_swap_funnel_) &&
+ return (active_tree_needs_first_draw_ || did_perform_swap_in_last_draw_) &&
!send_begin_main_frame_funnel_;
}
@@ -909,15 +934,6 @@ bool SchedulerStateMachine::MainThreadIsInHighLatencyMode() const {
return active_tree_needs_first_draw_;
}
-void SchedulerStateMachine::DidEnterPollForAnticipatedDrawTriggers() {
- AdvanceCurrentFrameNumber();
- inside_poll_for_anticipated_draw_triggers_ = true;
-}
-
-void SchedulerStateMachine::DidLeavePollForAnticipatedDrawTriggers() {
- inside_poll_for_anticipated_draw_triggers_ = false;
-}
-
void SchedulerStateMachine::SetVisible(bool visible) { visible_ = visible; }
void SchedulerStateMachine::SetCanDraw(bool can_draw) { can_draw_ = can_draw; }
@@ -942,9 +958,8 @@ void SchedulerStateMachine::SetMaxSwapsPending(int max) {
void SchedulerStateMachine::DidSwapBuffers() {
pending_swaps_++;
DCHECK_LE(pending_swaps_, max_pending_swaps_);
- DCHECK(!perform_swap_funnel_);
- perform_swap_funnel_ = true;
+ did_perform_swap_in_last_draw_ = true;
last_frame_number_swap_performed_ = current_frame_number_;
}
diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h
index 3914039..0901ca8 100644
--- a/cc/scheduler/scheduler_state_machine.h
+++ b/cc/scheduler/scheduler_state_machine.h
@@ -50,10 +50,10 @@ class CC_EXPORT SchedulerStateMachine {
};
static const char* OutputSurfaceStateToString(OutputSurfaceState state);
- // Note: BeginImplFrameState will always cycle through all the states in
- // order. Whether or not it actually waits or draws, it will at least try to
- // wait in BEGIN_IMPL_FRAME_STATE_INSIDE_BEGIN_FRAME and try to draw in
- // BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE
+ // Note: BeginImplFrameState does not cycle through these states in a fixed
+ // order on all platforms. It's up to the scheduler to set these correctly.
+ // TODO(sunnyps): Rename the states to IDLE, ANIMATE, WAITING_FOR_DEADLINE and
+ // DRAW.
enum BeginImplFrameState {
BEGIN_IMPL_FRAME_STATE_IDLE,
BEGIN_IMPL_FRAME_STATE_BEGIN_FRAME_STARTING,
@@ -63,6 +63,7 @@ class CC_EXPORT SchedulerStateMachine {
static const char* BeginImplFrameStateToString(BeginImplFrameState state);
enum BeginImplFrameDeadlineMode {
+ BEGIN_IMPL_FRAME_DEADLINE_MODE_NONE,
BEGIN_IMPL_FRAME_DEADLINE_MODE_IMMEDIATE,
BEGIN_IMPL_FRAME_DEADLINE_MODE_REGULAR,
BEGIN_IMPL_FRAME_DEADLINE_MODE_LATE,
@@ -110,6 +111,7 @@ class CC_EXPORT SchedulerStateMachine {
ACTION_DRAW_AND_SWAP_ABORT,
ACTION_BEGIN_OUTPUT_SURFACE_CREATION,
ACTION_PREPARE_TILES,
+ ACTION_INVALIDATE_OUTPUT_SURFACE,
};
static const char* ActionToString(Action action);
@@ -123,17 +125,14 @@ class CC_EXPORT SchedulerStateMachine {
// to make progress.
bool BeginFrameNeeded() const;
- // Indicates that we need to independently poll for new state and actions
- // because we can't expect a BeginImplFrame. This is mostly used to avoid
- // drawing repeat frames with the synchronous compositor without dropping
- // necessary actions on the floor.
- bool ShouldPollForAnticipatedDrawTriggers() const;
-
// Indicates that the system has entered and left a BeginImplFrame callback.
// The scheduler will not draw more than once in a given BeginImplFrame
// callback nor send more than one BeginMainFrame message.
void OnBeginImplFrame();
void OnBeginImplFrameDeadlinePending();
+ // Indicates that the scheduler has entered the draw phase. The scheduler
+ // will not draw more than once in a single draw phase.
+ // TODO(sunnyps): Rename OnBeginImplFrameDeadline to OnDraw or similar.
void OnBeginImplFrameDeadline();
void OnBeginImplFrameIdle();
BeginImplFrameState begin_impl_frame_state() const {
@@ -145,15 +144,6 @@ class CC_EXPORT SchedulerStateMachine {
// impl thread to draw, it is in a high latency mode.
bool MainThreadIsInHighLatencyMode() const;
- // PollForAnticipatedDrawTriggers is used by the synchronous compositor to
- // avoid requesting BeginImplFrames when we won't actually draw but still
- // need to advance our state at vsync intervals.
- void DidEnterPollForAnticipatedDrawTriggers();
- void DidLeavePollForAnticipatedDrawTriggers();
- bool inside_poll_for_anticipated_draw_triggers() const {
- return inside_poll_for_anticipated_draw_triggers_;
- }
-
// Indicates whether the LayerTreeHostImpl is visible.
void SetVisible(bool visible);
bool visible() const { return visible_; }
@@ -240,8 +230,6 @@ class CC_EXPORT SchedulerStateMachine {
// True if we need to abort draws to make forward progress.
bool PendingDrawsShouldBeAborted() const;
- bool SupportsProactiveBeginFrame() const;
-
void SetContinuousPainting(bool continuous_painting) {
continuous_painting_ = continuous_painting;
}
@@ -276,13 +264,16 @@ class CC_EXPORT SchedulerStateMachine {
bool ShouldSendBeginMainFrame() const;
bool ShouldCommit() const;
bool ShouldPrepareTiles() const;
+ bool ShouldInvalidateOutputSurface() const;
- void AdvanceCurrentFrameNumber();
-
+ void UpdateStateOnAnimate();
+ void UpdateStateOnSendBeginMainFrame();
void UpdateStateOnCommit(bool commit_had_no_updates);
void UpdateStateOnActivation();
void UpdateStateOnDraw(bool did_request_swap);
+ void UpdateStateOnBeginOutputSurfaceCreation();
void UpdateStateOnPrepareTiles();
+ void UpdateStateOnInvalidateOutputSurface();
const SchedulerSettings settings_;
@@ -298,13 +289,14 @@ class CC_EXPORT SchedulerStateMachine {
int last_frame_number_swap_performed_;
int last_frame_number_swap_requested_;
int last_frame_number_begin_main_frame_sent_;
+ int last_frame_number_invalidate_output_surface_performed_;
// These are used to ensure that an action only happens once per frame,
// deadline, etc.
bool animate_funnel_;
- bool perform_swap_funnel_;
bool request_swap_funnel_;
bool send_begin_main_frame_funnel_;
+ bool invalidate_output_surface_funnel_;
// prepare_tiles_funnel_ is "filled" each time PrepareTiles is called
// and "drained" on each BeginImplFrame. If the funnel gets too full,
// we start throttling ACTION_PREPARE_TILES such that we average one
@@ -318,7 +310,6 @@ class CC_EXPORT SchedulerStateMachine {
bool needs_animate_;
bool needs_prepare_tiles_;
bool needs_commit_;
- bool inside_poll_for_anticipated_draw_triggers_;
bool visible_;
bool can_start_;
bool can_draw_;
@@ -333,6 +324,8 @@ class CC_EXPORT SchedulerStateMachine {
bool children_need_begin_frames_;
bool defer_commits_;
bool last_commit_had_no_updates_;
+ bool did_request_swap_in_last_frame_;
+ bool did_perform_swap_in_last_draw_;
private:
DISALLOW_COPY_AND_ASSIGN(SchedulerStateMachine);
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index 935a80a..3daf16a 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -141,6 +141,10 @@ class FakeSchedulerClient : public SchedulerClient {
void ScheduledActionPrepareTiles() override {
PushAction("ScheduledActionPrepareTiles");
}
+ void ScheduledActionInvalidateOutputSurface() override {
+ actions_.push_back("ScheduledActionInvalidateOutputSurface");
+ states_.push_back(scheduler_->AsValue());
+ }
void DidAnticipatedDrawTimeChange(base::TimeTicks) override {
if (log_anticipated_draw_time_change_)
PushAction("DidAnticipatedDrawTimeChange");
@@ -306,12 +310,18 @@ class SchedulerTest : public testing::Test {
scheduler_->NotifyBeginMainFrameStarted();
scheduler_->NotifyReadyToCommitThenActivateIfNeeded();
- // Run the posted deadline task.
- EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
- task_runner_->RunTasksWhile(client_->ImplFrameDeadlinePending(true));
- EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
-
EXPECT_FALSE(scheduler_->CommitPending());
+
+ if (scheduler_settings_.using_synchronous_renderer_compositor) {
+ scheduler_->SetNeedsRedraw();
+ scheduler_->OnDrawForOutputSurface();
+ } else {
+ // Run the posted deadline task.
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+ task_runner_->RunTasksWhile(client_->ImplFrameDeadlinePending(true));
+ }
+
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
}
client_->Reset();
@@ -321,9 +331,12 @@ class SchedulerTest : public testing::Test {
"Run second frame so Scheduler calls SetNeedsBeginFrame(false).");
AdvanceFrame();
- // Run the posted deadline task.
- EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
- task_runner_->RunTasksWhile(client_->ImplFrameDeadlinePending(true));
+ if (!scheduler_settings_.using_synchronous_renderer_compositor) {
+ // Run the posted deadline task.
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+ task_runner_->RunTasksWhile(client_->ImplFrameDeadlinePending(true));
+ }
+
EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
}
@@ -348,13 +361,14 @@ class SchedulerTest : public testing::Test {
if (scheduler_->settings().use_external_begin_frame_source &&
scheduler_->FrameProductionThrottled()) {
SendNextBeginFrame();
- EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
}
- // Then run tasks until new deadline is scheduled.
- EXPECT_TRUE(
- task_runner_->RunTasksWhile(client_->ImplFrameDeadlinePending(false)));
- EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+ if (!scheduler_->settings().using_synchronous_renderer_compositor) {
+ // Then run tasks until new deadline is scheduled.
+ EXPECT_TRUE(task_runner_->RunTasksWhile(
+ client_->ImplFrameDeadlinePending(false)));
+ EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+ }
}
void SendNextBeginFrame() {
@@ -2269,6 +2283,132 @@ TEST_F(SchedulerTest, SendBeginMainFrameNotExpectedSoon) {
client_->Reset();
}
+TEST_F(SchedulerTest, UsingSynchronousCompositor) {
+ scheduler_settings_.using_synchronous_renderer_compositor = true;
+ scheduler_settings_.use_external_begin_frame_source = true;
+ scheduler_settings_.impl_side_painting = true;
+ SetUpScheduler(true);
+
+ // Compositor thread initiated input/animation.
+ // --------------------------------------------
+ scheduler_->SetNeedsAnimate();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_);
+ client_->Reset();
+
+ // Next vsync.
+ AdvanceFrame();
+ EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3);
+ EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 3);
+ EXPECT_ACTION("ScheduledActionInvalidateOutputSurface", client_, 2, 3);
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ client_->Reset();
+
+ // Continue with animation.
+ scheduler_->SetNeedsAnimate();
+ EXPECT_NO_ACTION(client_);
+
+ // Android onDraw.
+ scheduler_->SetNeedsRedraw();
+ scheduler_->OnDrawForOutputSurface();
+ EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client_);
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ client_->Reset();
+
+ // Next vsync.
+ AdvanceFrame();
+ EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3);
+ EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 3);
+ EXPECT_ACTION("ScheduledActionInvalidateOutputSurface", client_, 2, 3);
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ client_->Reset();
+
+ // Android onDraw.
+ scheduler_->SetNeedsRedraw();
+ scheduler_->OnDrawForOutputSurface();
+ EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client_);
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ client_->Reset();
+
+ // Idle on next vsync.
+ AdvanceFrame();
+ EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3);
+ EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3);
+ EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3);
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ client_->Reset();
+
+ // Android onDraw after idle.
+ // --------------------------
+ scheduler_->SetNeedsRedraw();
+ scheduler_->OnDrawForOutputSurface();
+ EXPECT_ACTION("SetNeedsBeginFrames(true)", client_, 0, 3);
+ EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 3);
+ EXPECT_ACTION("ScheduledActionDrawAndSwapIfPossible", client_, 2, 3);
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ client_->Reset();
+
+ // Idle on next vsync.
+ AdvanceFrame();
+ EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3);
+ EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3);
+ EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3);
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ client_->Reset();
+
+ // Main thread initiated activity.
+ // -------------------------------
+ scheduler_->SetNeedsCommit();
+ EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_);
+ client_->Reset();
+
+ // Next vsync.
+ AdvanceFrame();
+ EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2);
+ EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2);
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ client_->Reset();
+
+ scheduler_->NotifyBeginMainFrameStarted();
+ EXPECT_NO_ACTION(client_);
+
+ // Next vsync.
+ AdvanceFrame();
+ EXPECT_SINGLE_ACTION("WillBeginImplFrame", client_);
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ client_->Reset();
+
+ scheduler_->NotifyReadyToCommit();
+ EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_);
+ client_->Reset();
+
+ scheduler_->NotifyReadyToActivate();
+ EXPECT_SINGLE_ACTION("ScheduledActionActivateSyncTree", client_);
+ client_->Reset();
+
+ // Next vsync.
+ AdvanceFrame();
+ EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3);
+ EXPECT_ACTION("ScheduledActionAnimate", client_, 1, 3);
+ EXPECT_ACTION("ScheduledActionInvalidateOutputSurface", client_, 2, 3);
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ client_->Reset();
+
+ // Android onDraw.
+ scheduler_->SetNeedsRedraw();
+ scheduler_->OnDrawForOutputSurface();
+ EXPECT_SINGLE_ACTION("ScheduledActionDrawAndSwapIfPossible", client_);
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ client_->Reset();
+
+ // Idle on next vsync.
+ AdvanceFrame();
+ EXPECT_ACTION("WillBeginImplFrame", client_, 0, 3);
+ EXPECT_ACTION("SetNeedsBeginFrames(false)", client_, 1, 3);
+ EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3);
+ EXPECT_FALSE(scheduler_->BeginImplFrameDeadlinePending());
+ client_->Reset();
+}
+
TEST_F(SchedulerTest, AuthoritativeVSyncInterval) {
SetUpScheduler(true);
diff --git a/cc/surfaces/display.cc b/cc/surfaces/display.cc
index 7ec4672..6de3c30 100644
--- a/cc/surfaces/display.cc
+++ b/cc/surfaces/display.cc
@@ -196,6 +196,10 @@ void Display::SetMemoryPolicy(const ManagedMemoryPolicy& policy) {
client_->SetMemoryPolicy(policy);
}
+void Display::OnDraw() {
+ NOTREACHED();
+}
+
void Display::OnSurfaceDamaged(SurfaceId surface_id, bool* changed) {
if (aggregator_ &&
aggregator_->previous_contained_surfaces().count(surface_id)) {
diff --git a/cc/surfaces/display.h b/cc/surfaces/display.h
index 30db259..9c5a349 100644
--- a/cc/surfaces/display.h
+++ b/cc/surfaces/display.h
@@ -84,6 +84,7 @@ class CC_SURFACES_EXPORT Display : public OutputSurfaceClient,
bool resourceless_software_draw) override {}
void SetMemoryPolicy(const ManagedMemoryPolicy& policy) override;
void SetTreeActivationCallback(const base::Closure& callback) override {}
+ void OnDraw() override;
// RendererClient implementation.
void SetFullRootLayerDamage() override {}
diff --git a/cc/test/fake_layer_tree_host_impl_client.h b/cc/test/fake_layer_tree_host_impl_client.h
index 63d7f43..9d946aa 100644
--- a/cc/test/fake_layer_tree_host_impl_client.h
+++ b/cc/test/fake_layer_tree_host_impl_client.h
@@ -40,6 +40,7 @@ class FakeLayerTreeHostImplClient : public LayerTreeHostImplClient {
void DidActivateSyncTree() override {}
void DidPrepareTiles() override {}
void DidCompletePageScaleAnimationOnImplThread() override {}
+ void OnDrawForOutputSurface() override {}
};
} // namespace cc
diff --git a/cc/test/fake_output_surface.h b/cc/test/fake_output_surface.h
index 39a78ea..2248b84 100644
--- a/cc/test/fake_output_surface.h
+++ b/cc/test/fake_output_surface.h
@@ -101,6 +101,7 @@ class FakeOutputSurface : public OutputSurface {
void SwapBuffers(CompositorFrame* frame) override;
+ OutputSurfaceClient* client() { return client_; }
bool BindToClient(OutputSurfaceClient* client) override;
void set_framebuffer(unsigned framebuffer) { framebuffer_ = framebuffer; }
diff --git a/cc/test/fake_output_surface_client.h b/cc/test/fake_output_surface_client.h
index 310842d..ddfbd11 100644
--- a/cc/test/fake_output_surface_client.h
+++ b/cc/test/fake_output_surface_client.h
@@ -46,6 +46,7 @@ class FakeOutputSurfaceClient : public OutputSurfaceClient {
bool resourceless_software_draw) override {}
void SetMemoryPolicy(const ManagedMemoryPolicy& policy) override;
void SetTreeActivationCallback(const base::Closure&) override {}
+ void OnDraw() override {}
int swap_count() { return swap_count_; }
diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc
index 2fff95f..87c4035 100644
--- a/cc/test/layer_tree_test.cc
+++ b/cc/test/layer_tree_test.cc
@@ -120,6 +120,11 @@ class ThreadProxyForTest : public ThreadProxy {
test_hooks_->ScheduledActionPrepareTiles();
}
+ void ScheduledActionInvalidateOutputSurface() override {
+ ThreadProxy::ScheduledActionInvalidateOutputSurface();
+ test_hooks_->ScheduledActionInvalidateOutputSurface();
+ }
+
ThreadProxyForTest(
TestHooks* test_hooks,
LayerTreeHost* host,
diff --git a/cc/test/layer_tree_test.h b/cc/test/layer_tree_test.h
index 35a3546..df5f500 100644
--- a/cc/test/layer_tree_test.h
+++ b/cc/test/layer_tree_test.h
@@ -99,6 +99,7 @@ class TestHooks : public AnimationDelegate {
virtual void ScheduledActionCommit() {}
virtual void ScheduledActionBeginOutputSurfaceCreation() {}
virtual void ScheduledActionPrepareTiles() {}
+ virtual void ScheduledActionInvalidateOutputSurface() {}
// Implementation of AnimationDelegate:
void NotifyAnimationStarted(base::TimeTicks monotonic_time,
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 00ae766..c9bba83 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -1435,6 +1435,10 @@ void LayerTreeHostImpl::ReclaimResources(const CompositorFrameAck* ack) {
}
}
+void LayerTreeHostImpl::OnDraw() {
+ client_->OnDrawForOutputSurface();
+}
+
void LayerTreeHostImpl::OnCanDrawStateChangedForTree() {
client_->OnCanDrawStateChanged(CanDraw());
}
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 388de5e..6bbc488 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -121,6 +121,9 @@ class LayerTreeHostImplClient {
// Called when page scale animation has completed on the impl thread.
virtual void DidCompletePageScaleAnimationOnImplThread() = 0;
+ // Called when output surface asks for a draw.
+ virtual void OnDrawForOutputSurface() = 0;
+
protected:
virtual ~LayerTreeHostImplClient() {}
};
@@ -295,6 +298,7 @@ class CC_EXPORT LayerTreeHostImpl
void ReclaimResources(const CompositorFrameAck* ack) override;
void SetMemoryPolicy(const ManagedMemoryPolicy& policy) override;
void SetTreeActivationCallback(const base::Closure& callback) override;
+ void OnDraw() override;
// Called from LayerTreeImpl.
void OnCanDrawStateChangedForTree();
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 818c97e..0092148 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -162,6 +162,7 @@ class LayerTreeHostImplTest : public testing::Test,
void DidCompletePageScaleAnimationOnImplThread() override {
did_complete_page_scale_animation_ = true;
}
+ void OnDrawForOutputSurface() override {}
void set_reduce_memory_result(bool reduce_memory_result) {
reduce_memory_result_ = reduce_memory_result;
diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc
index c2f3417..00f4f9a 100644
--- a/cc/trees/layer_tree_host_unittest.cc
+++ b/cc/trees/layer_tree_host_unittest.cc
@@ -2707,10 +2707,26 @@ class LayerTreeHostTestAbortedCommitDoesntStall : public LayerTreeHostTest {
class LayerTreeHostTestAbortedCommitDoesntStallSynchronousCompositor
: public LayerTreeHostTestAbortedCommitDoesntStall {
+ protected:
void InitializeSettings(LayerTreeSettings* settings) override {
LayerTreeHostTestAbortedCommitDoesntStall::InitializeSettings(settings);
settings->using_synchronous_renderer_compositor = true;
}
+
+ void ScheduledActionInvalidateOutputSurface() override {
+ ImplThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &LayerTreeHostTestAbortedCommitDoesntStallSynchronousCompositor::
+ CallOnDraw,
+ base::Unretained(this)));
+ }
+
+ void CallOnDraw() {
+ // Synchronous compositor does not draw unless told to do so by the output
+ // surface.
+ output_surface()->client()->OnDraw();
+ }
};
MULTI_THREAD_TEST_F(
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index 3633585..f16803d 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -560,6 +560,10 @@ void SingleThreadProxy::DidSwapBuffersCompleteOnImplThread() {
layer_tree_host_->DidCompleteSwapBuffers();
}
+void SingleThreadProxy::OnDrawForOutputSurface() {
+ NOTREACHED() << "Implemented by ThreadProxy for synchronous compositor.";
+}
+
void SingleThreadProxy::CompositeImmediately(base::TimeTicks frame_begin_time) {
TRACE_EVENT0("cc,benchmark", "SingleThreadProxy::CompositeImmediately");
DCHECK(Proxy::IsMainThread());
@@ -576,7 +580,7 @@ void SingleThreadProxy::CompositeImmediately(base::TimeTicks frame_begin_time) {
{
BeginFrameArgs begin_frame_args(BeginFrameArgs::Create(
BEGINFRAME_FROM_HERE, frame_begin_time, base::TimeTicks(),
- BeginFrameArgs::DefaultInterval(), BeginFrameArgs::SYNCHRONOUS));
+ BeginFrameArgs::DefaultInterval(), BeginFrameArgs::NORMAL));
DoBeginMainFrame(begin_frame_args);
DoCommit();
@@ -910,6 +914,10 @@ void SingleThreadProxy::ScheduledActionPrepareTiles() {
layer_tree_host_impl_->PrepareTiles();
}
+void SingleThreadProxy::ScheduledActionInvalidateOutputSurface() {
+ NOTREACHED();
+}
+
void SingleThreadProxy::DidAnticipatedDrawTimeChange(base::TimeTicks time) {
}
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h
index 0ccd28c..47c556d 100644
--- a/cc/trees/single_thread_proxy.h
+++ b/cc/trees/single_thread_proxy.h
@@ -74,6 +74,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy,
void ScheduledActionActivateSyncTree() override;
void ScheduledActionBeginOutputSurfaceCreation() override;
void ScheduledActionPrepareTiles() override;
+ void ScheduledActionInvalidateOutputSurface() override;
void DidAnticipatedDrawTimeChange(base::TimeTicks time) override;
base::TimeDelta DrawDurationEstimate() override;
base::TimeDelta BeginMainFrameToCommitDurationEstimate() override;
@@ -110,6 +111,8 @@ class CC_EXPORT SingleThreadProxy : public Proxy,
void DidActivateSyncTree() override;
void DidPrepareTiles() override;
void DidCompletePageScaleAnimationOnImplThread() override;
+ void OnDrawForOutputSurface() override;
+
void SetDebugState(const LayerTreeDebugState& debug_state) override {}
void RequestNewOutputSurface();
diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc
index 9a095ef..2253a4f 100644
--- a/cc/trees/thread_proxy.cc
+++ b/cc/trees/thread_proxy.cc
@@ -1114,6 +1114,12 @@ DrawResult ThreadProxy::ScheduledActionDrawAndSwapForced() {
return DrawSwapInternal(forced_draw);
}
+void ThreadProxy::ScheduledActionInvalidateOutputSurface() {
+ TRACE_EVENT0("cc", "ThreadProxy::ScheduledActionInvalidateOutputSurface");
+ DCHECK(impl().layer_tree_host_impl->output_surface());
+ impl().layer_tree_host_impl->output_surface()->Invalidate();
+}
+
void ThreadProxy::DidAnticipatedDrawTimeChange(base::TimeTicks time) {
if (impl().current_resource_update_controller)
impl().current_resource_update_controller->PerformMoreUpdates(time);
@@ -1375,4 +1381,9 @@ void ThreadProxy::DidCompletePageScaleAnimationOnImplThread() {
main_thread_weak_ptr_));
}
+void ThreadProxy::OnDrawForOutputSurface() {
+ DCHECK(IsImplThread());
+ impl().scheduler->OnDrawForOutputSurface();
+}
+
} // namespace cc
diff --git a/cc/trees/thread_proxy.h b/cc/trees/thread_proxy.h
index 07f4c6c..1f06611 100644
--- a/cc/trees/thread_proxy.h
+++ b/cc/trees/thread_proxy.h
@@ -211,6 +211,7 @@ class CC_EXPORT ThreadProxy : public Proxy,
void DidActivateSyncTree() override;
void DidPrepareTiles() override;
void DidCompletePageScaleAnimationOnImplThread() override;
+ void OnDrawForOutputSurface() override;
// SchedulerClient implementation
void WillBeginImplFrame(const BeginFrameArgs& args) override;
@@ -222,6 +223,7 @@ class CC_EXPORT ThreadProxy : public Proxy,
void ScheduledActionActivateSyncTree() override;
void ScheduledActionBeginOutputSurfaceCreation() override;
void ScheduledActionPrepareTiles() override;
+ void ScheduledActionInvalidateOutputSurface() override;
void DidAnticipatedDrawTimeChange(base::TimeTicks time) override;
base::TimeDelta DrawDurationEstimate() override;
base::TimeDelta BeginMainFrameToCommitDurationEstimate() override;
diff --git a/content/browser/android/in_process/synchronous_compositor_external_begin_frame_source.cc b/content/browser/android/in_process/synchronous_compositor_external_begin_frame_source.cc
index 0c272fd..f1105f7 100644
--- a/content/browser/android/in_process/synchronous_compositor_external_begin_frame_source.cc
+++ b/content/browser/android/in_process/synchronous_compositor_external_begin_frame_source.cc
@@ -30,11 +30,10 @@ SynchronousCompositorExternalBeginFrameSource::
DCHECK(!compositor_);
}
-void SynchronousCompositorExternalBeginFrameSource::BeginFrame() {
+void SynchronousCompositorExternalBeginFrameSource::BeginFrame(
+ const cc::BeginFrameArgs& args) {
DCHECK(CalledOnValidThread());
- CallOnBeginFrame(cc::BeginFrameArgs::Create(
- BEGINFRAME_FROM_HERE, gfx::FrameTime::Now(), base::TimeTicks(),
- cc::BeginFrameArgs::DefaultInterval(), cc::BeginFrameArgs::SYNCHRONOUS));
+ CallOnBeginFrame(args);
}
void SynchronousCompositorExternalBeginFrameSource::SetCompositor(
@@ -46,9 +45,8 @@ void SynchronousCompositorExternalBeginFrameSource::SetCompositor(
void SynchronousCompositorExternalBeginFrameSource::OnNeedsBeginFramesChange(
bool needs_begin_frames) {
DCHECK(CalledOnValidThread());
-
if (compositor_)
- compositor_->NeedsBeginFramesChanged();
+ compositor_->OnNeedsBeginFramesChange(needs_begin_frames);
}
void SynchronousCompositorExternalBeginFrameSource::SetClientReady() {
diff --git a/content/browser/android/in_process/synchronous_compositor_external_begin_frame_source.h b/content/browser/android/in_process/synchronous_compositor_external_begin_frame_source.h
index c235c77..ee82f70 100644
--- a/content/browser/android/in_process/synchronous_compositor_external_begin_frame_source.h
+++ b/content/browser/android/in_process/synchronous_compositor_external_begin_frame_source.h
@@ -20,8 +20,7 @@ class SynchronousCompositorExternalBeginFrameSource
explicit SynchronousCompositorExternalBeginFrameSource(int routing_id);
~SynchronousCompositorExternalBeginFrameSource() override;
- void BeginFrame();
-
+ void BeginFrame(const cc::BeginFrameArgs& args);
void SetCompositor(SynchronousCompositorImpl* compositor);
// cc::BeginFrameSourceMixIn implementation.
diff --git a/content/browser/android/in_process/synchronous_compositor_impl.cc b/content/browser/android/in_process/synchronous_compositor_impl.cc
index 44d686c..67a04ed 100644
--- a/content/browser/android/in_process/synchronous_compositor_impl.cc
+++ b/content/browser/android/in_process/synchronous_compositor_impl.cc
@@ -73,7 +73,8 @@ SynchronousCompositorImpl::SynchronousCompositorImpl(WebContents* contents)
contents_(contents),
routing_id_(contents->GetRoutingID()),
input_handler_(NULL),
- invoking_composite_(false),
+ is_active_(false),
+ renderer_needs_begin_frames_(false),
weak_ptr_factory_(this) {
DCHECK(contents);
DCHECK_NE(routing_id_, MSG_ROUTING_NONE);
@@ -120,11 +121,14 @@ void SynchronousCompositorImpl::DidInitializeRendererObjects(
begin_frame_source_ = begin_frame_source;
begin_frame_source_->SetCompositor(this);
- output_surface_->SetBeginFrameSource(begin_frame_source_);
+ output_surface_->SetCompositor(this);
+
output_surface_->SetTreeActivationCallback(
base::Bind(&SynchronousCompositorImpl::DidActivatePendingTree,
weak_ptr_factory_.GetWeakPtr()));
- NeedsBeginFramesChanged();
+
+ OnNeedsBeginFramesChange(begin_frame_source_->NeedsBeginFrames());
+
compositor_client_->DidInitializeCompositor(this);
}
@@ -133,7 +137,7 @@ void SynchronousCompositorImpl::DidDestroyRendererObjects() {
DCHECK(begin_frame_source_);
begin_frame_source_->SetCompositor(nullptr);
- output_surface_->SetBeginFrameSource(nullptr);
+ output_surface_->SetCompositor(nullptr);
if (compositor_client_)
compositor_client_->DidDestroyCompositor(this);
compositor_client_ = nullptr;
@@ -181,12 +185,9 @@ scoped_ptr<cc::CompositorFrame> SynchronousCompositorImpl::DemandDrawHw(
const gfx::Transform& transform_for_tile_priority) {
DCHECK(CalledOnValidThread());
DCHECK(output_surface_);
- DCHECK(!invoking_composite_);
DCHECK(compositor_client_);
DCHECK(begin_frame_source_);
- base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_,
- true);
scoped_ptr<cc::CompositorFrame> frame =
output_surface_->DemandDrawHw(surface_size,
transform,
@@ -194,12 +195,10 @@ scoped_ptr<cc::CompositorFrame> SynchronousCompositorImpl::DemandDrawHw(
clip,
viewport_rect_for_tile_priority,
transform_for_tile_priority);
+
if (frame.get())
UpdateFrameMetaData(frame->metadata);
- compositor_client_->SetContinuousInvalidate(
- begin_frame_source_->NeedsBeginFrames());
-
return frame.Pass();
}
@@ -212,20 +211,15 @@ void SynchronousCompositorImpl::ReturnResources(
bool SynchronousCompositorImpl::DemandDrawSw(SkCanvas* canvas) {
DCHECK(CalledOnValidThread());
DCHECK(output_surface_);
- DCHECK(!invoking_composite_);
DCHECK(compositor_client_);
DCHECK(begin_frame_source_);
- base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_,
- true);
scoped_ptr<cc::CompositorFrame> frame =
output_surface_->DemandDrawSw(canvas);
+
if (frame.get())
UpdateFrameMetaData(frame->metadata);
- compositor_client_->SetContinuousInvalidate(
- begin_frame_source_->NeedsBeginFrames());
-
return !!frame.get();
}
@@ -245,11 +239,42 @@ void SynchronousCompositorImpl::SetMemoryPolicy(size_t bytes_limit) {
output_surface_->SetMemoryPolicy(bytes_limit);
}
+void SynchronousCompositorImpl::PostInvalidate() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(compositor_client_);
+ compositor_client_->PostInvalidate();
+}
+
void SynchronousCompositorImpl::DidChangeRootLayerScrollOffset() {
if (input_handler_)
input_handler_->OnRootLayerDelegatedScrollOffsetChanged();
}
+void SynchronousCompositorImpl::SetIsActive(bool is_active) {
+ TRACE_EVENT1("cc", "SynchronousCompositorImpl::SetIsActive", "is_active",
+ is_active);
+ is_active_ = is_active;
+ UpdateNeedsBeginFrames();
+}
+
+void SynchronousCompositorImpl::OnNeedsBeginFramesChange(
+ bool needs_begin_frames) {
+ renderer_needs_begin_frames_ = needs_begin_frames;
+ UpdateNeedsBeginFrames();
+}
+
+void SynchronousCompositorImpl::BeginFrame(const cc::BeginFrameArgs& args) {
+ if (begin_frame_source_)
+ begin_frame_source_->BeginFrame(args);
+}
+
+void SynchronousCompositorImpl::UpdateNeedsBeginFrames() {
+ RenderWidgetHostViewAndroid* rwhv = static_cast<RenderWidgetHostViewAndroid*>(
+ contents_->GetRenderWidgetHostView());
+ if (rwhv)
+ rwhv->OnSetNeedsBeginFrames(is_active_ && renderer_needs_begin_frames_);
+}
+
void SynchronousCompositorImpl::SetInputHandler(
cc::InputHandler* input_handler) {
DCHECK(CalledOnValidThread());
@@ -279,18 +304,6 @@ void SynchronousCompositorImpl::DidStopFlinging() {
rwhv->DidStopFlinging();
}
-void SynchronousCompositorImpl::NeedsBeginFramesChanged() const {
- DCHECK(CalledOnValidThread());
- DCHECK(begin_frame_source_);
- if (invoking_composite_)
- return;
-
- if (compositor_client_) {
- compositor_client_->SetContinuousInvalidate(
- begin_frame_source_->NeedsBeginFrames());
- }
-}
-
InputEventAckState SynchronousCompositorImpl::HandleInputEvent(
const blink::WebInputEvent& input_event) {
DCHECK(CalledOnValidThread());
diff --git a/content/browser/android/in_process/synchronous_compositor_impl.h b/content/browser/android/in_process/synchronous_compositor_impl.h
index d6cacf0..ee5acc1 100644
--- a/content/browser/android/in_process/synchronous_compositor_impl.h
+++ b/content/browser/android/in_process/synchronous_compositor_impl.h
@@ -18,7 +18,7 @@
#include "ipc/ipc_message.h"
namespace cc {
-class BeginFrameSource;
+struct BeginFrameArgs;
class InputHandler;
}
@@ -56,7 +56,13 @@ class SynchronousCompositorImpl
void DidDestroyRendererObjects();
// Called by SynchronousCompositorExternalBeginFrameSource.
- void NeedsBeginFramesChanged() const;
+ void OnNeedsBeginFramesChange(bool needs_begin_frames);
+
+ // Called by SynchronousCompositorOutputSurface.
+ void PostInvalidate();
+
+ // Called by RenderWidgetHostViewAndroid.
+ void BeginFrame(const cc::BeginFrameArgs& args);
// SynchronousCompositor
bool InitializeHwDraw() override;
@@ -72,6 +78,7 @@ class SynchronousCompositorImpl
void ReturnResources(const cc::CompositorFrameAck& frame_ack) override;
void SetMemoryPolicy(size_t bytes_limit) override;
void DidChangeRootLayerScrollOffset() override;
+ void SetIsActive(bool is_active) override;
// LayerScrollOffsetDelegate
gfx::ScrollOffset GetTotalScrollOffset() override;
@@ -99,6 +106,7 @@ class SynchronousCompositorImpl
void DidActivatePendingTree();
void DeliverMessages();
bool CalledOnValidThread() const;
+ void UpdateNeedsBeginFrames();
SynchronousCompositorClient* compositor_client_;
SynchronousCompositorOutputSurface* output_surface_;
@@ -106,7 +114,8 @@ class SynchronousCompositorImpl
WebContents* contents_;
const int routing_id_;
cc::InputHandler* input_handler_;
- bool invoking_composite_;
+ bool is_active_;
+ bool renderer_needs_begin_frames_;
base::WeakPtrFactory<SynchronousCompositorImpl> weak_ptr_factory_;
diff --git a/content/browser/android/in_process/synchronous_compositor_output_surface.cc b/content/browser/android/in_process/synchronous_compositor_output_surface.cc
index 48c2bf1..172f639 100644
--- a/content/browser/android/in_process/synchronous_compositor_output_surface.cc
+++ b/content/browser/android/in_process/synchronous_compositor_output_surface.cc
@@ -71,9 +71,7 @@ SynchronousCompositorOutputSurface::SynchronousCompositorOutputSurface(
registered_(false),
current_sw_canvas_(nullptr),
memory_policy_(0),
- output_surface_client_(nullptr),
- frame_swap_message_queue_(frame_swap_message_queue),
- begin_frame_source_(nullptr) {
+ frame_swap_message_queue_(frame_swap_message_queue) {
capabilities_.deferred_gl_initialization = true;
capabilities_.draw_and_swap_full_viewport_every_frame = true;
capabilities_.adjust_deadline_for_parent = false;
@@ -89,7 +87,6 @@ SynchronousCompositorOutputSurface::~SynchronousCompositorOutputSurface() {
SynchronousCompositorRegistry::GetInstance()->UnregisterOutputSurface(
routing_id_, this);
}
- DCHECK(!begin_frame_source_);
}
bool SynchronousCompositorOutputSurface::BindToClient(
@@ -98,8 +95,7 @@ bool SynchronousCompositorOutputSurface::BindToClient(
if (!cc::OutputSurface::BindToClient(surface_client))
return false;
- output_surface_client_ = surface_client;
- output_surface_client_->SetMemoryPolicy(memory_policy_);
+ client_->SetMemoryPolicy(memory_policy_);
SynchronousCompositorRegistry::GetInstance()->RegisterOutputSurface(
routing_id_, this);
@@ -108,6 +104,12 @@ bool SynchronousCompositorOutputSurface::BindToClient(
return true;
}
+void SynchronousCompositorOutputSurface::SetCompositor(
+ SynchronousCompositorImpl* compositor) {
+ DCHECK(CalledOnValidThread());
+ compositor_ = compositor;
+}
+
void SynchronousCompositorOutputSurface::Reshape(
const gfx::Size& size, float scale_factor) {
// Intentional no-op: surface size is controlled by the embedder.
@@ -123,9 +125,9 @@ void SynchronousCompositorOutputSurface::SwapBuffers(
client_->DidSwapBuffers();
}
-void SynchronousCompositorOutputSurface::SetBeginFrameSource(
- SynchronousCompositorExternalBeginFrameSource* begin_frame_source) {
- begin_frame_source_ = begin_frame_source;
+void SynchronousCompositorOutputSurface::Invalidate() {
+ DCHECK(CalledOnValidThread());
+ compositor_->PostInvalidate();
}
namespace {
@@ -179,6 +181,7 @@ SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) {
DCHECK(CalledOnValidThread());
DCHECK(canvas);
DCHECK(!current_sw_canvas_);
+
base::AutoReset<SkCanvas*> canvas_resetter(&current_sw_canvas_, canvas);
SkIRect canvas_clip;
@@ -212,7 +215,6 @@ void SynchronousCompositorOutputSurface::InvokeComposite(
gfx::Transform transform_for_tile_priority,
bool hardware_draw) {
DCHECK(!frame_holder_.get());
- DCHECK(begin_frame_source_);
gfx::Transform adjusted_transform = transform;
AdjustTransform(&adjusted_transform, viewport);
@@ -224,7 +226,7 @@ void SynchronousCompositorOutputSurface::InvokeComposite(
!hardware_draw);
SetNeedsRedrawRect(gfx::Rect(viewport.size()));
- begin_frame_source_->BeginFrame();
+ client_->OnDraw();
// After software draws (which might move the viewport arbitrarily), restore
// the previous hardware viewport to allow CC's tile manager to prioritize
@@ -260,8 +262,8 @@ void SynchronousCompositorOutputSurface::SetMemoryPolicy(size_t bytes_limit) {
memory_policy_.bytes_limit_when_visible = bytes_limit;
memory_policy_.num_resources_limit = kNumResourcesLimit;
- if (output_surface_client_)
- output_surface_client_->SetMemoryPolicy(memory_policy_);
+ if (client_)
+ client_->SetMemoryPolicy(memory_policy_);
}
void SynchronousCompositorOutputSurface::SetTreeActivationCallback(
diff --git a/content/browser/android/in_process/synchronous_compositor_output_surface.h b/content/browser/android/in_process/synchronous_compositor_output_surface.h
index 584bedc..adc65b4 100644
--- a/content/browser/android/in_process/synchronous_compositor_output_surface.h
+++ b/content/browser/android/in_process/synchronous_compositor_output_surface.h
@@ -32,7 +32,7 @@ namespace content {
class FrameSwapMessageQueue;
class SynchronousCompositorClient;
-class SynchronousCompositorExternalBeginFrameSource;
+class SynchronousCompositorImpl;
class SynchronousCompositorOutputSurface;
class WebGraphicsContext3DCommandBufferImpl;
@@ -52,13 +52,13 @@ class SynchronousCompositorOutputSurface
scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue);
~SynchronousCompositorOutputSurface() override;
+ void SetCompositor(SynchronousCompositorImpl* compositor);
+
// OutputSurface.
bool BindToClient(cc::OutputSurfaceClient* surface_client) override;
void Reshape(const gfx::Size& size, float scale_factor) override;
void SwapBuffers(cc::CompositorFrame* frame) override;
-
- void SetBeginFrameSource(
- SynchronousCompositorExternalBeginFrameSource* begin_frame_source);
+ void Invalidate() override;
// Partial SynchronousCompositor API implementation.
bool InitializeHwDraw(
@@ -93,6 +93,9 @@ class SynchronousCompositorOutputSurface
const int routing_id_;
bool registered_;
+ // Not owned.
+ SynchronousCompositorImpl* compositor_;
+
gfx::Transform cached_hw_transform_;
gfx::Rect cached_hw_viewport_;
gfx::Rect cached_hw_clip_;
@@ -104,13 +107,10 @@ class SynchronousCompositorOutputSurface
cc::ManagedMemoryPolicy memory_policy_;
- cc::OutputSurfaceClient* output_surface_client_;
scoped_ptr<cc::CompositorFrame> frame_holder_;
scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue_;
- SynchronousCompositorExternalBeginFrameSource* begin_frame_source_;
-
DISALLOW_COPY_AND_ASSIGN(SynchronousCompositorOutputSurface);
};
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 76affb9..318584e 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -778,7 +778,6 @@ void RenderWidgetHostViewAndroid::OnDidChangeBodyBackgroundColor(
}
void RenderWidgetHostViewAndroid::OnSetNeedsBeginFrames(bool enabled) {
- DCHECK(using_browser_compositor_);
TRACE_EVENT1("cc", "RenderWidgetHostViewAndroid::OnSetNeedsBeginFrames",
"enabled", enabled);
if (enabled)
@@ -1531,10 +1530,6 @@ void RenderWidgetHostViewAndroid::RemoveLayers() {
}
void RenderWidgetHostViewAndroid::RequestVSyncUpdate(uint32 requests) {
- // The synchronous compositor does not requre BeginFrame messages.
- if (!using_browser_compositor_)
- requests &= FLUSH_INPUT;
-
bool should_request_vsync = !outstanding_vsync_requests_ && requests;
outstanding_vsync_requests_ |= requests;
@@ -1582,15 +1577,28 @@ void RenderWidgetHostViewAndroid::SendBeginFrame(base::TimeTicks frame_time,
base::TimeDelta vsync_period) {
TRACE_EVENT1("cc", "RenderWidgetHostViewAndroid::SendBeginFrame",
"frame_time_us", frame_time.ToInternalValue());
- base::TimeTicks display_time = frame_time + vsync_period;
- base::TimeTicks deadline =
- display_time - host_->GetEstimatedBrowserCompositeTime();
+ if (using_browser_compositor_) {
+ base::TimeTicks display_time = frame_time + vsync_period;
+
+ base::TimeTicks deadline =
+ display_time - host_->GetEstimatedBrowserCompositeTime();
- host_->Send(new ViewMsg_BeginFrame(
- host_->GetRoutingID(),
- cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline,
- vsync_period, cc::BeginFrameArgs::NORMAL)));
+ host_->Send(new ViewMsg_BeginFrame(
+ host_->GetRoutingID(),
+ cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline,
+ vsync_period, cc::BeginFrameArgs::NORMAL)));
+ } else {
+ SynchronousCompositorImpl* compositor = SynchronousCompositorImpl::FromID(
+ host_->GetProcess()->GetID(), host_->GetRoutingID());
+ if (compositor) {
+ // The synchronous compositor synchronously does it's work in this call.
+ // It does not use a deadline.
+ compositor->BeginFrame(cc::BeginFrameArgs::Create(
+ BEGINFRAME_FROM_HERE, frame_time, base::TimeTicks(), vsync_period,
+ cc::BeginFrameArgs::NORMAL));
+ }
+ }
}
bool RenderWidgetHostViewAndroid::Animate(base::TimeTicks frame_time) {
@@ -1925,19 +1933,22 @@ void RenderWidgetHostViewAndroid::OnVSync(base::TimeTicks frame_time,
if (!host_ || host_->is_hidden())
return;
- const uint32 current_vsync_requests = outstanding_vsync_requests_;
- outstanding_vsync_requests_ = 0;
-
- if (current_vsync_requests & FLUSH_INPUT)
+ if (outstanding_vsync_requests_ & FLUSH_INPUT) {
+ outstanding_vsync_requests_ &= ~FLUSH_INPUT;
host_->FlushInput();
+ }
- if (current_vsync_requests & BEGIN_FRAME ||
- current_vsync_requests & PERSISTENT_BEGIN_FRAME) {
+ if (outstanding_vsync_requests_ & BEGIN_FRAME ||
+ outstanding_vsync_requests_ & PERSISTENT_BEGIN_FRAME) {
+ outstanding_vsync_requests_ &= ~BEGIN_FRAME;
SendBeginFrame(frame_time, vsync_period);
}
- if (current_vsync_requests & PERSISTENT_BEGIN_FRAME)
- RequestVSyncUpdate(PERSISTENT_BEGIN_FRAME);
+ // This allows for SendBeginFrame and FlushInput to modify
+ // outstanding_vsync_requests.
+ uint32 outstanding_vsync_requests = outstanding_vsync_requests_;
+ outstanding_vsync_requests_ = 0;
+ RequestVSyncUpdate(outstanding_vsync_requests);
}
void RenderWidgetHostViewAndroid::OnAnimate(base::TimeTicks begin_frame_time) {
diff --git a/content/public/browser/android/synchronous_compositor.h b/content/public/browser/android/synchronous_compositor.h
index 476b86d..be79209 100644
--- a/content/public/browser/android/synchronous_compositor.h
+++ b/content/public/browser/android/synchronous_compositor.h
@@ -87,6 +87,11 @@ class CONTENT_EXPORT SynchronousCompositor {
// SynchronousCompositorClient::GetTotalRootLayerScrollOffset).
virtual void DidChangeRootLayerScrollOffset() = 0;
+ // Called by the embedder to notify that the compositor is active. The
+ // compositor won't ask for vsyncs when it's inactive. NOTE: The compositor
+ // starts off as inactive and needs a SetActive(true) call to begin.
+ virtual void SetIsActive(bool is_active) = 0;
+
protected:
virtual ~SynchronousCompositor() {}
};
diff --git a/content/public/browser/android/synchronous_compositor_client.h b/content/public/browser/android/synchronous_compositor_client.h
index c3b91dd..e42e09a 100644
--- a/content/public/browser/android/synchronous_compositor_client.h
+++ b/content/public/browser/android/synchronous_compositor_client.h
@@ -39,10 +39,7 @@ class SynchronousCompositorClient {
gfx::Vector2dF latest_overscroll_delta,
gfx::Vector2dF current_fling_velocity) = 0;
- // When true, should periodically call
- // SynchronousCompositorOutputSurface::DemandDrawHw. Note that this value
- // can change inside DemandDrawHw call.
- virtual void SetContinuousInvalidate(bool invalidate) = 0;
+ virtual void PostInvalidate() = 0;
virtual void DidUpdateContent() = 0;
diff --git a/content/public/test/test_synchronous_compositor_android.h b/content/public/test/test_synchronous_compositor_android.h
index a3d3177..ea8982a 100644
--- a/content/public/test/test_synchronous_compositor_android.h
+++ b/content/public/test/test_synchronous_compositor_android.h
@@ -31,6 +31,7 @@ class CONTENT_EXPORT TestSynchronousCompositor : public SynchronousCompositor {
bool DemandDrawSw(SkCanvas* canvas) override;
void SetMemoryPolicy(size_t bytes_limit) override;
void DidChangeRootLayerScrollOffset() override {}
+ void SetIsActive(bool is_active) override {}
private:
SynchronousCompositorClient* client_;