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