Home | History | Annotate | Download | only in client
      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 "gpu/command_buffer/client/gl_in_process_context.h"
      6 
      7 #include <set>
      8 #include <utility>
      9 #include <vector>
     10 
     11 #include <GLES2/gl2.h>
     12 #ifndef GL_GLEXT_PROTOTYPES
     13 #define GL_GLEXT_PROTOTYPES 1
     14 #endif
     15 #include <GLES2/gl2ext.h>
     16 #include <GLES2/gl2extchromium.h>
     17 
     18 #include "base/bind.h"
     19 #include "base/bind_helpers.h"
     20 #include "base/lazy_instance.h"
     21 #include "base/logging.h"
     22 #include "base/memory/scoped_ptr.h"
     23 #include "base/memory/weak_ptr.h"
     24 #include "base/message_loop/message_loop.h"
     25 #include "gpu/command_buffer/client/gles2_implementation.h"
     26 #include "gpu/command_buffer/client/gpu_memory_buffer_factory.h"
     27 #include "gpu/command_buffer/client/image_factory.h"
     28 #include "gpu/command_buffer/client/transfer_buffer.h"
     29 #include "gpu/command_buffer/common/command_buffer.h"
     30 #include "gpu/command_buffer/common/constants.h"
     31 #include "gpu/command_buffer/service/in_process_command_buffer.h"
     32 #include "ui/gfx/size.h"
     33 #include "ui/gl/gl_image.h"
     34 
     35 #if defined(OS_ANDROID)
     36 #include "ui/gl/android/surface_texture_bridge.h"
     37 #endif
     38 
     39 namespace gpu {
     40 
     41 namespace {
     42 
     43 const int32 kCommandBufferSize = 1024 * 1024;
     44 // TODO(kbr): make the transfer buffer size configurable via context
     45 // creation attributes.
     46 const size_t kStartTransferBufferSize = 4 * 1024 * 1024;
     47 const size_t kMinTransferBufferSize = 1 * 256 * 1024;
     48 const size_t kMaxTransferBufferSize = 16 * 1024 * 1024;
     49 
     50 static GpuMemoryBufferFactory* g_gpu_memory_buffer_factory = NULL;
     51 
     52 class GLInProcessContextImpl
     53     : public GLInProcessContext,
     54       public gles2::ImageFactory,
     55       public base::SupportsWeakPtr<GLInProcessContextImpl> {
     56  public:
     57   explicit GLInProcessContextImpl();
     58   virtual ~GLInProcessContextImpl();
     59 
     60   bool Initialize(scoped_refptr<gfx::GLSurface> surface,
     61                   bool is_offscreen,
     62                   bool share_resources,
     63                   gfx::AcceleratedWidget window,
     64                   const gfx::Size& size,
     65                   const char* allowed_extensions,
     66                   const GLInProcessContextAttribs& attribs,
     67                   gfx::GpuPreference gpu_preference);
     68 
     69   // GLInProcessContext implementation:
     70   virtual void SetContextLostCallback(const base::Closure& callback) OVERRIDE;
     71   virtual void SignalSyncPoint(unsigned sync_point,
     72                                const base::Closure& callback) OVERRIDE;
     73   virtual void SignalQuery(unsigned query, const base::Closure& callback)
     74       OVERRIDE;
     75   virtual gles2::GLES2Implementation* GetImplementation() OVERRIDE;
     76 
     77   // ImageFactory implementation:
     78   virtual scoped_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBuffer(
     79       int width, int height, GLenum internalformat,
     80       unsigned* image_id) OVERRIDE;
     81   virtual void DeleteGpuMemoryBuffer(unsigned image_id) OVERRIDE;
     82 
     83 #if defined(OS_ANDROID)
     84   virtual scoped_refptr<gfx::SurfaceTextureBridge> GetSurfaceTexture(
     85       uint32 stream_id) OVERRIDE;
     86 #endif
     87 
     88  private:
     89   void Destroy();
     90   void PollQueryCallbacks();
     91   void CallQueryCallback(size_t index);
     92   void OnContextLost();
     93   void OnSignalSyncPoint(const base::Closure& callback);
     94 
     95   scoped_ptr<gles2::GLES2CmdHelper> gles2_helper_;
     96   scoped_ptr<TransferBuffer> transfer_buffer_;
     97   scoped_ptr<gles2::GLES2Implementation> gles2_implementation_;
     98   scoped_ptr<InProcessCommandBuffer> command_buffer_;
     99 
    100   typedef std::pair<unsigned, base::Closure> QueryCallback;
    101   std::vector<QueryCallback> query_callbacks_;
    102 
    103   unsigned int share_group_id_;
    104   bool context_lost_;
    105   base::Closure context_lost_callback_;
    106 
    107   DISALLOW_COPY_AND_ASSIGN(GLInProcessContextImpl);
    108 };
    109 
    110 base::LazyInstance<base::Lock> g_all_shared_contexts_lock =
    111     LAZY_INSTANCE_INITIALIZER;
    112 base::LazyInstance<std::set<GLInProcessContextImpl*> > g_all_shared_contexts =
    113     LAZY_INSTANCE_INITIALIZER;
    114 
    115 size_t SharedContextCount() {
    116   base::AutoLock lock(g_all_shared_contexts_lock.Get());
    117   return g_all_shared_contexts.Get().size();
    118 }
    119 
    120 scoped_ptr<gfx::GpuMemoryBuffer> GLInProcessContextImpl::CreateGpuMemoryBuffer(
    121     int width, int height, GLenum internalformat, unsigned int* image_id) {
    122   scoped_ptr<gfx::GpuMemoryBuffer> buffer(
    123       g_gpu_memory_buffer_factory->CreateGpuMemoryBuffer(width,
    124                                                          height,
    125                                                          internalformat));
    126   if (!buffer)
    127     return scoped_ptr<gfx::GpuMemoryBuffer>();
    128 
    129   *image_id = command_buffer_->CreateImageForGpuMemoryBuffer(
    130       buffer->GetHandle(), gfx::Size(width, height));
    131   return buffer.Pass();
    132 }
    133 
    134 void GLInProcessContextImpl::DeleteGpuMemoryBuffer(unsigned int image_id) {
    135   command_buffer_->RemoveImage(image_id);
    136 }
    137 
    138 GLInProcessContextImpl::GLInProcessContextImpl()
    139     : share_group_id_(0), context_lost_(false) {}
    140 
    141 GLInProcessContextImpl::~GLInProcessContextImpl() {
    142   {
    143     base::AutoLock lock(g_all_shared_contexts_lock.Get());
    144     g_all_shared_contexts.Get().erase(this);
    145   }
    146   Destroy();
    147 }
    148 
    149 void GLInProcessContextImpl::SignalSyncPoint(unsigned sync_point,
    150                                              const base::Closure& callback) {
    151   DCHECK(!callback.is_null());
    152   base::Closure wrapped_callback = base::Bind(
    153       &GLInProcessContextImpl::OnSignalSyncPoint, AsWeakPtr(), callback);
    154   command_buffer_->SignalSyncPoint(sync_point, wrapped_callback);
    155 }
    156 
    157 gles2::GLES2Implementation* GLInProcessContextImpl::GetImplementation() {
    158   return gles2_implementation_.get();
    159 }
    160 
    161 void GLInProcessContextImpl::SetContextLostCallback(
    162     const base::Closure& callback) {
    163   context_lost_callback_ = callback;
    164 }
    165 
    166 void GLInProcessContextImpl::OnContextLost() {
    167   context_lost_ = true;
    168   if (!context_lost_callback_.is_null()) {
    169     context_lost_callback_.Run();
    170   }
    171 }
    172 
    173 void GLInProcessContextImpl::OnSignalSyncPoint(const base::Closure& callback) {
    174   // TODO: Should it always trigger callbacks?
    175   if (!context_lost_)
    176     callback.Run();
    177 }
    178 
    179 bool GLInProcessContextImpl::Initialize(
    180     scoped_refptr<gfx::GLSurface> surface,
    181     bool is_offscreen,
    182     bool share_resources,
    183     gfx::AcceleratedWidget window,
    184     const gfx::Size& size,
    185     const char* allowed_extensions,
    186     const GLInProcessContextAttribs& attribs,
    187     gfx::GpuPreference gpu_preference) {
    188   DCHECK(size.width() >= 0 && size.height() >= 0);
    189 
    190   const int32 ALPHA_SIZE     = 0x3021;
    191   const int32 BLUE_SIZE      = 0x3022;
    192   const int32 GREEN_SIZE     = 0x3023;
    193   const int32 RED_SIZE       = 0x3024;
    194   const int32 DEPTH_SIZE     = 0x3025;
    195   const int32 STENCIL_SIZE   = 0x3026;
    196   const int32 SAMPLES        = 0x3031;
    197   const int32 SAMPLE_BUFFERS = 0x3032;
    198   const int32 NONE           = 0x3038;
    199 
    200   std::vector<int32> attrib_vector;
    201   if (attribs.alpha_size >= 0) {
    202     attrib_vector.push_back(ALPHA_SIZE);
    203     attrib_vector.push_back(attribs.alpha_size);
    204   }
    205   if (attribs.blue_size >= 0) {
    206     attrib_vector.push_back(BLUE_SIZE);
    207     attrib_vector.push_back(attribs.blue_size);
    208   }
    209   if (attribs.green_size >= 0) {
    210     attrib_vector.push_back(GREEN_SIZE);
    211     attrib_vector.push_back(attribs.green_size);
    212   }
    213   if (attribs.red_size >= 0) {
    214     attrib_vector.push_back(RED_SIZE);
    215     attrib_vector.push_back(attribs.red_size);
    216   }
    217   if (attribs.depth_size >= 0) {
    218     attrib_vector.push_back(DEPTH_SIZE);
    219     attrib_vector.push_back(attribs.depth_size);
    220   }
    221   if (attribs.stencil_size >= 0) {
    222     attrib_vector.push_back(STENCIL_SIZE);
    223     attrib_vector.push_back(attribs.stencil_size);
    224   }
    225   if (attribs.samples >= 0) {
    226     attrib_vector.push_back(SAMPLES);
    227     attrib_vector.push_back(attribs.samples);
    228   }
    229   if (attribs.sample_buffers >= 0) {
    230     attrib_vector.push_back(SAMPLE_BUFFERS);
    231     attrib_vector.push_back(attribs.sample_buffers);
    232   }
    233   attrib_vector.push_back(NONE);
    234 
    235   base::Closure wrapped_callback =
    236       base::Bind(&GLInProcessContextImpl::OnContextLost, AsWeakPtr());
    237   command_buffer_.reset(new InProcessCommandBuffer());
    238 
    239   scoped_ptr<base::AutoLock> scoped_shared_context_lock;
    240   scoped_refptr<gles2::ShareGroup> share_group;
    241   if (share_resources) {
    242     scoped_shared_context_lock.reset(
    243         new base::AutoLock(g_all_shared_contexts_lock.Get()));
    244     for (std::set<GLInProcessContextImpl*>::const_iterator it =
    245              g_all_shared_contexts.Get().begin();
    246          it != g_all_shared_contexts.Get().end();
    247          it++) {
    248       const GLInProcessContextImpl* context = *it;
    249       if (!context->context_lost_) {
    250         share_group = context->gles2_implementation_->share_group();
    251         DCHECK(share_group);
    252         share_group_id_ = context->share_group_id_;
    253         break;
    254       }
    255       share_group_id_ = std::max(share_group_id_, context->share_group_id_);
    256     }
    257     if (!share_group && !++share_group_id_)
    258         ++share_group_id_;
    259   }
    260   if (!command_buffer_->Initialize(surface,
    261                                    is_offscreen,
    262                                    share_resources,
    263                                    window,
    264                                    size,
    265                                    allowed_extensions,
    266                                    attrib_vector,
    267                                    gpu_preference,
    268                                    wrapped_callback,
    269                                    share_group_id_)) {
    270     LOG(INFO) << "Failed to initialize InProcessCommmandBuffer";
    271     return false;
    272   }
    273 
    274   // Create the GLES2 helper, which writes the command buffer protocol.
    275   gles2_helper_.reset(new gles2::GLES2CmdHelper(command_buffer_.get()));
    276   if (!gles2_helper_->Initialize(kCommandBufferSize)) {
    277     LOG(INFO) << "Failed to initialize GLES2CmdHelper";
    278     Destroy();
    279     return false;
    280   }
    281 
    282   // Create a transfer buffer.
    283   transfer_buffer_.reset(new TransferBuffer(gles2_helper_.get()));
    284 
    285   // Create the object exposing the OpenGL API.
    286   gles2_implementation_.reset(new gles2::GLES2Implementation(
    287       gles2_helper_.get(),
    288       share_group,
    289       transfer_buffer_.get(),
    290       false,
    291       this));
    292 
    293   if (share_resources) {
    294     g_all_shared_contexts.Get().insert(this);
    295     scoped_shared_context_lock.reset();
    296   }
    297 
    298   if (!gles2_implementation_->Initialize(
    299       kStartTransferBufferSize,
    300       kMinTransferBufferSize,
    301       kMaxTransferBufferSize)) {
    302     return false;
    303   }
    304 
    305   return true;
    306 }
    307 
    308 void GLInProcessContextImpl::Destroy() {
    309   while (!query_callbacks_.empty()) {
    310     CallQueryCallback(0);
    311   }
    312 
    313   if (gles2_implementation_) {
    314     // First flush the context to ensure that any pending frees of resources
    315     // are completed. Otherwise, if this context is part of a share group,
    316     // those resources might leak. Also, any remaining side effects of commands
    317     // issued on this context might not be visible to other contexts in the
    318     // share group.
    319     gles2_implementation_->Flush();
    320 
    321     gles2_implementation_.reset();
    322   }
    323 
    324   transfer_buffer_.reset();
    325   gles2_helper_.reset();
    326   command_buffer_.reset();
    327 }
    328 
    329 void GLInProcessContextImpl::CallQueryCallback(size_t index) {
    330   DCHECK_LT(index, query_callbacks_.size());
    331   QueryCallback query_callback = query_callbacks_[index];
    332   query_callbacks_[index] = query_callbacks_.back();
    333   query_callbacks_.pop_back();
    334   query_callback.second.Run();
    335 }
    336 
    337 // TODO(sievers): Move this to the service side
    338 void GLInProcessContextImpl::PollQueryCallbacks() {
    339   for (size_t i = 0; i < query_callbacks_.size();) {
    340     unsigned query = query_callbacks_[i].first;
    341     GLuint param = 0;
    342     gles2::GLES2Implementation* gl = GetImplementation();
    343     if (gl->IsQueryEXT(query)) {
    344       gl->GetQueryObjectuivEXT(query, GL_QUERY_RESULT_AVAILABLE_EXT, &param);
    345     } else {
    346       param = 1;
    347     }
    348     if (param) {
    349       CallQueryCallback(i);
    350     } else {
    351       i++;
    352     }
    353   }
    354   if (!query_callbacks_.empty()) {
    355     base::MessageLoop::current()->PostDelayedTask(
    356         FROM_HERE,
    357         base::Bind(&GLInProcessContextImpl::PollQueryCallbacks,
    358                    this->AsWeakPtr()),
    359         base::TimeDelta::FromMilliseconds(5));
    360   }
    361 }
    362 
    363 void GLInProcessContextImpl::SignalQuery(
    364     unsigned query,
    365     const base::Closure& callback) {
    366   query_callbacks_.push_back(std::make_pair(query, callback));
    367   // If size > 1, there is already a poll callback pending.
    368   if (query_callbacks_.size() == 1) {
    369     PollQueryCallbacks();
    370   }
    371 }
    372 
    373 #if defined(OS_ANDROID)
    374 scoped_refptr<gfx::SurfaceTextureBridge>
    375 GLInProcessContextImpl::GetSurfaceTexture(uint32 stream_id) {
    376   return command_buffer_->GetSurfaceTexture(stream_id);
    377 }
    378 #endif
    379 
    380 }  // anonymous namespace
    381 
    382 GLInProcessContextAttribs::GLInProcessContextAttribs()
    383     : alpha_size(-1),
    384       blue_size(-1),
    385       green_size(-1),
    386       red_size(-1),
    387       depth_size(-1),
    388       stencil_size(-1),
    389       samples(-1),
    390       sample_buffers(-1) {}
    391 
    392 // static
    393 GLInProcessContext* GLInProcessContext::CreateContext(
    394     bool is_offscreen,
    395     gfx::AcceleratedWidget window,
    396     const gfx::Size& size,
    397     bool share_resources,
    398     const char* allowed_extensions,
    399     const GLInProcessContextAttribs& attribs,
    400     gfx::GpuPreference gpu_preference) {
    401   scoped_ptr<GLInProcessContextImpl> context(
    402       new GLInProcessContextImpl());
    403   if (!context->Initialize(
    404       NULL /* surface */,
    405       is_offscreen,
    406       share_resources,
    407       window,
    408       size,
    409       allowed_extensions,
    410       attribs,
    411       gpu_preference))
    412     return NULL;
    413 
    414   return context.release();
    415 }
    416 
    417 // static
    418 GLInProcessContext* GLInProcessContext::CreateWithSurface(
    419     scoped_refptr<gfx::GLSurface> surface,
    420     bool share_resources,
    421     const char* allowed_extensions,
    422     const GLInProcessContextAttribs& attribs,
    423     gfx::GpuPreference gpu_preference) {
    424   scoped_ptr<GLInProcessContextImpl> context(
    425       new GLInProcessContextImpl());
    426   if (!context->Initialize(
    427       surface,
    428       surface->IsOffscreen(),
    429       share_resources,
    430       gfx::kNullAcceleratedWidget,
    431       surface->GetSize(),
    432       allowed_extensions,
    433       attribs,
    434       gpu_preference))
    435     return NULL;
    436 
    437   return context.release();
    438 }
    439 
    440 // static
    441 void GLInProcessContext::SetGpuMemoryBufferFactory(
    442     GpuMemoryBufferFactory* factory) {
    443   DCHECK_EQ(0u, SharedContextCount());
    444   g_gpu_memory_buffer_factory = factory;
    445 }
    446 
    447 }  // namespace gpu
    448