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/debug/trace_event.h"
13 #include "base/memory/singleton.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/metrics/histogram.h"
16 #include "base/run_loop.h"
17 #include "base/strings/string_util.h"
18 #include "base/sys_info.h"
19 #include "base/threading/thread.h"
20 #include "base/threading/thread_restrictions.h"
21 #include "cc/base/latency_info_swap_promise.h"
22 #include "cc/base/switches.h"
23 #include "cc/input/input_handler.h"
24 #include "cc/layers/layer.h"
25 #include "cc/output/context_provider.h"
26 #include "cc/trees/layer_tree_host.h"
27 #include "third_party/skia/include/core/SkBitmap.h"
28 #include "ui/compositor/compositor_observer.h"
29 #include "ui/compositor/compositor_switches.h"
30 #include "ui/compositor/compositor_vsync_manager.h"
31 #include "ui/compositor/dip_util.h"
32 #include "ui/compositor/layer.h"
33 #include "ui/gfx/frame_time.h"
34 #include "ui/gl/gl_context.h"
35 #include "ui/gl/gl_switches.h"
39 const double kDefaultRefreshRate
= 60.0;
40 const double kTestRefreshRate
= 200.0;
46 bool g_compositor_initialized
= false;
47 base::Thread
* g_compositor_thread
= NULL
;
49 ui::ContextFactory
* g_context_factory
= NULL
;
51 const int kCompositorLockTimeoutMs
= 67;
55 PendingSwap(SwapType type
, ui::PostedSwapQueue
* posted_swaps
);
58 SwapType
type() const { return type_
; }
59 bool posted() const { return posted_
; }
62 friend class ui::PostedSwapQueue
;
66 ui::PostedSwapQueue
* posted_swaps_
;
68 DISALLOW_COPY_AND_ASSIGN(PendingSwap
);
76 ContextFactory
* ContextFactory::GetInstance() {
77 DCHECK(g_context_factory
);
78 return g_context_factory
;
82 void ContextFactory::SetInstance(ContextFactory
* instance
) {
83 g_context_factory
= instance
;
86 Texture::Texture(bool flipped
, const gfx::Size
& size
, float device_scale_factor
)
89 device_scale_factor_(device_scale_factor
) {
95 std::string
Texture::Produce() {
99 CompositorLock::CompositorLock(Compositor
* compositor
)
100 : compositor_(compositor
) {
101 base::MessageLoop::current()->PostDelayedTask(
103 base::Bind(&CompositorLock::CancelLock
, AsWeakPtr()),
104 base::TimeDelta::FromMilliseconds(kCompositorLockTimeoutMs
));
107 CompositorLock::~CompositorLock() {
111 void CompositorLock::CancelLock() {
114 compositor_
->UnlockCompositor();
118 class PostedSwapQueue
{
120 PostedSwapQueue() : pending_swap_(NULL
) {
124 DCHECK(!pending_swap_
);
127 SwapType
NextPostedSwap() const {
128 return queue_
.front();
131 bool AreSwapsPosted() const {
132 return !queue_
.empty();
135 int NumSwapsPosted(SwapType type
) const {
137 for (std::deque
<SwapType
>::const_iterator it
= queue_
.begin();
138 it
!= queue_
.end(); ++it
) {
146 DCHECK(pending_swap_
);
147 queue_
.push_back(pending_swap_
->type());
148 pending_swap_
->posted_
= true;
156 friend class ::PendingSwap
;
158 PendingSwap
* pending_swap_
;
159 std::deque
<SwapType
> queue_
;
161 DISALLOW_COPY_AND_ASSIGN(PostedSwapQueue
);
168 PendingSwap::PendingSwap(SwapType type
, ui::PostedSwapQueue
* posted_swaps
)
169 : type_(type
), posted_(false), posted_swaps_(posted_swaps
) {
170 // Only one pending swap in flight.
171 DCHECK_EQ(static_cast<PendingSwap
*>(NULL
), posted_swaps_
->pending_swap_
);
172 posted_swaps_
->pending_swap_
= this;
175 PendingSwap::~PendingSwap() {
176 DCHECK_EQ(this, posted_swaps_
->pending_swap_
);
177 posted_swaps_
->pending_swap_
= NULL
;
184 Compositor::Compositor(gfx::AcceleratedWidget widget
)
187 vsync_manager_(new CompositorVSyncManager()),
188 posted_swaps_(new PostedSwapQueue()),
189 device_scale_factor_(0.0f
),
190 last_started_frame_(0),
191 last_ended_frame_(0),
192 next_draw_is_resize_(false),
193 disable_schedule_composite_(false),
194 compositor_lock_(NULL
),
195 defer_draw_scheduling_(false),
196 waiting_on_compositing_end_(false),
197 draw_on_compositing_end_(false),
198 schedule_draw_factory_(this) {
199 DCHECK(g_compositor_initialized
)
200 << "Compositor::Initialize must be called before creating a Compositor.";
202 root_web_layer_
= cc::Layer::Create();
203 root_web_layer_
->SetAnchorPoint(gfx::PointF(0.f
, 0.f
));
205 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
207 cc::LayerTreeSettings settings
;
208 settings
.refresh_rate
=
209 ContextFactory::GetInstance()->DoesCreateTestContexts()
211 : kDefaultRefreshRate
;
212 settings
.deadline_scheduling_enabled
=
213 switches::IsUIDeadlineSchedulingEnabled();
214 settings
.partial_swap_enabled
=
215 !command_line
->HasSwitch(cc::switches::kUIDisablePartialSwap
);
216 settings
.per_tile_painting_enabled
=
217 command_line
->HasSwitch(cc::switches::kUIEnablePerTilePainting
);
219 // These flags should be mirrored by renderer versions in content/renderer/.
220 settings
.initial_debug_state
.show_debug_borders
=
221 command_line
->HasSwitch(cc::switches::kUIShowCompositedLayerBorders
);
222 settings
.initial_debug_state
.show_fps_counter
=
223 command_line
->HasSwitch(cc::switches::kUIShowFPSCounter
);
224 settings
.initial_debug_state
.show_layer_animation_bounds_rects
=
225 command_line
->HasSwitch(cc::switches::kUIShowLayerAnimationBounds
);
226 settings
.initial_debug_state
.show_paint_rects
=
227 command_line
->HasSwitch(switches::kUIShowPaintRects
);
228 settings
.initial_debug_state
.show_property_changed_rects
=
229 command_line
->HasSwitch(cc::switches::kUIShowPropertyChangedRects
);
230 settings
.initial_debug_state
.show_surface_damage_rects
=
231 command_line
->HasSwitch(cc::switches::kUIShowSurfaceDamageRects
);
232 settings
.initial_debug_state
.show_screen_space_rects
=
233 command_line
->HasSwitch(cc::switches::kUIShowScreenSpaceRects
);
234 settings
.initial_debug_state
.show_replica_screen_space_rects
=
235 command_line
->HasSwitch(cc::switches::kUIShowReplicaScreenSpaceRects
);
236 settings
.initial_debug_state
.show_occluding_rects
=
237 command_line
->HasSwitch(cc::switches::kUIShowOccludingRects
);
238 settings
.initial_debug_state
.show_non_occluding_rects
=
239 command_line
->HasSwitch(cc::switches::kUIShowNonOccludingRects
);
241 settings
.initial_debug_state
.SetRecordRenderingStats(
242 command_line
->HasSwitch(cc::switches::kEnableGpuBenchmarking
));
244 base::TimeTicks before_create
= base::TimeTicks::Now();
245 if (!!g_compositor_thread
) {
246 host_
= cc::LayerTreeHost::CreateThreaded(
247 this, NULL
, settings
, g_compositor_thread
->message_loop_proxy());
249 host_
= cc::LayerTreeHost::CreateSingleThreaded(this, this, NULL
, settings
);
251 UMA_HISTOGRAM_TIMES("GPU.CreateBrowserCompositor",
252 base::TimeTicks::Now() - before_create
);
253 host_
->SetRootLayer(root_web_layer_
);
254 host_
->SetLayerTreeHostClientReady();
257 Compositor::~Compositor() {
258 TRACE_EVENT0("shutdown", "Compositor::destructor");
260 DCHECK(g_compositor_initialized
);
262 CancelCompositorLock();
263 DCHECK(!compositor_lock_
);
266 root_layer_
->SetCompositor(NULL
);
268 // Stop all outstanding draws before telling the ContextFactory to tear
269 // down any contexts that the |host_| may rely upon.
272 ContextFactory::GetInstance()->RemoveCompositor(this);
276 void Compositor::Initialize() {
277 #if defined(OS_CHROMEOS)
278 bool use_thread
= !CommandLine::ForCurrentProcess()->HasSwitch(
279 switches::kUIDisableThreadedCompositing
);
282 CommandLine::ForCurrentProcess()->HasSwitch(
283 switches::kUIEnableThreadedCompositing
) &&
284 !CommandLine::ForCurrentProcess()->HasSwitch(
285 switches::kUIDisableThreadedCompositing
);
288 g_compositor_thread
= new base::Thread("Browser Compositor");
289 g_compositor_thread
->Start();
292 DCHECK(!g_compositor_initialized
) << "Compositor initialized twice.";
293 g_compositor_initialized
= true;
297 bool Compositor::WasInitializedWithThread() {
298 DCHECK(g_compositor_initialized
);
299 return !!g_compositor_thread
;
303 scoped_refptr
<base::MessageLoopProxy
> Compositor::GetCompositorMessageLoop() {
304 scoped_refptr
<base::MessageLoopProxy
> proxy
;
305 if (g_compositor_thread
)
306 proxy
= g_compositor_thread
->message_loop_proxy();
311 void Compositor::Terminate() {
312 if (g_compositor_thread
) {
313 g_compositor_thread
->Stop();
314 delete g_compositor_thread
;
315 g_compositor_thread
= NULL
;
318 DCHECK(g_compositor_initialized
) << "Compositor::Initialize() didn't happen.";
319 g_compositor_initialized
= false;
322 void Compositor::ScheduleDraw() {
323 if (g_compositor_thread
) {
324 host_
->Composite(gfx::FrameTime::Now());
325 } else if (!defer_draw_scheduling_
) {
326 defer_draw_scheduling_
= true;
327 base::MessageLoop::current()->PostTask(
329 base::Bind(&Compositor::Draw
, schedule_draw_factory_
.GetWeakPtr()));
333 void Compositor::SetRootLayer(Layer
* root_layer
) {
334 if (root_layer_
== root_layer
)
337 root_layer_
->SetCompositor(NULL
);
338 root_layer_
= root_layer
;
339 if (root_layer_
&& !root_layer_
->GetCompositor())
340 root_layer_
->SetCompositor(this);
341 root_web_layer_
->RemoveAllChildren();
343 root_web_layer_
->AddChild(root_layer_
->cc_layer());
346 void Compositor::SetHostHasTransparentBackground(
347 bool host_has_transparent_background
) {
348 host_
->set_has_transparent_background(host_has_transparent_background
);
351 void Compositor::Draw() {
352 DCHECK(!g_compositor_thread
);
354 defer_draw_scheduling_
= false;
355 if (waiting_on_compositing_end_
) {
356 draw_on_compositing_end_
= true;
359 waiting_on_compositing_end_
= true;
361 TRACE_EVENT_ASYNC_BEGIN0("ui", "Compositor::Draw", last_started_frame_
+ 1);
366 last_started_frame_
++;
367 PendingSwap
pending_swap(DRAW_SWAP
, posted_swaps_
.get());
369 // TODO(nduca): Temporary while compositor calls
370 // compositeImmediately() directly.
372 host_
->Composite(gfx::FrameTime::Now());
375 // While we resize, we are usually a few frames behind. By blocking
376 // the UI thread here we minize the area that is mis-painted, specially
377 // in the non-client area. See RenderWidgetHostViewAura::SetBounds for
378 // more details and bug 177115.
379 if (next_draw_is_resize_
&& (last_ended_frame_
> 1)) {
380 next_draw_is_resize_
= false;
381 host_
->FinishAllRendering();
386 if (!pending_swap
.posted())
390 void Compositor::ScheduleFullRedraw() {
391 host_
->SetNeedsRedraw();
394 void Compositor::ScheduleRedrawRect(const gfx::Rect
& damage_rect
) {
395 host_
->SetNeedsRedrawRect(damage_rect
);
398 void Compositor::SetLatencyInfo(const ui::LatencyInfo
& latency_info
) {
399 scoped_ptr
<cc::SwapPromise
> swap_promise(
400 new cc::LatencyInfoSwapPromise(latency_info
));
401 host_
->QueueSwapPromise(swap_promise
.Pass());
404 void Compositor::SetScaleAndSize(float scale
, const gfx::Size
& size_in_pixel
) {
406 if (!size_in_pixel
.IsEmpty()) {
407 size_
= size_in_pixel
;
408 host_
->SetViewportSize(size_in_pixel
);
409 root_web_layer_
->SetBounds(size_in_pixel
);
411 next_draw_is_resize_
= true;
413 if (device_scale_factor_
!= scale
) {
414 device_scale_factor_
= scale
;
416 root_layer_
->OnDeviceScaleFactorChanged(scale
);
420 void Compositor::SetBackgroundColor(SkColor color
) {
421 host_
->set_background_color(color
);
425 scoped_refptr
<CompositorVSyncManager
> Compositor::vsync_manager() const {
426 return vsync_manager_
;
429 void Compositor::AddObserver(CompositorObserver
* observer
) {
430 observer_list_
.AddObserver(observer
);
433 void Compositor::RemoveObserver(CompositorObserver
* observer
) {
434 observer_list_
.RemoveObserver(observer
);
437 bool Compositor::HasObserver(CompositorObserver
* observer
) {
438 return observer_list_
.HasObserver(observer
);
441 void Compositor::Layout() {
442 // We're sending damage that will be addressed during this composite
443 // cycle, so we don't need to schedule another composite to address it.
444 disable_schedule_composite_
= true;
446 root_layer_
->SendDamagedRects();
447 disable_schedule_composite_
= false;
450 scoped_ptr
<cc::OutputSurface
> Compositor::CreateOutputSurface(bool fallback
) {
451 return ContextFactory::GetInstance()->CreateOutputSurface(this, fallback
);
454 void Compositor::DidCommit() {
456 FOR_EACH_OBSERVER(CompositorObserver
,
458 OnCompositingDidCommit(this));
461 void Compositor::DidCommitAndDrawFrame() {
462 base::TimeTicks start_time
= gfx::FrameTime::Now();
463 FOR_EACH_OBSERVER(CompositorObserver
,
465 OnCompositingStarted(this, start_time
));
468 void Compositor::DidCompleteSwapBuffers() {
469 if (g_compositor_thread
) {
472 DCHECK(posted_swaps_
->AreSwapsPosted());
473 DCHECK_GE(1, posted_swaps_
->NumSwapsPosted(DRAW_SWAP
));
474 if (posted_swaps_
->NextPostedSwap() == DRAW_SWAP
)
476 posted_swaps_
->EndSwap();
480 scoped_refptr
<cc::ContextProvider
> Compositor::OffscreenContextProvider() {
481 return ContextFactory::GetInstance()->OffscreenCompositorContextProvider();
484 void Compositor::ScheduleComposite() {
485 if (!disable_schedule_composite_
)
489 void Compositor::ScheduleAnimation() {
493 void Compositor::DidPostSwapBuffers() {
494 DCHECK(!g_compositor_thread
);
495 posted_swaps_
->PostSwap();
498 void Compositor::DidAbortSwapBuffers() {
499 if (!g_compositor_thread
) {
500 DCHECK_GE(1, posted_swaps_
->NumSwapsPosted(DRAW_SWAP
));
502 // We've just lost the context, so unwind all posted_swaps.
503 while (posted_swaps_
->AreSwapsPosted()) {
504 if (posted_swaps_
->NextPostedSwap() == DRAW_SWAP
)
506 posted_swaps_
->EndSwap();
510 FOR_EACH_OBSERVER(CompositorObserver
,
512 OnCompositingAborted(this));
515 const cc::LayerTreeDebugState
& Compositor::GetLayerTreeDebugState() const {
516 return host_
->debug_state();
519 void Compositor::SetLayerTreeDebugState(
520 const cc::LayerTreeDebugState
& debug_state
) {
521 host_
->SetDebugState(debug_state
);
524 scoped_refptr
<CompositorLock
> Compositor::GetCompositorLock() {
525 if (!compositor_lock_
) {
526 compositor_lock_
= new CompositorLock(this);
527 if (g_compositor_thread
)
528 host_
->SetDeferCommits(true);
529 FOR_EACH_OBSERVER(CompositorObserver
,
531 OnCompositingLockStateChanged(this));
533 return compositor_lock_
;
536 void Compositor::UnlockCompositor() {
537 DCHECK(compositor_lock_
);
538 compositor_lock_
= NULL
;
539 if (g_compositor_thread
)
540 host_
->SetDeferCommits(false);
541 FOR_EACH_OBSERVER(CompositorObserver
,
543 OnCompositingLockStateChanged(this));
546 void Compositor::CancelCompositorLock() {
547 if (compositor_lock_
)
548 compositor_lock_
->CancelLock();
551 void Compositor::NotifyEnd() {
553 TRACE_EVENT_ASYNC_END0("ui", "Compositor::Draw", last_ended_frame_
);
554 waiting_on_compositing_end_
= false;
555 if (draw_on_compositing_end_
) {
556 draw_on_compositing_end_
= false;
558 // Call ScheduleDraw() instead of Draw() in order to allow other
559 // CompositorObservers to be notified before starting another
563 FOR_EACH_OBSERVER(CompositorObserver
,
565 OnCompositingEnded(this));