1 // Copyright (c) 2013 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 "cc/output/output_surface.h"
12 #include "base/bind.h"
13 #include "base/debug/trace_event.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/metrics/histogram.h"
17 #include "base/strings/string_split.h"
18 #include "base/strings/string_util.h"
19 #include "cc/output/compositor_frame.h"
20 #include "cc/output/compositor_frame_ack.h"
21 #include "cc/output/managed_memory_policy.h"
22 #include "cc/output/output_surface_client.h"
23 #include "cc/scheduler/delay_based_time_source.h"
24 #include "gpu/GLES2/gl2extchromium.h"
25 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
26 #include "third_party/khronos/GLES2/gl2.h"
27 #include "third_party/khronos/GLES2/gl2ext.h"
28 #include "ui/gfx/frame_time.h"
29 #include "ui/gfx/rect.h"
30 #include "ui/gfx/size.h"
38 const size_t kGpuLatencyHistorySize
= 60;
39 const double kGpuLatencyEstimationPercentile
= 100.0;
45 OutputSurface::OutputSurface(scoped_refptr
<ContextProvider
> context_provider
)
46 : context_provider_(context_provider
),
47 has_gl_discard_backbuffer_(false),
48 has_swap_buffers_complete_callback_(false),
49 device_scale_factor_(-1),
50 max_frames_pending_(0),
51 pending_swap_buffers_(0),
52 needs_begin_impl_frame_(false),
53 client_ready_for_begin_impl_frame_(true),
55 check_for_retroactive_begin_impl_frame_pending_(false),
56 external_stencil_test_enabled_(false),
57 weak_ptr_factory_(this),
58 gpu_latency_history_(kGpuLatencyHistorySize
) {}
60 OutputSurface::OutputSurface(
61 scoped_ptr
<cc::SoftwareOutputDevice
> software_device
)
62 : software_device_(software_device
.Pass()),
63 has_gl_discard_backbuffer_(false),
64 has_swap_buffers_complete_callback_(false),
65 device_scale_factor_(-1),
66 max_frames_pending_(0),
67 pending_swap_buffers_(0),
68 needs_begin_impl_frame_(false),
69 client_ready_for_begin_impl_frame_(true),
71 check_for_retroactive_begin_impl_frame_pending_(false),
72 external_stencil_test_enabled_(false),
73 weak_ptr_factory_(this),
74 gpu_latency_history_(kGpuLatencyHistorySize
) {}
76 OutputSurface::OutputSurface(
77 scoped_refptr
<ContextProvider
> context_provider
,
78 scoped_ptr
<cc::SoftwareOutputDevice
> software_device
)
79 : context_provider_(context_provider
),
80 software_device_(software_device
.Pass()),
81 has_gl_discard_backbuffer_(false),
82 has_swap_buffers_complete_callback_(false),
83 device_scale_factor_(-1),
84 max_frames_pending_(0),
85 pending_swap_buffers_(0),
86 needs_begin_impl_frame_(false),
87 client_ready_for_begin_impl_frame_(true),
89 check_for_retroactive_begin_impl_frame_pending_(false),
90 external_stencil_test_enabled_(false),
91 weak_ptr_factory_(this),
92 gpu_latency_history_(kGpuLatencyHistorySize
) {}
94 void OutputSurface::InitializeBeginImplFrameEmulation(
95 base::SingleThreadTaskRunner
* task_runner
,
96 bool throttle_frame_production
,
97 base::TimeDelta interval
) {
98 if (throttle_frame_production
) {
99 scoped_refptr
<DelayBasedTimeSource
> time_source
;
100 if (gfx::FrameTime::TimestampsAreHighRes())
101 time_source
= DelayBasedTimeSourceHighRes::Create(interval
, task_runner
);
103 time_source
= DelayBasedTimeSource::Create(interval
, task_runner
);
104 frame_rate_controller_
.reset(new FrameRateController(time_source
));
106 frame_rate_controller_
.reset(new FrameRateController(task_runner
));
109 frame_rate_controller_
->SetClient(this);
110 frame_rate_controller_
->SetMaxSwapsPending(max_frames_pending_
);
111 frame_rate_controller_
->SetDeadlineAdjustment(
112 capabilities_
.adjust_deadline_for_parent
?
113 BeginFrameArgs::DefaultDeadlineAdjustment() : base::TimeDelta());
115 // The new frame rate controller will consume the swap acks of the old
116 // frame rate controller, so we set that expectation up here.
117 for (int i
= 0; i
< pending_swap_buffers_
; i
++)
118 frame_rate_controller_
->DidSwapBuffers();
121 void OutputSurface::SetMaxFramesPending(int max_frames_pending
) {
122 if (frame_rate_controller_
)
123 frame_rate_controller_
->SetMaxSwapsPending(max_frames_pending
);
124 max_frames_pending_
= max_frames_pending
;
127 void OutputSurface::OnVSyncParametersChanged(base::TimeTicks timebase
,
128 base::TimeDelta interval
) {
129 TRACE_EVENT2("cc", "OutputSurface::OnVSyncParametersChanged",
130 "timebase", (timebase
- base::TimeTicks()).InSecondsF(),
131 "interval", interval
.InSecondsF());
132 if (frame_rate_controller_
)
133 frame_rate_controller_
->SetTimebaseAndInterval(timebase
, interval
);
136 void OutputSurface::FrameRateControllerTick(bool throttled
,
137 const BeginFrameArgs
& args
) {
138 DCHECK(frame_rate_controller_
);
140 skipped_begin_impl_frame_args_
= args
;
142 BeginImplFrame(args
);
145 // Forwarded to OutputSurfaceClient
146 void OutputSurface::SetNeedsRedrawRect(gfx::Rect damage_rect
) {
147 TRACE_EVENT0("cc", "OutputSurface::SetNeedsRedrawRect");
148 client_
->SetNeedsRedrawRect(damage_rect
);
151 void OutputSurface::SetNeedsBeginImplFrame(bool enable
) {
152 TRACE_EVENT1("cc", "OutputSurface::SetNeedsBeginImplFrame", "enable", enable
);
153 needs_begin_impl_frame_
= enable
;
154 client_ready_for_begin_impl_frame_
= true;
155 if (frame_rate_controller_
) {
156 BeginFrameArgs skipped
= frame_rate_controller_
->SetActive(enable
);
157 if (skipped
.IsValid())
158 skipped_begin_impl_frame_args_
= skipped
;
160 if (needs_begin_impl_frame_
)
161 PostCheckForRetroactiveBeginImplFrame();
164 void OutputSurface::BeginImplFrame(const BeginFrameArgs
& args
) {
165 TRACE_EVENT2("cc", "OutputSurface::BeginImplFrame",
166 "client_ready_for_begin_impl_frame_",
167 client_ready_for_begin_impl_frame_
,
168 "pending_swap_buffers_", pending_swap_buffers_
);
169 if (!needs_begin_impl_frame_
|| !client_ready_for_begin_impl_frame_
||
170 (pending_swap_buffers_
>= max_frames_pending_
&&
171 max_frames_pending_
> 0)) {
172 skipped_begin_impl_frame_args_
= args
;
174 client_ready_for_begin_impl_frame_
= false;
175 client_
->BeginImplFrame(args
);
176 // args might be an alias for skipped_begin_impl_frame_args_.
177 // Do not reset it before calling BeginImplFrame!
178 skipped_begin_impl_frame_args_
= BeginFrameArgs();
182 base::TimeTicks
OutputSurface::RetroactiveBeginImplFrameDeadline() {
183 // TODO(brianderson): Remove the alternative deadline once we have better
184 // deadline estimations.
185 base::TimeTicks alternative_deadline
=
186 skipped_begin_impl_frame_args_
.frame_time
+
187 BeginFrameArgs::DefaultRetroactiveBeginFramePeriod();
188 return std::max(skipped_begin_impl_frame_args_
.deadline
,
189 alternative_deadline
);
192 void OutputSurface::PostCheckForRetroactiveBeginImplFrame() {
193 if (!skipped_begin_impl_frame_args_
.IsValid() ||
194 check_for_retroactive_begin_impl_frame_pending_
)
197 base::MessageLoop::current()->PostTask(
199 base::Bind(&OutputSurface::CheckForRetroactiveBeginImplFrame
,
200 weak_ptr_factory_
.GetWeakPtr()));
201 check_for_retroactive_begin_impl_frame_pending_
= true;
204 void OutputSurface::CheckForRetroactiveBeginImplFrame() {
205 TRACE_EVENT0("cc", "OutputSurface::CheckForRetroactiveBeginImplFrame");
206 check_for_retroactive_begin_impl_frame_pending_
= false;
207 if (gfx::FrameTime::Now() < RetroactiveBeginImplFrameDeadline())
208 BeginImplFrame(skipped_begin_impl_frame_args_
);
211 void OutputSurface::DidSwapBuffers() {
212 pending_swap_buffers_
++;
213 TRACE_EVENT1("cc", "OutputSurface::DidSwapBuffers",
214 "pending_swap_buffers_", pending_swap_buffers_
);
215 if (frame_rate_controller_
)
216 frame_rate_controller_
->DidSwapBuffers();
217 PostCheckForRetroactiveBeginImplFrame();
220 void OutputSurface::OnSwapBuffersComplete() {
221 pending_swap_buffers_
--;
222 TRACE_EVENT1("cc", "OutputSurface::OnSwapBuffersComplete",
223 "pending_swap_buffers_", pending_swap_buffers_
);
224 client_
->OnSwapBuffersComplete();
225 if (frame_rate_controller_
)
226 frame_rate_controller_
->DidSwapBuffersComplete();
227 PostCheckForRetroactiveBeginImplFrame();
230 void OutputSurface::ReclaimResources(const CompositorFrameAck
* ack
) {
231 client_
->ReclaimResources(ack
);
234 void OutputSurface::DidLoseOutputSurface() {
235 TRACE_EVENT0("cc", "OutputSurface::DidLoseOutputSurface");
236 client_ready_for_begin_impl_frame_
= true;
237 pending_swap_buffers_
= 0;
238 skipped_begin_impl_frame_args_
= BeginFrameArgs();
239 if (frame_rate_controller_
)
240 frame_rate_controller_
->SetActive(false);
241 pending_gpu_latency_query_ids_
.clear();
242 available_gpu_latency_query_ids_
.clear();
243 client_
->DidLoseOutputSurface();
246 void OutputSurface::SetExternalStencilTest(bool enabled
) {
247 external_stencil_test_enabled_
= enabled
;
250 void OutputSurface::SetExternalDrawConstraints(const gfx::Transform
& transform
,
253 bool valid_for_tile_management
) {
254 client_
->SetExternalDrawConstraints(
255 transform
, viewport
, clip
, valid_for_tile_management
);
258 OutputSurface::~OutputSurface() {
259 if (frame_rate_controller_
)
260 frame_rate_controller_
->SetActive(false);
264 bool OutputSurface::HasExternalStencilTest() const {
265 return external_stencil_test_enabled_
;
268 bool OutputSurface::ForcedDrawToSoftwareDevice() const { return false; }
270 bool OutputSurface::BindToClient(cc::OutputSurfaceClient
* client
) {
275 if (context_provider_
) {
276 success
= context_provider_
->BindToCurrentThread();
287 bool OutputSurface::InitializeAndSetContext3d(
288 scoped_refptr
<ContextProvider
> context_provider
,
289 scoped_refptr
<ContextProvider
> offscreen_context_provider
) {
290 DCHECK(!context_provider_
);
291 DCHECK(context_provider
);
294 bool success
= false;
295 if (context_provider
->BindToCurrentThread()) {
296 context_provider_
= context_provider
;
298 if (client_
->DeferredInitialize(offscreen_context_provider
))
308 void OutputSurface::ReleaseGL() {
310 DCHECK(context_provider_
);
311 client_
->ReleaseGL();
315 void OutputSurface::SetUpContext3d() {
316 DCHECK(context_provider_
);
319 const ContextProvider::Capabilities
& caps
=
320 context_provider_
->ContextCapabilities();
322 has_gl_discard_backbuffer_
= caps
.discard_backbuffer
;
323 has_swap_buffers_complete_callback_
= caps
.swapbuffers_complete_callback
;
325 context_provider_
->SetLostContextCallback(
326 base::Bind(&OutputSurface::DidLoseOutputSurface
,
327 base::Unretained(this)));
328 context_provider_
->SetSwapBuffersCompleteCallback(base::Bind(
329 &OutputSurface::OnSwapBuffersComplete
, base::Unretained(this)));
330 context_provider_
->SetMemoryPolicyChangedCallback(
331 base::Bind(&OutputSurface::SetMemoryPolicy
,
332 base::Unretained(this)));
335 void OutputSurface::ResetContext3d() {
336 if (context_provider_
.get()) {
337 context_provider_
->SetLostContextCallback(
338 ContextProvider::LostContextCallback());
339 context_provider_
->SetSwapBuffersCompleteCallback(
340 ContextProvider::SwapBuffersCompleteCallback());
341 context_provider_
->SetMemoryPolicyChangedCallback(
342 ContextProvider::MemoryPolicyChangedCallback());
344 context_provider_
= NULL
;
347 void OutputSurface::EnsureBackbuffer() {
348 if (context_provider_
&& has_gl_discard_backbuffer_
)
349 context_provider_
->Context3d()->ensureBackbufferCHROMIUM();
350 if (software_device_
)
351 software_device_
->EnsureBackbuffer();
354 void OutputSurface::DiscardBackbuffer() {
355 if (context_provider_
&& has_gl_discard_backbuffer_
)
356 context_provider_
->Context3d()->discardBackbufferCHROMIUM();
357 if (software_device_
)
358 software_device_
->DiscardBackbuffer();
361 void OutputSurface::Reshape(gfx::Size size
, float scale_factor
) {
362 if (size
== surface_size_
&& scale_factor
== device_scale_factor_
)
365 surface_size_
= size
;
366 device_scale_factor_
= scale_factor
;
367 if (context_provider_
) {
368 context_provider_
->Context3d()->reshapeWithScaleFactor(
369 size
.width(), size
.height(), scale_factor
);
371 if (software_device_
)
372 software_device_
->Resize(size
);
375 gfx::Size
OutputSurface::SurfaceSize() const {
376 return surface_size_
;
379 void OutputSurface::BindFramebuffer() {
380 DCHECK(context_provider_
);
381 context_provider_
->Context3d()->bindFramebuffer(GL_FRAMEBUFFER
, 0);
384 void OutputSurface::SwapBuffers(cc::CompositorFrame
* frame
) {
385 if (frame
->software_frame_data
) {
386 PostSwapBuffersComplete();
391 DCHECK(context_provider_
);
392 DCHECK(frame
->gl_frame_data
);
394 UpdateAndMeasureGpuLatency();
395 if (frame
->gl_frame_data
->sub_buffer_rect
==
396 gfx::Rect(frame
->gl_frame_data
->size
)) {
397 // Note that currently this has the same effect as SwapBuffers; we should
398 // consider exposing a different entry point on WebGraphicsContext3D.
399 context_provider_
->Context3d()->prepareTexture();
401 gfx::Rect sub_buffer_rect
= frame
->gl_frame_data
->sub_buffer_rect
;
402 context_provider_
->Context3d()->postSubBufferCHROMIUM(
405 sub_buffer_rect
.width(),
406 sub_buffer_rect
.height());
409 if (!has_swap_buffers_complete_callback_
)
410 PostSwapBuffersComplete();
415 base::TimeDelta
OutputSurface::GpuLatencyEstimate() {
416 if (context_provider_
&& !capabilities_
.adjust_deadline_for_parent
)
417 return gpu_latency_history_
.Percentile(kGpuLatencyEstimationPercentile
);
419 return base::TimeDelta();
422 void OutputSurface::UpdateAndMeasureGpuLatency() {
423 // We only care about GPU latency for surfaces that do not have a parent
424 // compositor, since surfaces that do have a parent compositor can use
425 // mailboxes or delegated rendering to send frames to their parent without
426 // incurring GPU latency.
427 if (capabilities_
.adjust_deadline_for_parent
)
430 while (pending_gpu_latency_query_ids_
.size()) {
431 unsigned query_id
= pending_gpu_latency_query_ids_
.front();
432 unsigned query_complete
= 1;
433 context_provider_
->Context3d()->getQueryObjectuivEXT(
434 query_id
, GL_QUERY_RESULT_AVAILABLE_EXT
, &query_complete
);
439 context_provider_
->Context3d()->getQueryObjectuivEXT(
440 query_id
, GL_QUERY_RESULT_EXT
, &value
);
441 pending_gpu_latency_query_ids_
.pop_front();
442 available_gpu_latency_query_ids_
.push_back(query_id
);
444 base::TimeDelta latency
= base::TimeDelta::FromMicroseconds(value
);
445 base::TimeDelta latency_estimate
= GpuLatencyEstimate();
446 gpu_latency_history_
.InsertSample(latency
);
448 base::TimeDelta latency_overestimate
;
449 base::TimeDelta latency_underestimate
;
450 if (latency
> latency_estimate
)
451 latency_underestimate
= latency
- latency_estimate
;
453 latency_overestimate
= latency_estimate
- latency
;
454 UMA_HISTOGRAM_CUSTOM_TIMES("Renderer.GpuLatency",
456 base::TimeDelta::FromMilliseconds(1),
457 base::TimeDelta::FromMilliseconds(100),
459 UMA_HISTOGRAM_CUSTOM_TIMES("Renderer.GpuLatencyUnderestimate",
460 latency_underestimate
,
461 base::TimeDelta::FromMilliseconds(1),
462 base::TimeDelta::FromMilliseconds(100),
464 UMA_HISTOGRAM_CUSTOM_TIMES("Renderer.GpuLatencyOverestimate",
465 latency_overestimate
,
466 base::TimeDelta::FromMilliseconds(1),
467 base::TimeDelta::FromMilliseconds(100),
471 unsigned gpu_latency_query_id
;
472 if (available_gpu_latency_query_ids_
.size()) {
473 gpu_latency_query_id
= available_gpu_latency_query_ids_
.front();
474 available_gpu_latency_query_ids_
.pop_front();
476 gpu_latency_query_id
= context_provider_
->Context3d()->createQueryEXT();
479 context_provider_
->Context3d()->beginQueryEXT(GL_LATENCY_QUERY_CHROMIUM
,
480 gpu_latency_query_id
);
481 context_provider_
->Context3d()->endQueryEXT(GL_LATENCY_QUERY_CHROMIUM
);
482 pending_gpu_latency_query_ids_
.push_back(gpu_latency_query_id
);
485 void OutputSurface::PostSwapBuffersComplete() {
486 base::MessageLoop::current()->PostTask(
488 base::Bind(&OutputSurface::OnSwapBuffersComplete
,
489 weak_ptr_factory_
.GetWeakPtr()));
492 void OutputSurface::SetMemoryPolicy(const ManagedMemoryPolicy
& policy
) {
493 TRACE_EVENT1("cc", "OutputSurface::SetMemoryPolicy",
494 "bytes_limit_when_visible", policy
.bytes_limit_when_visible
);
495 // Just ignore the memory manager when it says to set the limit to zero
496 // bytes. This will happen when the memory manager thinks that the renderer
497 // is not visible (which the renderer knows better).
498 if (policy
.bytes_limit_when_visible
)
499 client_
->SetMemoryPolicy(policy
);