compositor.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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/memory/singleton.h" 13#include "base/message_loop.h" 14#include "base/string_util.h" 15#include "base/threading/thread.h" 16#include "base/threading/thread_restrictions.h" 17#include "cc/base/switches.h" 18#include "cc/base/thread_impl.h" 19#include "cc/input/input_handler.h" 20#include "cc/layers/layer.h" 21#include "cc/output/context_provider.h" 22#include "cc/output/output_surface.h" 23#include "cc/trees/layer_tree_host.h" 24#include "third_party/skia/include/core/SkBitmap.h" 25#include "ui/compositor/compositor_observer.h" 26#include "ui/compositor/compositor_switches.h" 27#include "ui/compositor/dip_util.h" 28#include "ui/compositor/layer.h" 29#include "ui/compositor/test_web_graphics_context_3d.h" 30#include "ui/gl/gl_context.h" 31#include "ui/gl/gl_implementation.h" 32#include "ui/gl/gl_surface.h" 33#include "ui/gl/gl_switches.h" 34#include "webkit/gpu/grcontext_for_webgraphicscontext3d.h" 35#include "webkit/gpu/webgraphicscontext3d_in_process_impl.h" 36 37#if defined(OS_CHROMEOS) 38#include "base/chromeos/chromeos_version.h" 39#endif 40 41namespace { 42 43const double kDefaultRefreshRate = 60.0; 44const double kTestRefreshRate = 100.0; 45 46enum SwapType { 47 DRAW_SWAP, 48 READPIXELS_SWAP, 49}; 50 51base::Thread* g_compositor_thread = NULL; 52 53bool g_test_compositor_enabled = false; 54 55ui::ContextFactory* g_implicit_factory = NULL; 56ui::ContextFactory* g_context_factory = NULL; 57 58const int kCompositorLockTimeoutMs = 67; 59 60class PendingSwap { 61 public: 62 PendingSwap(SwapType type, ui::PostedSwapQueue* posted_swaps); 63 ~PendingSwap(); 64 65 SwapType type() const { return type_; } 66 bool posted() const { return posted_; } 67 68 private: 69 friend class ui::PostedSwapQueue; 70 71 SwapType type_; 72 bool posted_; 73 ui::PostedSwapQueue* posted_swaps_; 74 75 DISALLOW_COPY_AND_ASSIGN(PendingSwap); 76}; 77 78void SetupImplicitFactory() { 79 // We leak the implicit factory so that we don't race with the tear down of 80 // the gl_bindings. 81 DCHECK(!g_context_factory); 82 DCHECK(!g_implicit_factory); 83 if (g_test_compositor_enabled) { 84 g_implicit_factory = new ui::TestContextFactory; 85 } else { 86 DVLOG(1) << "Using DefaultContextFactory"; 87 scoped_ptr<ui::DefaultContextFactory> instance( 88 new ui::DefaultContextFactory()); 89 if (instance->Initialize()) 90 g_implicit_factory = instance.release(); 91 } 92 g_context_factory = g_implicit_factory; 93} 94 95void ResetImplicitFactory() { 96 if (!g_implicit_factory || g_context_factory != g_implicit_factory) 97 return; 98 delete g_implicit_factory; 99 g_implicit_factory = NULL; 100 g_context_factory = NULL; 101} 102 103} // namespace 104 105namespace ui { 106 107// static 108ContextFactory* ContextFactory::GetInstance() { 109 if (!g_context_factory) 110 SetupImplicitFactory(); 111 return g_context_factory; 112} 113 114// static 115void ContextFactory::SetInstance(ContextFactory* instance) { 116 g_context_factory = instance; 117} 118 119class ContextProviderFromContextFactory : public cc::ContextProvider { 120 public: 121 static scoped_refptr<ContextProviderFromContextFactory> Create( 122 ContextFactory* factory) { 123 scoped_refptr<ContextProviderFromContextFactory> provider = 124 new ContextProviderFromContextFactory(factory); 125 if (!provider->InitializeOnMainThread()) 126 return NULL; 127 return provider; 128 } 129 130 virtual bool BindToCurrentThread() OVERRIDE { 131 DCHECK(context3d_); 132 133 return context3d_->makeContextCurrent(); 134 } 135 136 virtual WebKit::WebGraphicsContext3D* Context3d() OVERRIDE { 137 DCHECK(context3d_); 138 139 return context3d_.get(); 140 } 141 142 virtual class GrContext* GrContext() OVERRIDE { 143 DCHECK(context3d_); 144 145 if (!gr_context_) { 146 gr_context_.reset( 147 new webkit::gpu::GrContextForWebGraphicsContext3D(context3d_.get())); 148 } 149 return gr_context_->get(); 150 } 151 152 virtual void VerifyContexts() OVERRIDE { 153 DCHECK(context3d_); 154 155 if (context3d_->isContextLost()) { 156 base::AutoLock lock(destroyed_lock_); 157 destroyed_ = true; 158 } 159 } 160 161 virtual bool DestroyedOnMainThread() OVERRIDE { 162 base::AutoLock lock(destroyed_lock_); 163 return destroyed_; 164 } 165 166 protected: 167 explicit ContextProviderFromContextFactory(ContextFactory* factory) 168 : factory_(factory), 169 destroyed_(false) {} 170 virtual ~ContextProviderFromContextFactory() {} 171 172 bool InitializeOnMainThread() { 173 if (context3d_) 174 return true; 175 context3d_.reset(factory_->CreateOffscreenContext()); 176 return !!context3d_; 177 } 178 179 private: 180 ContextFactory* factory_; 181 base::Lock destroyed_lock_; 182 bool destroyed_; 183 scoped_ptr<WebKit::WebGraphicsContext3D> context3d_; 184 scoped_ptr<webkit::gpu::GrContextForWebGraphicsContext3D> gr_context_; 185}; 186 187DefaultContextFactory::DefaultContextFactory() { 188} 189 190DefaultContextFactory::~DefaultContextFactory() { 191} 192 193bool DefaultContextFactory::Initialize() { 194 if (!gfx::GLSurface::InitializeOneOff() || 195 gfx::GetGLImplementation() == gfx::kGLImplementationNone) { 196 LOG(ERROR) << "Could not load the GL bindings"; 197 return false; 198 } 199 return true; 200} 201 202cc::OutputSurface* DefaultContextFactory::CreateOutputSurface( 203 Compositor* compositor) { 204 return new cc::OutputSurface( 205 make_scoped_ptr(CreateContextCommon(compositor, false))); 206} 207 208WebKit::WebGraphicsContext3D* DefaultContextFactory::CreateOffscreenContext() { 209 return CreateContextCommon(NULL, true); 210} 211 212scoped_refptr<cc::ContextProvider> 213DefaultContextFactory::OffscreenContextProviderForMainThread() { 214 if (!offscreen_contexts_main_thread_ || 215 !offscreen_contexts_main_thread_->DestroyedOnMainThread()) { 216 offscreen_contexts_main_thread_ = 217 ContextProviderFromContextFactory::Create(this); 218 if (offscreen_contexts_main_thread_ && 219 !offscreen_contexts_main_thread_->BindToCurrentThread()) 220 offscreen_contexts_main_thread_ = NULL; 221 } 222 return offscreen_contexts_main_thread_; 223} 224 225scoped_refptr<cc::ContextProvider> 226DefaultContextFactory::OffscreenContextProviderForCompositorThread() { 227 if (!offscreen_contexts_compositor_thread_ || 228 !offscreen_contexts_compositor_thread_->DestroyedOnMainThread()) { 229 offscreen_contexts_compositor_thread_ = 230 ContextProviderFromContextFactory::Create(this); 231 } 232 return offscreen_contexts_compositor_thread_; 233} 234 235void DefaultContextFactory::RemoveCompositor(Compositor* compositor) { 236} 237 238WebKit::WebGraphicsContext3D* DefaultContextFactory::CreateContextCommon( 239 Compositor* compositor, 240 bool offscreen) { 241 DCHECK(offscreen || compositor); 242 WebKit::WebGraphicsContext3D::Attributes attrs; 243 attrs.depth = false; 244 attrs.stencil = false; 245 attrs.antialias = false; 246 attrs.shareResources = true; 247 WebKit::WebGraphicsContext3D* context = 248 offscreen ? 249 webkit::gpu::WebGraphicsContext3DInProcessImpl::CreateForWebView( 250 attrs, false) : 251 webkit::gpu::WebGraphicsContext3DInProcessImpl::CreateForWindow( 252 attrs, compositor->widget(), NULL); 253 if (!context) 254 return NULL; 255 256 CommandLine* command_line = CommandLine::ForCurrentProcess(); 257 if (!offscreen) { 258 context->makeContextCurrent(); 259 gfx::GLContext* gl_context = gfx::GLContext::GetCurrent(); 260 bool vsync = !command_line->HasSwitch(switches::kDisableGpuVsync); 261 gl_context->SetSwapInterval(vsync ? 1 : 0); 262 gl_context->ReleaseCurrent(NULL); 263 } 264 return context; 265} 266 267TestContextFactory::TestContextFactory() {} 268 269TestContextFactory::~TestContextFactory() {} 270 271cc::OutputSurface* TestContextFactory::CreateOutputSurface( 272 Compositor* compositor) { 273 return new cc::OutputSurface(make_scoped_ptr(CreateOffscreenContext())); 274} 275 276WebKit::WebGraphicsContext3D* TestContextFactory::CreateOffscreenContext() { 277 ui::TestWebGraphicsContext3D* context = new ui::TestWebGraphicsContext3D; 278 context->Initialize(); 279 return context; 280} 281 282scoped_refptr<cc::ContextProvider> 283TestContextFactory::OffscreenContextProviderForMainThread() { 284 if (!offscreen_contexts_main_thread_ || 285 offscreen_contexts_main_thread_->DestroyedOnMainThread()) { 286 offscreen_contexts_main_thread_ = 287 ContextProviderFromContextFactory::Create(this); 288 CHECK(offscreen_contexts_main_thread_->BindToCurrentThread()); 289 } 290 return offscreen_contexts_main_thread_; 291} 292 293scoped_refptr<cc::ContextProvider> 294TestContextFactory::OffscreenContextProviderForCompositorThread() { 295 if (!offscreen_contexts_compositor_thread_ || 296 offscreen_contexts_compositor_thread_->DestroyedOnMainThread()) { 297 offscreen_contexts_compositor_thread_ = 298 ContextProviderFromContextFactory::Create(this); 299 } 300 return offscreen_contexts_compositor_thread_; 301} 302 303void TestContextFactory::RemoveCompositor(Compositor* compositor) { 304} 305 306Texture::Texture(bool flipped, const gfx::Size& size, float device_scale_factor) 307 : size_(size), 308 flipped_(flipped), 309 device_scale_factor_(device_scale_factor) { 310} 311 312Texture::~Texture() { 313} 314 315std::string Texture::Produce() { 316 return EmptyString(); 317} 318 319CompositorLock::CompositorLock(Compositor* compositor) 320 : compositor_(compositor) { 321 MessageLoop::current()->PostDelayedTask( 322 FROM_HERE, 323 base::Bind(&CompositorLock::CancelLock, AsWeakPtr()), 324 base::TimeDelta::FromMilliseconds(kCompositorLockTimeoutMs)); 325} 326 327CompositorLock::~CompositorLock() { 328 CancelLock(); 329} 330 331void CompositorLock::CancelLock() { 332 if (!compositor_) 333 return; 334 compositor_->UnlockCompositor(); 335 compositor_ = NULL; 336} 337 338class PostedSwapQueue { 339 public: 340 PostedSwapQueue() : pending_swap_(NULL) { 341 } 342 343 ~PostedSwapQueue() { 344 DCHECK(!pending_swap_); 345 } 346 347 SwapType NextPostedSwap() const { 348 return queue_.front(); 349 } 350 351 bool AreSwapsPosted() const { 352 return !queue_.empty(); 353 } 354 355 int NumSwapsPosted(SwapType type) const { 356 int count = 0; 357 for (std::deque<SwapType>::const_iterator it = queue_.begin(); 358 it != queue_.end(); ++it) { 359 if (*it == type) 360 count++; 361 } 362 return count; 363 } 364 365 void PostSwap() { 366 DCHECK(pending_swap_); 367 queue_.push_back(pending_swap_->type()); 368 pending_swap_->posted_ = true; 369 } 370 371 void EndSwap() { 372 queue_.pop_front(); 373 } 374 375 private: 376 friend class ::PendingSwap; 377 378 PendingSwap* pending_swap_; 379 std::deque<SwapType> queue_; 380 381 DISALLOW_COPY_AND_ASSIGN(PostedSwapQueue); 382}; 383 384} // namespace ui 385 386namespace { 387 388PendingSwap::PendingSwap(SwapType type, ui::PostedSwapQueue* posted_swaps) 389 : type_(type), posted_(false), posted_swaps_(posted_swaps) { 390 // Only one pending swap in flight. 391 DCHECK_EQ(static_cast<PendingSwap*>(NULL), posted_swaps_->pending_swap_); 392 posted_swaps_->pending_swap_ = this; 393} 394 395PendingSwap::~PendingSwap() { 396 DCHECK_EQ(this, posted_swaps_->pending_swap_); 397 posted_swaps_->pending_swap_ = NULL; 398} 399 400} // namespace 401 402namespace ui { 403 404Compositor::Compositor(CompositorDelegate* delegate, 405 gfx::AcceleratedWidget widget) 406 : delegate_(delegate), 407 root_layer_(NULL), 408 widget_(widget), 409 posted_swaps_(new PostedSwapQueue()), 410 device_scale_factor_(0.0f), 411 last_started_frame_(0), 412 last_ended_frame_(0), 413 disable_schedule_composite_(false), 414 compositor_lock_(NULL) { 415 root_web_layer_ = cc::Layer::Create(); 416 root_web_layer_->SetAnchorPoint(gfx::PointF(0.f, 0.f)); 417 418 CommandLine* command_line = CommandLine::ForCurrentProcess(); 419 420 cc::LayerTreeSettings settings; 421 settings.refresh_rate = 422 g_test_compositor_enabled ? kTestRefreshRate : kDefaultRefreshRate; 423 settings.partial_swap_enabled = 424 command_line->HasSwitch(cc::switches::kUIEnablePartialSwap); 425 settings.per_tile_painting_enabled = 426 command_line->HasSwitch(cc::switches::kUIEnablePerTilePainting); 427 428 // These flags should be mirrored by renderer versions in content/renderer/. 429 settings.initial_debug_state.show_debug_borders = 430 command_line->HasSwitch(cc::switches::kUIShowCompositedLayerBorders); 431 settings.initial_debug_state.show_fps_counter = 432 command_line->HasSwitch(cc::switches::kUIShowFPSCounter); 433 settings.initial_debug_state.show_paint_rects = 434 command_line->HasSwitch(switches::kUIShowPaintRects); 435 settings.initial_debug_state.show_platform_layer_tree = 436 command_line->HasSwitch(cc::switches::kUIShowCompositedLayerTree); 437 settings.initial_debug_state.show_property_changed_rects = 438 command_line->HasSwitch(cc::switches::kUIShowPropertyChangedRects); 439 settings.initial_debug_state.show_surface_damage_rects = 440 command_line->HasSwitch(cc::switches::kUIShowSurfaceDamageRects); 441 settings.initial_debug_state.show_screen_space_rects = 442 command_line->HasSwitch(cc::switches::kUIShowScreenSpaceRects); 443 settings.initial_debug_state.show_replica_screen_space_rects = 444 command_line->HasSwitch(cc::switches::kUIShowReplicaScreenSpaceRects); 445 settings.initial_debug_state.show_occluding_rects = 446 command_line->HasSwitch(cc::switches::kUIShowOccludingRects); 447 settings.initial_debug_state.show_non_occluding_rects = 448 command_line->HasSwitch(cc::switches::kUIShowNonOccludingRects); 449 450 scoped_ptr<cc::Thread> thread; 451 if (g_compositor_thread) { 452 thread = cc::ThreadImpl::CreateForDifferentThread( 453 g_compositor_thread->message_loop_proxy()); 454 } 455 456 host_ = cc::LayerTreeHost::Create(this, settings, thread.Pass()); 457 host_->SetRootLayer(root_web_layer_); 458 host_->SetSurfaceReady(); 459} 460 461Compositor::~Compositor() { 462 CancelCompositorLock(); 463 DCHECK(!compositor_lock_); 464 465 // Don't call |CompositorDelegate::ScheduleDraw| from this point. 466 delegate_ = NULL; 467 if (root_layer_) 468 root_layer_->SetCompositor(NULL); 469 470 // Stop all outstanding draws before telling the ContextFactory to tear 471 // down any contexts that the |host_| may rely upon. 472 host_.reset(); 473 474 ContextFactory::GetInstance()->RemoveCompositor(this); 475} 476 477void Compositor::Initialize(bool use_thread) { 478 if (use_thread) { 479 g_compositor_thread = new base::Thread("Browser Compositor"); 480 g_compositor_thread->Start(); 481 } 482} 483 484void Compositor::Terminate() { 485 if (g_compositor_thread) { 486 g_compositor_thread->Stop(); 487 delete g_compositor_thread; 488 g_compositor_thread = NULL; 489 } 490} 491 492void Compositor::ScheduleDraw() { 493 if (g_compositor_thread) 494 host_->Composite(base::TimeTicks::Now()); 495 else if (delegate_) 496 delegate_->ScheduleDraw(); 497} 498 499void Compositor::SetRootLayer(Layer* root_layer) { 500 if (root_layer_ == root_layer) 501 return; 502 if (root_layer_) 503 root_layer_->SetCompositor(NULL); 504 root_layer_ = root_layer; 505 if (root_layer_ && !root_layer_->GetCompositor()) 506 root_layer_->SetCompositor(this); 507 root_web_layer_->RemoveAllChildren(); 508 if (root_layer_) 509 root_web_layer_->AddChild(root_layer_->cc_layer()); 510} 511 512void Compositor::SetHostHasTransparentBackground( 513 bool host_has_transparent_background) { 514 host_->set_has_transparent_background(host_has_transparent_background); 515} 516 517void Compositor::Draw(bool force_clear) { 518 DCHECK(!g_compositor_thread); 519 520 if (!root_layer_) 521 return; 522 523 last_started_frame_++; 524 PendingSwap pending_swap(DRAW_SWAP, posted_swaps_.get()); 525 if (!IsLocked()) { 526 // TODO(nduca): Temporary while compositor calls 527 // compositeImmediately() directly. 528 Layout(); 529 host_->Composite(base::TimeTicks::Now()); 530 } 531 if (!pending_swap.posted()) 532 NotifyEnd(); 533} 534 535void Compositor::ScheduleFullDraw() { 536 host_->SetNeedsRedraw(); 537} 538 539bool Compositor::ReadPixels(SkBitmap* bitmap, 540 const gfx::Rect& bounds_in_pixel) { 541 if (bounds_in_pixel.right() > size().width() || 542 bounds_in_pixel.bottom() > size().height()) 543 return false; 544 bitmap->setConfig(SkBitmap::kARGB_8888_Config, 545 bounds_in_pixel.width(), bounds_in_pixel.height()); 546 bitmap->allocPixels(); 547 SkAutoLockPixels lock_image(*bitmap); 548 unsigned char* pixels = static_cast<unsigned char*>(bitmap->getPixels()); 549 CancelCompositorLock(); 550 PendingSwap pending_swap(READPIXELS_SWAP, posted_swaps_.get()); 551 return host_->CompositeAndReadback(pixels, bounds_in_pixel); 552} 553 554void Compositor::SetScaleAndSize(float scale, const gfx::Size& size_in_pixel) { 555 DCHECK_GT(scale, 0); 556 if (!size_in_pixel.IsEmpty()) { 557 size_ = size_in_pixel; 558 host_->SetViewportSize(size_in_pixel, size_in_pixel); 559 root_web_layer_->SetBounds(size_in_pixel); 560 } 561 if (device_scale_factor_ != scale) { 562 device_scale_factor_ = scale; 563 if (root_layer_) 564 root_layer_->OnDeviceScaleFactorChanged(scale); 565 } 566} 567 568void Compositor::AddObserver(CompositorObserver* observer) { 569 observer_list_.AddObserver(observer); 570} 571 572void Compositor::RemoveObserver(CompositorObserver* observer) { 573 observer_list_.RemoveObserver(observer); 574} 575 576bool Compositor::HasObserver(CompositorObserver* observer) { 577 return observer_list_.HasObserver(observer); 578} 579 580void Compositor::OnSwapBuffersPosted() { 581 DCHECK(!g_compositor_thread); 582 posted_swaps_->PostSwap(); 583} 584 585void Compositor::OnSwapBuffersComplete() { 586 DCHECK(!g_compositor_thread); 587 DCHECK(posted_swaps_->AreSwapsPosted()); 588 DCHECK_GE(1, posted_swaps_->NumSwapsPosted(DRAW_SWAP)); 589 if (posted_swaps_->NextPostedSwap() == DRAW_SWAP) 590 NotifyEnd(); 591 posted_swaps_->EndSwap(); 592} 593 594void Compositor::OnSwapBuffersAborted() { 595 DCHECK(!g_compositor_thread); 596 DCHECK_GE(1, posted_swaps_->NumSwapsPosted(DRAW_SWAP)); 597 598 // We've just lost the context, so unwind all posted_swaps. 599 while (posted_swaps_->AreSwapsPosted()) { 600 if (posted_swaps_->NextPostedSwap() == DRAW_SWAP) 601 NotifyEnd(); 602 posted_swaps_->EndSwap(); 603 } 604 605 FOR_EACH_OBSERVER(CompositorObserver, 606 observer_list_, 607 OnCompositingAborted(this)); 608} 609 610void Compositor::OnUpdateVSyncParameters(base::TimeTicks timebase, 611 base::TimeDelta interval) { 612 FOR_EACH_OBSERVER(CompositorObserver, 613 observer_list_, 614 OnUpdateVSyncParameters(this, timebase, interval)); 615} 616 617void Compositor::Layout() { 618 // We're sending damage that will be addressed during this composite 619 // cycle, so we don't need to schedule another composite to address it. 620 disable_schedule_composite_ = true; 621 if (root_layer_) 622 root_layer_->SendDamagedRects(); 623 disable_schedule_composite_ = false; 624} 625 626scoped_ptr<cc::OutputSurface> Compositor::CreateOutputSurface() { 627 return make_scoped_ptr( 628 ContextFactory::GetInstance()->CreateOutputSurface(this)); 629} 630 631scoped_ptr<cc::InputHandler> Compositor::CreateInputHandler() { 632 return scoped_ptr<cc::InputHandler>(); 633} 634 635void Compositor::DidCommit() { 636 DCHECK(!IsLocked()); 637 FOR_EACH_OBSERVER(CompositorObserver, 638 observer_list_, 639 OnCompositingDidCommit(this)); 640} 641 642void Compositor::DidCommitAndDrawFrame() { 643 base::TimeTicks start_time = base::TimeTicks::Now(); 644 FOR_EACH_OBSERVER(CompositorObserver, 645 observer_list_, 646 OnCompositingStarted(this, start_time)); 647} 648 649void Compositor::DidCompleteSwapBuffers() { 650 DCHECK(g_compositor_thread); 651 NotifyEnd(); 652} 653 654void Compositor::ScheduleComposite() { 655 if (!disable_schedule_composite_) 656 ScheduleDraw(); 657} 658 659scoped_refptr<cc::ContextProvider> 660Compositor::OffscreenContextProviderForMainThread() { 661 return ContextFactory::GetInstance()->OffscreenContextProviderForMainThread(); 662} 663 664scoped_refptr<cc::ContextProvider> 665Compositor::OffscreenContextProviderForCompositorThread() { 666 return ContextFactory::GetInstance()-> 667 OffscreenContextProviderForCompositorThread(); 668} 669 670scoped_refptr<CompositorLock> Compositor::GetCompositorLock() { 671 if (!compositor_lock_) { 672 compositor_lock_ = new CompositorLock(this); 673 if (g_compositor_thread) 674 host_->SetDeferCommits(true); 675 FOR_EACH_OBSERVER(CompositorObserver, 676 observer_list_, 677 OnCompositingLockStateChanged(this)); 678 } 679 return compositor_lock_; 680} 681 682void Compositor::UnlockCompositor() { 683 DCHECK(compositor_lock_); 684 compositor_lock_ = NULL; 685 if (g_compositor_thread) 686 host_->SetDeferCommits(false); 687 FOR_EACH_OBSERVER(CompositorObserver, 688 observer_list_, 689 OnCompositingLockStateChanged(this)); 690} 691 692void Compositor::CancelCompositorLock() { 693 if (compositor_lock_) 694 compositor_lock_->CancelLock(); 695} 696 697void Compositor::NotifyEnd() { 698 last_ended_frame_++; 699 FOR_EACH_OBSERVER(CompositorObserver, 700 observer_list_, 701 OnCompositingEnded(this)); 702} 703 704COMPOSITOR_EXPORT void SetupTestCompositor() { 705 if (!CommandLine::ForCurrentProcess()->HasSwitch( 706 switches::kDisableTestCompositor)) { 707 g_test_compositor_enabled = true; 708 } 709#if defined(OS_CHROMEOS) 710 // If the test is running on the chromeos envrionment (such as 711 // device or vm bots), use the real compositor. 712 if (base::chromeos::IsRunningOnChromeOS()) 713 g_test_compositor_enabled = false; 714#endif 715 ResetImplicitFactory(); 716} 717 718COMPOSITOR_EXPORT void DisableTestCompositor() { 719 ResetImplicitFactory(); 720 g_test_compositor_enabled = false; 721} 722 723COMPOSITOR_EXPORT bool IsTestCompositorEnabled() { 724 return g_test_compositor_enabled; 725} 726 727} // namespace ui 728