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 
     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