Index: content/browser/renderer_host/compositor_impl_android.cc |
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc |
index eaf4bcd4e906e0b557b66e319336af226c150aae..b6453a6e887128451f8c9600b08034adfdd2e136 100644 |
--- a/content/browser/renderer_host/compositor_impl_android.cc |
+++ b/content/browser/renderer_host/compositor_impl_android.cc |
@@ -69,6 +69,7 @@ |
namespace { |
+const unsigned int kMaxUiSwapBuffers = 1U; |
const unsigned int kMaxDisplaySwapBuffers = 1U; |
// Used to override capabilities_.adjust_deadline_for_parent to false |
@@ -141,7 +142,8 @@ |
OutputSurface::OnSwapBuffersComplete(); |
} |
- void OnVSync(base::TimeTicks timebase, base::TimeDelta interval) override { |
+ void OnUpdateVSyncParameters(base::TimeTicks timebase, |
+ base::TimeDelta interval) override { |
CommitVSyncParameters(timebase, interval); |
} |
@@ -151,36 +153,6 @@ |
gfx::SwapResult)> |
swap_buffers_completion_callback_; |
scoped_ptr<cc::OverlayCandidateValidator> overlay_candidate_validator_; |
-}; |
- |
-class ExternalBeginFrameSource : public cc::BeginFrameSourceBase, |
- public CompositorImpl::VSyncObserver { |
- public: |
- ExternalBeginFrameSource(CompositorImpl* compositor) |
- : compositor_(compositor) { |
- compositor_->AddObserver(this); |
- } |
- |
- ~ExternalBeginFrameSource() override { |
- compositor_->RemoveObserver(this); |
- } |
- |
- // cc::BeginFrameSourceBase implementation: |
- void OnNeedsBeginFramesChange( |
- bool needs_begin_frames) override { |
- compositor_->OnNeedsBeginFramesChange(needs_begin_frames); |
- } |
- |
- // CompositorImpl::VSyncObserver implementation: |
- void OnVSync(base::TimeTicks frame_time, |
- base::TimeDelta vsync_period) override { |
- CallOnBeginFrame(cc::BeginFrameArgs::Create( |
- BEGINFRAME_FROM_HERE, frame_time, base::TimeTicks::Now(), vsync_period, |
- cc::BeginFrameArgs::NORMAL)); |
- } |
- |
- private: |
- CompositorImpl* compositor_; |
}; |
static bool g_initialized = false; |
@@ -265,11 +237,15 @@ |
surface_id_(0), |
client_(client), |
root_window_(root_window), |
+ did_post_swapbuffers_(false), |
+ ignore_schedule_composite_(false), |
+ needs_composite_(false), |
needs_animate_(false), |
+ will_composite_immediately_(false), |
+ composite_on_vsync_trigger_(DO_NOT_COMPOSITE), |
pending_swapbuffers_(0U), |
num_successive_context_creation_failures_(0), |
output_surface_request_pending_(false), |
- needs_begin_frames_(false), |
weak_factory_(this) { |
DCHECK(client); |
DCHECK(root_window); |
@@ -282,6 +258,111 @@ |
root_window_->DetachCompositor(); |
// Clean-up any surface references. |
SetSurface(NULL); |
+} |
+ |
+void CompositorImpl::PostComposite(CompositingTrigger trigger) { |
+ DCHECK(host_->visible()); |
+ DCHECK(needs_composite_); |
+ DCHECK(trigger == COMPOSITE_IMMEDIATELY || trigger == COMPOSITE_EVENTUALLY); |
+ |
+ if (will_composite_immediately_ || |
+ (trigger == COMPOSITE_EVENTUALLY && WillComposite())) { |
+ // We will already composite soon enough. |
+ DCHECK(WillComposite()); |
+ return; |
+ } |
+ |
+ if (DidCompositeThisFrame()) { |
+ DCHECK(!WillCompositeThisFrame()); |
+ if (composite_on_vsync_trigger_ != COMPOSITE_IMMEDIATELY) { |
+ composite_on_vsync_trigger_ = trigger; |
+ root_window_->RequestVSyncUpdate(); |
+ } |
+ DCHECK(WillComposite()); |
+ return; |
+ } |
+ |
+ base::TimeDelta delay; |
+ if (trigger == COMPOSITE_IMMEDIATELY) { |
+ will_composite_immediately_ = true; |
+ composite_on_vsync_trigger_ = DO_NOT_COMPOSITE; |
+ } else { |
+ DCHECK(!WillComposite()); |
+ const base::TimeDelta estimated_composite_time = vsync_period_ / 4; |
+ const base::TimeTicks now = base::TimeTicks::Now(); |
+ |
+ if (!last_vsync_.is_null() && (now - last_vsync_) < vsync_period_) { |
+ base::TimeTicks next_composite = |
+ last_vsync_ + vsync_period_ - estimated_composite_time; |
+ if (next_composite < now) { |
+ // It's too late, we will reschedule composite as needed on the next |
+ // vsync. |
+ composite_on_vsync_trigger_ = COMPOSITE_EVENTUALLY; |
+ root_window_->RequestVSyncUpdate(); |
+ DCHECK(WillComposite()); |
+ return; |
+ } |
+ |
+ delay = next_composite - now; |
+ } |
+ } |
+ TRACE_EVENT2("cc,benchmark", "CompositorImpl::PostComposite", |
+ "trigger", trigger, |
+ "delay", delay.InMillisecondsF()); |
+ |
+ DCHECK(composite_on_vsync_trigger_ == DO_NOT_COMPOSITE); |
+ if (current_composite_task_) |
+ current_composite_task_->Cancel(); |
+ |
+ // Unretained because we cancel the task on shutdown. |
+ current_composite_task_.reset(new base::CancelableClosure( |
+ base::Bind(&CompositorImpl::Composite, base::Unretained(this), trigger))); |
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
+ FROM_HERE, current_composite_task_->callback(), delay); |
+} |
+ |
+void CompositorImpl::Composite(CompositingTrigger trigger) { |
+ if (trigger == COMPOSITE_IMMEDIATELY) |
+ will_composite_immediately_ = false; |
+ |
+ DCHECK(host_->visible()); |
+ DCHECK(trigger == COMPOSITE_IMMEDIATELY || trigger == COMPOSITE_EVENTUALLY); |
+ DCHECK(needs_composite_); |
+ DCHECK(!DidCompositeThisFrame()); |
+ |
+ DCHECK_LE(pending_swapbuffers_, kMaxUiSwapBuffers); |
+ // Swap Ack accounting is unreliable if the OutputSurface was lost. |
+ // In that case still attempt to composite, which will cause creation of a |
+ // new OutputSurface and reset pending_swapbuffers_. |
+ if (pending_swapbuffers_ == kMaxUiSwapBuffers && |
+ !host_->output_surface_lost()) { |
+ TRACE_EVENT0("compositor", "CompositorImpl_SwapLimit"); |
+ return; |
+ } |
+ |
+ // Reset state before Layout+Composite since that might create more |
+ // requests to Composite that we need to respect. |
+ needs_composite_ = false; |
+ |
+ // Only allow compositing once per vsync. |
+ current_composite_task_->Cancel(); |
+ DCHECK(DidCompositeThisFrame() && !WillComposite()); |
+ |
+ const base::TimeTicks frame_time = base::TimeTicks::Now(); |
+ if (needs_animate_) { |
+ base::AutoReset<bool> auto_reset_ignore_schedule( |
+ &ignore_schedule_composite_, true); |
+ needs_animate_ = false; |
+ root_window_->Animate(frame_time); |
+ } |
+ |
+ did_post_swapbuffers_ = false; |
+ host_->Composite(frame_time); |
+ if (did_post_swapbuffers_) |
+ pending_swapbuffers_++; |
+ |
+ // Need to track vsync to avoid compositing more than once per frame. |
+ root_window_->RequestVSyncUpdate(); |
} |
ui::UIResourceProvider& CompositorImpl::GetUIResourceProvider() { |
@@ -344,20 +425,27 @@ |
void CompositorImpl::CreateLayerTreeHost() { |
DCHECK(!host_); |
+ DCHECK(!WillCompositeThisFrame()); |
+ |
+ // Just in case, since we immediately hide the LTH in this function, |
+ // and we do not want to end up with a pending Composite task when the |
+ // host is hidden. |
+ base::AutoReset<bool> auto_reset_ignore_schedule(&ignore_schedule_composite_, |
+ true); |
cc::LayerTreeSettings settings; |
settings.renderer_settings.refresh_rate = 60.0; |
settings.renderer_settings.allow_antialiasing = false; |
settings.renderer_settings.highp_threshold_min = 2048; |
settings.use_zero_copy = true; |
- settings.use_external_begin_frame_source = true; |
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
settings.initial_debug_state.SetRecordRenderingStats( |
command_line->HasSwitch(cc::switches::kEnableGpuBenchmarking)); |
if (command_line->HasSwitch(cc::switches::kDisableCompositorPropertyTrees)) |
settings.use_property_trees = false; |
- settings.single_thread_proxy_scheduler = true; |
+ // TODO(enne): Update this this compositor to use the scheduler. |
+ settings.single_thread_proxy_scheduler = false; |
settings.use_compositor_animation_timelines = !command_line->HasSwitch( |
switches::kDisableAndroidCompositorAnimationTimelines); |
@@ -369,7 +457,6 @@ |
params.task_graph_runner = g_task_graph_runner.Pointer(); |
params.main_task_runner = base::ThreadTaskRunnerHandle::Get(); |
params.settings = &settings; |
- params.external_begin_frame_source.reset(new ExternalBeginFrameSource(this)); |
host_ = cc::LayerTreeHost::CreateSingleThreaded(this, ¶ms); |
DCHECK(!host_->visible()); |
host_->SetRootLayer(root_layer_); |
@@ -385,16 +472,39 @@ |
TRACE_EVENT1("cc", "CompositorImpl::SetVisible", "visible", visible); |
if (!visible) { |
DCHECK(host_->visible()); |
+ // Look for any layers that were attached to the root for readback |
+ // and are waiting for Composite() to happen. |
+ bool readback_pending = false; |
+ for (size_t i = 0; i < root_layer_->children().size(); ++i) { |
+ if (root_layer_->children()[i]->HasCopyRequest()) { |
+ readback_pending = true; |
+ break; |
+ } |
+ } |
+ if (readback_pending) { |
+ base::AutoReset<bool> auto_reset_ignore_schedule( |
+ &ignore_schedule_composite_, true); |
+ host_->Composite(base::TimeTicks::Now()); |
+ } |
+ if (WillComposite()) |
+ CancelComposite(); |
host_->SetVisible(false); |
if (!host_->output_surface_lost()) |
host_->ReleaseOutputSurface(); |
pending_swapbuffers_ = 0; |
+ needs_composite_ = false; |
+ composite_on_vsync_trigger_ = DO_NOT_COMPOSITE; |
establish_gpu_channel_timeout_.Stop(); |
display_client_.reset(); |
+ if (current_composite_task_) { |
+ current_composite_task_->Cancel(); |
+ current_composite_task_.reset(); |
+ } |
} else { |
host_->SetVisible(true); |
if (output_surface_request_pending_) |
RequestNewOutputSurface(); |
+ SetNeedsComposite(); |
} |
} |
@@ -425,7 +535,10 @@ |
void CompositorImpl::SetNeedsComposite() { |
if (!host_->visible()) |
return; |
- host_->SetNeedsAnimate(); |
+ DCHECK(!needs_composite_ || WillComposite()); |
+ |
+ needs_composite_ = true; |
+ PostComposite(COMPOSITE_IMMEDIATELY); |
} |
static scoped_ptr<WebGraphicsContext3DCommandBufferImpl> |
@@ -459,11 +572,9 @@ |
} |
void CompositorImpl::UpdateLayerTreeHost() { |
+ base::AutoReset<bool> auto_reset_ignore_schedule(&ignore_schedule_composite_, |
+ true); |
client_->UpdateLayerTreeHost(); |
- if (needs_animate_) { |
- needs_animate_ = false; |
- root_window_->Animate(base::TimeTicks::Now()); |
- } |
} |
void CompositorImpl::OnGpuChannelEstablished() { |
@@ -593,15 +704,44 @@ |
return gpu_capabilities_.texture_format_etc1_npot; |
} |
+void CompositorImpl::ScheduleComposite() { |
+ if (ignore_schedule_composite_ || !host_->visible()) |
+ return; |
+ |
+ DCHECK(!needs_composite_ || WillComposite()); |
+ needs_composite_ = true; |
+ // We currently expect layer tree invalidations at most once per frame |
+ // during normal operation and therefore try to composite immediately |
+ // to minimize latency. |
+ PostComposite(COMPOSITE_IMMEDIATELY); |
+} |
+ |
+void CompositorImpl::ScheduleAnimation() { |
+ needs_animate_ = true; |
+ |
+ if (!host_->visible()) |
+ return; |
+ |
+ if (needs_composite_) { |
+ DCHECK(WillComposite()); |
+ return; |
+ } |
+ |
+ TRACE_EVENT0("cc", "CompositorImpl::ScheduleAnimation"); |
+ needs_composite_ = true; |
+ PostComposite(COMPOSITE_EVENTUALLY); |
+} |
+ |
void CompositorImpl::DidPostSwapBuffers() { |
TRACE_EVENT0("compositor", "CompositorImpl::DidPostSwapBuffers"); |
- pending_swapbuffers_++; |
+ did_post_swapbuffers_ = true; |
} |
void CompositorImpl::DidCompleteSwapBuffers() { |
TRACE_EVENT0("compositor", "CompositorImpl::DidCompleteSwapBuffers"); |
DCHECK_GT(pending_swapbuffers_, 0U); |
- pending_swapbuffers_--; |
+ if (pending_swapbuffers_-- == kMaxUiSwapBuffers && needs_composite_) |
+ PostComposite(COMPOSITE_IMMEDIATELY); |
client_->OnSwapBuffersCompleted(pending_swapbuffers_); |
} |
@@ -610,8 +750,7 @@ |
// This really gets called only once from |
// SingleThreadProxy::DidLoseOutputSurfaceOnImplThread() when the |
// context was lost. |
- if (host_->visible()) |
- host_->SetNeedsCommit(); |
+ ScheduleComposite(); |
client_->OnSwapBuffersCompleted(0); |
} |
@@ -630,19 +769,28 @@ |
void CompositorImpl::OnVSync(base::TimeTicks frame_time, |
base::TimeDelta vsync_period) { |
+ vsync_period_ = vsync_period; |
+ last_vsync_ = frame_time; |
+ |
+ if (WillCompositeThisFrame()) { |
+ // We somehow missed the last vsync interval, so reschedule for deadline. |
+ // We cannot schedule immediately, or will get us out-of-phase with new |
+ // renderer frames. |
+ CancelComposite(); |
+ composite_on_vsync_trigger_ = COMPOSITE_EVENTUALLY; |
+ } else { |
+ current_composite_task_.reset(); |
+ } |
+ |
+ DCHECK(!DidCompositeThisFrame() && !WillCompositeThisFrame()); |
+ if (composite_on_vsync_trigger_ != DO_NOT_COMPOSITE) { |
+ CompositingTrigger trigger = composite_on_vsync_trigger_; |
+ composite_on_vsync_trigger_ = DO_NOT_COMPOSITE; |
+ PostComposite(trigger); |
+ } |
+ |
FOR_EACH_OBSERVER(VSyncObserver, observer_list_, |
- OnVSync(frame_time, vsync_period)); |
- if (needs_begin_frames_) |
- root_window_->RequestVSyncUpdate(); |
-} |
- |
-void CompositorImpl::OnNeedsBeginFramesChange(bool needs_begin_frames) { |
- if (needs_begin_frames_ == needs_begin_frames) |
- return; |
- |
- needs_begin_frames_ = needs_begin_frames; |
- if (needs_begin_frames_) |
- root_window_->RequestVSyncUpdate(); |
+ OnUpdateVSyncParameters(frame_time, vsync_period)); |
} |
void CompositorImpl::SetNeedsAnimate() { |