1// Copyright 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "cc/trees/single_thread_proxy.h"
6
7#include "base/auto_reset.h"
8#include "base/debug/trace_event.h"
9#include "cc/debug/benchmark_instrumentation.h"
10#include "cc/output/context_provider.h"
11#include "cc/output/output_surface.h"
12#include "cc/quads/draw_quad.h"
13#include "cc/resources/prioritized_resource_manager.h"
14#include "cc/resources/resource_update_controller.h"
15#include "cc/trees/blocking_task_runner.h"
16#include "cc/trees/layer_tree_host.h"
17#include "cc/trees/layer_tree_host_single_thread_client.h"
18#include "cc/trees/layer_tree_impl.h"
19#include "ui/gfx/frame_time.h"
20
21namespace cc {
22
23scoped_ptr<Proxy> SingleThreadProxy::Create(
24    LayerTreeHost* layer_tree_host,
25    LayerTreeHostSingleThreadClient* client) {
26  return make_scoped_ptr(
27      new SingleThreadProxy(layer_tree_host, client)).PassAs<Proxy>();
28}
29
30SingleThreadProxy::SingleThreadProxy(LayerTreeHost* layer_tree_host,
31                                     LayerTreeHostSingleThreadClient* client)
32    : Proxy(NULL),
33      layer_tree_host_(layer_tree_host),
34      client_(client),
35      next_frame_is_newly_committed_frame_(false),
36      inside_draw_(false) {
37  TRACE_EVENT0("cc", "SingleThreadProxy::SingleThreadProxy");
38  DCHECK(Proxy::IsMainThread());
39  DCHECK(layer_tree_host);
40
41  // Impl-side painting not supported without threaded compositing.
42  CHECK(!layer_tree_host->settings().impl_side_painting)
43      << "Threaded compositing must be enabled to use impl-side painting.";
44}
45
46void SingleThreadProxy::Start() {
47  DebugScopedSetImplThread impl(this);
48  layer_tree_host_impl_ = layer_tree_host_->CreateLayerTreeHostImpl(this);
49}
50
51SingleThreadProxy::~SingleThreadProxy() {
52  TRACE_EVENT0("cc", "SingleThreadProxy::~SingleThreadProxy");
53  DCHECK(Proxy::IsMainThread());
54  // Make sure Stop() got called or never Started.
55  DCHECK(!layer_tree_host_impl_);
56}
57
58void SingleThreadProxy::FinishAllRendering() {
59  TRACE_EVENT0("cc", "SingleThreadProxy::FinishAllRendering");
60  DCHECK(Proxy::IsMainThread());
61  {
62    DebugScopedSetImplThread impl(this);
63    layer_tree_host_impl_->FinishAllRendering();
64  }
65}
66
67bool SingleThreadProxy::IsStarted() const {
68  DCHECK(Proxy::IsMainThread());
69  return layer_tree_host_impl_;
70}
71
72void SingleThreadProxy::SetLayerTreeHostClientReady() {
73  TRACE_EVENT0("cc", "SingleThreadProxy::SetLayerTreeHostClientReady");
74  // Scheduling is controlled by the embedder in the single thread case, so
75  // nothing to do.
76}
77
78void SingleThreadProxy::SetVisible(bool visible) {
79  TRACE_EVENT0("cc", "SingleThreadProxy::SetVisible");
80  DebugScopedSetImplThread impl(this);
81  layer_tree_host_impl_->SetVisible(visible);
82
83  // Changing visibility could change ShouldComposite().
84  UpdateBackgroundAnimateTicking();
85}
86
87void SingleThreadProxy::CreateAndInitializeOutputSurface() {
88  TRACE_EVENT0(
89      "cc", "SingleThreadProxy::CreateAndInitializeOutputSurface");
90  DCHECK(Proxy::IsMainThread());
91  DCHECK(layer_tree_host_->output_surface_lost());
92
93  scoped_ptr<OutputSurface> output_surface =
94      layer_tree_host_->CreateOutputSurface();
95
96  renderer_capabilities_for_main_thread_ = RendererCapabilities();
97
98  bool success = !!output_surface;
99  if (success) {
100    DebugScopedSetMainThreadBlocked main_thread_blocked(this);
101    DebugScopedSetImplThread impl(this);
102    layer_tree_host_->DeleteContentsTexturesOnImplThread(
103        layer_tree_host_impl_->resource_provider());
104    success = layer_tree_host_impl_->InitializeRenderer(output_surface.Pass());
105  }
106
107  layer_tree_host_->OnCreateAndInitializeOutputSurfaceAttempted(success);
108
109  if (!success) {
110    // Force another recreation attempt to happen by requesting another commit.
111    SetNeedsCommit();
112  }
113}
114
115const RendererCapabilities& SingleThreadProxy::GetRendererCapabilities() const {
116  DCHECK(Proxy::IsMainThread());
117  DCHECK(!layer_tree_host_->output_surface_lost());
118  return renderer_capabilities_for_main_thread_;
119}
120
121void SingleThreadProxy::SetNeedsAnimate() {
122  TRACE_EVENT0("cc", "SingleThreadProxy::SetNeedsAnimate");
123  DCHECK(Proxy::IsMainThread());
124  client_->ScheduleAnimation();
125}
126
127void SingleThreadProxy::SetNeedsUpdateLayers() {
128  TRACE_EVENT0("cc", "SingleThreadProxy::SetNeedsUpdateLayers");
129  DCHECK(Proxy::IsMainThread());
130  client_->ScheduleComposite();
131}
132
133void SingleThreadProxy::DoCommit(scoped_ptr<ResourceUpdateQueue> queue) {
134  TRACE_EVENT0("cc", "SingleThreadProxy::DoCommit");
135  DCHECK(Proxy::IsMainThread());
136  // Commit immediately.
137  {
138    DebugScopedSetMainThreadBlocked main_thread_blocked(this);
139    DebugScopedSetImplThread impl(this);
140
141    // This CapturePostTasks should be destroyed before CommitComplete() is
142    // called since that goes out to the embedder, and we want the embedder
143    // to receive its callbacks before that.
144    BlockingTaskRunner::CapturePostTasks blocked;
145
146    layer_tree_host_impl_->BeginCommit();
147
148    if (PrioritizedResourceManager* contents_texture_manager =
149        layer_tree_host_->contents_texture_manager()) {
150      contents_texture_manager->PushTexturePrioritiesToBackings();
151    }
152    layer_tree_host_->BeginCommitOnImplThread(layer_tree_host_impl_.get());
153
154    scoped_ptr<ResourceUpdateController> update_controller =
155        ResourceUpdateController::Create(
156            NULL,
157            Proxy::MainThreadTaskRunner(),
158            queue.Pass(),
159            layer_tree_host_impl_->resource_provider());
160    update_controller->Finalize();
161
162    if (layer_tree_host_impl_->EvictedUIResourcesExist())
163      layer_tree_host_->RecreateUIResources();
164
165    layer_tree_host_->FinishCommitOnImplThread(layer_tree_host_impl_.get());
166
167    layer_tree_host_impl_->CommitComplete();
168
169#if DCHECK_IS_ON
170    // In the single-threaded case, the scale and scroll deltas should never be
171    // touched on the impl layer tree.
172    scoped_ptr<ScrollAndScaleSet> scroll_info =
173        layer_tree_host_impl_->ProcessScrollDeltas();
174    DCHECK(!scroll_info->scrolls.size());
175    DCHECK_EQ(1.f, scroll_info->page_scale_delta);
176#endif
177
178    RenderingStatsInstrumentation* stats_instrumentation =
179        layer_tree_host_->rendering_stats_instrumentation();
180    BenchmarkInstrumentation::IssueMainThreadRenderingStatsEvent(
181        stats_instrumentation->main_thread_rendering_stats());
182    stats_instrumentation->AccumulateAndClearMainThreadStats();
183  }
184  layer_tree_host_->CommitComplete();
185  next_frame_is_newly_committed_frame_ = true;
186}
187
188void SingleThreadProxy::SetNeedsCommit() {
189  DCHECK(Proxy::IsMainThread());
190  client_->ScheduleComposite();
191}
192
193void SingleThreadProxy::SetNeedsRedraw(const gfx::Rect& damage_rect) {
194  TRACE_EVENT0("cc", "SingleThreadProxy::SetNeedsRedraw");
195  SetNeedsRedrawRectOnImplThread(damage_rect);
196  client_->ScheduleComposite();
197}
198
199void SingleThreadProxy::SetNextCommitWaitsForActivation() {
200  // There is no activation here other than commit. So do nothing.
201}
202
203void SingleThreadProxy::SetDeferCommits(bool defer_commits) {
204  // Thread-only feature.
205  NOTREACHED();
206}
207
208bool SingleThreadProxy::CommitRequested() const { return false; }
209
210bool SingleThreadProxy::BeginMainFrameRequested() const { return false; }
211
212size_t SingleThreadProxy::MaxPartialTextureUpdates() const {
213  return std::numeric_limits<size_t>::max();
214}
215
216void SingleThreadProxy::Stop() {
217  TRACE_EVENT0("cc", "SingleThreadProxy::stop");
218  DCHECK(Proxy::IsMainThread());
219  {
220    DebugScopedSetMainThreadBlocked main_thread_blocked(this);
221    DebugScopedSetImplThread impl(this);
222
223    BlockingTaskRunner::CapturePostTasks blocked;
224    layer_tree_host_->DeleteContentsTexturesOnImplThread(
225        layer_tree_host_impl_->resource_provider());
226    layer_tree_host_impl_.reset();
227  }
228  layer_tree_host_ = NULL;
229}
230
231void SingleThreadProxy::OnCanDrawStateChanged(bool can_draw) {
232  TRACE_EVENT1(
233      "cc", "SingleThreadProxy::OnCanDrawStateChanged", "can_draw", can_draw);
234  DCHECK(Proxy::IsImplThread());
235  UpdateBackgroundAnimateTicking();
236}
237
238void SingleThreadProxy::NotifyReadyToActivate() {
239  // Thread-only feature.
240  NOTREACHED();
241}
242
243void SingleThreadProxy::SetNeedsRedrawOnImplThread() {
244  client_->ScheduleComposite();
245}
246
247void SingleThreadProxy::SetNeedsAnimateOnImplThread() {
248  SetNeedsRedrawOnImplThread();
249}
250
251void SingleThreadProxy::SetNeedsManageTilesOnImplThread() {
252  // Thread-only/Impl-side-painting-only feature.
253  NOTREACHED();
254}
255
256void SingleThreadProxy::SetNeedsRedrawRectOnImplThread(
257    const gfx::Rect& damage_rect) {
258  // TODO(brianderson): Once we move render_widget scheduling into this class,
259  // we can treat redraw requests more efficiently than CommitAndRedraw
260  // requests.
261  layer_tree_host_impl_->SetViewportDamage(damage_rect);
262  SetNeedsCommit();
263}
264
265void SingleThreadProxy::DidInitializeVisibleTileOnImplThread() {
266  // Impl-side painting only.
267  NOTREACHED();
268}
269
270void SingleThreadProxy::SetNeedsCommitOnImplThread() {
271  client_->ScheduleComposite();
272}
273
274void SingleThreadProxy::PostAnimationEventsToMainThreadOnImplThread(
275    scoped_ptr<AnimationEventsVector> events) {
276  TRACE_EVENT0(
277      "cc", "SingleThreadProxy::PostAnimationEventsToMainThreadOnImplThread");
278  DCHECK(Proxy::IsImplThread());
279  DebugScopedSetMainThread main(this);
280  layer_tree_host_->SetAnimationEvents(events.Pass());
281}
282
283bool SingleThreadProxy::ReduceContentsTextureMemoryOnImplThread(
284    size_t limit_bytes,
285    int priority_cutoff) {
286  DCHECK(IsImplThread());
287  PrioritizedResourceManager* contents_texture_manager =
288      layer_tree_host_->contents_texture_manager();
289
290  ResourceProvider* resource_provider =
291      layer_tree_host_impl_->resource_provider();
292
293  if (!contents_texture_manager || !resource_provider)
294    return false;
295
296  return contents_texture_manager->ReduceMemoryOnImplThread(
297      limit_bytes, priority_cutoff, resource_provider);
298}
299
300bool SingleThreadProxy::IsInsideDraw() { return inside_draw_; }
301
302void SingleThreadProxy::UpdateRendererCapabilitiesOnImplThread() {
303  DCHECK(IsImplThread());
304  renderer_capabilities_for_main_thread_ =
305      layer_tree_host_impl_->GetRendererCapabilities().MainThreadCapabilities();
306}
307
308void SingleThreadProxy::DidLoseOutputSurfaceOnImplThread() {
309  TRACE_EVENT0("cc", "SingleThreadProxy::DidLoseOutputSurfaceOnImplThread");
310  // Cause a commit so we can notice the lost context.
311  SetNeedsCommitOnImplThread();
312  client_->DidAbortSwapBuffers();
313}
314
315void SingleThreadProxy::DidSwapBuffersOnImplThread() {
316  client_->DidPostSwapBuffers();
317}
318
319void SingleThreadProxy::DidSwapBuffersCompleteOnImplThread() {
320  TRACE_EVENT0("cc", "SingleThreadProxy::DidSwapBuffersCompleteOnImplThread");
321  client_->DidCompleteSwapBuffers();
322}
323
324// Called by the legacy scheduling path (e.g. where render_widget does the
325// scheduling)
326void SingleThreadProxy::CompositeImmediately(base::TimeTicks frame_begin_time) {
327  TRACE_EVENT0("cc", "SingleThreadProxy::CompositeImmediately");
328  DCHECK(Proxy::IsMainThread());
329  DCHECK(!layer_tree_host_->output_surface_lost());
330
331  layer_tree_host_->AnimateLayers(frame_begin_time);
332
333  if (PrioritizedResourceManager* contents_texture_manager =
334          layer_tree_host_->contents_texture_manager()) {
335    contents_texture_manager->UnlinkAndClearEvictedBackings();
336    contents_texture_manager->SetMaxMemoryLimitBytes(
337        layer_tree_host_impl_->memory_allocation_limit_bytes());
338    contents_texture_manager->SetExternalPriorityCutoff(
339        layer_tree_host_impl_->memory_allocation_priority_cutoff());
340  }
341
342  scoped_ptr<ResourceUpdateQueue> queue =
343      make_scoped_ptr(new ResourceUpdateQueue);
344  layer_tree_host_->UpdateLayers(queue.get());
345  layer_tree_host_->WillCommit();
346  DoCommit(queue.Pass());
347  layer_tree_host_->DidBeginMainFrame();
348
349  LayerTreeHostImpl::FrameData frame;
350  if (DoComposite(frame_begin_time, &frame)) {
351    {
352      DebugScopedSetMainThreadBlocked main_thread_blocked(this);
353      DebugScopedSetImplThread impl(this);
354
355      // This CapturePostTasks should be destroyed before
356      // DidCommitAndDrawFrame() is called since that goes out to the embedder,
357      // and we want the embedder to receive its callbacks before that.
358      // NOTE: This maintains consistent ordering with the ThreadProxy since
359      // the DidCommitAndDrawFrame() must be post-tasked from the impl thread
360      // there as the main thread is not blocked, so any posted tasks inside
361      // the swap buffers will execute first.
362      BlockingTaskRunner::CapturePostTasks blocked;
363
364      layer_tree_host_impl_->SwapBuffers(frame);
365    }
366    DidSwapFrame();
367  }
368}
369
370scoped_ptr<base::Value> SingleThreadProxy::AsValue() const {
371  scoped_ptr<base::DictionaryValue> state(new base::DictionaryValue());
372  {
373    // The following line casts away const modifiers because it is just
374    // setting debug state. We still want the AsValue() function and its
375    // call chain to be const throughout.
376    DebugScopedSetImplThread impl(const_cast<SingleThreadProxy*>(this));
377
378    state->Set("layer_tree_host_impl",
379               layer_tree_host_impl_->AsValue().release());
380  }
381  return state.PassAs<base::Value>();
382}
383
384void SingleThreadProxy::ForceSerializeOnSwapBuffers() {
385  {
386    DebugScopedSetImplThread impl(this);
387    if (layer_tree_host_impl_->renderer()) {
388      DCHECK(!layer_tree_host_->output_surface_lost());
389      layer_tree_host_impl_->renderer()->DoNoOp();
390    }
391  }
392}
393
394bool SingleThreadProxy::ShouldComposite() const {
395  DCHECK(Proxy::IsImplThread());
396  return layer_tree_host_impl_->visible() &&
397         layer_tree_host_impl_->CanDraw();
398}
399
400void SingleThreadProxy::UpdateBackgroundAnimateTicking() {
401  DCHECK(Proxy::IsImplThread());
402  layer_tree_host_impl_->UpdateBackgroundAnimateTicking(
403      !ShouldComposite() && layer_tree_host_impl_->active_tree()->root_layer());
404}
405
406bool SingleThreadProxy::DoComposite(
407    base::TimeTicks frame_begin_time,
408    LayerTreeHostImpl::FrameData* frame) {
409  TRACE_EVENT0("cc", "SingleThreadProxy::DoComposite");
410  DCHECK(!layer_tree_host_->output_surface_lost());
411
412  bool lost_output_surface = false;
413  {
414    DebugScopedSetImplThread impl(this);
415    base::AutoReset<bool> mark_inside(&inside_draw_, true);
416
417    // We guard PrepareToDraw() with CanDraw() because it always returns a valid
418    // frame, so can only be used when such a frame is possible. Since
419    // DrawLayers() depends on the result of PrepareToDraw(), it is guarded on
420    // CanDraw() as well.
421    if (!ShouldComposite()) {
422      UpdateBackgroundAnimateTicking();
423      return false;
424    }
425
426    layer_tree_host_impl_->Animate(
427        layer_tree_host_impl_->CurrentFrameTimeTicks());
428    UpdateBackgroundAnimateTicking();
429
430    if (!layer_tree_host_impl_->IsContextLost()) {
431      layer_tree_host_impl_->PrepareToDraw(frame);
432      layer_tree_host_impl_->DrawLayers(frame, frame_begin_time);
433      layer_tree_host_impl_->DidDrawAllLayers(*frame);
434    }
435    lost_output_surface = layer_tree_host_impl_->IsContextLost();
436
437    bool start_ready_animations = true;
438    layer_tree_host_impl_->UpdateAnimationState(start_ready_animations);
439
440    layer_tree_host_impl_->ResetCurrentFrameTimeForNextFrame();
441  }
442
443  if (lost_output_surface) {
444    layer_tree_host_->DidLoseOutputSurface();
445    return false;
446  }
447
448  return true;
449}
450
451void SingleThreadProxy::DidSwapFrame() {
452  if (next_frame_is_newly_committed_frame_) {
453    next_frame_is_newly_committed_frame_ = false;
454    layer_tree_host_->DidCommitAndDrawFrame();
455  }
456}
457
458bool SingleThreadProxy::CommitPendingForTesting() { return false; }
459
460}  // namespace cc
461