Home | History | Annotate | Download | only in service
      1 // Copyright 2014 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/service/texture_definition.h"
      6 
      7 #include "gpu/command_buffer/service/texture_manager.h"
      8 #include "ui/gl/gl_image.h"
      9 #include "ui/gl/gl_implementation.h"
     10 #include "ui/gl/scoped_binders.h"
     11 
     12 #if !defined(OS_MACOSX)
     13 #include "ui/gl/gl_surface_egl.h"
     14 #endif
     15 
     16 namespace gpu {
     17 namespace gles2 {
     18 
     19 namespace {
     20 
     21 class GLImageSync : public gfx::GLImage {
     22  public:
     23   explicit GLImageSync(
     24       const scoped_refptr<NativeImageBuffer>& buffer);
     25 
     26   // Implement GLImage.
     27   virtual void Destroy() OVERRIDE;
     28   virtual gfx::Size GetSize() OVERRIDE;
     29   virtual bool BindTexImage(unsigned target) OVERRIDE;
     30   virtual void ReleaseTexImage(unsigned target) OVERRIDE;
     31   virtual void WillUseTexImage() OVERRIDE;
     32   virtual void WillModifyTexImage() OVERRIDE;
     33   virtual void DidModifyTexImage() OVERRIDE;
     34 
     35   virtual void DidUseTexImage() OVERRIDE;
     36   virtual void SetReleaseAfterUse() OVERRIDE;
     37 
     38  protected:
     39   virtual ~GLImageSync();
     40 
     41  private:
     42   scoped_refptr<NativeImageBuffer> buffer_;
     43 
     44   DISALLOW_COPY_AND_ASSIGN(GLImageSync);
     45 };
     46 
     47 GLImageSync::GLImageSync(const scoped_refptr<NativeImageBuffer>& buffer)
     48     : buffer_(buffer) {
     49   if (buffer)
     50     buffer->AddClient(this);
     51 }
     52 
     53 GLImageSync::~GLImageSync() {
     54   if (buffer_)
     55     buffer_->RemoveClient(this);
     56 }
     57 
     58 void GLImageSync::Destroy() {}
     59 
     60 gfx::Size GLImageSync::GetSize() {
     61   NOTREACHED();
     62   return gfx::Size();
     63 }
     64 
     65 bool GLImageSync::BindTexImage(unsigned target) {
     66   NOTREACHED();
     67   return false;
     68 }
     69 
     70 void GLImageSync::ReleaseTexImage(unsigned target) {
     71   NOTREACHED();
     72 }
     73 
     74 void GLImageSync::WillUseTexImage() {
     75   if (buffer_)
     76     buffer_->WillRead(this);
     77 }
     78 
     79 void GLImageSync::DidUseTexImage() {
     80   if (buffer_)
     81     buffer_->DidRead(this);
     82 }
     83 
     84 void GLImageSync::WillModifyTexImage() {
     85   if (buffer_)
     86     buffer_->WillWrite(this);
     87 }
     88 
     89 void GLImageSync::DidModifyTexImage() {
     90   if (buffer_)
     91     buffer_->DidWrite(this);
     92 }
     93 
     94 void GLImageSync::SetReleaseAfterUse() {
     95   NOTREACHED();
     96 }
     97 
     98 #if !defined(OS_MACOSX)
     99 class NativeImageBufferEGL : public NativeImageBuffer {
    100  public:
    101   static scoped_refptr<NativeImageBufferEGL> Create(GLuint texture_id);
    102 
    103  private:
    104   explicit NativeImageBufferEGL(EGLDisplay display, EGLImageKHR image);
    105   virtual ~NativeImageBufferEGL();
    106   virtual void BindToTexture(GLenum target) OVERRIDE;
    107 
    108   EGLDisplay egl_display_;
    109   EGLImageKHR egl_image_;
    110 
    111   DISALLOW_COPY_AND_ASSIGN(NativeImageBufferEGL);
    112 };
    113 
    114 scoped_refptr<NativeImageBufferEGL> NativeImageBufferEGL::Create(
    115     GLuint texture_id) {
    116   EGLDisplay egl_display = gfx::GLSurfaceEGL::GetHardwareDisplay();
    117   EGLContext egl_context = eglGetCurrentContext();
    118 
    119   DCHECK_NE(EGL_NO_CONTEXT, egl_context);
    120   DCHECK_NE(EGL_NO_DISPLAY, egl_display);
    121   DCHECK(glIsTexture(texture_id));
    122 
    123   DCHECK(gfx::g_driver_egl.ext.b_EGL_KHR_image_base &&
    124          gfx::g_driver_egl.ext.b_EGL_KHR_gl_texture_2D_image &&
    125          gfx::g_driver_gl.ext.b_GL_OES_EGL_image &&
    126          gfx::g_driver_egl.ext.b_EGL_KHR_fence_sync);
    127 
    128   const EGLint egl_attrib_list[] = {
    129       EGL_GL_TEXTURE_LEVEL_KHR, 0, EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
    130   EGLClientBuffer egl_buffer = reinterpret_cast<EGLClientBuffer>(texture_id);
    131   EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR; // TODO
    132 
    133   EGLImageKHR egl_image = eglCreateImageKHR(
    134       egl_display, egl_context, egl_target, egl_buffer, egl_attrib_list);
    135 
    136   if (egl_image == EGL_NO_IMAGE_KHR)
    137     return NULL;
    138 
    139   return new NativeImageBufferEGL(egl_display, egl_image);
    140 }
    141 
    142 NativeImageBufferEGL::NativeImageBufferEGL(EGLDisplay display,
    143                                            EGLImageKHR image)
    144     : egl_display_(display), egl_image_(image) {
    145   DCHECK(egl_display_ != EGL_NO_DISPLAY);
    146   DCHECK(egl_image_ != EGL_NO_IMAGE_KHR);
    147 }
    148 
    149 NativeImageBufferEGL::~NativeImageBufferEGL() {
    150   if (egl_image_ != EGL_NO_IMAGE_KHR)
    151     eglDestroyImageKHR(egl_display_, egl_image_);
    152 }
    153 
    154 void NativeImageBufferEGL::BindToTexture(GLenum target) {
    155   DCHECK(egl_image_ != EGL_NO_IMAGE_KHR);
    156   glEGLImageTargetTexture2DOES(target, egl_image_);
    157   DCHECK_EQ(static_cast<EGLint>(EGL_SUCCESS), eglGetError());
    158   DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
    159 }
    160 #endif
    161 
    162 class NativeImageBufferStub : public NativeImageBuffer {
    163  public:
    164   NativeImageBufferStub() {}
    165 
    166  private:
    167   virtual ~NativeImageBufferStub() {}
    168   virtual void BindToTexture(GLenum target) OVERRIDE {}
    169 
    170   DISALLOW_COPY_AND_ASSIGN(NativeImageBufferStub);
    171 };
    172 
    173 }  // anonymous namespace
    174 
    175 // static
    176 scoped_refptr<NativeImageBuffer> NativeImageBuffer::Create(GLuint texture_id) {
    177   switch (gfx::GetGLImplementation()) {
    178 #if !defined(OS_MACOSX)
    179     case gfx::kGLImplementationEGLGLES2:
    180       return NativeImageBufferEGL::Create(texture_id);
    181 #endif
    182     case gfx::kGLImplementationMockGL:
    183       return new NativeImageBufferStub;
    184     default:
    185       NOTREACHED();
    186       return NULL;
    187   }
    188 }
    189 
    190 NativeImageBuffer::ClientInfo::ClientInfo(gfx::GLImage* client)
    191     : client(client), needs_wait_before_read(true) {}
    192 
    193 NativeImageBuffer::ClientInfo::~ClientInfo() {}
    194 
    195 NativeImageBuffer::NativeImageBuffer() : write_client_(NULL) {
    196   write_fence_.reset(gfx::GLFence::Create());
    197 }
    198 
    199 NativeImageBuffer::~NativeImageBuffer() {
    200   DCHECK(client_infos_.empty());
    201 }
    202 
    203 void NativeImageBuffer::AddClient(gfx::GLImage* client) {
    204   base::AutoLock lock(lock_);
    205   client_infos_.push_back(ClientInfo(client));
    206 }
    207 
    208 void NativeImageBuffer::RemoveClient(gfx::GLImage* client) {
    209   base::AutoLock lock(lock_);
    210   if (write_client_ == client)
    211     write_client_ = NULL;
    212   for (std::list<ClientInfo>::iterator it = client_infos_.begin();
    213        it != client_infos_.end();
    214        it++) {
    215     if (it->client == client) {
    216       client_infos_.erase(it);
    217       return;
    218     }
    219   }
    220   NOTREACHED();
    221 }
    222 
    223 bool NativeImageBuffer::IsClient(gfx::GLImage* client) {
    224   base::AutoLock lock(lock_);
    225   for (std::list<ClientInfo>::iterator it = client_infos_.begin();
    226        it != client_infos_.end();
    227        it++) {
    228     if (it->client == client)
    229       return true;
    230   }
    231   return false;
    232 }
    233 
    234 void NativeImageBuffer::WillRead(gfx::GLImage* client) {
    235   base::AutoLock lock(lock_);
    236   if (!write_fence_.get() || write_client_ == client)
    237     return;
    238 
    239   for (std::list<ClientInfo>::iterator it = client_infos_.begin();
    240        it != client_infos_.end();
    241        it++) {
    242     if (it->client == client) {
    243       if (it->needs_wait_before_read) {
    244         it->needs_wait_before_read = false;
    245         write_fence_->ServerWait();
    246       }
    247       return;
    248     }
    249   }
    250   NOTREACHED();
    251 }
    252 
    253 void NativeImageBuffer::WillWrite(gfx::GLImage* client) {
    254   base::AutoLock lock(lock_);
    255   if (write_client_ != client)
    256     write_fence_->ServerWait();
    257 
    258   for (std::list<ClientInfo>::iterator it = client_infos_.begin();
    259        it != client_infos_.end();
    260        it++) {
    261     if (it->read_fence.get() && it->client != client)
    262       it->read_fence->ServerWait();
    263   }
    264 }
    265 
    266 void NativeImageBuffer::DidRead(gfx::GLImage* client) {
    267   base::AutoLock lock(lock_);
    268   for (std::list<ClientInfo>::iterator it = client_infos_.begin();
    269        it != client_infos_.end();
    270        it++) {
    271     if (it->client == client) {
    272       it->read_fence = make_linked_ptr(gfx::GLFence::Create());
    273       return;
    274     }
    275   }
    276   NOTREACHED();
    277 }
    278 
    279 void NativeImageBuffer::DidWrite(gfx::GLImage* client) {
    280   base::AutoLock lock(lock_);
    281   // TODO(sievers): crbug.com/352419
    282   // This is super-risky. We need to somehow find out about when the current
    283   // context gets flushed, so that we will only ever wait on the write fence
    284   // (esp. from another context) if it was flushed and is guaranteed to clear.
    285   // On the other hand, proactively flushing here is not feasible in terms
    286   // of perf when there are multiple draw calls per frame.
    287   write_fence_.reset(gfx::GLFence::CreateWithoutFlush());
    288   write_client_ = client;
    289   for (std::list<ClientInfo>::iterator it = client_infos_.begin();
    290        it != client_infos_.end();
    291        it++) {
    292     it->needs_wait_before_read = true;
    293   }
    294 }
    295 
    296 TextureDefinition::LevelInfo::LevelInfo(GLenum target,
    297                                         GLenum internal_format,
    298                                         GLsizei width,
    299                                         GLsizei height,
    300                                         GLsizei depth,
    301                                         GLint border,
    302                                         GLenum format,
    303                                         GLenum type,
    304                                         bool cleared)
    305     : target(target),
    306       internal_format(internal_format),
    307       width(width),
    308       height(height),
    309       depth(depth),
    310       border(border),
    311       format(format),
    312       type(type),
    313       cleared(cleared) {}
    314 
    315 TextureDefinition::LevelInfo::~LevelInfo() {}
    316 
    317 TextureDefinition::TextureDefinition(
    318     GLenum target,
    319     Texture* texture,
    320     unsigned int version,
    321     const scoped_refptr<NativeImageBuffer>& image_buffer)
    322     : version_(version),
    323       target_(target),
    324       image_buffer_(image_buffer ? image_buffer : NativeImageBuffer::Create(
    325                                                       texture->service_id())),
    326       min_filter_(texture->min_filter()),
    327       mag_filter_(texture->mag_filter()),
    328       wrap_s_(texture->wrap_s()),
    329       wrap_t_(texture->wrap_t()),
    330       usage_(texture->usage()),
    331       immutable_(texture->IsImmutable()) {
    332 
    333   // TODO
    334   DCHECK(!texture->level_infos_.empty());
    335   DCHECK(!texture->level_infos_[0].empty());
    336   DCHECK(!texture->NeedsMips());
    337   DCHECK(texture->level_infos_[0][0].width);
    338   DCHECK(texture->level_infos_[0][0].height);
    339 
    340   scoped_refptr<gfx::GLImage> gl_image(new GLImageSync(image_buffer_));
    341   texture->SetLevelImage(NULL, target, 0, gl_image);
    342 
    343   // TODO: all levels
    344   level_infos_.clear();
    345   const Texture::LevelInfo& level = texture->level_infos_[0][0];
    346   LevelInfo info(level.target,
    347                  level.internal_format,
    348                  level.width,
    349                  level.height,
    350                  level.depth,
    351                  level.border,
    352                  level.format,
    353                  level.type,
    354                  level.cleared);
    355   std::vector<LevelInfo> infos;
    356   infos.push_back(info);
    357   level_infos_.push_back(infos);
    358 
    359 }
    360 
    361 TextureDefinition::~TextureDefinition() {
    362 }
    363 
    364 Texture* TextureDefinition::CreateTexture() const {
    365   if (!image_buffer_)
    366     return NULL;
    367 
    368   GLuint texture_id;
    369   glGenTextures(1, &texture_id);
    370 
    371   Texture* texture(new Texture(texture_id));
    372   UpdateTexture(texture);
    373 
    374   return texture;
    375 }
    376 
    377 void TextureDefinition::UpdateTexture(Texture* texture) const {
    378   gfx::ScopedTextureBinder texture_binder(target_, texture->service_id());
    379   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_);
    380   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_);
    381   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s_);
    382   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t_);
    383   if (image_buffer_)
    384     image_buffer_->BindToTexture(target_);
    385   // We have to make sure the changes are visible to other clients in this share
    386   // group. As far as the clients are concerned, the mailbox semantics only
    387   // demand a single flush from the client after changes are first made,
    388   // and it is not visible to them when another share group boundary is crossed.
    389   // We could probably track this and be a bit smarter about when to flush
    390   // though.
    391   glFlush();
    392 
    393   texture->level_infos_.resize(1);
    394   for (size_t i = 0; i < level_infos_.size(); i++) {
    395     const LevelInfo& base_info = level_infos_[i][0];
    396     const size_t levels_needed = TextureManager::ComputeMipMapCount(
    397         base_info.target, base_info.width, base_info.height, base_info.depth);
    398     DCHECK(level_infos_.size() <= levels_needed);
    399     texture->level_infos_[0].resize(levels_needed);
    400     for (size_t n = 0; n < level_infos_.size(); n++) {
    401       const LevelInfo& info = level_infos_[i][n];
    402       texture->SetLevelInfo(NULL,
    403                             info.target,
    404                             i,
    405                             info.internal_format,
    406                             info.width,
    407                             info.height,
    408                             info.depth,
    409                             info.border,
    410                             info.format,
    411                             info.type,
    412                             info.cleared);
    413     }
    414   }
    415   if (image_buffer_)
    416     texture->SetLevelImage(NULL, target_, 0, new GLImageSync(image_buffer_));
    417 
    418   texture->target_ = target_;
    419   texture->SetImmutable(immutable_);
    420   texture->min_filter_ = min_filter_;
    421   texture->mag_filter_ = mag_filter_;
    422   texture->wrap_s_ = wrap_s_;
    423   texture->wrap_t_ = wrap_t_;
    424   texture->usage_ = usage_;
    425 }
    426 
    427 bool TextureDefinition::Matches(const Texture* texture) const {
    428   DCHECK(target_ == texture->target());
    429   if (texture->min_filter_ != min_filter_ ||
    430       texture->mag_filter_ != mag_filter_ ||
    431       texture->wrap_s_ != wrap_s_ ||
    432       texture->wrap_t_ != wrap_t_) {
    433     return false;
    434   }
    435 
    436   // All structural changes should have orphaned the texture.
    437   if (image_buffer_ && !texture->GetLevelImage(texture->target(), 0))
    438     return false;
    439 
    440   return true;
    441 }
    442 
    443 }  // namespace gles2
    444 }  // namespace gpu
    445