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