compositor.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright (c) 2012 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 "ui/compositor/compositor.h"
6
7#include <algorithm>
8#include <deque>
9
10#include "base/bind.h"
11#include "base/command_line.h"
12#include "base/debug/trace_event.h"
13#include "base/memory/singleton.h"
14#include "base/message_loop/message_loop.h"
15#include "base/metrics/histogram.h"
16#include "base/run_loop.h"
17#include "base/strings/string_util.h"
18#include "base/sys_info.h"
19#include "base/threading/thread.h"
20#include "base/threading/thread_restrictions.h"
21#include "cc/base/latency_info_swap_promise.h"
22#include "cc/base/switches.h"
23#include "cc/input/input_handler.h"
24#include "cc/layers/layer.h"
25#include "cc/output/context_provider.h"
26#include "cc/trees/layer_tree_host.h"
27#include "third_party/skia/include/core/SkBitmap.h"
28#include "ui/compositor/compositor_observer.h"
29#include "ui/compositor/compositor_switches.h"
30#include "ui/compositor/compositor_vsync_manager.h"
31#include "ui/compositor/dip_util.h"
32#include "ui/compositor/layer.h"
33#include "ui/gfx/frame_time.h"
34#include "ui/gl/gl_context.h"
35#include "ui/gl/gl_switches.h"
36
37namespace {
38
39const double kDefaultRefreshRate = 60.0;
40const double kTestRefreshRate = 200.0;
41
42enum SwapType {
43  DRAW_SWAP,
44};
45
46bool g_compositor_initialized = false;
47base::Thread* g_compositor_thread = NULL;
48
49ui::ContextFactory* g_context_factory = NULL;
50
51const int kCompositorLockTimeoutMs = 67;
52
53class PendingSwap {
54 public:
55  PendingSwap(SwapType type, ui::PostedSwapQueue* posted_swaps);
56  ~PendingSwap();
57
58  SwapType type() const { return type_; }
59  bool posted() const { return posted_; }
60
61 private:
62  friend class ui::PostedSwapQueue;
63
64  SwapType type_;
65  bool posted_;
66  ui::PostedSwapQueue* posted_swaps_;
67
68  DISALLOW_COPY_AND_ASSIGN(PendingSwap);
69};
70
71}  // namespace
72
73namespace ui {
74
75// static
76ContextFactory* ContextFactory::GetInstance() {
77  DCHECK(g_context_factory);
78  return g_context_factory;
79}
80
81// static
82void ContextFactory::SetInstance(ContextFactory* instance) {
83  g_context_factory = instance;
84}
85
86Texture::Texture(bool flipped, const gfx::Size& size, float device_scale_factor)
87    : size_(size),
88      flipped_(flipped),
89      device_scale_factor_(device_scale_factor) {
90}
91
92Texture::~Texture() {
93}
94
95gpu::Mailbox Texture::Produce() {
96  return gpu::Mailbox();
97}
98
99CompositorLock::CompositorLock(Compositor* compositor)
100    : compositor_(compositor) {
101  base::MessageLoop::current()->PostDelayedTask(
102      FROM_HERE,
103      base::Bind(&CompositorLock::CancelLock, AsWeakPtr()),
104      base::TimeDelta::FromMilliseconds(kCompositorLockTimeoutMs));
105}
106
107CompositorLock::~CompositorLock() {
108  CancelLock();
109}
110
111void CompositorLock::CancelLock() {
112  if (!compositor_)
113    return;
114  compositor_->UnlockCompositor();
115  compositor_ = NULL;
116}
117
118class PostedSwapQueue {
119 public:
120  PostedSwapQueue() : pending_swap_(NULL) {
121  }
122
123  ~PostedSwapQueue() {
124    DCHECK(!pending_swap_);
125  }
126
127  SwapType NextPostedSwap() const {
128    return queue_.front();
129  }
130
131  bool AreSwapsPosted() const {
132    return !queue_.empty();
133  }
134
135  int NumSwapsPosted(SwapType type) const {
136    int count = 0;
137    for (std::deque<SwapType>::const_iterator it = queue_.begin();
138         it != queue_.end(); ++it) {
139      if (*it == type)
140        count++;
141    }
142    return count;
143  }
144
145  void PostSwap() {
146    DCHECK(pending_swap_);
147    queue_.push_back(pending_swap_->type());
148    pending_swap_->posted_ = true;
149  }
150
151  void EndSwap() {
152    queue_.pop_front();
153  }
154
155 private:
156  friend class ::PendingSwap;
157
158  PendingSwap* pending_swap_;
159  std::deque<SwapType> queue_;
160
161  DISALLOW_COPY_AND_ASSIGN(PostedSwapQueue);
162};
163
164}  // namespace ui
165
166namespace {
167
168PendingSwap::PendingSwap(SwapType type, ui::PostedSwapQueue* posted_swaps)
169    : type_(type), posted_(false), posted_swaps_(posted_swaps) {
170  // Only one pending swap in flight.
171  DCHECK_EQ(static_cast<PendingSwap*>(NULL), posted_swaps_->pending_swap_);
172  posted_swaps_->pending_swap_ = this;
173}
174
175PendingSwap::~PendingSwap() {
176  DCHECK_EQ(this, posted_swaps_->pending_swap_);
177  posted_swaps_->pending_swap_ = NULL;
178}
179
180}  // namespace
181
182namespace ui {
183
184Compositor::Compositor(gfx::AcceleratedWidget widget)
185    : root_layer_(NULL),
186      widget_(widget),
187      vsync_manager_(new CompositorVSyncManager()),
188      posted_swaps_(new PostedSwapQueue()),
189      device_scale_factor_(0.0f),
190      last_started_frame_(0),
191      last_ended_frame_(0),
192      next_draw_is_resize_(false),
193      disable_schedule_composite_(false),
194      compositor_lock_(NULL),
195      defer_draw_scheduling_(false),
196      waiting_on_compositing_end_(false),
197      draw_on_compositing_end_(false),
198      schedule_draw_factory_(this) {
199  DCHECK(g_compositor_initialized)
200      << "Compositor::Initialize must be called before creating a Compositor.";
201
202  root_web_layer_ = cc::Layer::Create();
203  root_web_layer_->SetAnchorPoint(gfx::PointF(0.f, 0.f));
204
205  CommandLine* command_line = CommandLine::ForCurrentProcess();
206
207  cc::LayerTreeSettings settings;
208  settings.refresh_rate =
209      ContextFactory::GetInstance()->DoesCreateTestContexts()
210      ? kTestRefreshRate
211      : kDefaultRefreshRate;
212  settings.deadline_scheduling_enabled =
213      switches::IsUIDeadlineSchedulingEnabled();
214  settings.partial_swap_enabled =
215      !command_line->HasSwitch(cc::switches::kUIDisablePartialSwap);
216  settings.per_tile_painting_enabled =
217      command_line->HasSwitch(cc::switches::kUIEnablePerTilePainting);
218
219  // These flags should be mirrored by renderer versions in content/renderer/.
220  settings.initial_debug_state.show_debug_borders =
221      command_line->HasSwitch(cc::switches::kUIShowCompositedLayerBorders);
222  settings.initial_debug_state.show_fps_counter =
223      command_line->HasSwitch(cc::switches::kUIShowFPSCounter);
224  settings.initial_debug_state.show_layer_animation_bounds_rects =
225      command_line->HasSwitch(cc::switches::kUIShowLayerAnimationBounds);
226  settings.initial_debug_state.show_paint_rects =
227      command_line->HasSwitch(switches::kUIShowPaintRects);
228  settings.initial_debug_state.show_property_changed_rects =
229      command_line->HasSwitch(cc::switches::kUIShowPropertyChangedRects);
230  settings.initial_debug_state.show_surface_damage_rects =
231      command_line->HasSwitch(cc::switches::kUIShowSurfaceDamageRects);
232  settings.initial_debug_state.show_screen_space_rects =
233      command_line->HasSwitch(cc::switches::kUIShowScreenSpaceRects);
234  settings.initial_debug_state.show_replica_screen_space_rects =
235      command_line->HasSwitch(cc::switches::kUIShowReplicaScreenSpaceRects);
236  settings.initial_debug_state.show_occluding_rects =
237      command_line->HasSwitch(cc::switches::kUIShowOccludingRects);
238  settings.initial_debug_state.show_non_occluding_rects =
239      command_line->HasSwitch(cc::switches::kUIShowNonOccludingRects);
240
241  settings.initial_debug_state.SetRecordRenderingStats(
242      command_line->HasSwitch(cc::switches::kEnableGpuBenchmarking));
243
244  base::TimeTicks before_create = base::TimeTicks::Now();
245  if (!!g_compositor_thread) {
246    host_ = cc::LayerTreeHost::CreateThreaded(
247        this, NULL, settings, g_compositor_thread->message_loop_proxy());
248  } else {
249    host_ = cc::LayerTreeHost::CreateSingleThreaded(this, this, NULL, settings);
250  }
251  UMA_HISTOGRAM_TIMES("GPU.CreateBrowserCompositor",
252                      base::TimeTicks::Now() - before_create);
253  host_->SetRootLayer(root_web_layer_);
254  host_->SetLayerTreeHostClientReady();
255}
256
257Compositor::~Compositor() {
258  TRACE_EVENT0("shutdown", "Compositor::destructor");
259
260  DCHECK(g_compositor_initialized);
261
262  CancelCompositorLock();
263  DCHECK(!compositor_lock_);
264
265  if (root_layer_)
266    root_layer_->SetCompositor(NULL);
267
268  // Stop all outstanding draws before telling the ContextFactory to tear
269  // down any contexts that the |host_| may rely upon.
270  host_.reset();
271
272  ContextFactory::GetInstance()->RemoveCompositor(this);
273}
274
275// static
276void Compositor::Initialize() {
277#if defined(OS_CHROMEOS)
278  bool use_thread = !CommandLine::ForCurrentProcess()->HasSwitch(
279      switches::kUIDisableThreadedCompositing);
280#else
281  bool use_thread = false;
282#endif
283  if (use_thread) {
284    g_compositor_thread = new base::Thread("Browser Compositor");
285    g_compositor_thread->Start();
286  }
287
288  DCHECK(!g_compositor_initialized) << "Compositor initialized twice.";
289  g_compositor_initialized = true;
290}
291
292// static
293bool Compositor::WasInitializedWithThread() {
294  DCHECK(g_compositor_initialized);
295  return !!g_compositor_thread;
296}
297
298// static
299scoped_refptr<base::MessageLoopProxy> Compositor::GetCompositorMessageLoop() {
300  scoped_refptr<base::MessageLoopProxy> proxy;
301  if (g_compositor_thread)
302    proxy = g_compositor_thread->message_loop_proxy();
303  return proxy;
304}
305
306// static
307void Compositor::Terminate() {
308  if (g_compositor_thread) {
309    g_compositor_thread->Stop();
310    delete g_compositor_thread;
311    g_compositor_thread = NULL;
312  }
313
314  DCHECK(g_compositor_initialized) << "Compositor::Initialize() didn't happen.";
315  g_compositor_initialized = false;
316}
317
318void Compositor::ScheduleDraw() {
319  if (g_compositor_thread) {
320    host_->Composite(gfx::FrameTime::Now());
321  } else if (!defer_draw_scheduling_) {
322    defer_draw_scheduling_ = true;
323    base::MessageLoop::current()->PostTask(
324        FROM_HERE,
325        base::Bind(&Compositor::Draw, schedule_draw_factory_.GetWeakPtr()));
326  }
327}
328
329void Compositor::SetRootLayer(Layer* root_layer) {
330  if (root_layer_ == root_layer)
331    return;
332  if (root_layer_)
333    root_layer_->SetCompositor(NULL);
334  root_layer_ = root_layer;
335  if (root_layer_ && !root_layer_->GetCompositor())
336    root_layer_->SetCompositor(this);
337  root_web_layer_->RemoveAllChildren();
338  if (root_layer_)
339    root_web_layer_->AddChild(root_layer_->cc_layer());
340}
341
342void Compositor::SetHostHasTransparentBackground(
343    bool host_has_transparent_background) {
344  host_->set_has_transparent_background(host_has_transparent_background);
345}
346
347void Compositor::Draw() {
348  DCHECK(!g_compositor_thread);
349
350  defer_draw_scheduling_ = false;
351  if (waiting_on_compositing_end_) {
352    draw_on_compositing_end_ = true;
353    return;
354  }
355  waiting_on_compositing_end_ = true;
356
357  TRACE_EVENT_ASYNC_BEGIN0("ui", "Compositor::Draw", last_started_frame_ + 1);
358
359  if (!root_layer_)
360    return;
361
362  last_started_frame_++;
363  PendingSwap pending_swap(DRAW_SWAP, posted_swaps_.get());
364  if (!IsLocked()) {
365    // TODO(nduca): Temporary while compositor calls
366    // compositeImmediately() directly.
367    Layout();
368    host_->Composite(gfx::FrameTime::Now());
369
370#if defined(OS_WIN)
371    // While we resize, we are usually a few frames behind. By blocking
372    // the UI thread here we minize the area that is mis-painted, specially
373    // in the non-client area. See RenderWidgetHostViewAura::SetBounds for
374    // more details and bug 177115.
375    if (next_draw_is_resize_ && (last_ended_frame_ > 1)) {
376      next_draw_is_resize_ = false;
377      host_->FinishAllRendering();
378    }
379#endif
380
381  }
382  if (!pending_swap.posted())
383    NotifyEnd();
384}
385
386void Compositor::ScheduleFullRedraw() {
387  host_->SetNeedsRedraw();
388}
389
390void Compositor::ScheduleRedrawRect(const gfx::Rect& damage_rect) {
391  host_->SetNeedsRedrawRect(damage_rect);
392}
393
394void Compositor::SetLatencyInfo(const ui::LatencyInfo& latency_info) {
395  scoped_ptr<cc::SwapPromise> swap_promise(
396      new cc::LatencyInfoSwapPromise(latency_info));
397  host_->QueueSwapPromise(swap_promise.Pass());
398}
399
400void Compositor::SetScaleAndSize(float scale, const gfx::Size& size_in_pixel) {
401  DCHECK_GT(scale, 0);
402  if (!size_in_pixel.IsEmpty()) {
403    size_ = size_in_pixel;
404    host_->SetViewportSize(size_in_pixel);
405    root_web_layer_->SetBounds(size_in_pixel);
406
407    next_draw_is_resize_ = true;
408  }
409  if (device_scale_factor_ != scale) {
410    device_scale_factor_ = scale;
411    if (root_layer_)
412      root_layer_->OnDeviceScaleFactorChanged(scale);
413  }
414}
415
416void Compositor::SetBackgroundColor(SkColor color) {
417  host_->set_background_color(color);
418  ScheduleDraw();
419}
420
421scoped_refptr<CompositorVSyncManager> Compositor::vsync_manager() const {
422  return vsync_manager_;
423}
424
425void Compositor::AddObserver(CompositorObserver* observer) {
426  observer_list_.AddObserver(observer);
427}
428
429void Compositor::RemoveObserver(CompositorObserver* observer) {
430  observer_list_.RemoveObserver(observer);
431}
432
433bool Compositor::HasObserver(CompositorObserver* observer) {
434  return observer_list_.HasObserver(observer);
435}
436
437void Compositor::Layout() {
438  // We're sending damage that will be addressed during this composite
439  // cycle, so we don't need to schedule another composite to address it.
440  disable_schedule_composite_ = true;
441  if (root_layer_)
442    root_layer_->SendDamagedRects();
443  disable_schedule_composite_ = false;
444}
445
446scoped_ptr<cc::OutputSurface> Compositor::CreateOutputSurface(bool fallback) {
447  return ContextFactory::GetInstance()->CreateOutputSurface(this, fallback);
448}
449
450void Compositor::DidCommit() {
451  DCHECK(!IsLocked());
452  FOR_EACH_OBSERVER(CompositorObserver,
453                    observer_list_,
454                    OnCompositingDidCommit(this));
455}
456
457void Compositor::DidCommitAndDrawFrame() {
458  base::TimeTicks start_time = gfx::FrameTime::Now();
459  FOR_EACH_OBSERVER(CompositorObserver,
460                    observer_list_,
461                    OnCompositingStarted(this, start_time));
462}
463
464void Compositor::DidCompleteSwapBuffers() {
465  if (g_compositor_thread) {
466    NotifyEnd();
467  } else {
468    DCHECK(posted_swaps_->AreSwapsPosted());
469    DCHECK_GE(1, posted_swaps_->NumSwapsPosted(DRAW_SWAP));
470    if (posted_swaps_->NextPostedSwap() == DRAW_SWAP)
471      NotifyEnd();
472    posted_swaps_->EndSwap();
473  }
474}
475
476scoped_refptr<cc::ContextProvider> Compositor::OffscreenContextProvider() {
477  return ContextFactory::GetInstance()->OffscreenCompositorContextProvider();
478}
479
480void Compositor::ScheduleComposite() {
481  if (!disable_schedule_composite_)
482    ScheduleDraw();
483}
484
485void Compositor::ScheduleAnimation() {
486  ScheduleComposite();
487}
488
489void Compositor::DidPostSwapBuffers() {
490  DCHECK(!g_compositor_thread);
491  posted_swaps_->PostSwap();
492}
493
494void Compositor::DidAbortSwapBuffers() {
495  if (!g_compositor_thread) {
496    DCHECK_GE(1, posted_swaps_->NumSwapsPosted(DRAW_SWAP));
497
498    // We've just lost the context, so unwind all posted_swaps.
499    while (posted_swaps_->AreSwapsPosted()) {
500      if (posted_swaps_->NextPostedSwap() == DRAW_SWAP)
501        NotifyEnd();
502      posted_swaps_->EndSwap();
503    }
504  }
505
506  FOR_EACH_OBSERVER(CompositorObserver,
507                    observer_list_,
508                    OnCompositingAborted(this));
509}
510
511const cc::LayerTreeDebugState& Compositor::GetLayerTreeDebugState() const {
512  return host_->debug_state();
513}
514
515void Compositor::SetLayerTreeDebugState(
516    const cc::LayerTreeDebugState& debug_state) {
517  host_->SetDebugState(debug_state);
518}
519
520scoped_refptr<CompositorLock> Compositor::GetCompositorLock() {
521  if (!compositor_lock_) {
522    compositor_lock_ = new CompositorLock(this);
523    if (g_compositor_thread)
524      host_->SetDeferCommits(true);
525    FOR_EACH_OBSERVER(CompositorObserver,
526                      observer_list_,
527                      OnCompositingLockStateChanged(this));
528  }
529  return compositor_lock_;
530}
531
532void Compositor::UnlockCompositor() {
533  DCHECK(compositor_lock_);
534  compositor_lock_ = NULL;
535  if (g_compositor_thread)
536    host_->SetDeferCommits(false);
537  FOR_EACH_OBSERVER(CompositorObserver,
538                    observer_list_,
539                    OnCompositingLockStateChanged(this));
540}
541
542void Compositor::CancelCompositorLock() {
543  if (compositor_lock_)
544    compositor_lock_->CancelLock();
545}
546
547void Compositor::NotifyEnd() {
548  last_ended_frame_++;
549  TRACE_EVENT_ASYNC_END0("ui", "Compositor::Draw", last_ended_frame_);
550  waiting_on_compositing_end_ = false;
551  if (draw_on_compositing_end_) {
552    draw_on_compositing_end_ = false;
553
554    // Call ScheduleDraw() instead of Draw() in order to allow other
555    // CompositorObservers to be notified before starting another
556    // draw cycle.
557    ScheduleDraw();
558  }
559  FOR_EACH_OBSERVER(CompositorObserver,
560                    observer_list_,
561                    OnCompositingEnded(this));
562}
563
564}  // namespace ui
565