Home | History | Annotate | Download | only in gpu
      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