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