| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/compositor/compositor.h" |
| |
| #include <algorithm> |
| #include <deque> |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/memory/singleton.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_util.h" |
| #include "base/threading/thread.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "cc/base/switches.h" |
| #include "cc/input/input_handler.h" |
| #include "cc/layers/layer.h" |
| #include "cc/output/context_provider.h" |
| #include "cc/output/output_surface.h" |
| #include "cc/trees/layer_tree_host.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/compositor/compositor_observer.h" |
| #include "ui/compositor/compositor_switches.h" |
| #include "ui/compositor/context_provider_from_context_factory.h" |
| #include "ui/compositor/dip_util.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/compositor/reflector.h" |
| #include "ui/compositor/test_web_graphics_context_3d.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/gl_implementation.h" |
| #include "ui/gl/gl_surface.h" |
| #include "ui/gl/gl_switches.h" |
| #include "webkit/common/gpu/grcontext_for_webgraphicscontext3d.h" |
| #include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h" |
| |
| #if defined(OS_CHROMEOS) |
| #include "base/chromeos/chromeos_version.h" |
| #endif |
| |
| namespace { |
| |
| const double kDefaultRefreshRate = 60.0; |
| const double kTestRefreshRate = 200.0; |
| |
| enum SwapType { |
| DRAW_SWAP, |
| READPIXELS_SWAP, |
| }; |
| |
| bool g_compositor_initialized = false; |
| base::Thread* g_compositor_thread = NULL; |
| |
| ui::ContextFactory* g_implicit_factory = NULL; |
| ui::ContextFactory* g_context_factory = NULL; |
| |
| const int kCompositorLockTimeoutMs = 67; |
| |
| class PendingSwap { |
| public: |
| PendingSwap(SwapType type, ui::PostedSwapQueue* posted_swaps); |
| ~PendingSwap(); |
| |
| SwapType type() const { return type_; } |
| bool posted() const { return posted_; } |
| |
| private: |
| friend class ui::PostedSwapQueue; |
| |
| SwapType type_; |
| bool posted_; |
| ui::PostedSwapQueue* posted_swaps_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PendingSwap); |
| }; |
| |
| } // namespace |
| |
| namespace ui { |
| |
| // static |
| ContextFactory* ContextFactory::GetInstance() { |
| DCHECK(g_context_factory); |
| return g_context_factory; |
| } |
| |
| // static |
| void ContextFactory::SetInstance(ContextFactory* instance) { |
| g_context_factory = instance; |
| } |
| |
| DefaultContextFactory::DefaultContextFactory() { |
| } |
| |
| DefaultContextFactory::~DefaultContextFactory() { |
| } |
| |
| bool DefaultContextFactory::Initialize() { |
| if (!gfx::GLSurface::InitializeOneOff() || |
| gfx::GetGLImplementation() == gfx::kGLImplementationNone) { |
| LOG(ERROR) << "Could not load the GL bindings"; |
| return false; |
| } |
| return true; |
| } |
| |
| scoped_ptr<cc::OutputSurface> DefaultContextFactory::CreateOutputSurface( |
| Compositor* compositor) { |
| return make_scoped_ptr(new cc::OutputSurface( |
| CreateContextCommon(compositor, false))); |
| } |
| |
| scoped_ptr<WebKit::WebGraphicsContext3D> |
| DefaultContextFactory::CreateOffscreenContext() { |
| return CreateContextCommon(NULL, true); |
| } |
| |
| scoped_refptr<Reflector> DefaultContextFactory::CreateReflector( |
| Compositor* mirroed_compositor, |
| Layer* mirroring_layer) { |
| return NULL; |
| } |
| |
| void DefaultContextFactory::RemoveReflector( |
| scoped_refptr<Reflector> reflector) { |
| } |
| |
| scoped_refptr<cc::ContextProvider> |
| DefaultContextFactory::OffscreenContextProviderForMainThread() { |
| if (!offscreen_contexts_main_thread_.get() || |
| !offscreen_contexts_main_thread_->DestroyedOnMainThread()) { |
| offscreen_contexts_main_thread_ = |
| webkit::gpu::ContextProviderInProcess::CreateOffscreen(); |
| if (offscreen_contexts_main_thread_.get() && |
| !offscreen_contexts_main_thread_->BindToCurrentThread()) |
| offscreen_contexts_main_thread_ = NULL; |
| } |
| return offscreen_contexts_main_thread_; |
| } |
| |
| scoped_refptr<cc::ContextProvider> |
| DefaultContextFactory::OffscreenContextProviderForCompositorThread() { |
| if (!offscreen_contexts_compositor_thread_.get() || |
| !offscreen_contexts_compositor_thread_->DestroyedOnMainThread()) { |
| offscreen_contexts_compositor_thread_ = |
| webkit::gpu::ContextProviderInProcess::CreateOffscreen(); |
| } |
| return offscreen_contexts_compositor_thread_; |
| } |
| |
| void DefaultContextFactory::RemoveCompositor(Compositor* compositor) { |
| } |
| |
| bool DefaultContextFactory::DoesCreateTestContexts() { return false; } |
| |
| scoped_ptr<WebKit::WebGraphicsContext3D> |
| DefaultContextFactory::CreateContextCommon(Compositor* compositor, |
| bool offscreen) { |
| DCHECK(offscreen || compositor); |
| WebKit::WebGraphicsContext3D::Attributes attrs; |
| attrs.depth = false; |
| attrs.stencil = false; |
| attrs.antialias = false; |
| attrs.shareResources = true; |
| using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl; |
| if (offscreen) { |
| return WebGraphicsContext3DInProcessCommandBufferImpl:: |
| CreateOffscreenContext(attrs); |
| } |
| return WebGraphicsContext3DInProcessCommandBufferImpl::CreateViewContext( |
| attrs, compositor->widget()); |
| } |
| |
| TestContextFactory::TestContextFactory() {} |
| |
| TestContextFactory::~TestContextFactory() {} |
| |
| scoped_ptr<cc::OutputSurface> TestContextFactory::CreateOutputSurface( |
| Compositor* compositor) { |
| return make_scoped_ptr(new cc::OutputSurface(CreateOffscreenContext())); |
| } |
| |
| scoped_ptr<WebKit::WebGraphicsContext3D> |
| TestContextFactory::CreateOffscreenContext() { |
| scoped_ptr<ui::TestWebGraphicsContext3D> context( |
| new ui::TestWebGraphicsContext3D); |
| context->Initialize(); |
| return context.PassAs<WebKit::WebGraphicsContext3D>(); |
| } |
| |
| scoped_refptr<Reflector> TestContextFactory::CreateReflector( |
| Compositor* mirrored_compositor, |
| Layer* mirroring_layer) { |
| return new Reflector(); |
| } |
| |
| void TestContextFactory::RemoveReflector(scoped_refptr<Reflector> reflector) { |
| } |
| |
| scoped_refptr<cc::ContextProvider> |
| TestContextFactory::OffscreenContextProviderForMainThread() { |
| if (!offscreen_contexts_main_thread_.get() || |
| offscreen_contexts_main_thread_->DestroyedOnMainThread()) { |
| offscreen_contexts_main_thread_ = |
| ContextProviderFromContextFactory::CreateForOffscreen(this); |
| CHECK(offscreen_contexts_main_thread_->BindToCurrentThread()); |
| } |
| return offscreen_contexts_main_thread_; |
| } |
| |
| scoped_refptr<cc::ContextProvider> |
| TestContextFactory::OffscreenContextProviderForCompositorThread() { |
| if (!offscreen_contexts_compositor_thread_.get() || |
| offscreen_contexts_compositor_thread_->DestroyedOnMainThread()) { |
| offscreen_contexts_compositor_thread_ = |
| ContextProviderFromContextFactory::CreateForOffscreen(this); |
| } |
| return offscreen_contexts_compositor_thread_; |
| } |
| |
| void TestContextFactory::RemoveCompositor(Compositor* compositor) { |
| } |
| |
| bool TestContextFactory::DoesCreateTestContexts() { return true; } |
| |
| Texture::Texture(bool flipped, const gfx::Size& size, float device_scale_factor) |
| : size_(size), |
| flipped_(flipped), |
| device_scale_factor_(device_scale_factor) { |
| } |
| |
| Texture::~Texture() { |
| } |
| |
| std::string Texture::Produce() { |
| return EmptyString(); |
| } |
| |
| CompositorLock::CompositorLock(Compositor* compositor) |
| : compositor_(compositor) { |
| base::MessageLoop::current()->PostDelayedTask( |
| FROM_HERE, |
| base::Bind(&CompositorLock::CancelLock, AsWeakPtr()), |
| base::TimeDelta::FromMilliseconds(kCompositorLockTimeoutMs)); |
| } |
| |
| CompositorLock::~CompositorLock() { |
| CancelLock(); |
| } |
| |
| void CompositorLock::CancelLock() { |
| if (!compositor_) |
| return; |
| compositor_->UnlockCompositor(); |
| compositor_ = NULL; |
| } |
| |
| // static |
| void DrawWaiterForTest::Wait(Compositor* compositor) { |
| DrawWaiterForTest waiter; |
| waiter.wait_for_commit_ = false; |
| waiter.WaitImpl(compositor); |
| } |
| |
| // static |
| void DrawWaiterForTest::WaitForCommit(Compositor* compositor) { |
| DrawWaiterForTest waiter; |
| waiter.wait_for_commit_ = true; |
| waiter.WaitImpl(compositor); |
| } |
| |
| DrawWaiterForTest::DrawWaiterForTest() { |
| } |
| |
| DrawWaiterForTest::~DrawWaiterForTest() { |
| } |
| |
| void DrawWaiterForTest::WaitImpl(Compositor* compositor) { |
| compositor->AddObserver(this); |
| wait_run_loop_.reset(new base::RunLoop()); |
| wait_run_loop_->Run(); |
| compositor->RemoveObserver(this); |
| } |
| |
| void DrawWaiterForTest::OnCompositingDidCommit(Compositor* compositor) { |
| if (wait_for_commit_) |
| wait_run_loop_->Quit(); |
| } |
| |
| void DrawWaiterForTest::OnCompositingStarted(Compositor* compositor, |
| base::TimeTicks start_time) { |
| } |
| |
| void DrawWaiterForTest::OnCompositingEnded(Compositor* compositor) { |
| if (!wait_for_commit_) |
| wait_run_loop_->Quit(); |
| } |
| |
| void DrawWaiterForTest::OnCompositingAborted(Compositor* compositor) { |
| } |
| |
| void DrawWaiterForTest::OnCompositingLockStateChanged(Compositor* compositor) { |
| } |
| |
| void DrawWaiterForTest::OnUpdateVSyncParameters(Compositor* compositor, |
| base::TimeTicks timebase, |
| base::TimeDelta interval) { |
| } |
| |
| class PostedSwapQueue { |
| public: |
| PostedSwapQueue() : pending_swap_(NULL) { |
| } |
| |
| ~PostedSwapQueue() { |
| DCHECK(!pending_swap_); |
| } |
| |
| SwapType NextPostedSwap() const { |
| return queue_.front(); |
| } |
| |
| bool AreSwapsPosted() const { |
| return !queue_.empty(); |
| } |
| |
| int NumSwapsPosted(SwapType type) const { |
| int count = 0; |
| for (std::deque<SwapType>::const_iterator it = queue_.begin(); |
| it != queue_.end(); ++it) { |
| if (*it == type) |
| count++; |
| } |
| return count; |
| } |
| |
| void PostSwap() { |
| DCHECK(pending_swap_); |
| queue_.push_back(pending_swap_->type()); |
| pending_swap_->posted_ = true; |
| } |
| |
| void EndSwap() { |
| queue_.pop_front(); |
| } |
| |
| private: |
| friend class ::PendingSwap; |
| |
| PendingSwap* pending_swap_; |
| std::deque<SwapType> queue_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PostedSwapQueue); |
| }; |
| |
| } // namespace ui |
| |
| namespace { |
| |
| PendingSwap::PendingSwap(SwapType type, ui::PostedSwapQueue* posted_swaps) |
| : type_(type), posted_(false), posted_swaps_(posted_swaps) { |
| // Only one pending swap in flight. |
| DCHECK_EQ(static_cast<PendingSwap*>(NULL), posted_swaps_->pending_swap_); |
| posted_swaps_->pending_swap_ = this; |
| } |
| |
| PendingSwap::~PendingSwap() { |
| DCHECK_EQ(this, posted_swaps_->pending_swap_); |
| posted_swaps_->pending_swap_ = NULL; |
| } |
| |
| } // namespace |
| |
| namespace ui { |
| |
| Compositor::Compositor(CompositorDelegate* delegate, |
| gfx::AcceleratedWidget widget) |
| : delegate_(delegate), |
| root_layer_(NULL), |
| widget_(widget), |
| posted_swaps_(new PostedSwapQueue()), |
| device_scale_factor_(0.0f), |
| last_started_frame_(0), |
| last_ended_frame_(0), |
| next_draw_is_resize_(false), |
| disable_schedule_composite_(false), |
| compositor_lock_(NULL) { |
| DCHECK(g_compositor_initialized) |
| << "Compositor::Initialize must be called before creating a Compositor."; |
| |
| root_web_layer_ = cc::Layer::Create(); |
| root_web_layer_->SetAnchorPoint(gfx::PointF(0.f, 0.f)); |
| |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| |
| cc::LayerTreeSettings settings; |
| settings.refresh_rate = |
| ContextFactory::GetInstance()->DoesCreateTestContexts() |
| ? kTestRefreshRate |
| : kDefaultRefreshRate; |
| settings.partial_swap_enabled = |
| !command_line->HasSwitch(cc::switches::kUIDisablePartialSwap); |
| settings.per_tile_painting_enabled = |
| command_line->HasSwitch(cc::switches::kUIEnablePerTilePainting); |
| |
| // These flags should be mirrored by renderer versions in content/renderer/. |
| settings.initial_debug_state.show_debug_borders = |
| command_line->HasSwitch(cc::switches::kUIShowCompositedLayerBorders); |
| settings.initial_debug_state.show_fps_counter = |
| command_line->HasSwitch(cc::switches::kUIShowFPSCounter); |
| settings.initial_debug_state.show_paint_rects = |
| command_line->HasSwitch(switches::kUIShowPaintRects); |
| settings.initial_debug_state.show_property_changed_rects = |
| command_line->HasSwitch(cc::switches::kUIShowPropertyChangedRects); |
| settings.initial_debug_state.show_surface_damage_rects = |
| command_line->HasSwitch(cc::switches::kUIShowSurfaceDamageRects); |
| settings.initial_debug_state.show_screen_space_rects = |
| command_line->HasSwitch(cc::switches::kUIShowScreenSpaceRects); |
| settings.initial_debug_state.show_replica_screen_space_rects = |
| command_line->HasSwitch(cc::switches::kUIShowReplicaScreenSpaceRects); |
| settings.initial_debug_state.show_occluding_rects = |
| command_line->HasSwitch(cc::switches::kUIShowOccludingRects); |
| settings.initial_debug_state.show_non_occluding_rects = |
| command_line->HasSwitch(cc::switches::kUIShowNonOccludingRects); |
| |
| scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner = |
| g_compositor_thread ? g_compositor_thread->message_loop_proxy() : NULL; |
| |
| host_ = cc::LayerTreeHost::Create(this, settings, compositor_task_runner); |
| host_->SetRootLayer(root_web_layer_); |
| host_->SetLayerTreeHostClientReady(); |
| } |
| |
| Compositor::~Compositor() { |
| DCHECK(g_compositor_initialized); |
| |
| CancelCompositorLock(); |
| DCHECK(!compositor_lock_); |
| |
| // Don't call |CompositorDelegate::ScheduleDraw| from this point. |
| delegate_ = NULL; |
| if (root_layer_) |
| root_layer_->SetCompositor(NULL); |
| |
| // Stop all outstanding draws before telling the ContextFactory to tear |
| // down any contexts that the |host_| may rely upon. |
| host_.reset(); |
| |
| ContextFactory::GetInstance()->RemoveCompositor(this); |
| } |
| |
| // static |
| void Compositor::InitializeContextFactoryForTests(bool allow_test_contexts) { |
| DCHECK(!g_context_factory) << "ContextFactory already initialized."; |
| DCHECK(!g_implicit_factory) << |
| "ContextFactory for tests already initialized."; |
| |
| bool use_test_contexts = true; |
| |
| // Always use test contexts unless the disable command line flag is used. |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(switches::kDisableTestCompositor)) |
| use_test_contexts = false; |
| |
| #if defined(OS_CHROMEOS) |
| // If the test is running on the chromeos envrionment (such as |
| // device or vm bots), always use real contexts. |
| if (base::chromeos::IsRunningOnChromeOS()) |
| use_test_contexts = false; |
| #endif |
| |
| if (!allow_test_contexts) |
| use_test_contexts = false; |
| |
| if (use_test_contexts) { |
| g_implicit_factory = new ui::TestContextFactory; |
| } else { |
| DVLOG(1) << "Using DefaultContextFactory"; |
| scoped_ptr<ui::DefaultContextFactory> instance( |
| new ui::DefaultContextFactory()); |
| if (instance->Initialize()) |
| g_implicit_factory = instance.release(); |
| } |
| g_context_factory = g_implicit_factory; |
| } |
| |
| // static |
| void Compositor::Initialize() { |
| #if defined(OS_CHROMEOS) |
| bool use_thread = !CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kUIDisableThreadedCompositing); |
| #else |
| bool use_thread = |
| CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kUIEnableThreadedCompositing) && |
| !CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kUIDisableThreadedCompositing); |
| #endif |
| if (use_thread) { |
| g_compositor_thread = new base::Thread("Browser Compositor"); |
| #if defined(OS_POSIX) |
| // Workaround for crbug.com/293736 |
| // On Posix, MessagePumpDefault uses system time, so delayed tasks (for |
| // compositor scheduling) work incorrectly across system time changes (e.g. |
| // tlsdate). So instead, use an IO loop, which uses libevent, that uses |
| // monotonic time (immune to these problems). |
| base::Thread::Options options; |
| options.message_loop_type = base::MessageLoop::TYPE_IO; |
| g_compositor_thread->StartWithOptions(options); |
| #else |
| g_compositor_thread->Start(); |
| #endif |
| } |
| |
| DCHECK(!g_compositor_initialized) << "Compositor initialized twice."; |
| g_compositor_initialized = true; |
| } |
| |
| // static |
| bool Compositor::WasInitializedWithThread() { |
| return !!g_compositor_thread; |
| } |
| |
| // static |
| scoped_refptr<base::MessageLoopProxy> Compositor::GetCompositorMessageLoop() { |
| scoped_refptr<base::MessageLoopProxy> proxy; |
| if (g_compositor_thread) |
| proxy = g_compositor_thread->message_loop_proxy(); |
| return proxy; |
| } |
| |
| // static |
| void Compositor::Terminate() { |
| if (g_context_factory) { |
| if (g_implicit_factory) { |
| delete g_implicit_factory; |
| g_implicit_factory = NULL; |
| } |
| g_context_factory = NULL; |
| } |
| |
| if (g_compositor_thread) { |
| DCHECK(!g_context_factory) |
| << "The ContextFactory should not outlive the compositor thread."; |
| g_compositor_thread->Stop(); |
| delete g_compositor_thread; |
| g_compositor_thread = NULL; |
| } |
| |
| DCHECK(g_compositor_initialized) << "Compositor::Initialize() didn't happen."; |
| g_compositor_initialized = false; |
| } |
| |
| void Compositor::ScheduleDraw() { |
| if (g_compositor_thread) |
| host_->Composite(base::TimeTicks::Now()); |
| else if (delegate_) |
| delegate_->ScheduleDraw(); |
| } |
| |
| void Compositor::SetRootLayer(Layer* root_layer) { |
| if (root_layer_ == root_layer) |
| return; |
| if (root_layer_) |
| root_layer_->SetCompositor(NULL); |
| root_layer_ = root_layer; |
| if (root_layer_ && !root_layer_->GetCompositor()) |
| root_layer_->SetCompositor(this); |
| root_web_layer_->RemoveAllChildren(); |
| if (root_layer_) |
| root_web_layer_->AddChild(root_layer_->cc_layer()); |
| } |
| |
| void Compositor::SetHostHasTransparentBackground( |
| bool host_has_transparent_background) { |
| host_->set_has_transparent_background(host_has_transparent_background); |
| } |
| |
| void Compositor::Draw() { |
| DCHECK(!g_compositor_thread); |
| |
| if (!root_layer_) |
| return; |
| |
| last_started_frame_++; |
| PendingSwap pending_swap(DRAW_SWAP, posted_swaps_.get()); |
| if (!IsLocked()) { |
| // TODO(nduca): Temporary while compositor calls |
| // compositeImmediately() directly. |
| Layout(); |
| host_->Composite(base::TimeTicks::Now()); |
| |
| #if defined(OS_WIN) |
| // While we resize, we are usually a few frames behind. By blocking |
| // the UI thread here we minize the area that is mis-painted, specially |
| // in the non-client area. See RenderWidgetHostViewAura::SetBounds for |
| // more details and bug 177115. |
| if (next_draw_is_resize_ && (last_ended_frame_ > 1)) { |
| next_draw_is_resize_ = false; |
| host_->FinishAllRendering(); |
| } |
| #endif |
| |
| } |
| if (!pending_swap.posted()) |
| NotifyEnd(); |
| } |
| |
| void Compositor::ScheduleFullRedraw() { |
| host_->SetNeedsRedraw(); |
| } |
| |
| void Compositor::ScheduleRedrawRect(const gfx::Rect& damage_rect) { |
| host_->SetNeedsRedrawRect(damage_rect); |
| } |
| |
| void Compositor::SetLatencyInfo(const ui::LatencyInfo& latency_info) { |
| host_->SetLatencyInfo(latency_info); |
| } |
| |
| bool Compositor::ReadPixels(SkBitmap* bitmap, |
| const gfx::Rect& bounds_in_pixel) { |
| if (bounds_in_pixel.right() > size().width() || |
| bounds_in_pixel.bottom() > size().height()) |
| return false; |
| bitmap->setConfig(SkBitmap::kARGB_8888_Config, |
| bounds_in_pixel.width(), bounds_in_pixel.height()); |
| bitmap->allocPixels(); |
| SkAutoLockPixels lock_image(*bitmap); |
| unsigned char* pixels = static_cast<unsigned char*>(bitmap->getPixels()); |
| CancelCompositorLock(); |
| PendingSwap pending_swap(READPIXELS_SWAP, posted_swaps_.get()); |
| return host_->CompositeAndReadback(pixels, bounds_in_pixel); |
| } |
| |
| void Compositor::SetScaleAndSize(float scale, const gfx::Size& size_in_pixel) { |
| DCHECK_GT(scale, 0); |
| if (!size_in_pixel.IsEmpty()) { |
| size_ = size_in_pixel; |
| host_->SetViewportSize(size_in_pixel); |
| root_web_layer_->SetBounds(size_in_pixel); |
| |
| next_draw_is_resize_ = true; |
| } |
| if (device_scale_factor_ != scale) { |
| device_scale_factor_ = scale; |
| if (root_layer_) |
| root_layer_->OnDeviceScaleFactorChanged(scale); |
| } |
| } |
| |
| void Compositor::SetBackgroundColor(SkColor color) { |
| host_->set_background_color(color); |
| ScheduleDraw(); |
| } |
| |
| void Compositor::AddObserver(CompositorObserver* observer) { |
| observer_list_.AddObserver(observer); |
| } |
| |
| void Compositor::RemoveObserver(CompositorObserver* observer) { |
| observer_list_.RemoveObserver(observer); |
| } |
| |
| bool Compositor::HasObserver(CompositorObserver* observer) { |
| return observer_list_.HasObserver(observer); |
| } |
| |
| void Compositor::OnSwapBuffersPosted() { |
| DCHECK(!g_compositor_thread); |
| posted_swaps_->PostSwap(); |
| } |
| |
| void Compositor::OnSwapBuffersComplete() { |
| DCHECK(!g_compositor_thread); |
| DCHECK(posted_swaps_->AreSwapsPosted()); |
| DCHECK_GE(1, posted_swaps_->NumSwapsPosted(DRAW_SWAP)); |
| if (posted_swaps_->NextPostedSwap() == DRAW_SWAP) |
| NotifyEnd(); |
| posted_swaps_->EndSwap(); |
| } |
| |
| void Compositor::OnSwapBuffersAborted() { |
| if (!g_compositor_thread) { |
| DCHECK_GE(1, posted_swaps_->NumSwapsPosted(DRAW_SWAP)); |
| |
| // We've just lost the context, so unwind all posted_swaps. |
| while (posted_swaps_->AreSwapsPosted()) { |
| if (posted_swaps_->NextPostedSwap() == DRAW_SWAP) |
| NotifyEnd(); |
| posted_swaps_->EndSwap(); |
| } |
| } |
| |
| FOR_EACH_OBSERVER(CompositorObserver, |
| observer_list_, |
| OnCompositingAborted(this)); |
| } |
| |
| void Compositor::OnUpdateVSyncParameters(base::TimeTicks timebase, |
| base::TimeDelta interval) { |
| FOR_EACH_OBSERVER(CompositorObserver, |
| observer_list_, |
| OnUpdateVSyncParameters(this, timebase, interval)); |
| } |
| |
| void Compositor::Layout() { |
| // We're sending damage that will be addressed during this composite |
| // cycle, so we don't need to schedule another composite to address it. |
| disable_schedule_composite_ = true; |
| if (root_layer_) |
| root_layer_->SendDamagedRects(); |
| disable_schedule_composite_ = false; |
| } |
| |
| scoped_ptr<cc::OutputSurface> Compositor::CreateOutputSurface(bool fallback) { |
| return ContextFactory::GetInstance()->CreateOutputSurface(this); |
| } |
| |
| void Compositor::DidCommit() { |
| DCHECK(!IsLocked()); |
| FOR_EACH_OBSERVER(CompositorObserver, |
| observer_list_, |
| OnCompositingDidCommit(this)); |
| } |
| |
| void Compositor::DidCommitAndDrawFrame() { |
| base::TimeTicks start_time = base::TimeTicks::Now(); |
| FOR_EACH_OBSERVER(CompositorObserver, |
| observer_list_, |
| OnCompositingStarted(this, start_time)); |
| } |
| |
| void Compositor::DidCompleteSwapBuffers() { |
| DCHECK(g_compositor_thread); |
| NotifyEnd(); |
| } |
| |
| void Compositor::ScheduleComposite() { |
| if (!disable_schedule_composite_) |
| ScheduleDraw(); |
| } |
| |
| scoped_refptr<cc::ContextProvider> |
| Compositor::OffscreenContextProviderForMainThread() { |
| return ContextFactory::GetInstance()->OffscreenContextProviderForMainThread(); |
| } |
| |
| scoped_refptr<cc::ContextProvider> |
| Compositor::OffscreenContextProviderForCompositorThread() { |
| return ContextFactory::GetInstance()-> |
| OffscreenContextProviderForCompositorThread(); |
| } |
| |
| const cc::LayerTreeDebugState& Compositor::GetLayerTreeDebugState() const { |
| return host_->debug_state(); |
| } |
| |
| void Compositor::SetLayerTreeDebugState( |
| const cc::LayerTreeDebugState& debug_state) { |
| host_->SetDebugState(debug_state); |
| } |
| |
| scoped_refptr<CompositorLock> Compositor::GetCompositorLock() { |
| if (!compositor_lock_) { |
| compositor_lock_ = new CompositorLock(this); |
| if (g_compositor_thread) |
| host_->SetDeferCommits(true); |
| FOR_EACH_OBSERVER(CompositorObserver, |
| observer_list_, |
| OnCompositingLockStateChanged(this)); |
| } |
| return compositor_lock_; |
| } |
| |
| void Compositor::UnlockCompositor() { |
| DCHECK(compositor_lock_); |
| compositor_lock_ = NULL; |
| if (g_compositor_thread) |
| host_->SetDeferCommits(false); |
| FOR_EACH_OBSERVER(CompositorObserver, |
| observer_list_, |
| OnCompositingLockStateChanged(this)); |
| } |
| |
| void Compositor::CancelCompositorLock() { |
| if (compositor_lock_) |
| compositor_lock_->CancelLock(); |
| } |
| |
| void Compositor::NotifyEnd() { |
| last_ended_frame_++; |
| FOR_EACH_OBSERVER(CompositorObserver, |
| observer_list_, |
| OnCompositingEnded(this)); |
| } |
| |
| } // namespace ui |