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