compositor.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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/dip_util.h"
31#include "ui/compositor/layer.h"
32#include "ui/gfx/frame_time.h"
33#include "ui/gl/gl_context.h"
34#include "ui/gl/gl_switches.h"
35
36namespace {
37
38const double kDefaultRefreshRate = 60.0;
39const double kTestRefreshRate = 200.0;
40
41enum SwapType {
42  DRAW_SWAP,
43  READPIXELS_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
95std::string Texture::Produce() {
96  return std::string();
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
118// static
119void DrawWaiterForTest::Wait(Compositor* compositor) {
120  DrawWaiterForTest waiter;
121  waiter.wait_for_commit_ = false;
122  waiter.WaitImpl(compositor);
123}
124
125// static
126void DrawWaiterForTest::WaitForCommit(Compositor* compositor) {
127  DrawWaiterForTest waiter;
128  waiter.wait_for_commit_ = true;
129  waiter.WaitImpl(compositor);
130}
131
132DrawWaiterForTest::DrawWaiterForTest() {
133}
134
135DrawWaiterForTest::~DrawWaiterForTest() {
136}
137
138void DrawWaiterForTest::WaitImpl(Compositor* compositor) {
139  compositor->AddObserver(this);
140  wait_run_loop_.reset(new base::RunLoop());
141  wait_run_loop_->Run();
142  compositor->RemoveObserver(this);
143}
144
145void DrawWaiterForTest::OnCompositingDidCommit(Compositor* compositor) {
146  if (wait_for_commit_)
147    wait_run_loop_->Quit();
148}
149
150void DrawWaiterForTest::OnCompositingStarted(Compositor* compositor,
151                                             base::TimeTicks start_time) {
152}
153
154void DrawWaiterForTest::OnCompositingEnded(Compositor* compositor) {
155  if (!wait_for_commit_)
156    wait_run_loop_->Quit();
157}
158
159void DrawWaiterForTest::OnCompositingAborted(Compositor* compositor) {
160}
161
162void DrawWaiterForTest::OnCompositingLockStateChanged(Compositor* compositor) {
163}
164
165void DrawWaiterForTest::OnUpdateVSyncParameters(Compositor* compositor,
166                                                base::TimeTicks timebase,
167                                                base::TimeDelta interval) {
168}
169
170class PostedSwapQueue {
171 public:
172  PostedSwapQueue() : pending_swap_(NULL) {
173  }
174
175  ~PostedSwapQueue() {
176    DCHECK(!pending_swap_);
177  }
178
179  SwapType NextPostedSwap() const {
180    return queue_.front();
181  }
182
183  bool AreSwapsPosted() const {
184    return !queue_.empty();
185  }
186
187  int NumSwapsPosted(SwapType type) const {
188    int count = 0;
189    for (std::deque<SwapType>::const_iterator it = queue_.begin();
190         it != queue_.end(); ++it) {
191      if (*it == type)
192        count++;
193    }
194    return count;
195  }
196
197  void PostSwap() {
198    DCHECK(pending_swap_);
199    queue_.push_back(pending_swap_->type());
200    pending_swap_->posted_ = true;
201  }
202
203  void EndSwap() {
204    queue_.pop_front();
205  }
206
207 private:
208  friend class ::PendingSwap;
209
210  PendingSwap* pending_swap_;
211  std::deque<SwapType> queue_;
212
213  DISALLOW_COPY_AND_ASSIGN(PostedSwapQueue);
214};
215
216}  // namespace ui
217
218namespace {
219
220PendingSwap::PendingSwap(SwapType type, ui::PostedSwapQueue* posted_swaps)
221    : type_(type), posted_(false), posted_swaps_(posted_swaps) {
222  // Only one pending swap in flight.
223  DCHECK_EQ(static_cast<PendingSwap*>(NULL), posted_swaps_->pending_swap_);
224  posted_swaps_->pending_swap_ = this;
225}
226
227PendingSwap::~PendingSwap() {
228  DCHECK_EQ(this, posted_swaps_->pending_swap_);
229  posted_swaps_->pending_swap_ = NULL;
230}
231
232}  // namespace
233
234namespace ui {
235
236Compositor::Compositor(gfx::AcceleratedWidget widget)
237    : root_layer_(NULL),
238      widget_(widget),
239      posted_swaps_(new PostedSwapQueue()),
240      device_scale_factor_(0.0f),
241      last_started_frame_(0),
242      last_ended_frame_(0),
243      next_draw_is_resize_(false),
244      disable_schedule_composite_(false),
245      compositor_lock_(NULL),
246      defer_draw_scheduling_(false),
247      waiting_on_compositing_end_(false),
248      draw_on_compositing_end_(false),
249      schedule_draw_factory_(this) {
250  DCHECK(g_compositor_initialized)
251      << "Compositor::Initialize must be called before creating a Compositor.";
252
253  root_web_layer_ = cc::Layer::Create();
254  root_web_layer_->SetAnchorPoint(gfx::PointF(0.f, 0.f));
255
256  CommandLine* command_line = CommandLine::ForCurrentProcess();
257
258  cc::LayerTreeSettings settings;
259  settings.refresh_rate =
260      ContextFactory::GetInstance()->DoesCreateTestContexts()
261      ? kTestRefreshRate
262      : kDefaultRefreshRate;
263  settings.deadline_scheduling_enabled =
264      switches::IsUIDeadlineSchedulingEnabled();
265  settings.partial_swap_enabled =
266      !command_line->HasSwitch(cc::switches::kUIDisablePartialSwap);
267  settings.per_tile_painting_enabled =
268      command_line->HasSwitch(cc::switches::kUIEnablePerTilePainting);
269
270  // These flags should be mirrored by renderer versions in content/renderer/.
271  settings.initial_debug_state.show_debug_borders =
272      command_line->HasSwitch(cc::switches::kUIShowCompositedLayerBorders);
273  settings.initial_debug_state.show_fps_counter =
274      command_line->HasSwitch(cc::switches::kUIShowFPSCounter);
275  settings.initial_debug_state.show_layer_animation_bounds_rects =
276      command_line->HasSwitch(cc::switches::kUIShowLayerAnimationBounds);
277  settings.initial_debug_state.show_paint_rects =
278      command_line->HasSwitch(switches::kUIShowPaintRects);
279  settings.initial_debug_state.show_property_changed_rects =
280      command_line->HasSwitch(cc::switches::kUIShowPropertyChangedRects);
281  settings.initial_debug_state.show_surface_damage_rects =
282      command_line->HasSwitch(cc::switches::kUIShowSurfaceDamageRects);
283  settings.initial_debug_state.show_screen_space_rects =
284      command_line->HasSwitch(cc::switches::kUIShowScreenSpaceRects);
285  settings.initial_debug_state.show_replica_screen_space_rects =
286      command_line->HasSwitch(cc::switches::kUIShowReplicaScreenSpaceRects);
287  settings.initial_debug_state.show_occluding_rects =
288      command_line->HasSwitch(cc::switches::kUIShowOccludingRects);
289  settings.initial_debug_state.show_non_occluding_rects =
290      command_line->HasSwitch(cc::switches::kUIShowNonOccludingRects);
291
292  base::TimeTicks before_create = base::TimeTicks::Now();
293  if (!!g_compositor_thread) {
294    host_ = cc::LayerTreeHost::CreateThreaded(
295        this, NULL, settings, g_compositor_thread->message_loop_proxy());
296  } else {
297    host_ = cc::LayerTreeHost::CreateSingleThreaded(this, this, NULL, settings);
298  }
299  UMA_HISTOGRAM_TIMES("GPU.CreateBrowserCompositor",
300                      base::TimeTicks::Now() - before_create);
301  host_->SetRootLayer(root_web_layer_);
302  host_->SetLayerTreeHostClientReady();
303}
304
305Compositor::~Compositor() {
306  TRACE_EVENT0("shutdown", "Compositor::destructor");
307
308  DCHECK(g_compositor_initialized);
309
310  CancelCompositorLock();
311  DCHECK(!compositor_lock_);
312
313  if (root_layer_)
314    root_layer_->SetCompositor(NULL);
315
316  // Stop all outstanding draws before telling the ContextFactory to tear
317  // down any contexts that the |host_| may rely upon.
318  host_.reset();
319
320  ContextFactory::GetInstance()->RemoveCompositor(this);
321}
322
323// static
324void Compositor::Initialize() {
325#if defined(OS_CHROMEOS)
326  bool use_thread = !CommandLine::ForCurrentProcess()->HasSwitch(
327      switches::kUIDisableThreadedCompositing);
328#else
329  bool use_thread =
330      CommandLine::ForCurrentProcess()->HasSwitch(
331          switches::kUIEnableThreadedCompositing) &&
332      !CommandLine::ForCurrentProcess()->HasSwitch(
333          switches::kUIDisableThreadedCompositing);
334#endif
335  if (use_thread) {
336    g_compositor_thread = new base::Thread("Browser Compositor");
337    g_compositor_thread->Start();
338  }
339
340  DCHECK(!g_compositor_initialized) << "Compositor initialized twice.";
341  g_compositor_initialized = true;
342}
343
344// static
345bool Compositor::WasInitializedWithThread() {
346  DCHECK(g_compositor_initialized);
347  return !!g_compositor_thread;
348}
349
350// static
351scoped_refptr<base::MessageLoopProxy> Compositor::GetCompositorMessageLoop() {
352  scoped_refptr<base::MessageLoopProxy> proxy;
353  if (g_compositor_thread)
354    proxy = g_compositor_thread->message_loop_proxy();
355  return proxy;
356}
357
358// static
359void Compositor::Terminate() {
360  if (g_compositor_thread) {
361    g_compositor_thread->Stop();
362    delete g_compositor_thread;
363    g_compositor_thread = NULL;
364  }
365
366  DCHECK(g_compositor_initialized) << "Compositor::Initialize() didn't happen.";
367  g_compositor_initialized = false;
368}
369
370void Compositor::ScheduleDraw() {
371  if (g_compositor_thread) {
372    host_->Composite(gfx::FrameTime::Now());
373  } else if (!defer_draw_scheduling_) {
374    defer_draw_scheduling_ = true;
375    base::MessageLoop::current()->PostTask(
376        FROM_HERE,
377        base::Bind(&Compositor::Draw, schedule_draw_factory_.GetWeakPtr()));
378  }
379}
380
381void Compositor::SetRootLayer(Layer* root_layer) {
382  if (root_layer_ == root_layer)
383    return;
384  if (root_layer_)
385    root_layer_->SetCompositor(NULL);
386  root_layer_ = root_layer;
387  if (root_layer_ && !root_layer_->GetCompositor())
388    root_layer_->SetCompositor(this);
389  root_web_layer_->RemoveAllChildren();
390  if (root_layer_)
391    root_web_layer_->AddChild(root_layer_->cc_layer());
392}
393
394void Compositor::SetHostHasTransparentBackground(
395    bool host_has_transparent_background) {
396  host_->set_has_transparent_background(host_has_transparent_background);
397}
398
399void Compositor::Draw() {
400  DCHECK(!g_compositor_thread);
401
402  defer_draw_scheduling_ = false;
403  if (waiting_on_compositing_end_) {
404    draw_on_compositing_end_ = true;
405    return;
406  }
407  waiting_on_compositing_end_ = true;
408
409  TRACE_EVENT_ASYNC_BEGIN0("ui", "Compositor::Draw", last_started_frame_ + 1);
410
411  if (!root_layer_)
412    return;
413
414  last_started_frame_++;
415  PendingSwap pending_swap(DRAW_SWAP, posted_swaps_.get());
416  if (!IsLocked()) {
417    // TODO(nduca): Temporary while compositor calls
418    // compositeImmediately() directly.
419    Layout();
420    host_->Composite(gfx::FrameTime::Now());
421
422#if defined(OS_WIN)
423    // While we resize, we are usually a few frames behind. By blocking
424    // the UI thread here we minize the area that is mis-painted, specially
425    // in the non-client area. See RenderWidgetHostViewAura::SetBounds for
426    // more details and bug 177115.
427    if (next_draw_is_resize_ && (last_ended_frame_ > 1)) {
428      next_draw_is_resize_ = false;
429      host_->FinishAllRendering();
430    }
431#endif
432
433  }
434  if (!pending_swap.posted())
435    NotifyEnd();
436}
437
438void Compositor::ScheduleFullRedraw() {
439  host_->SetNeedsRedraw();
440}
441
442void Compositor::ScheduleRedrawRect(const gfx::Rect& damage_rect) {
443  host_->SetNeedsRedrawRect(damage_rect);
444}
445
446void Compositor::SetLatencyInfo(const ui::LatencyInfo& latency_info) {
447  scoped_ptr<cc::SwapPromise> swap_promise(
448      new cc::LatencyInfoSwapPromise(latency_info));
449  host_->QueueSwapPromise(swap_promise.Pass());
450}
451
452bool Compositor::ReadPixels(SkBitmap* bitmap,
453                            const gfx::Rect& bounds_in_pixel) {
454  if (bounds_in_pixel.right() > size().width() ||
455      bounds_in_pixel.bottom() > size().height())
456    return false;
457  bitmap->setConfig(SkBitmap::kARGB_8888_Config,
458                    bounds_in_pixel.width(), bounds_in_pixel.height());
459  bitmap->allocPixels();
460  SkAutoLockPixels lock_image(*bitmap);
461  unsigned char* pixels = static_cast<unsigned char*>(bitmap->getPixels());
462  CancelCompositorLock();
463  PendingSwap pending_swap(READPIXELS_SWAP, posted_swaps_.get());
464  return host_->CompositeAndReadback(pixels, bounds_in_pixel);
465}
466
467void Compositor::SetScaleAndSize(float scale, const gfx::Size& size_in_pixel) {
468  DCHECK_GT(scale, 0);
469  if (!size_in_pixel.IsEmpty()) {
470    size_ = size_in_pixel;
471    host_->SetViewportSize(size_in_pixel);
472    root_web_layer_->SetBounds(size_in_pixel);
473
474    next_draw_is_resize_ = true;
475  }
476  if (device_scale_factor_ != scale) {
477    device_scale_factor_ = scale;
478    if (root_layer_)
479      root_layer_->OnDeviceScaleFactorChanged(scale);
480  }
481}
482
483void Compositor::SetBackgroundColor(SkColor color) {
484  host_->set_background_color(color);
485  ScheduleDraw();
486}
487
488void Compositor::AddObserver(CompositorObserver* observer) {
489  observer_list_.AddObserver(observer);
490}
491
492void Compositor::RemoveObserver(CompositorObserver* observer) {
493  observer_list_.RemoveObserver(observer);
494}
495
496bool Compositor::HasObserver(CompositorObserver* observer) {
497  return observer_list_.HasObserver(observer);
498}
499
500void Compositor::OnUpdateVSyncParameters(base::TimeTicks timebase,
501                                         base::TimeDelta interval) {
502  FOR_EACH_OBSERVER(CompositorObserver,
503                    observer_list_,
504                    OnUpdateVSyncParameters(this, timebase, interval));
505}
506
507void Compositor::Layout() {
508  // We're sending damage that will be addressed during this composite
509  // cycle, so we don't need to schedule another composite to address it.
510  disable_schedule_composite_ = true;
511  if (root_layer_)
512    root_layer_->SendDamagedRects();
513  disable_schedule_composite_ = false;
514}
515
516scoped_ptr<cc::OutputSurface> Compositor::CreateOutputSurface(bool fallback) {
517  return ContextFactory::GetInstance()->CreateOutputSurface(this, fallback);
518}
519
520void Compositor::DidCommit() {
521  DCHECK(!IsLocked());
522  FOR_EACH_OBSERVER(CompositorObserver,
523                    observer_list_,
524                    OnCompositingDidCommit(this));
525}
526
527void Compositor::DidCommitAndDrawFrame() {
528  base::TimeTicks start_time = gfx::FrameTime::Now();
529  FOR_EACH_OBSERVER(CompositorObserver,
530                    observer_list_,
531                    OnCompositingStarted(this, start_time));
532}
533
534void Compositor::DidCompleteSwapBuffers() {
535  if (g_compositor_thread) {
536    NotifyEnd();
537  } else {
538    DCHECK(posted_swaps_->AreSwapsPosted());
539    DCHECK_GE(1, posted_swaps_->NumSwapsPosted(DRAW_SWAP));
540    if (posted_swaps_->NextPostedSwap() == DRAW_SWAP)
541      NotifyEnd();
542    posted_swaps_->EndSwap();
543  }
544}
545
546scoped_refptr<cc::ContextProvider> Compositor::OffscreenContextProvider() {
547  return ContextFactory::GetInstance()->OffscreenCompositorContextProvider();
548}
549
550void Compositor::ScheduleComposite() {
551  if (!disable_schedule_composite_)
552    ScheduleDraw();
553}
554
555void Compositor::ScheduleAnimation() {
556  ScheduleComposite();
557}
558
559void Compositor::DidPostSwapBuffers() {
560  DCHECK(!g_compositor_thread);
561  posted_swaps_->PostSwap();
562}
563
564void Compositor::DidAbortSwapBuffers() {
565  if (!g_compositor_thread) {
566    DCHECK_GE(1, posted_swaps_->NumSwapsPosted(DRAW_SWAP));
567
568    // We've just lost the context, so unwind all posted_swaps.
569    while (posted_swaps_->AreSwapsPosted()) {
570      if (posted_swaps_->NextPostedSwap() == DRAW_SWAP)
571        NotifyEnd();
572      posted_swaps_->EndSwap();
573    }
574  }
575
576  FOR_EACH_OBSERVER(CompositorObserver,
577                    observer_list_,
578                    OnCompositingAborted(this));
579}
580
581const cc::LayerTreeDebugState& Compositor::GetLayerTreeDebugState() const {
582  return host_->debug_state();
583}
584
585void Compositor::SetLayerTreeDebugState(
586    const cc::LayerTreeDebugState& debug_state) {
587  host_->SetDebugState(debug_state);
588}
589
590scoped_refptr<CompositorLock> Compositor::GetCompositorLock() {
591  if (!compositor_lock_) {
592    compositor_lock_ = new CompositorLock(this);
593    if (g_compositor_thread)
594      host_->SetDeferCommits(true);
595    FOR_EACH_OBSERVER(CompositorObserver,
596                      observer_list_,
597                      OnCompositingLockStateChanged(this));
598  }
599  return compositor_lock_;
600}
601
602void Compositor::UnlockCompositor() {
603  DCHECK(compositor_lock_);
604  compositor_lock_ = NULL;
605  if (g_compositor_thread)
606    host_->SetDeferCommits(false);
607  FOR_EACH_OBSERVER(CompositorObserver,
608                    observer_list_,
609                    OnCompositingLockStateChanged(this));
610}
611
612void Compositor::CancelCompositorLock() {
613  if (compositor_lock_)
614    compositor_lock_->CancelLock();
615}
616
617void Compositor::NotifyEnd() {
618  last_ended_frame_++;
619  TRACE_EVENT_ASYNC_END0("ui", "Compositor::Draw", last_ended_frame_);
620  waiting_on_compositing_end_ = false;
621  if (draw_on_compositing_end_) {
622    draw_on_compositing_end_ = false;
623
624    // Call ScheduleDraw() instead of Draw() in order to allow other
625    // CompositorObservers to be notified before starting another
626    // draw cycle.
627    ScheduleDraw();
628  }
629  FOR_EACH_OBSERVER(CompositorObserver,
630                    observer_list_,
631                    OnCompositingEnded(this));
632}
633
634}  // namespace ui
635