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 <set> 8 #include <string> 9 #include <vector> 10 11 #include "base/bind.h" 12 #include "base/debug/trace_event.h" 13 #include "base/logging.h" 14 #include "base/message_loop/message_loop.h" 15 #include "base/strings/string_split.h" 16 #include "base/strings/string_util.h" 17 #include "cc/output/compositor_frame.h" 18 #include "cc/output/managed_memory_policy.h" 19 #include "cc/output/output_surface_client.h" 20 #include "cc/scheduler/delay_based_time_source.h" 21 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" 22 #include "third_party/WebKit/public/platform/WebGraphicsMemoryAllocation.h" 23 #include "third_party/khronos/GLES2/gl2.h" 24 #include "third_party/khronos/GLES2/gl2ext.h" 25 #include "ui/gfx/rect.h" 26 #include "ui/gfx/size.h" 27 28 using std::set; 29 using std::string; 30 using std::vector; 31 32 namespace cc { 33 namespace { 34 35 ManagedMemoryPolicy::PriorityCutoff ConvertPriorityCutoff( 36 WebKit::WebGraphicsMemoryAllocation::PriorityCutoff priority_cutoff) { 37 // This is simple a 1:1 map, the names differ only because the WebKit names 38 // should be to match the cc names. 39 switch (priority_cutoff) { 40 case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowNothing: 41 return ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING; 42 case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowVisibleOnly: 43 return ManagedMemoryPolicy::CUTOFF_ALLOW_REQUIRED_ONLY; 44 case WebKit::WebGraphicsMemoryAllocation:: 45 PriorityCutoffAllowVisibleAndNearby: 46 return ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE; 47 case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowEverything: 48 return ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING; 49 } 50 NOTREACHED(); 51 return ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING; 52 } 53 54 } // anonymous namespace 55 56 class OutputSurfaceCallbacks 57 : public WebKit::WebGraphicsContext3D:: 58 WebGraphicsSwapBuffersCompleteCallbackCHROMIUM, 59 public WebKit::WebGraphicsContext3D::WebGraphicsContextLostCallback, 60 public WebKit::WebGraphicsContext3D:: 61 WebGraphicsMemoryAllocationChangedCallbackCHROMIUM { 62 public: 63 explicit OutputSurfaceCallbacks(OutputSurface* client) 64 : client_(client) { 65 DCHECK(client_); 66 } 67 68 // WK:WGC3D::WGSwapBuffersCompleteCallbackCHROMIUM implementation. 69 virtual void onSwapBuffersComplete() { client_->OnSwapBuffersComplete(NULL); } 70 71 // WK:WGC3D::WGContextLostCallback implementation. 72 virtual void onContextLost() { client_->DidLoseOutputSurface(); } 73 74 // WK:WGC3D::WGMemoryAllocationChangedCallbackCHROMIUM implementation. 75 virtual void onMemoryAllocationChanged( 76 WebKit::WebGraphicsMemoryAllocation allocation) { 77 ManagedMemoryPolicy policy( 78 allocation.bytesLimitWhenVisible, 79 ConvertPriorityCutoff(allocation.priorityCutoffWhenVisible), 80 allocation.bytesLimitWhenNotVisible, 81 ConvertPriorityCutoff(allocation.priorityCutoffWhenNotVisible), 82 ManagedMemoryPolicy::kDefaultNumResourcesLimit); 83 bool discard_backbuffer = !allocation.suggestHaveBackbuffer; 84 client_->SetMemoryPolicy(policy, discard_backbuffer); 85 } 86 87 private: 88 OutputSurface* client_; 89 }; 90 91 OutputSurface::OutputSurface( 92 scoped_ptr<WebKit::WebGraphicsContext3D> context3d) 93 : context3d_(context3d.Pass()), 94 has_gl_discard_backbuffer_(false), 95 has_swap_buffers_complete_callback_(false), 96 device_scale_factor_(-1), 97 weak_ptr_factory_(this), 98 max_frames_pending_(0), 99 pending_swap_buffers_(0), 100 needs_begin_frame_(false), 101 begin_frame_pending_(false), 102 client_(NULL), 103 check_for_retroactive_begin_frame_pending_(false) { 104 } 105 106 OutputSurface::OutputSurface( 107 scoped_ptr<cc::SoftwareOutputDevice> software_device) 108 : software_device_(software_device.Pass()), 109 has_gl_discard_backbuffer_(false), 110 has_swap_buffers_complete_callback_(false), 111 device_scale_factor_(-1), 112 weak_ptr_factory_(this), 113 max_frames_pending_(0), 114 pending_swap_buffers_(0), 115 needs_begin_frame_(false), 116 begin_frame_pending_(false), 117 client_(NULL), 118 check_for_retroactive_begin_frame_pending_(false) { 119 } 120 121 OutputSurface::OutputSurface( 122 scoped_ptr<WebKit::WebGraphicsContext3D> context3d, 123 scoped_ptr<cc::SoftwareOutputDevice> software_device) 124 : context3d_(context3d.Pass()), 125 software_device_(software_device.Pass()), 126 has_gl_discard_backbuffer_(false), 127 has_swap_buffers_complete_callback_(false), 128 device_scale_factor_(-1), 129 weak_ptr_factory_(this), 130 max_frames_pending_(0), 131 pending_swap_buffers_(0), 132 needs_begin_frame_(false), 133 begin_frame_pending_(false), 134 client_(NULL), 135 check_for_retroactive_begin_frame_pending_(false) { 136 } 137 138 void OutputSurface::InitializeBeginFrameEmulation( 139 base::SingleThreadTaskRunner* task_runner, 140 bool throttle_frame_production, 141 base::TimeDelta interval) { 142 if (throttle_frame_production) { 143 frame_rate_controller_.reset( 144 new FrameRateController( 145 DelayBasedTimeSource::Create(interval, task_runner))); 146 } else { 147 frame_rate_controller_.reset(new FrameRateController(task_runner)); 148 } 149 150 frame_rate_controller_->SetClient(this); 151 frame_rate_controller_->SetMaxSwapsPending(max_frames_pending_); 152 frame_rate_controller_->SetDeadlineAdjustment( 153 capabilities_.adjust_deadline_for_parent ? 154 BeginFrameArgs::DefaultDeadlineAdjustment() : base::TimeDelta()); 155 156 // The new frame rate controller will consume the swap acks of the old 157 // frame rate controller, so we set that expectation up here. 158 for (int i = 0; i < pending_swap_buffers_; i++) 159 frame_rate_controller_->DidSwapBuffers(); 160 } 161 162 void OutputSurface::SetMaxFramesPending(int max_frames_pending) { 163 if (frame_rate_controller_) 164 frame_rate_controller_->SetMaxSwapsPending(max_frames_pending); 165 max_frames_pending_ = max_frames_pending; 166 } 167 168 void OutputSurface::OnVSyncParametersChanged(base::TimeTicks timebase, 169 base::TimeDelta interval) { 170 TRACE_EVENT2("cc", "OutputSurface::OnVSyncParametersChanged", 171 "timebase", (timebase - base::TimeTicks()).InSecondsF(), 172 "interval", interval.InSecondsF()); 173 if (frame_rate_controller_) 174 frame_rate_controller_->SetTimebaseAndInterval(timebase, interval); 175 } 176 177 void OutputSurface::FrameRateControllerTick(bool throttled, 178 const BeginFrameArgs& args) { 179 DCHECK(frame_rate_controller_); 180 if (throttled) 181 skipped_begin_frame_args_ = args; 182 else 183 BeginFrame(args); 184 } 185 186 // Forwarded to OutputSurfaceClient 187 void OutputSurface::SetNeedsRedrawRect(gfx::Rect damage_rect) { 188 TRACE_EVENT0("cc", "OutputSurface::SetNeedsRedrawRect"); 189 client_->SetNeedsRedrawRect(damage_rect); 190 } 191 192 void OutputSurface::SetNeedsBeginFrame(bool enable) { 193 TRACE_EVENT1("cc", "OutputSurface::SetNeedsBeginFrame", "enable", enable); 194 needs_begin_frame_ = enable; 195 begin_frame_pending_ = false; 196 if (frame_rate_controller_) { 197 BeginFrameArgs skipped = frame_rate_controller_->SetActive(enable); 198 if (skipped.IsValid()) 199 skipped_begin_frame_args_ = skipped; 200 } 201 if (needs_begin_frame_) 202 PostCheckForRetroactiveBeginFrame(); 203 } 204 205 void OutputSurface::BeginFrame(const BeginFrameArgs& args) { 206 TRACE_EVENT2("cc", "OutputSurface::BeginFrame", 207 "begin_frame_pending_", begin_frame_pending_, 208 "pending_swap_buffers_", pending_swap_buffers_); 209 if (!needs_begin_frame_ || begin_frame_pending_ || 210 (pending_swap_buffers_ >= max_frames_pending_ && 211 max_frames_pending_ > 0)) { 212 skipped_begin_frame_args_ = args; 213 } else { 214 begin_frame_pending_ = true; 215 client_->BeginFrame(args); 216 // args might be an alias for skipped_begin_frame_args_. 217 // Do not reset it before calling BeginFrame! 218 skipped_begin_frame_args_ = BeginFrameArgs(); 219 } 220 } 221 222 base::TimeDelta OutputSurface::RetroactiveBeginFramePeriod() { 223 return BeginFrameArgs::DefaultRetroactiveBeginFramePeriod(); 224 } 225 226 void OutputSurface::PostCheckForRetroactiveBeginFrame() { 227 if (!skipped_begin_frame_args_.IsValid() || 228 check_for_retroactive_begin_frame_pending_) 229 return; 230 231 base::MessageLoop::current()->PostTask( 232 FROM_HERE, 233 base::Bind(&OutputSurface::CheckForRetroactiveBeginFrame, 234 weak_ptr_factory_.GetWeakPtr())); 235 check_for_retroactive_begin_frame_pending_ = true; 236 } 237 238 void OutputSurface::CheckForRetroactiveBeginFrame() { 239 TRACE_EVENT0("cc", "OutputSurface::CheckForRetroactiveBeginFrame"); 240 check_for_retroactive_begin_frame_pending_ = false; 241 base::TimeTicks now = base::TimeTicks::Now(); 242 base::TimeTicks alternative_deadline = 243 skipped_begin_frame_args_.frame_time + 244 RetroactiveBeginFramePeriod(); 245 if (now < skipped_begin_frame_args_.deadline || 246 now < alternative_deadline) { 247 BeginFrame(skipped_begin_frame_args_); 248 } 249 } 250 251 void OutputSurface::DidSwapBuffers() { 252 begin_frame_pending_ = false; 253 pending_swap_buffers_++; 254 TRACE_EVENT1("cc", "OutputSurface::DidSwapBuffers", 255 "pending_swap_buffers_", pending_swap_buffers_); 256 if (frame_rate_controller_) 257 frame_rate_controller_->DidSwapBuffers(); 258 PostCheckForRetroactiveBeginFrame(); 259 } 260 261 void OutputSurface::OnSwapBuffersComplete(const CompositorFrameAck* ack) { 262 pending_swap_buffers_--; 263 TRACE_EVENT1("cc", "OutputSurface::OnSwapBuffersComplete", 264 "pending_swap_buffers_", pending_swap_buffers_); 265 client_->OnSwapBuffersComplete(ack); 266 if (frame_rate_controller_) 267 frame_rate_controller_->DidSwapBuffersComplete(); 268 PostCheckForRetroactiveBeginFrame(); 269 } 270 271 void OutputSurface::DidLoseOutputSurface() { 272 TRACE_EVENT0("cc", "OutputSurface::DidLoseOutputSurface"); 273 begin_frame_pending_ = false; 274 pending_swap_buffers_ = 0; 275 client_->DidLoseOutputSurface(); 276 } 277 278 void OutputSurface::SetExternalStencilTest(bool enabled) { 279 client_->SetExternalStencilTest(enabled); 280 } 281 282 void OutputSurface::SetExternalDrawConstraints(const gfx::Transform& transform, 283 gfx::Rect viewport, 284 gfx::Rect clip, 285 bool valid_for_tile_management) { 286 client_->SetExternalDrawConstraints( 287 transform, viewport, clip, valid_for_tile_management); 288 } 289 290 OutputSurface::~OutputSurface() { 291 if (frame_rate_controller_) 292 frame_rate_controller_->SetActive(false); 293 294 if (context3d_) { 295 context3d_->setSwapBuffersCompleteCallbackCHROMIUM(NULL); 296 context3d_->setContextLostCallback(NULL); 297 context3d_->setMemoryAllocationChangedCallbackCHROMIUM(NULL); 298 } 299 } 300 301 bool OutputSurface::ForcedDrawToSoftwareDevice() const { 302 return false; 303 } 304 305 bool OutputSurface::BindToClient(cc::OutputSurfaceClient* client) { 306 DCHECK(client); 307 client_ = client; 308 bool success = true; 309 310 if (context3d_) { 311 success = context3d_->makeContextCurrent(); 312 if (success) 313 SetContext3D(context3d_.Pass()); 314 } 315 316 if (!success) 317 client_ = NULL; 318 319 return success; 320 } 321 322 bool OutputSurface::InitializeAndSetContext3D( 323 scoped_ptr<WebKit::WebGraphicsContext3D> context3d, 324 scoped_refptr<ContextProvider> offscreen_context_provider) { 325 DCHECK(!context3d_); 326 DCHECK(context3d); 327 DCHECK(client_); 328 329 bool success = false; 330 if (context3d->makeContextCurrent()) { 331 SetContext3D(context3d.Pass()); 332 if (client_->DeferredInitialize(offscreen_context_provider)) 333 success = true; 334 } 335 336 if (!success) 337 ResetContext3D(); 338 339 return success; 340 } 341 342 void OutputSurface::ReleaseGL() { 343 DCHECK(client_); 344 DCHECK(context3d_); 345 client_->ReleaseGL(); 346 ResetContext3D(); 347 } 348 349 void OutputSurface::SetContext3D( 350 scoped_ptr<WebKit::WebGraphicsContext3D> context3d) { 351 DCHECK(!context3d_); 352 DCHECK(context3d); 353 DCHECK(client_); 354 355 string extensions_string = UTF16ToASCII(context3d->getString(GL_EXTENSIONS)); 356 vector<string> extensions_list; 357 base::SplitString(extensions_string, ' ', &extensions_list); 358 set<string> extensions(extensions_list.begin(), extensions_list.end()); 359 has_gl_discard_backbuffer_ = 360 extensions.count("GL_CHROMIUM_discard_backbuffer") > 0; 361 has_swap_buffers_complete_callback_ = 362 extensions.count("GL_CHROMIUM_swapbuffers_complete_callback") > 0; 363 364 365 context3d_ = context3d.Pass(); 366 callbacks_.reset(new OutputSurfaceCallbacks(this)); 367 context3d_->setSwapBuffersCompleteCallbackCHROMIUM(callbacks_.get()); 368 context3d_->setContextLostCallback(callbacks_.get()); 369 context3d_->setMemoryAllocationChangedCallbackCHROMIUM(callbacks_.get()); 370 } 371 372 void OutputSurface::ResetContext3D() { 373 context3d_.reset(); 374 callbacks_.reset(); 375 } 376 377 void OutputSurface::EnsureBackbuffer() { 378 DCHECK(context3d_); 379 if (has_gl_discard_backbuffer_) 380 context3d_->ensureBackbufferCHROMIUM(); 381 } 382 383 void OutputSurface::DiscardBackbuffer() { 384 DCHECK(context3d_); 385 if (has_gl_discard_backbuffer_) 386 context3d_->discardBackbufferCHROMIUM(); 387 } 388 389 void OutputSurface::Reshape(gfx::Size size, float scale_factor) { 390 if (size == surface_size_ && scale_factor == device_scale_factor_) 391 return; 392 393 surface_size_ = size; 394 device_scale_factor_ = scale_factor; 395 if (context3d_) { 396 context3d_->reshapeWithScaleFactor( 397 size.width(), size.height(), scale_factor); 398 } 399 if (software_device_) 400 software_device_->Resize(size); 401 } 402 403 gfx::Size OutputSurface::SurfaceSize() const { 404 return surface_size_; 405 } 406 407 void OutputSurface::BindFramebuffer() { 408 DCHECK(context3d_); 409 context3d_->bindFramebuffer(GL_FRAMEBUFFER, 0); 410 } 411 412 void OutputSurface::SwapBuffers(cc::CompositorFrame* frame) { 413 if (frame->software_frame_data) { 414 PostSwapBuffersComplete(); 415 DidSwapBuffers(); 416 return; 417 } 418 419 DCHECK(context3d_); 420 DCHECK(frame->gl_frame_data); 421 422 if (frame->gl_frame_data->sub_buffer_rect == 423 gfx::Rect(frame->gl_frame_data->size)) { 424 // Note that currently this has the same effect as SwapBuffers; we should 425 // consider exposing a different entry point on WebGraphicsContext3D. 426 context3d()->prepareTexture(); 427 } else { 428 gfx::Rect sub_buffer_rect = frame->gl_frame_data->sub_buffer_rect; 429 context3d()->postSubBufferCHROMIUM(sub_buffer_rect.x(), 430 sub_buffer_rect.y(), 431 sub_buffer_rect.width(), 432 sub_buffer_rect.height()); 433 } 434 435 if (!has_swap_buffers_complete_callback_) 436 PostSwapBuffersComplete(); 437 438 DidSwapBuffers(); 439 } 440 441 void OutputSurface::PostSwapBuffersComplete() { 442 base::MessageLoop::current()->PostTask( 443 FROM_HERE, 444 base::Bind(&OutputSurface::OnSwapBuffersComplete, 445 weak_ptr_factory_.GetWeakPtr(), 446 static_cast<CompositorFrameAck*>(NULL))); 447 } 448 449 void OutputSurface::SetMemoryPolicy(const ManagedMemoryPolicy& policy, 450 bool discard_backbuffer) { 451 TRACE_EVENT2("cc", "OutputSurface::SetMemoryPolicy", 452 "bytes_limit_when_visible", policy.bytes_limit_when_visible, 453 "discard_backbuffer", discard_backbuffer); 454 // Just ignore the memory manager when it says to set the limit to zero 455 // bytes. This will happen when the memory manager thinks that the renderer 456 // is not visible (which the renderer knows better). 457 if (policy.bytes_limit_when_visible) 458 client_->SetMemoryPolicy(policy); 459 client_->SetDiscardBackBufferWhenNotVisible(discard_backbuffer); 460 } 461 462 } // namespace cc 463