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.
5 #include "ui/compositor/compositor.h"
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"
39 const double kDefaultRefreshRate = 60.0;
40 const double kTestRefreshRate = 200.0;
46 bool g_compositor_initialized = false;
47 base::Thread* g_compositor_thread = NULL;
48 cc::SharedBitmapManager* g_shared_bitmap_manager;
50 ui::ContextFactory* g_context_factory = NULL;
52 const int kCompositorLockTimeoutMs = 67;
56 PendingSwap(SwapType type, ui::PostedSwapQueue* posted_swaps);
59 SwapType type() const { return type_; }
60 bool posted() const { return posted_; }
63 friend class ui::PostedSwapQueue;
67 ui::PostedSwapQueue* posted_swaps_;
69 DISALLOW_COPY_AND_ASSIGN(PendingSwap);
77 ContextFactory* ContextFactory::GetInstance() {
78 DCHECK(g_context_factory);
79 return g_context_factory;
83 void ContextFactory::SetInstance(ContextFactory* instance) {
84 DCHECK_NE(!!g_context_factory, !!instance);
85 g_context_factory = instance;
88 Texture::Texture(bool flipped, const gfx::Size& size, float device_scale_factor)
91 device_scale_factor_(device_scale_factor) {
97 gpu::Mailbox Texture::Produce() {
98 return gpu::Mailbox();
101 CompositorLock::CompositorLock(Compositor* compositor)
102 : compositor_(compositor) {
103 base::MessageLoop::current()->PostDelayedTask(
105 base::Bind(&CompositorLock::CancelLock, AsWeakPtr()),
106 base::TimeDelta::FromMilliseconds(kCompositorLockTimeoutMs));
109 CompositorLock::~CompositorLock() {
113 void CompositorLock::CancelLock() {
116 compositor_->UnlockCompositor();
120 class PostedSwapQueue {
122 PostedSwapQueue() : pending_swap_(NULL) {
126 DCHECK(!pending_swap_);
129 SwapType NextPostedSwap() const {
130 return queue_.front();
133 bool AreSwapsPosted() const {
134 return !queue_.empty();
137 int NumSwapsPosted(SwapType type) const {
139 for (std::deque<SwapType>::const_iterator it = queue_.begin();
140 it != queue_.end(); ++it) {
148 DCHECK(pending_swap_);
149 queue_.push_back(pending_swap_->type());
150 pending_swap_->posted_ = true;
158 friend class ::PendingSwap;
160 PendingSwap* pending_swap_;
161 std::deque<SwapType> queue_;
163 DISALLOW_COPY_AND_ASSIGN(PostedSwapQueue);
170 PendingSwap::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;
177 PendingSwap::~PendingSwap() {
178 DCHECK_EQ(this, posted_swaps_->pending_swap_);
179 posted_swaps_->pending_swap_ = NULL;
186 Compositor::Compositor(gfx::AcceleratedWidget 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.";
204 root_web_layer_ = cc::Layer::Create();
205 root_web_layer_->SetAnchorPoint(gfx::PointF(0.f, 0.f));
207 CommandLine* command_line = CommandLine::ForCurrentProcess();
209 cc::LayerTreeSettings settings;
210 settings.refresh_rate =
211 ContextFactory::GetInstance()->DoesCreateTestContexts()
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;
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);
244 settings.initial_debug_state.SetRecordRenderingStats(
245 command_line->HasSwitch(cc::switches::kEnableGpuBenchmarking));
247 settings.impl_side_painting = IsUIImplSidePaintingEnabled();
248 settings.use_map_image = IsUIMapImageEnabled();
250 base::TimeTicks before_create = base::TimeTicks::Now();
251 if (!!g_compositor_thread) {
252 host_ = cc::LayerTreeHost::CreateThreaded(
254 g_shared_bitmap_manager,
256 g_compositor_thread->message_loop_proxy());
258 host_ = cc::LayerTreeHost::CreateSingleThreaded(
259 this, this, g_shared_bitmap_manager, settings);
261 UMA_HISTOGRAM_TIMES("GPU.CreateBrowserCompositor",
262 base::TimeTicks::Now() - before_create);
263 host_->SetRootLayer(root_web_layer_);
264 host_->SetLayerTreeHostClientReady();
267 Compositor::~Compositor() {
268 TRACE_EVENT0("shutdown", "Compositor::destructor");
270 DCHECK(g_compositor_initialized);
272 CancelCompositorLock();
273 DCHECK(!compositor_lock_);
276 root_layer_->SetCompositor(NULL);
278 // Stop all outstanding draws before telling the ContextFactory to tear
279 // down any contexts that the |host_| may rely upon.
282 ContextFactory::GetInstance()->RemoveCompositor(this);
286 void Compositor::Initialize() {
287 #if defined(OS_CHROMEOS)
288 bool use_thread = !CommandLine::ForCurrentProcess()->HasSwitch(
289 switches::kUIDisableThreadedCompositing);
291 bool use_thread = false;
294 g_compositor_thread = new base::Thread("Browser Compositor");
295 g_compositor_thread->Start();
298 DCHECK(!g_compositor_initialized) << "Compositor initialized twice.";
299 g_compositor_initialized = true;
303 bool Compositor::WasInitializedWithThread() {
304 DCHECK(g_compositor_initialized);
305 return !!g_compositor_thread;
309 scoped_refptr<base::MessageLoopProxy> Compositor::GetCompositorMessageLoop() {
310 scoped_refptr<base::MessageLoopProxy> proxy;
311 if (g_compositor_thread)
312 proxy = g_compositor_thread->message_loop_proxy();
317 void Compositor::Terminate() {
318 if (g_compositor_thread) {
319 g_compositor_thread->Stop();
320 delete g_compositor_thread;
321 g_compositor_thread = NULL;
324 DCHECK(g_compositor_initialized) << "Compositor::Initialize() didn't happen.";
325 g_compositor_initialized = false;
329 void Compositor::SetSharedBitmapManager(cc::SharedBitmapManager* manager) {
330 g_shared_bitmap_manager = manager;
333 void 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(
340 base::Bind(&Compositor::Draw, schedule_draw_factory_.GetWeakPtr()));
344 void Compositor::SetRootLayer(Layer* root_layer) {
345 if (root_layer_ == 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();
354 root_web_layer_->AddChild(root_layer_->cc_layer());
357 void Compositor::SetHostHasTransparentBackground(
358 bool host_has_transparent_background) {
359 host_->set_has_transparent_background(host_has_transparent_background);
362 void Compositor::Draw() {
363 DCHECK(!g_compositor_thread);
365 defer_draw_scheduling_ = false;
366 if (waiting_on_compositing_end_) {
367 draw_on_compositing_end_ = true;
370 waiting_on_compositing_end_ = true;
372 TRACE_EVENT_ASYNC_BEGIN0("ui", "Compositor::Draw", last_started_frame_ + 1);
377 last_started_frame_++;
378 PendingSwap pending_swap(DRAW_SWAP, posted_swaps_.get());
380 // TODO(nduca): Temporary while compositor calls
381 // compositeImmediately() directly.
383 host_->Composite(gfx::FrameTime::Now());
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();
397 if (!pending_swap.posted())
401 void Compositor::ScheduleFullRedraw() {
402 host_->SetNeedsRedraw();
405 void Compositor::ScheduleRedrawRect(const gfx::Rect& damage_rect) {
406 host_->SetNeedsRedrawRect(damage_rect);
409 void 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());
415 void Compositor::SetScaleAndSize(float scale, const gfx::Size& size_in_pixel) {
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);
422 next_draw_is_resize_ = true;
424 if (device_scale_factor_ != scale) {
425 device_scale_factor_ = scale;
427 root_layer_->OnDeviceScaleFactorChanged(scale);
431 void Compositor::SetBackgroundColor(SkColor color) {
432 host_->set_background_color(color);
436 scoped_refptr<CompositorVSyncManager> Compositor::vsync_manager() const {
437 return vsync_manager_;
440 void Compositor::AddObserver(CompositorObserver* observer) {
441 observer_list_.AddObserver(observer);
444 void Compositor::RemoveObserver(CompositorObserver* observer) {
445 observer_list_.RemoveObserver(observer);
448 bool Compositor::HasObserver(CompositorObserver* observer) {
449 return observer_list_.HasObserver(observer);
452 void 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;
457 root_layer_->SendDamagedRects();
458 disable_schedule_composite_ = false;
461 scoped_ptr<cc::OutputSurface> Compositor::CreateOutputSurface(bool fallback) {
462 return ContextFactory::GetInstance()->CreateOutputSurface(this, fallback);
465 void Compositor::DidCommit() {
467 FOR_EACH_OBSERVER(CompositorObserver,
469 OnCompositingDidCommit(this));
472 void Compositor::DidCommitAndDrawFrame() {
473 base::TimeTicks start_time = gfx::FrameTime::Now();
474 FOR_EACH_OBSERVER(CompositorObserver,
476 OnCompositingStarted(this, start_time));
479 void Compositor::DidCompleteSwapBuffers() {
480 if (g_compositor_thread) {
483 DCHECK(posted_swaps_->AreSwapsPosted());
484 DCHECK_GE(1, posted_swaps_->NumSwapsPosted(DRAW_SWAP));
485 if (posted_swaps_->NextPostedSwap() == DRAW_SWAP)
487 posted_swaps_->EndSwap();
491 scoped_refptr<cc::ContextProvider> Compositor::OffscreenContextProvider() {
492 return ContextFactory::GetInstance()->OffscreenCompositorContextProvider();
495 void Compositor::ScheduleComposite() {
496 if (!disable_schedule_composite_)
500 void Compositor::ScheduleAnimation() {
504 void Compositor::DidPostSwapBuffers() {
505 DCHECK(!g_compositor_thread);
506 posted_swaps_->PostSwap();
509 void Compositor::DidAbortSwapBuffers() {
510 if (!g_compositor_thread) {
511 DCHECK_GE(1, posted_swaps_->NumSwapsPosted(DRAW_SWAP));
513 // We've just lost the context, so unwind all posted_swaps.
514 while (posted_swaps_->AreSwapsPosted()) {
515 if (posted_swaps_->NextPostedSwap() == DRAW_SWAP)
517 posted_swaps_->EndSwap();
521 FOR_EACH_OBSERVER(CompositorObserver,
523 OnCompositingAborted(this));
526 const cc::LayerTreeDebugState& Compositor::GetLayerTreeDebugState() const {
527 return host_->debug_state();
530 void Compositor::SetLayerTreeDebugState(
531 const cc::LayerTreeDebugState& debug_state) {
532 host_->SetDebugState(debug_state);
535 scoped_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,
542 OnCompositingLockStateChanged(this));
544 return compositor_lock_;
547 void Compositor::UnlockCompositor() {
548 DCHECK(compositor_lock_);
549 compositor_lock_ = NULL;
550 if (g_compositor_thread)
551 host_->SetDeferCommits(false);
552 FOR_EACH_OBSERVER(CompositorObserver,
554 OnCompositingLockStateChanged(this));
557 void Compositor::CancelCompositorLock() {
558 if (compositor_lock_)
559 compositor_lock_->CancelLock();
562 void Compositor::NotifyEnd() {
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;
569 // Call ScheduleDraw() instead of Draw() in order to allow other
570 // CompositorObservers to be notified before starting another
574 FOR_EACH_OBSERVER(CompositorObserver,
576 OnCompositingEnded(this));