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. 4 5 #include "cc/output/output_surface.h" 6 7 #include <algorithm> 8 #include <set> 9 #include <string> 10 #include <vector> 11 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 "gpu/command_buffer/client/context_support.h" 26 #include "gpu/command_buffer/client/gles2_interface.h" 27 #include "ui/gfx/frame_time.h" 28 #include "ui/gfx/geometry/rect.h" 29 #include "ui/gfx/geometry/size.h" 30 31 using std::set; 32 using std::string; 33 using std::vector; 34 35 namespace { 36 37 const size_t kGpuLatencyHistorySize = 60; 38 const double kGpuLatencyEstimationPercentile = 90.0; 39 } 40 41 namespace cc { 42 43 OutputSurface::OutputSurface( 44 const scoped_refptr<ContextProvider>& context_provider) 45 : client_(NULL), 46 context_provider_(context_provider), 47 device_scale_factor_(-1), 48 external_stencil_test_enabled_(false), 49 gpu_latency_history_(kGpuLatencyHistorySize), 50 weak_ptr_factory_(this) { 51 } 52 53 OutputSurface::OutputSurface(scoped_ptr<SoftwareOutputDevice> software_device) 54 : client_(NULL), 55 software_device_(software_device.Pass()), 56 device_scale_factor_(-1), 57 external_stencil_test_enabled_(false), 58 gpu_latency_history_(kGpuLatencyHistorySize), 59 weak_ptr_factory_(this) { 60 } 61 62 OutputSurface::OutputSurface( 63 const scoped_refptr<ContextProvider>& context_provider, 64 scoped_ptr<SoftwareOutputDevice> software_device) 65 : client_(NULL), 66 context_provider_(context_provider), 67 software_device_(software_device.Pass()), 68 device_scale_factor_(-1), 69 external_stencil_test_enabled_(false), 70 gpu_latency_history_(kGpuLatencyHistorySize), 71 weak_ptr_factory_(this) { 72 } 73 74 void OutputSurface::CommitVSyncParameters(base::TimeTicks timebase, 75 base::TimeDelta interval) { 76 TRACE_EVENT2("cc", 77 "OutputSurface::CommitVSyncParameters", 78 "timebase", 79 (timebase - base::TimeTicks()).InSecondsF(), 80 "interval", 81 interval.InSecondsF()); 82 client_->CommitVSyncParameters(timebase, interval); 83 } 84 85 // Forwarded to OutputSurfaceClient 86 void OutputSurface::SetNeedsRedrawRect(const gfx::Rect& damage_rect) { 87 TRACE_EVENT0("cc", "OutputSurface::SetNeedsRedrawRect"); 88 client_->SetNeedsRedrawRect(damage_rect); 89 } 90 91 void OutputSurface::ReclaimResources(const CompositorFrameAck* ack) { 92 client_->ReclaimResources(ack); 93 } 94 95 void OutputSurface::DidLoseOutputSurface() { 96 TRACE_EVENT0("cc", "OutputSurface::DidLoseOutputSurface"); 97 pending_gpu_latency_query_ids_.clear(); 98 available_gpu_latency_query_ids_.clear(); 99 client_->DidLoseOutputSurface(); 100 } 101 102 void OutputSurface::SetExternalStencilTest(bool enabled) { 103 external_stencil_test_enabled_ = enabled; 104 } 105 106 void OutputSurface::SetExternalDrawConstraints( 107 const gfx::Transform& transform, 108 const gfx::Rect& viewport, 109 const gfx::Rect& clip, 110 const gfx::Rect& viewport_rect_for_tile_priority, 111 const gfx::Transform& transform_for_tile_priority, 112 bool resourceless_software_draw) { 113 client_->SetExternalDrawConstraints(transform, 114 viewport, 115 clip, 116 viewport_rect_for_tile_priority, 117 transform_for_tile_priority, 118 resourceless_software_draw); 119 } 120 121 OutputSurface::~OutputSurface() { 122 ResetContext3d(); 123 } 124 125 bool OutputSurface::HasExternalStencilTest() const { 126 return external_stencil_test_enabled_; 127 } 128 129 bool OutputSurface::BindToClient(OutputSurfaceClient* client) { 130 DCHECK(client); 131 client_ = client; 132 bool success = true; 133 134 if (context_provider_.get()) { 135 success = context_provider_->BindToCurrentThread(); 136 if (success) 137 SetUpContext3d(); 138 } 139 140 if (!success) 141 client_ = NULL; 142 143 return success; 144 } 145 146 bool OutputSurface::InitializeAndSetContext3d( 147 scoped_refptr<ContextProvider> context_provider) { 148 DCHECK(!context_provider_.get()); 149 DCHECK(context_provider.get()); 150 DCHECK(client_); 151 152 bool success = false; 153 if (context_provider->BindToCurrentThread()) { 154 context_provider_ = context_provider; 155 SetUpContext3d(); 156 client_->DeferredInitialize(); 157 success = true; 158 } 159 160 if (!success) 161 ResetContext3d(); 162 163 return success; 164 } 165 166 void OutputSurface::ReleaseGL() { 167 DCHECK(client_); 168 DCHECK(context_provider_.get()); 169 client_->ReleaseGL(); 170 DCHECK(!context_provider_.get()); 171 } 172 173 void OutputSurface::SetUpContext3d() { 174 DCHECK(context_provider_.get()); 175 DCHECK(client_); 176 177 context_provider_->SetLostContextCallback( 178 base::Bind(&OutputSurface::DidLoseOutputSurface, 179 base::Unretained(this))); 180 context_provider_->ContextSupport()->SetSwapBuffersCompleteCallback( 181 base::Bind(&OutputSurface::OnSwapBuffersComplete, 182 base::Unretained(this))); 183 context_provider_->SetMemoryPolicyChangedCallback( 184 base::Bind(&OutputSurface::SetMemoryPolicy, 185 base::Unretained(this))); 186 } 187 188 void OutputSurface::ReleaseContextProvider() { 189 DCHECK(client_); 190 DCHECK(context_provider_.get()); 191 ResetContext3d(); 192 } 193 194 void OutputSurface::ResetContext3d() { 195 if (context_provider_.get()) { 196 while (!pending_gpu_latency_query_ids_.empty()) { 197 unsigned query_id = pending_gpu_latency_query_ids_.front(); 198 pending_gpu_latency_query_ids_.pop_front(); 199 context_provider_->ContextGL()->DeleteQueriesEXT(1, &query_id); 200 } 201 while (!available_gpu_latency_query_ids_.empty()) { 202 unsigned query_id = available_gpu_latency_query_ids_.front(); 203 available_gpu_latency_query_ids_.pop_front(); 204 context_provider_->ContextGL()->DeleteQueriesEXT(1, &query_id); 205 } 206 context_provider_->SetLostContextCallback( 207 ContextProvider::LostContextCallback()); 208 context_provider_->SetMemoryPolicyChangedCallback( 209 ContextProvider::MemoryPolicyChangedCallback()); 210 if (gpu::ContextSupport* support = context_provider_->ContextSupport()) 211 support->SetSwapBuffersCompleteCallback(base::Closure()); 212 } 213 context_provider_ = NULL; 214 } 215 216 void OutputSurface::EnsureBackbuffer() { 217 if (software_device_) 218 software_device_->EnsureBackbuffer(); 219 } 220 221 void OutputSurface::DiscardBackbuffer() { 222 if (context_provider_.get()) 223 context_provider_->ContextGL()->DiscardBackbufferCHROMIUM(); 224 if (software_device_) 225 software_device_->DiscardBackbuffer(); 226 } 227 228 void OutputSurface::Reshape(const gfx::Size& size, float scale_factor) { 229 if (size == surface_size_ && scale_factor == device_scale_factor_) 230 return; 231 232 surface_size_ = size; 233 device_scale_factor_ = scale_factor; 234 if (context_provider_.get()) { 235 context_provider_->ContextGL()->ResizeCHROMIUM( 236 size.width(), size.height(), scale_factor); 237 } 238 if (software_device_) 239 software_device_->Resize(size, scale_factor); 240 } 241 242 gfx::Size OutputSurface::SurfaceSize() const { 243 return surface_size_; 244 } 245 246 void OutputSurface::BindFramebuffer() { 247 DCHECK(context_provider_.get()); 248 context_provider_->ContextGL()->BindFramebuffer(GL_FRAMEBUFFER, 0); 249 } 250 251 void OutputSurface::SwapBuffers(CompositorFrame* frame) { 252 if (frame->software_frame_data) { 253 PostSwapBuffersComplete(); 254 client_->DidSwapBuffers(); 255 return; 256 } 257 258 DCHECK(context_provider_.get()); 259 DCHECK(frame->gl_frame_data); 260 261 UpdateAndMeasureGpuLatency(); 262 if (frame->gl_frame_data->sub_buffer_rect == 263 gfx::Rect(frame->gl_frame_data->size)) { 264 context_provider_->ContextSupport()->Swap(); 265 } else { 266 context_provider_->ContextSupport()->PartialSwapBuffers( 267 frame->gl_frame_data->sub_buffer_rect); 268 } 269 270 client_->DidSwapBuffers(); 271 } 272 273 base::TimeDelta OutputSurface::GpuLatencyEstimate() { 274 if (context_provider_.get() && !capabilities_.adjust_deadline_for_parent) 275 return gpu_latency_history_.Percentile(kGpuLatencyEstimationPercentile); 276 else 277 return base::TimeDelta(); 278 } 279 280 void OutputSurface::UpdateAndMeasureGpuLatency() { 281 // We only care about GPU latency for surfaces that do not have a parent 282 // compositor, since surfaces that do have a parent compositor can use 283 // mailboxes or delegated rendering to send frames to their parent without 284 // incurring GPU latency. 285 if (capabilities_.adjust_deadline_for_parent) 286 return; 287 288 // Try to collect pending queries which may have completed 289 while (pending_gpu_latency_query_ids_.size()) { 290 unsigned query_id = pending_gpu_latency_query_ids_.front(); 291 unsigned query_complete = 1; 292 context_provider_->ContextGL()->GetQueryObjectuivEXT( 293 query_id, GL_QUERY_RESULT_AVAILABLE_EXT, &query_complete); 294 if (!query_complete) 295 break; 296 297 unsigned value = 0; 298 context_provider_->ContextGL()->GetQueryObjectuivEXT( 299 query_id, GL_QUERY_RESULT_EXT, &value); 300 pending_gpu_latency_query_ids_.pop_front(); 301 available_gpu_latency_query_ids_.push_back(query_id); 302 303 base::TimeDelta latency = base::TimeDelta::FromMicroseconds(value); 304 base::TimeDelta latency_estimate = GpuLatencyEstimate(); 305 gpu_latency_history_.InsertSample(latency); 306 307 base::TimeDelta latency_overestimate; 308 base::TimeDelta latency_underestimate; 309 if (latency > latency_estimate) 310 latency_underestimate = latency - latency_estimate; 311 else 312 latency_overestimate = latency_estimate - latency; 313 UMA_HISTOGRAM_CUSTOM_TIMES("Renderer.GpuLatency", 314 latency, 315 base::TimeDelta::FromMilliseconds(1), 316 base::TimeDelta::FromMilliseconds(100), 317 50); 318 UMA_HISTOGRAM_CUSTOM_TIMES("Renderer.GpuLatencyUnderestimate", 319 latency_underestimate, 320 base::TimeDelta::FromMilliseconds(1), 321 base::TimeDelta::FromMilliseconds(100), 322 50); 323 UMA_HISTOGRAM_CUSTOM_TIMES("Renderer.GpuLatencyOverestimate", 324 latency_overestimate, 325 base::TimeDelta::FromMilliseconds(1), 326 base::TimeDelta::FromMilliseconds(100), 327 50); 328 } 329 330 // Generate new query id or use a previous id which has completed 331 unsigned gpu_latency_query_id; 332 if (available_gpu_latency_query_ids_.size()) { 333 gpu_latency_query_id = available_gpu_latency_query_ids_.front(); 334 available_gpu_latency_query_ids_.pop_front(); 335 } else { 336 context_provider_->ContextGL()->GenQueriesEXT(1, &gpu_latency_query_id); 337 } 338 339 // Send new latency query 340 context_provider_->ContextGL()->BeginQueryEXT(GL_LATENCY_QUERY_CHROMIUM, 341 gpu_latency_query_id); 342 context_provider_->ContextGL()->EndQueryEXT(GL_LATENCY_QUERY_CHROMIUM); 343 pending_gpu_latency_query_ids_.push_back(gpu_latency_query_id); 344 } 345 346 void OutputSurface::PostSwapBuffersComplete() { 347 base::MessageLoop::current()->PostTask( 348 FROM_HERE, 349 base::Bind(&OutputSurface::OnSwapBuffersComplete, 350 weak_ptr_factory_.GetWeakPtr())); 351 } 352 353 // We don't post tasks bound to the client directly since they might run 354 // after the OutputSurface has been destroyed. 355 void OutputSurface::OnSwapBuffersComplete() { 356 client_->DidSwapBuffersComplete(); 357 } 358 359 void OutputSurface::SetMemoryPolicy(const ManagedMemoryPolicy& policy) { 360 TRACE_EVENT1("cc", "OutputSurface::SetMemoryPolicy", 361 "bytes_limit_when_visible", policy.bytes_limit_when_visible); 362 // Just ignore the memory manager when it says to set the limit to zero 363 // bytes. This will happen when the memory manager thinks that the renderer 364 // is not visible (which the renderer knows better). 365 if (policy.bytes_limit_when_visible) 366 client_->SetMemoryPolicy(policy); 367 } 368 369 } // namespace cc 370