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. 4 5 #include "content/common/gpu/image_transport_surface.h" 6 7 #include "base/mac/scoped_cftyperef.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "content/common/gpu/gpu_command_buffer_stub.h" 10 #include "content/common/gpu/gpu_messages.h" 11 #include "ui/gfx/native_widget_types.h" 12 #include "ui/gl/gl_bindings.h" 13 #include "ui/gl/gl_context.h" 14 #include "ui/gl/gl_implementation.h" 15 #include "ui/gl/gl_surface_cgl.h" 16 #include "ui/gl/gl_surface_osmesa.h" 17 #include "ui/gl/io_surface_support_mac.h" 18 19 namespace content { 20 namespace { 21 22 // IOSurface dimensions will be rounded up to a multiple of this value in order 23 // to reduce memory thrashing during resize. This must be a power of 2. 24 const uint32 kIOSurfaceDimensionRoundup = 64; 25 26 int RoundUpSurfaceDimension(int number) { 27 DCHECK(number >= 0); 28 // Cast into unsigned space for portable bitwise ops. 29 uint32 unsigned_number = static_cast<uint32>(number); 30 uint32 roundup_sub_1 = kIOSurfaceDimensionRoundup - 1; 31 unsigned_number = (unsigned_number + roundup_sub_1) & ~roundup_sub_1; 32 return static_cast<int>(unsigned_number); 33 } 34 35 // We are backed by an offscreen surface for the purposes of creating 36 // a context, but use FBOs to render to texture backed IOSurface 37 class IOSurfaceImageTransportSurface 38 : public gfx::NoOpGLSurfaceCGL, 39 public ImageTransportSurface, 40 public GpuCommandBufferStub::DestructionObserver { 41 public: 42 IOSurfaceImageTransportSurface(GpuChannelManager* manager, 43 GpuCommandBufferStub* stub, 44 gfx::PluginWindowHandle handle); 45 46 // GLSurface implementation 47 virtual bool Initialize() OVERRIDE; 48 virtual void Destroy() OVERRIDE; 49 virtual bool DeferDraws() OVERRIDE; 50 virtual bool IsOffscreen() OVERRIDE; 51 virtual bool SwapBuffers() OVERRIDE; 52 virtual bool PostSubBuffer(int x, int y, int width, int height) OVERRIDE; 53 virtual std::string GetExtensions() OVERRIDE; 54 virtual gfx::Size GetSize() OVERRIDE; 55 virtual bool OnMakeCurrent(gfx::GLContext* context) OVERRIDE; 56 virtual unsigned int GetBackingFrameBufferObject() OVERRIDE; 57 virtual bool SetBackbufferAllocation(bool allocated) OVERRIDE; 58 virtual void SetFrontbufferAllocation(bool allocated) OVERRIDE; 59 60 protected: 61 // ImageTransportSurface implementation 62 virtual void OnBufferPresented( 63 const AcceleratedSurfaceMsg_BufferPresented_Params& params) OVERRIDE; 64 virtual void OnResizeViewACK() OVERRIDE; 65 virtual void OnResize(gfx::Size size, float scale_factor) OVERRIDE; 66 virtual void SetLatencyInfo(const ui::LatencyInfo&) OVERRIDE; 67 virtual void WakeUpGpu() OVERRIDE; 68 69 // GpuCommandBufferStub::DestructionObserver implementation. 70 virtual void OnWillDestroyStub() OVERRIDE; 71 72 private: 73 virtual ~IOSurfaceImageTransportSurface() OVERRIDE; 74 75 void AdjustBufferAllocation(); 76 void UnrefIOSurface(); 77 void CreateIOSurface(); 78 79 // Tracks the current buffer allocation state. 80 bool backbuffer_suggested_allocation_; 81 bool frontbuffer_suggested_allocation_; 82 83 uint32 fbo_id_; 84 GLuint texture_id_; 85 GLuint depth_stencil_renderbuffer_id_; 86 87 base::ScopedCFTypeRef<CFTypeRef> io_surface_; 88 89 // The id of |io_surface_| or 0 if that's NULL. 90 uint64 io_surface_handle_; 91 92 // Weak pointer to the context that this was last made current to. 93 gfx::GLContext* context_; 94 95 gfx::Size size_; 96 gfx::Size rounded_size_; 97 float scale_factor_; 98 99 // Whether or not we've successfully made the surface current once. 100 bool made_current_; 101 102 // Whether a SwapBuffers is pending. 103 bool is_swap_buffers_pending_; 104 105 // Whether we unscheduled command buffer because of pending SwapBuffers. 106 bool did_unschedule_; 107 108 ui::LatencyInfo latency_info_; 109 110 scoped_ptr<ImageTransportHelper> helper_; 111 112 DISALLOW_COPY_AND_ASSIGN(IOSurfaceImageTransportSurface); 113 }; 114 115 void AddBooleanValue(CFMutableDictionaryRef dictionary, 116 const CFStringRef key, 117 bool value) { 118 CFDictionaryAddValue(dictionary, key, 119 (value ? kCFBooleanTrue : kCFBooleanFalse)); 120 } 121 122 void AddIntegerValue(CFMutableDictionaryRef dictionary, 123 const CFStringRef key, 124 int32 value) { 125 base::ScopedCFTypeRef<CFNumberRef> number( 126 CFNumberCreate(NULL, kCFNumberSInt32Type, &value)); 127 CFDictionaryAddValue(dictionary, key, number.get()); 128 } 129 130 IOSurfaceImageTransportSurface::IOSurfaceImageTransportSurface( 131 GpuChannelManager* manager, 132 GpuCommandBufferStub* stub, 133 gfx::PluginWindowHandle handle) 134 : gfx::NoOpGLSurfaceCGL(gfx::Size(1, 1)), 135 backbuffer_suggested_allocation_(true), 136 frontbuffer_suggested_allocation_(true), 137 fbo_id_(0), 138 texture_id_(0), 139 depth_stencil_renderbuffer_id_(0), 140 io_surface_handle_(0), 141 context_(NULL), 142 scale_factor_(1.f), 143 made_current_(false), 144 is_swap_buffers_pending_(false), 145 did_unschedule_(false) { 146 helper_.reset(new ImageTransportHelper(this, manager, stub, handle)); 147 } 148 149 IOSurfaceImageTransportSurface::~IOSurfaceImageTransportSurface() { 150 } 151 152 bool IOSurfaceImageTransportSurface::Initialize() { 153 // Only support IOSurfaces if the GL implementation is the native desktop GL. 154 // IO surfaces will not work with, for example, OSMesa software renderer 155 // GL contexts. 156 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL && 157 gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL) 158 return false; 159 160 if (!helper_->Initialize()) 161 return false; 162 163 if (!NoOpGLSurfaceCGL::Initialize()) { 164 helper_->Destroy(); 165 return false; 166 } 167 168 helper_->stub()->AddDestructionObserver(this); 169 return true; 170 } 171 172 void IOSurfaceImageTransportSurface::Destroy() { 173 UnrefIOSurface(); 174 175 helper_->Destroy(); 176 NoOpGLSurfaceCGL::Destroy(); 177 } 178 179 bool IOSurfaceImageTransportSurface::DeferDraws() { 180 // The command buffer hit a draw/clear command that could clobber the 181 // IOSurface in use by an earlier SwapBuffers. If a Swap is pending, abort 182 // processing of the command by returning true and unschedule until the Swap 183 // Ack arrives. 184 if(did_unschedule_) 185 return true; // Still unscheduled, so just return true. 186 if (is_swap_buffers_pending_) { 187 did_unschedule_ = true; 188 helper_->SetScheduled(false); 189 return true; 190 } 191 return false; 192 } 193 194 bool IOSurfaceImageTransportSurface::IsOffscreen() { 195 return false; 196 } 197 198 bool IOSurfaceImageTransportSurface::OnMakeCurrent(gfx::GLContext* context) { 199 context_ = context; 200 201 if (made_current_) 202 return true; 203 204 OnResize(gfx::Size(1, 1), 1.f); 205 206 made_current_ = true; 207 return true; 208 } 209 210 unsigned int IOSurfaceImageTransportSurface::GetBackingFrameBufferObject() { 211 return fbo_id_; 212 } 213 214 bool IOSurfaceImageTransportSurface::SetBackbufferAllocation(bool allocation) { 215 if (backbuffer_suggested_allocation_ == allocation) 216 return true; 217 backbuffer_suggested_allocation_ = allocation; 218 AdjustBufferAllocation(); 219 return true; 220 } 221 222 void IOSurfaceImageTransportSurface::SetFrontbufferAllocation(bool allocation) { 223 if (frontbuffer_suggested_allocation_ == allocation) 224 return; 225 frontbuffer_suggested_allocation_ = allocation; 226 AdjustBufferAllocation(); 227 } 228 229 void IOSurfaceImageTransportSurface::AdjustBufferAllocation() { 230 // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is 231 // free'd when both the browser and gpu processes have Unref'd the IOSurface. 232 if (!backbuffer_suggested_allocation_ && 233 !frontbuffer_suggested_allocation_ && 234 io_surface_.get()) { 235 UnrefIOSurface(); 236 helper_->Suspend(); 237 } else if (backbuffer_suggested_allocation_ && !io_surface_) { 238 CreateIOSurface(); 239 } 240 } 241 242 bool IOSurfaceImageTransportSurface::SwapBuffers() { 243 DCHECK(backbuffer_suggested_allocation_); 244 if (!frontbuffer_suggested_allocation_) 245 return true; 246 glFlush(); 247 248 GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; 249 params.surface_handle = io_surface_handle_; 250 params.size = GetSize(); 251 params.scale_factor = scale_factor_; 252 params.latency_info = latency_info_; 253 helper_->SendAcceleratedSurfaceBuffersSwapped(params); 254 255 DCHECK(!is_swap_buffers_pending_); 256 is_swap_buffers_pending_ = true; 257 return true; 258 } 259 260 bool IOSurfaceImageTransportSurface::PostSubBuffer( 261 int x, int y, int width, int height) { 262 DCHECK(backbuffer_suggested_allocation_); 263 if (!frontbuffer_suggested_allocation_) 264 return true; 265 glFlush(); 266 267 GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params; 268 params.surface_handle = io_surface_handle_; 269 params.x = x; 270 params.y = y; 271 params.width = width; 272 params.height = height; 273 params.surface_size = GetSize(); 274 params.surface_scale_factor = scale_factor_; 275 params.latency_info = latency_info_; 276 helper_->SendAcceleratedSurfacePostSubBuffer(params); 277 278 DCHECK(!is_swap_buffers_pending_); 279 is_swap_buffers_pending_ = true; 280 return true; 281 } 282 283 std::string IOSurfaceImageTransportSurface::GetExtensions() { 284 std::string extensions = gfx::GLSurface::GetExtensions(); 285 extensions += extensions.empty() ? "" : " "; 286 extensions += "GL_CHROMIUM_front_buffer_cached "; 287 extensions += "GL_CHROMIUM_post_sub_buffer"; 288 return extensions; 289 } 290 291 gfx::Size IOSurfaceImageTransportSurface::GetSize() { 292 return size_; 293 } 294 295 void IOSurfaceImageTransportSurface::OnBufferPresented( 296 const AcceleratedSurfaceMsg_BufferPresented_Params& params) { 297 DCHECK(is_swap_buffers_pending_); 298 299 context_->share_group()->SetRendererID(params.renderer_id); 300 is_swap_buffers_pending_ = false; 301 if (did_unschedule_) { 302 did_unschedule_ = false; 303 helper_->SetScheduled(true); 304 } 305 } 306 307 void IOSurfaceImageTransportSurface::OnResizeViewACK() { 308 NOTREACHED(); 309 } 310 311 void IOSurfaceImageTransportSurface::OnResize(gfx::Size size, 312 float scale_factor) { 313 // This trace event is used in gpu_feature_browsertest.cc - the test will need 314 // to be updated if this event is changed or moved. 315 TRACE_EVENT2("gpu", "IOSurfaceImageTransportSurface::OnResize", 316 "old_width", size_.width(), "new_width", size.width()); 317 // Caching |context_| from OnMakeCurrent. It should still be current. 318 DCHECK(context_->IsCurrent(this)); 319 320 size_ = size; 321 scale_factor_ = scale_factor; 322 323 CreateIOSurface(); 324 } 325 326 void IOSurfaceImageTransportSurface::SetLatencyInfo( 327 const ui::LatencyInfo& latency_info) { 328 latency_info_ = latency_info; 329 } 330 331 void IOSurfaceImageTransportSurface::WakeUpGpu() { 332 NOTIMPLEMENTED(); 333 } 334 335 void IOSurfaceImageTransportSurface::OnWillDestroyStub() { 336 helper_->stub()->RemoveDestructionObserver(this); 337 Destroy(); 338 } 339 340 void IOSurfaceImageTransportSurface::UnrefIOSurface() { 341 // If we have resources to destroy, then make sure that we have a current 342 // context which we can use to delete the resources. 343 if (context_ || fbo_id_ || texture_id_ || depth_stencil_renderbuffer_id_) { 344 DCHECK(gfx::GLContext::GetCurrent() == context_); 345 DCHECK(context_->IsCurrent(this)); 346 DCHECK(CGLGetCurrentContext()); 347 } 348 349 if (fbo_id_) { 350 glDeleteFramebuffersEXT(1, &fbo_id_); 351 fbo_id_ = 0; 352 } 353 354 if (texture_id_) { 355 glDeleteTextures(1, &texture_id_); 356 texture_id_ = 0; 357 } 358 359 if (depth_stencil_renderbuffer_id_) { 360 glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_); 361 depth_stencil_renderbuffer_id_ = 0; 362 } 363 364 io_surface_.reset(); 365 io_surface_handle_ = 0; 366 } 367 368 void IOSurfaceImageTransportSurface::CreateIOSurface() { 369 gfx::Size new_rounded_size(RoundUpSurfaceDimension(size_.width()), 370 RoundUpSurfaceDimension(size_.height())); 371 372 // Only recreate surface when the rounded up size has changed. 373 if (io_surface_.get() && new_rounded_size == rounded_size_) 374 return; 375 376 // This trace event is used in gpu_feature_browsertest.cc - the test will need 377 // to be updated if this event is changed or moved. 378 TRACE_EVENT2("gpu", "IOSurfaceImageTransportSurface::CreateIOSurface", 379 "width", new_rounded_size.width(), 380 "height", new_rounded_size.height()); 381 382 rounded_size_ = new_rounded_size; 383 384 GLint previous_texture_id = 0; 385 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &previous_texture_id); 386 387 // Free the old IO Surface first to reduce memory fragmentation. 388 UnrefIOSurface(); 389 390 glGenFramebuffersEXT(1, &fbo_id_); 391 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_); 392 393 IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); 394 395 glGenTextures(1, &texture_id_); 396 397 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on 398 // Mac OS X and is required for IOSurface interoperability. 399 GLenum target = GL_TEXTURE_RECTANGLE_ARB; 400 glBindTexture(target, texture_id_); 401 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 402 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 403 glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 404 glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 405 406 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, 407 GL_COLOR_ATTACHMENT0_EXT, 408 target, 409 texture_id_, 410 0); 411 412 413 // Search through the provided attributes; if the caller has 414 // requested a stencil buffer, try to get one. 415 416 int32 stencil_bits = 417 helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE); 418 if (stencil_bits > 0) { 419 // Create and bind the stencil buffer 420 bool has_packed_depth_stencil = 421 GLSurface::ExtensionsContain( 422 reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)), 423 "GL_EXT_packed_depth_stencil"); 424 425 if (has_packed_depth_stencil) { 426 glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_); 427 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 428 depth_stencil_renderbuffer_id_); 429 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, 430 rounded_size_.width(), rounded_size_.height()); 431 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, 432 GL_STENCIL_ATTACHMENT_EXT, 433 GL_RENDERBUFFER_EXT, 434 depth_stencil_renderbuffer_id_); 435 } 436 437 // If we asked for stencil but the extension isn't present, 438 // it's OK to silently fail; subsequent code will/must check 439 // for the presence of a stencil buffer before attempting to 440 // do stencil-based operations. 441 } 442 443 // Allocate a new IOSurface, which is the GPU resource that can be 444 // shared across processes. 445 base::ScopedCFTypeRef<CFMutableDictionaryRef> properties; 446 properties.reset(CFDictionaryCreateMutable(kCFAllocatorDefault, 447 0, 448 &kCFTypeDictionaryKeyCallBacks, 449 &kCFTypeDictionaryValueCallBacks)); 450 AddIntegerValue(properties, 451 io_surface_support->GetKIOSurfaceWidth(), 452 rounded_size_.width()); 453 AddIntegerValue(properties, 454 io_surface_support->GetKIOSurfaceHeight(), 455 rounded_size_.height()); 456 AddIntegerValue(properties, 457 io_surface_support->GetKIOSurfaceBytesPerElement(), 4); 458 AddBooleanValue(properties, 459 io_surface_support->GetKIOSurfaceIsGlobal(), true); 460 // I believe we should be able to unreference the IOSurfaces without 461 // synchronizing with the browser process because they are 462 // ultimately reference counted by the operating system. 463 io_surface_.reset(io_surface_support->IOSurfaceCreate(properties)); 464 io_surface_handle_ = io_surface_support->IOSurfaceGetID(io_surface_); 465 466 // Don't think we need to identify a plane. 467 GLuint plane = 0; 468 CGLError cglerror = 469 io_surface_support->CGLTexImageIOSurface2D( 470 static_cast<CGLContextObj>(context_->GetHandle()), 471 target, 472 GL_RGBA, 473 rounded_size_.width(), 474 rounded_size_.height(), 475 GL_BGRA, 476 GL_UNSIGNED_INT_8_8_8_8_REV, 477 io_surface_.get(), 478 plane); 479 if (cglerror != kCGLNoError) { 480 UnrefIOSurface(); 481 return; 482 } 483 484 glFlush(); 485 486 GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); 487 if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { 488 DLOG(ERROR) << "Framebuffer was incomplete: " << status; 489 UnrefIOSurface(); 490 return; 491 } 492 493 glBindTexture(target, previous_texture_id); 494 // The FBO remains bound for this GL context. 495 } 496 497 // A subclass of GLSurfaceOSMesa that doesn't print an error message when 498 // SwapBuffers() is called. 499 class DRTSurfaceOSMesa : public gfx::GLSurfaceOSMesa { 500 public: 501 // Size doesn't matter, the surface is resized to the right size later. 502 DRTSurfaceOSMesa() : GLSurfaceOSMesa(GL_RGBA, gfx::Size(1, 1)) {} 503 504 // Implement a subset of GLSurface. 505 virtual bool SwapBuffers() OVERRIDE; 506 507 private: 508 virtual ~DRTSurfaceOSMesa() {} 509 DISALLOW_COPY_AND_ASSIGN(DRTSurfaceOSMesa); 510 }; 511 512 bool DRTSurfaceOSMesa::SwapBuffers() { 513 return true; 514 } 515 516 bool g_allow_os_mesa = false; 517 518 } // namespace 519 520 // static 521 scoped_refptr<gfx::GLSurface> ImageTransportSurface::CreateNativeSurface( 522 GpuChannelManager* manager, 523 GpuCommandBufferStub* stub, 524 const gfx::GLSurfaceHandle& surface_handle) { 525 DCHECK(surface_handle.transport_type == gfx::NATIVE_TRANSPORT); 526 IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); 527 528 switch (gfx::GetGLImplementation()) { 529 case gfx::kGLImplementationDesktopGL: 530 case gfx::kGLImplementationAppleGL: 531 if (!io_surface_support) { 532 DLOG(WARNING) << "No IOSurface support"; 533 return scoped_refptr<gfx::GLSurface>(); 534 } 535 return scoped_refptr<gfx::GLSurface>(new IOSurfaceImageTransportSurface( 536 manager, stub, surface_handle.handle)); 537 538 default: 539 // Content shell in DRT mode spins up a gpu process which needs an 540 // image transport surface, but that surface isn't used to read pixel 541 // baselines. So this is mostly a dummy surface. 542 if (!g_allow_os_mesa) { 543 NOTREACHED(); 544 return scoped_refptr<gfx::GLSurface>(); 545 } 546 scoped_refptr<gfx::GLSurface> surface(new DRTSurfaceOSMesa()); 547 if (!surface.get() || !surface->Initialize()) 548 return surface; 549 return scoped_refptr<gfx::GLSurface>(new PassThroughImageTransportSurface( 550 manager, stub, surface.get(), false)); 551 } 552 } 553 554 // static 555 void ImageTransportSurface::SetAllowOSMesaForTesting(bool allow) { 556 g_allow_os_mesa = allow; 557 } 558 559 } // namespace content 560