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