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