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.
5 #include "ui/compositor/compositor.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/message_loop.h"
13 #include "base/string_util.h"
14 #include "base/threading/thread.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "cc/font_atlas.h"
17 #include "cc/input_handler.h"
19 #include "cc/layer_tree_host.h"
20 #include "cc/output_surface.h"
21 #include "cc/thread_impl.h"
22 #include "third_party/skia/include/core/SkBitmap.h"
23 #include "ui/compositor/compositor_observer.h"
24 #include "ui/compositor/compositor_switches.h"
25 #include "ui/compositor/dip_util.h"
26 #include "ui/compositor/layer.h"
27 #include "ui/compositor/test_web_graphics_context_3d.h"
28 #include "ui/gl/gl_context.h"
29 #include "ui/gl/gl_implementation.h"
30 #include "ui/gl/gl_surface.h"
31 #include "ui/gl/gl_switches.h"
32 #include "webkit/gpu/webgraphicscontext3d_in_process_impl.h"
34 #if defined(OS_CHROMEOS)
35 #include "base/chromeos/chromeos_version.h"
40 const double kDefaultRefreshRate
= 60.0;
41 const double kTestRefreshRate
= 100.0;
48 base::Thread
* g_compositor_thread
= NULL
;
50 bool g_test_compositor_enabled
= false;
52 ui::ContextFactory
* g_context_factory
= NULL
;
54 const int kCompositorLockTimeoutMs
= 67;
56 // Adapts a pure WebGraphicsContext3D into a cc::OutputSurface.
57 class WebGraphicsContextToOutputSurfaceAdapter
58 : public cc::OutputSurface
{
60 explicit WebGraphicsContextToOutputSurfaceAdapter(
61 WebKit::WebGraphicsContext3D
* context
)
62 : context3D_(context
),
66 virtual bool BindToClient(
67 cc::OutputSurfaceClient
* client
) OVERRIDE
{
69 if (!context3D_
->makeContextCurrent())
75 virtual const struct Capabilities
& Capabilities() const OVERRIDE
{
79 virtual WebKit::WebGraphicsContext3D
* Context3D() const OVERRIDE
{
80 return context3D_
.get();
83 virtual cc::SoftwareOutputDevice
* SoftwareDevice() const OVERRIDE
{
87 virtual void SendFrameToParentCompositor(cc::CompositorFrame
*) OVERRIDE
{
91 scoped_ptr
<WebKit::WebGraphicsContext3D
> context3D_
;
92 struct Capabilities capabilities_
;
93 cc::OutputSurfaceClient
* client_
;
98 PendingSwap(SwapType type
, ui::PostedSwapQueue
* posted_swaps
);
101 SwapType
type() const { return type_
; }
102 bool posted() const { return posted_
; }
105 friend class ui::PostedSwapQueue
;
109 ui::PostedSwapQueue
* posted_swaps_
;
111 DISALLOW_COPY_AND_ASSIGN(PendingSwap
);
119 ContextFactory
* ContextFactory::GetInstance() {
120 // We leak the shared resources so that we don't race with
121 // the tear down of the gl_bindings.
122 if (!g_context_factory
) {
123 DVLOG(1) << "Using DefaultSharedResource";
124 scoped_ptr
<DefaultContextFactory
> instance(
125 new DefaultContextFactory());
126 if (instance
->Initialize())
127 g_context_factory
= instance
.release();
129 return g_context_factory
;
133 void ContextFactory::SetInstance(ContextFactory
* instance
) {
134 g_context_factory
= instance
;
137 DefaultContextFactory::DefaultContextFactory() {
140 DefaultContextFactory::~DefaultContextFactory() {
143 bool DefaultContextFactory::Initialize() {
144 // The following line of code exists soley to disable IO restrictions
145 // on this thread long enough to perform the GL bindings.
146 // TODO(wjmaclean) Remove this when GL initialisation cleaned up.
147 base::ThreadRestrictions::ScopedAllowIO allow_io
;
148 if (!gfx::GLSurface::InitializeOneOff() ||
149 gfx::GetGLImplementation() == gfx::kGLImplementationNone
) {
150 LOG(ERROR
) << "Could not load the GL bindings";
156 cc::OutputSurface
* DefaultContextFactory::CreateOutputSurface(
157 Compositor
* compositor
) {
158 return new WebGraphicsContextToOutputSurfaceAdapter(
159 CreateContextCommon(compositor
, false));
162 WebKit::WebGraphicsContext3D
* DefaultContextFactory::CreateOffscreenContext() {
163 return CreateContextCommon(NULL
, true);
166 void DefaultContextFactory::RemoveCompositor(Compositor
* compositor
) {
169 WebKit::WebGraphicsContext3D
* DefaultContextFactory::CreateContextCommon(
170 Compositor
* compositor
,
172 DCHECK(offscreen
|| compositor
);
173 WebKit::WebGraphicsContext3D::Attributes attrs
;
175 attrs
.stencil
= false;
176 attrs
.antialias
= false;
177 attrs
.shareResources
= true;
178 WebKit::WebGraphicsContext3D
* context
=
180 webkit::gpu::WebGraphicsContext3DInProcessImpl::CreateForWebView(
182 webkit::gpu::WebGraphicsContext3DInProcessImpl::CreateForWindow(
183 attrs
, compositor
->widget(), share_group_
.get());
187 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
189 context
->makeContextCurrent();
190 gfx::GLContext
* gl_context
= gfx::GLContext::GetCurrent();
191 bool vsync
= !command_line
->HasSwitch(switches::kDisableGpuVsync
);
192 gl_context
->SetSwapInterval(vsync
? 1 : 0);
193 gl_context
->ReleaseCurrent(NULL
);
198 Texture::Texture(bool flipped
, const gfx::Size
& size
, float device_scale_factor
)
201 device_scale_factor_(device_scale_factor
) {
204 Texture::~Texture() {
207 std::string
Texture::Produce() {
208 return EmptyString();
211 CompositorLock::CompositorLock(Compositor
* compositor
)
212 : compositor_(compositor
) {
213 MessageLoop::current()->PostDelayedTask(
215 base::Bind(&CompositorLock::CancelLock
, AsWeakPtr()),
216 base::TimeDelta::FromMilliseconds(kCompositorLockTimeoutMs
));
219 CompositorLock::~CompositorLock() {
223 void CompositorLock::CancelLock() {
226 compositor_
->UnlockCompositor();
230 class PostedSwapQueue
{
232 PostedSwapQueue() : pending_swap_(NULL
) {
236 DCHECK(!pending_swap_
);
239 SwapType
NextPostedSwap() const {
240 return queue_
.front();
243 bool AreSwapsPosted() const {
244 return !queue_
.empty();
247 int NumSwapsPosted(SwapType type
) const {
249 for (std::deque
<SwapType
>::const_iterator it
= queue_
.begin();
250 it
!= queue_
.end(); ++it
) {
258 DCHECK(pending_swap_
);
259 queue_
.push_back(pending_swap_
->type());
260 pending_swap_
->posted_
= true;
268 friend class ::PendingSwap
;
270 PendingSwap
* pending_swap_
;
271 std::deque
<SwapType
> queue_
;
273 DISALLOW_COPY_AND_ASSIGN(PostedSwapQueue
);
280 PendingSwap::PendingSwap(SwapType type
, ui::PostedSwapQueue
* posted_swaps
)
281 : type_(type
), posted_(false), posted_swaps_(posted_swaps
) {
282 // Only one pending swap in flight.
283 DCHECK_EQ(static_cast<PendingSwap
*>(NULL
), posted_swaps_
->pending_swap_
);
284 posted_swaps_
->pending_swap_
= this;
287 PendingSwap::~PendingSwap() {
288 DCHECK_EQ(this, posted_swaps_
->pending_swap_
);
289 posted_swaps_
->pending_swap_
= NULL
;
296 Compositor::Compositor(CompositorDelegate
* delegate
,
297 gfx::AcceleratedWidget widget
)
298 : delegate_(delegate
),
301 posted_swaps_(new PostedSwapQueue()),
302 device_scale_factor_(0.0f
),
303 last_started_frame_(0),
304 last_ended_frame_(0),
305 disable_schedule_composite_(false),
306 compositor_lock_(NULL
) {
307 root_web_layer_
= cc::Layer::create();
308 root_web_layer_
->setAnchorPoint(gfx::PointF(0.f
, 0.f
));
310 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
311 cc::LayerTreeSettings settings
;
312 settings
.initialDebugState
.showFPSCounter
=
313 command_line
->HasSwitch(switches::kUIShowFPSCounter
);
314 settings
.initialDebugState
.showPlatformLayerTree
=
315 command_line
->HasSwitch(switches::kUIShowLayerTree
);
316 settings
.refreshRate
=
317 g_test_compositor_enabled
? kTestRefreshRate
: kDefaultRefreshRate
;
318 settings
.initialDebugState
.showDebugBorders
=
319 command_line
->HasSwitch(switches::kUIShowLayerBorders
);
320 settings
.partialSwapEnabled
=
321 command_line
->HasSwitch(switches::kUIEnablePartialSwap
);
322 settings
.perTilePaintingEnabled
=
323 command_line
->HasSwitch(switches::kUIEnablePerTilePainting
);
325 scoped_ptr
<cc::Thread
> thread
;
326 if (g_compositor_thread
) {
327 thread
= cc::ThreadImpl::createForDifferentThread(
328 g_compositor_thread
->message_loop_proxy());
331 host_
= cc::LayerTreeHost::create(this, settings
, thread
.Pass());
332 host_
->setRootLayer(root_web_layer_
);
333 host_
->setSurfaceReady();
336 Compositor::~Compositor() {
337 CancelCompositorLock();
338 DCHECK(!compositor_lock_
);
340 // Don't call |CompositorDelegate::ScheduleDraw| from this point.
343 root_layer_
->SetCompositor(NULL
);
345 // Stop all outstanding draws before telling the ContextFactory to tear
346 // down any contexts that the |host_| may rely upon.
349 if (!g_test_compositor_enabled
)
350 ContextFactory::GetInstance()->RemoveCompositor(this);
353 void Compositor::Initialize(bool use_thread
) {
355 g_compositor_thread
= new base::Thread("Browser Compositor");
356 g_compositor_thread
->Start();
360 void Compositor::Terminate() {
361 if (g_compositor_thread
) {
362 g_compositor_thread
->Stop();
363 delete g_compositor_thread
;
364 g_compositor_thread
= NULL
;
368 void Compositor::ScheduleDraw() {
369 if (g_compositor_thread
)
372 delegate_
->ScheduleDraw();
375 void Compositor::SetRootLayer(Layer
* root_layer
) {
376 if (root_layer_
== 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();
385 root_web_layer_
->addChild(root_layer_
->cc_layer());
388 void Compositor::SetHostHasTransparentBackground(
389 bool host_has_transparent_background
) {
390 host_
->setHasTransparentBackground(host_has_transparent_background
);
393 void Compositor::Draw(bool force_clear
) {
394 DCHECK(!g_compositor_thread
);
399 last_started_frame_
++;
400 PendingSwap
pending_swap(DRAW_SWAP
, posted_swaps_
.get());
402 // TODO(nduca): Temporary while compositor calls
403 // compositeImmediately() directly.
407 if (!pending_swap
.posted())
411 void Compositor::ScheduleFullDraw() {
412 host_
->setNeedsRedraw();
415 bool Compositor::ReadPixels(SkBitmap
* bitmap
,
416 const gfx::Rect
& bounds_in_pixel
) {
417 if (bounds_in_pixel
.right() > size().width() ||
418 bounds_in_pixel
.bottom() > size().height())
420 bitmap
->setConfig(SkBitmap::kARGB_8888_Config
,
421 bounds_in_pixel
.width(), bounds_in_pixel
.height());
422 bitmap
->allocPixels();
423 SkAutoLockPixels
lock_image(*bitmap
);
424 unsigned char* pixels
= static_cast<unsigned char*>(bitmap
->getPixels());
425 CancelCompositorLock();
426 PendingSwap
pending_swap(READPIXELS_SWAP
, posted_swaps_
.get());
427 return host_
->compositeAndReadback(pixels
, bounds_in_pixel
);
430 void Compositor::SetScaleAndSize(float scale
, const gfx::Size
& size_in_pixel
) {
432 if (!size_in_pixel
.IsEmpty()) {
433 size_
= size_in_pixel
;
434 host_
->setViewportSize(size_in_pixel
, size_in_pixel
);
435 root_web_layer_
->setBounds(size_in_pixel
);
437 if (device_scale_factor_
!= scale
) {
438 device_scale_factor_
= scale
;
440 root_layer_
->OnDeviceScaleFactorChanged(scale
);
444 void Compositor::AddObserver(CompositorObserver
* observer
) {
445 observer_list_
.AddObserver(observer
);
448 void Compositor::RemoveObserver(CompositorObserver
* observer
) {
449 observer_list_
.RemoveObserver(observer
);
452 bool Compositor::HasObserver(CompositorObserver
* observer
) {
453 return observer_list_
.HasObserver(observer
);
456 void Compositor::OnSwapBuffersPosted() {
457 DCHECK(!g_compositor_thread
);
458 posted_swaps_
->PostSwap();
461 void Compositor::OnSwapBuffersComplete() {
462 DCHECK(!g_compositor_thread
);
463 DCHECK(posted_swaps_
->AreSwapsPosted());
464 DCHECK_GE(1, posted_swaps_
->NumSwapsPosted(DRAW_SWAP
));
465 if (posted_swaps_
->NextPostedSwap() == DRAW_SWAP
)
467 posted_swaps_
->EndSwap();
470 void Compositor::OnSwapBuffersAborted() {
471 DCHECK(!g_compositor_thread
);
472 DCHECK_GE(1, posted_swaps_
->NumSwapsPosted(DRAW_SWAP
));
474 // We've just lost the context, so unwind all posted_swaps.
475 while (posted_swaps_
->AreSwapsPosted()) {
476 if (posted_swaps_
->NextPostedSwap() == DRAW_SWAP
)
478 posted_swaps_
->EndSwap();
481 FOR_EACH_OBSERVER(CompositorObserver
,
483 OnCompositingAborted(this));
486 void Compositor::willBeginFrame() {
489 void Compositor::didBeginFrame() {
492 void Compositor::animate(double frameBeginTime
) {
495 void Compositor::layout() {
496 // We're sending damage that will be addressed during this composite
497 // cycle, so we don't need to schedule another composite to address it.
498 disable_schedule_composite_
= true;
500 root_layer_
->SendDamagedRects();
501 disable_schedule_composite_
= false;
504 void Compositor::applyScrollAndScale(gfx::Vector2d scrollDelta
,
508 scoped_ptr
<cc::OutputSurface
> Compositor::createOutputSurface() {
509 if (g_test_compositor_enabled
) {
510 ui::TestWebGraphicsContext3D
* test_context
=
511 new ui::TestWebGraphicsContext3D();
512 test_context
->Initialize();
513 return scoped_ptr
<cc::OutputSurface
>(
514 new WebGraphicsContextToOutputSurfaceAdapter(test_context
));
516 return scoped_ptr
<cc::OutputSurface
>(
517 ContextFactory::GetInstance()->CreateOutputSurface(this));
521 void Compositor::didRecreateOutputSurface(bool success
) {
524 scoped_ptr
<cc::InputHandler
> Compositor::createInputHandler() {
525 return scoped_ptr
<cc::InputHandler
>();
528 void Compositor::willCommit() {
531 void Compositor::didCommit() {
533 FOR_EACH_OBSERVER(CompositorObserver
,
535 OnCompositingDidCommit(this));
538 void Compositor::didCommitAndDrawFrame() {
539 FOR_EACH_OBSERVER(CompositorObserver
,
541 OnCompositingStarted(this));
544 void Compositor::didCompleteSwapBuffers() {
545 DCHECK(g_compositor_thread
);
549 void Compositor::scheduleComposite() {
550 if (!disable_schedule_composite_
)
554 scoped_ptr
<cc::FontAtlas
> Compositor::createFontAtlas() {
555 return scoped_ptr
<cc::FontAtlas
>();
558 scoped_refptr
<CompositorLock
> Compositor::GetCompositorLock() {
559 if (!compositor_lock_
) {
560 compositor_lock_
= new CompositorLock(this);
561 if (g_compositor_thread
)
562 host_
->setDeferCommits(true);
563 FOR_EACH_OBSERVER(CompositorObserver
,
565 OnCompositingLockStateChanged(this));
567 return compositor_lock_
;
570 void Compositor::UnlockCompositor() {
571 DCHECK(compositor_lock_
);
572 compositor_lock_
= NULL
;
573 if (g_compositor_thread
)
574 host_
->setDeferCommits(false);
575 FOR_EACH_OBSERVER(CompositorObserver
,
577 OnCompositingLockStateChanged(this));
580 void Compositor::CancelCompositorLock() {
581 if (compositor_lock_
)
582 compositor_lock_
->CancelLock();
585 void Compositor::NotifyEnd() {
587 FOR_EACH_OBSERVER(CompositorObserver
,
589 OnCompositingEnded(this));
592 COMPOSITOR_EXPORT
void SetupTestCompositor() {
593 if (!CommandLine::ForCurrentProcess()->HasSwitch(
594 switches::kDisableTestCompositor
)) {
595 g_test_compositor_enabled
= true;
597 #if defined(OS_CHROMEOS)
598 // If the test is running on the chromeos envrionment (such as
599 // device or vm bots), use the real compositor.
600 if (base::chromeos::IsRunningOnChromeOS())
601 g_test_compositor_enabled
= false;
605 COMPOSITOR_EXPORT
void DisableTestCompositor() {
606 g_test_compositor_enabled
= false;
609 COMPOSITOR_EXPORT
bool IsTestCompositorEnabled() {
610 return g_test_compositor_enabled
;