compositor.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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(bool use_software_renderer, 235 gfx::AcceleratedWidget widget) 236 : root_layer_(NULL), 237 widget_(widget), 238 posted_swaps_(new PostedSwapQueue()), 239 device_scale_factor_(0.0f), 240 last_started_frame_(0), 241 last_ended_frame_(0), 242 next_draw_is_resize_(false), 243 disable_schedule_composite_(false), 244 compositor_lock_(NULL), 245 defer_draw_scheduling_(false), 246 waiting_on_compositing_end_(false), 247 draw_on_compositing_end_(false), 248 schedule_draw_factory_(this), 249 use_software_renderer_(use_software_renderer) { 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_paint_rects = 276 command_line->HasSwitch(switches::kUIShowPaintRects); 277 settings.initial_debug_state.show_property_changed_rects = 278 command_line->HasSwitch(cc::switches::kUIShowPropertyChangedRects); 279 settings.initial_debug_state.show_surface_damage_rects = 280 command_line->HasSwitch(cc::switches::kUIShowSurfaceDamageRects); 281 settings.initial_debug_state.show_screen_space_rects = 282 command_line->HasSwitch(cc::switches::kUIShowScreenSpaceRects); 283 settings.initial_debug_state.show_replica_screen_space_rects = 284 command_line->HasSwitch(cc::switches::kUIShowReplicaScreenSpaceRects); 285 settings.initial_debug_state.show_occluding_rects = 286 command_line->HasSwitch(cc::switches::kUIShowOccludingRects); 287 settings.initial_debug_state.show_non_occluding_rects = 288 command_line->HasSwitch(cc::switches::kUIShowNonOccludingRects); 289 290 scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner = 291 g_compositor_thread ? g_compositor_thread->message_loop_proxy() : NULL; 292 293 host_ = 294 cc::LayerTreeHost::Create(this, NULL, settings, compositor_task_runner); 295 host_->SetRootLayer(root_web_layer_); 296 host_->SetLayerTreeHostClientReady(); 297} 298 299Compositor::~Compositor() { 300 TRACE_EVENT0("shutdown", "Compositor::destructor"); 301 302 DCHECK(g_compositor_initialized); 303 304 CancelCompositorLock(); 305 DCHECK(!compositor_lock_); 306 307 if (root_layer_) 308 root_layer_->SetCompositor(NULL); 309 310 // Stop all outstanding draws before telling the ContextFactory to tear 311 // down any contexts that the |host_| may rely upon. 312 host_.reset(); 313 314 ContextFactory::GetInstance()->RemoveCompositor(this); 315} 316 317// static 318void Compositor::Initialize() { 319#if defined(OS_CHROMEOS) 320 bool use_thread = !CommandLine::ForCurrentProcess()->HasSwitch( 321 switches::kUIDisableThreadedCompositing); 322#else 323 bool use_thread = 324 CommandLine::ForCurrentProcess()->HasSwitch( 325 switches::kUIEnableThreadedCompositing) && 326 !CommandLine::ForCurrentProcess()->HasSwitch( 327 switches::kUIDisableThreadedCompositing); 328#endif 329 if (use_thread) { 330 g_compositor_thread = new base::Thread("Browser Compositor"); 331 g_compositor_thread->Start(); 332 } 333 334 DCHECK(!g_compositor_initialized) << "Compositor initialized twice."; 335 g_compositor_initialized = true; 336} 337 338// static 339bool Compositor::WasInitializedWithThread() { 340 DCHECK(g_compositor_initialized); 341 return !!g_compositor_thread; 342} 343 344// static 345scoped_refptr<base::MessageLoopProxy> Compositor::GetCompositorMessageLoop() { 346 scoped_refptr<base::MessageLoopProxy> proxy; 347 if (g_compositor_thread) 348 proxy = g_compositor_thread->message_loop_proxy(); 349 return proxy; 350} 351 352// static 353void Compositor::Terminate() { 354 if (g_compositor_thread) { 355 g_compositor_thread->Stop(); 356 delete g_compositor_thread; 357 g_compositor_thread = NULL; 358 } 359 360 DCHECK(g_compositor_initialized) << "Compositor::Initialize() didn't happen."; 361 g_compositor_initialized = false; 362} 363 364void Compositor::ScheduleDraw() { 365 if (g_compositor_thread) { 366 host_->Composite(gfx::FrameTime::Now()); 367 } else if (!defer_draw_scheduling_) { 368 defer_draw_scheduling_ = true; 369 base::MessageLoop::current()->PostTask( 370 FROM_HERE, 371 base::Bind(&Compositor::Draw, schedule_draw_factory_.GetWeakPtr())); 372 } 373} 374 375void Compositor::SetRootLayer(Layer* root_layer) { 376 if (root_layer_ == root_layer) 377 return; 378 if (root_layer_) 379 root_layer_->SetCompositor(NULL); 380 root_layer_ = root_layer; 381 if (root_layer_ && !root_layer_->GetCompositor()) 382 root_layer_->SetCompositor(this); 383 root_web_layer_->RemoveAllChildren(); 384 if (root_layer_) 385 root_web_layer_->AddChild(root_layer_->cc_layer()); 386} 387 388void Compositor::SetHostHasTransparentBackground( 389 bool host_has_transparent_background) { 390 host_->set_has_transparent_background(host_has_transparent_background); 391} 392 393void Compositor::Draw() { 394 DCHECK(!g_compositor_thread); 395 396 defer_draw_scheduling_ = false; 397 if (waiting_on_compositing_end_) { 398 draw_on_compositing_end_ = true; 399 return; 400 } 401 waiting_on_compositing_end_ = true; 402 403 TRACE_EVENT_ASYNC_BEGIN0("ui", "Compositor::Draw", last_started_frame_ + 1); 404 405 if (!root_layer_) 406 return; 407 408 last_started_frame_++; 409 PendingSwap pending_swap(DRAW_SWAP, posted_swaps_.get()); 410 if (!IsLocked()) { 411 // TODO(nduca): Temporary while compositor calls 412 // compositeImmediately() directly. 413 Layout(); 414 host_->Composite(gfx::FrameTime::Now()); 415 416#if defined(OS_WIN) 417 // While we resize, we are usually a few frames behind. By blocking 418 // the UI thread here we minize the area that is mis-painted, specially 419 // in the non-client area. See RenderWidgetHostViewAura::SetBounds for 420 // more details and bug 177115. 421 if (next_draw_is_resize_ && (last_ended_frame_ > 1)) { 422 next_draw_is_resize_ = false; 423 host_->FinishAllRendering(); 424 } 425#endif 426 427 } 428 if (!pending_swap.posted()) 429 NotifyEnd(); 430} 431 432void Compositor::ScheduleFullRedraw() { 433 host_->SetNeedsRedraw(); 434} 435 436void Compositor::ScheduleRedrawRect(const gfx::Rect& damage_rect) { 437 host_->SetNeedsRedrawRect(damage_rect); 438} 439 440void Compositor::SetLatencyInfo(const ui::LatencyInfo& latency_info) { 441 host_->SetLatencyInfo(latency_info); 442} 443 444bool Compositor::ReadPixels(SkBitmap* bitmap, 445 const gfx::Rect& bounds_in_pixel) { 446 if (bounds_in_pixel.right() > size().width() || 447 bounds_in_pixel.bottom() > size().height()) 448 return false; 449 bitmap->setConfig(SkBitmap::kARGB_8888_Config, 450 bounds_in_pixel.width(), bounds_in_pixel.height()); 451 bitmap->allocPixels(); 452 SkAutoLockPixels lock_image(*bitmap); 453 unsigned char* pixels = static_cast<unsigned char*>(bitmap->getPixels()); 454 CancelCompositorLock(); 455 PendingSwap pending_swap(READPIXELS_SWAP, posted_swaps_.get()); 456 return host_->CompositeAndReadback(pixels, bounds_in_pixel); 457} 458 459void Compositor::SetScaleAndSize(float scale, const gfx::Size& size_in_pixel) { 460 DCHECK_GT(scale, 0); 461 if (!size_in_pixel.IsEmpty()) { 462 size_ = size_in_pixel; 463 host_->SetViewportSize(size_in_pixel); 464 root_web_layer_->SetBounds(size_in_pixel); 465 466 next_draw_is_resize_ = true; 467 } 468 if (device_scale_factor_ != scale) { 469 device_scale_factor_ = scale; 470 if (root_layer_) 471 root_layer_->OnDeviceScaleFactorChanged(scale); 472 } 473} 474 475void Compositor::SetBackgroundColor(SkColor color) { 476 host_->set_background_color(color); 477 ScheduleDraw(); 478} 479 480void Compositor::AddObserver(CompositorObserver* observer) { 481 observer_list_.AddObserver(observer); 482} 483 484void Compositor::RemoveObserver(CompositorObserver* observer) { 485 observer_list_.RemoveObserver(observer); 486} 487 488bool Compositor::HasObserver(CompositorObserver* observer) { 489 return observer_list_.HasObserver(observer); 490} 491 492void Compositor::OnSwapBuffersPosted() { 493 DCHECK(!g_compositor_thread); 494 posted_swaps_->PostSwap(); 495} 496 497void Compositor::OnSwapBuffersComplete() { 498 DCHECK(!g_compositor_thread); 499 DCHECK(posted_swaps_->AreSwapsPosted()); 500 DCHECK_GE(1, posted_swaps_->NumSwapsPosted(DRAW_SWAP)); 501 if (posted_swaps_->NextPostedSwap() == DRAW_SWAP) 502 NotifyEnd(); 503 posted_swaps_->EndSwap(); 504} 505 506void Compositor::OnSwapBuffersAborted() { 507 if (!g_compositor_thread) { 508 DCHECK_GE(1, posted_swaps_->NumSwapsPosted(DRAW_SWAP)); 509 510 // We've just lost the context, so unwind all posted_swaps. 511 while (posted_swaps_->AreSwapsPosted()) { 512 if (posted_swaps_->NextPostedSwap() == DRAW_SWAP) 513 NotifyEnd(); 514 posted_swaps_->EndSwap(); 515 } 516 } 517 518 FOR_EACH_OBSERVER(CompositorObserver, 519 observer_list_, 520 OnCompositingAborted(this)); 521} 522 523void Compositor::OnUpdateVSyncParameters(base::TimeTicks timebase, 524 base::TimeDelta interval) { 525 FOR_EACH_OBSERVER(CompositorObserver, 526 observer_list_, 527 OnUpdateVSyncParameters(this, timebase, interval)); 528} 529 530void Compositor::Layout() { 531 // We're sending damage that will be addressed during this composite 532 // cycle, so we don't need to schedule another composite to address it. 533 disable_schedule_composite_ = true; 534 if (root_layer_) 535 root_layer_->SendDamagedRects(); 536 disable_schedule_composite_ = false; 537} 538 539scoped_ptr<cc::OutputSurface> Compositor::CreateOutputSurface(bool fallback) { 540 return ContextFactory::GetInstance()->CreateOutputSurface(this); 541} 542 543void Compositor::DidCommit() { 544 DCHECK(!IsLocked()); 545 FOR_EACH_OBSERVER(CompositorObserver, 546 observer_list_, 547 OnCompositingDidCommit(this)); 548} 549 550void Compositor::DidCommitAndDrawFrame() { 551 base::TimeTicks start_time = gfx::FrameTime::Now(); 552 FOR_EACH_OBSERVER(CompositorObserver, 553 observer_list_, 554 OnCompositingStarted(this, start_time)); 555} 556 557void Compositor::DidCompleteSwapBuffers() { 558 DCHECK(g_compositor_thread); 559 NotifyEnd(); 560} 561 562void Compositor::ScheduleComposite() { 563 if (!disable_schedule_composite_) 564 ScheduleDraw(); 565} 566 567scoped_refptr<cc::ContextProvider> Compositor::OffscreenContextProvider() { 568 return ContextFactory::GetInstance()->OffscreenCompositorContextProvider(); 569} 570 571const cc::LayerTreeDebugState& Compositor::GetLayerTreeDebugState() const { 572 return host_->debug_state(); 573} 574 575void Compositor::SetLayerTreeDebugState( 576 const cc::LayerTreeDebugState& debug_state) { 577 host_->SetDebugState(debug_state); 578} 579 580scoped_refptr<CompositorLock> Compositor::GetCompositorLock() { 581 if (!compositor_lock_) { 582 compositor_lock_ = new CompositorLock(this); 583 if (g_compositor_thread) 584 host_->SetDeferCommits(true); 585 FOR_EACH_OBSERVER(CompositorObserver, 586 observer_list_, 587 OnCompositingLockStateChanged(this)); 588 } 589 return compositor_lock_; 590} 591 592void Compositor::UnlockCompositor() { 593 DCHECK(compositor_lock_); 594 compositor_lock_ = NULL; 595 if (g_compositor_thread) 596 host_->SetDeferCommits(false); 597 FOR_EACH_OBSERVER(CompositorObserver, 598 observer_list_, 599 OnCompositingLockStateChanged(this)); 600} 601 602void Compositor::CancelCompositorLock() { 603 if (compositor_lock_) 604 compositor_lock_->CancelLock(); 605} 606 607void Compositor::NotifyEnd() { 608 last_ended_frame_++; 609 TRACE_EVENT_ASYNC_END0("ui", "Compositor::Draw", last_ended_frame_); 610 waiting_on_compositing_end_ = false; 611 if (draw_on_compositing_end_) { 612 draw_on_compositing_end_ = false; 613 614 // Call ScheduleDraw() instead of Draw() in order to allow other 615 // CompositorObservers to be notified before starting another 616 // draw cycle. 617 ScheduleDraw(); 618 } 619 FOR_EACH_OBSERVER(CompositorObserver, 620 observer_list_, 621 OnCompositingEnded(this)); 622} 623 624} // namespace ui 625