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 31using std::set; 32using std::string; 33using std::vector; 34 35namespace { 36 37const size_t kGpuLatencyHistorySize = 60; 38const double kGpuLatencyEstimationPercentile = 90.0; 39} 40 41namespace cc { 42 43OutputSurface::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 53OutputSurface::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 62OutputSurface::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 74void 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 86void OutputSurface::SetNeedsRedrawRect(const gfx::Rect& damage_rect) { 87 TRACE_EVENT0("cc", "OutputSurface::SetNeedsRedrawRect"); 88 client_->SetNeedsRedrawRect(damage_rect); 89} 90 91void OutputSurface::ReclaimResources(const CompositorFrameAck* ack) { 92 client_->ReclaimResources(ack); 93} 94 95void 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 102void OutputSurface::SetExternalStencilTest(bool enabled) { 103 external_stencil_test_enabled_ = enabled; 104} 105 106void 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 121OutputSurface::~OutputSurface() { 122 ResetContext3d(); 123} 124 125bool OutputSurface::HasExternalStencilTest() const { 126 return external_stencil_test_enabled_; 127} 128 129bool 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 146bool 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 166void OutputSurface::ReleaseGL() { 167 DCHECK(client_); 168 DCHECK(context_provider_.get()); 169 client_->ReleaseGL(); 170 DCHECK(!context_provider_.get()); 171} 172 173void 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 188void OutputSurface::ReleaseContextProvider() { 189 DCHECK(client_); 190 DCHECK(context_provider_.get()); 191 ResetContext3d(); 192} 193 194void 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 216void OutputSurface::EnsureBackbuffer() { 217 if (software_device_) 218 software_device_->EnsureBackbuffer(); 219} 220 221void OutputSurface::DiscardBackbuffer() { 222 if (context_provider_.get()) 223 context_provider_->ContextGL()->DiscardBackbufferCHROMIUM(); 224 if (software_device_) 225 software_device_->DiscardBackbuffer(); 226} 227 228void 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 242gfx::Size OutputSurface::SurfaceSize() const { 243 return surface_size_; 244} 245 246void OutputSurface::BindFramebuffer() { 247 DCHECK(context_provider_.get()); 248 context_provider_->ContextGL()->BindFramebuffer(GL_FRAMEBUFFER, 0); 249} 250 251void 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 273base::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 280void 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 346void 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. 355void OutputSurface::OnSwapBuffersComplete() { 356 client_->DidSwapBuffersComplete(); 357} 358 359void 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