| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "gpu/command_buffer/service/texture_definition.h" |
| |
| #include <list> |
| |
| #include "base/memory/linked_ptr.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/synchronization/lock.h" |
| #include "gpu/command_buffer/service/texture_manager.h" |
| #include "ui/gl/gl_image.h" |
| #include "ui/gl/gl_implementation.h" |
| #include "ui/gl/scoped_binders.h" |
| |
| #if !defined(OS_MACOSX) |
| #include "ui/gl/gl_fence_egl.h" |
| #include "ui/gl/gl_surface_egl.h" |
| #endif |
| |
| namespace gpu { |
| namespace gles2 { |
| |
| namespace { |
| |
| class GLImageSync : public gfx::GLImage { |
| public: |
| explicit GLImageSync(const scoped_refptr<NativeImageBuffer>& buffer, |
| const gfx::Size& size); |
| |
| // Implement GLImage. |
| virtual void Destroy(bool have_context) OVERRIDE; |
| virtual gfx::Size GetSize() OVERRIDE; |
| virtual bool BindTexImage(unsigned target) OVERRIDE; |
| virtual void ReleaseTexImage(unsigned target) OVERRIDE; |
| virtual void WillUseTexImage() OVERRIDE; |
| virtual void WillModifyTexImage() OVERRIDE; |
| virtual void DidModifyTexImage() OVERRIDE; |
| virtual void DidUseTexImage() OVERRIDE; |
| virtual bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget, |
| int z_order, |
| gfx::OverlayTransform transform, |
| const gfx::Rect& bounds_rect, |
| const gfx::RectF& crop_rect) OVERRIDE; |
| virtual void SetReleaseAfterUse() OVERRIDE; |
| |
| protected: |
| virtual ~GLImageSync(); |
| |
| private: |
| scoped_refptr<NativeImageBuffer> buffer_; |
| gfx::Size size_; |
| |
| DISALLOW_COPY_AND_ASSIGN(GLImageSync); |
| }; |
| |
| GLImageSync::GLImageSync(const scoped_refptr<NativeImageBuffer>& buffer, |
| const gfx::Size& size) |
| : buffer_(buffer), size_(size) { |
| if (buffer) |
| buffer->AddClient(this); |
| } |
| |
| GLImageSync::~GLImageSync() { |
| if (buffer_) |
| buffer_->RemoveClient(this); |
| } |
| |
| void GLImageSync::Destroy(bool have_context) { |
| } |
| |
| gfx::Size GLImageSync::GetSize() { |
| return size_; |
| } |
| |
| bool GLImageSync::BindTexImage(unsigned target) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| void GLImageSync::ReleaseTexImage(unsigned target) { |
| NOTREACHED(); |
| } |
| |
| void GLImageSync::WillUseTexImage() { |
| if (buffer_) |
| buffer_->WillRead(this); |
| } |
| |
| void GLImageSync::DidUseTexImage() { |
| if (buffer_) |
| buffer_->DidRead(this); |
| } |
| |
| void GLImageSync::WillModifyTexImage() { |
| if (buffer_) |
| buffer_->WillWrite(this); |
| } |
| |
| void GLImageSync::DidModifyTexImage() { |
| if (buffer_) |
| buffer_->DidWrite(this); |
| } |
| |
| bool GLImageSync::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, |
| int z_order, |
| gfx::OverlayTransform transform, |
| const gfx::Rect& bounds_rect, |
| const gfx::RectF& crop_rect) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| void GLImageSync::SetReleaseAfterUse() { |
| NOTREACHED(); |
| } |
| |
| #if !defined(OS_MACOSX) |
| class NativeImageBufferEGL : public NativeImageBuffer { |
| public: |
| static scoped_refptr<NativeImageBufferEGL> Create(GLuint texture_id); |
| |
| private: |
| NativeImageBufferEGL(EGLDisplay display, EGLImageKHR image); |
| virtual ~NativeImageBufferEGL(); |
| virtual void AddClient(gfx::GLImage* client) OVERRIDE; |
| virtual void RemoveClient(gfx::GLImage* client) OVERRIDE; |
| virtual bool IsClient(gfx::GLImage* client) OVERRIDE; |
| virtual void BindToTexture(GLenum target) OVERRIDE; |
| virtual void WillRead(gfx::GLImage* client) OVERRIDE; |
| virtual void WillWrite(gfx::GLImage* client) OVERRIDE; |
| virtual void DidRead(gfx::GLImage* client) OVERRIDE; |
| virtual void DidWrite(gfx::GLImage* client) OVERRIDE; |
| |
| EGLDisplay egl_display_; |
| EGLImageKHR egl_image_; |
| |
| base::Lock lock_; |
| |
| struct ClientInfo { |
| ClientInfo(gfx::GLImage* client); |
| ~ClientInfo(); |
| |
| gfx::GLImage* client; |
| bool needs_wait_before_read; |
| linked_ptr<gfx::GLFence> read_fence; |
| }; |
| std::list<ClientInfo> client_infos_; |
| scoped_ptr<gfx::GLFence> write_fence_; |
| gfx::GLImage* write_client_; |
| |
| DISALLOW_COPY_AND_ASSIGN(NativeImageBufferEGL); |
| }; |
| |
| scoped_refptr<NativeImageBufferEGL> NativeImageBufferEGL::Create( |
| GLuint texture_id) { |
| EGLDisplay egl_display = gfx::GLSurfaceEGL::GetHardwareDisplay(); |
| EGLContext egl_context = eglGetCurrentContext(); |
| |
| DCHECK_NE(EGL_NO_CONTEXT, egl_context); |
| DCHECK_NE(EGL_NO_DISPLAY, egl_display); |
| DCHECK(glIsTexture(texture_id)); |
| |
| DCHECK(gfx::g_driver_egl.ext.b_EGL_KHR_image_base && |
| gfx::g_driver_egl.ext.b_EGL_KHR_gl_texture_2D_image && |
| gfx::g_driver_gl.ext.b_GL_OES_EGL_image && |
| gfx::g_driver_egl.ext.b_EGL_KHR_fence_sync); |
| |
| const EGLint egl_attrib_list[] = { |
| EGL_GL_TEXTURE_LEVEL_KHR, 0, EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; |
| EGLClientBuffer egl_buffer = reinterpret_cast<EGLClientBuffer>(texture_id); |
| EGLenum egl_target = EGL_GL_TEXTURE_2D_KHR; // TODO |
| |
| EGLImageKHR egl_image = eglCreateImageKHR( |
| egl_display, egl_context, egl_target, egl_buffer, egl_attrib_list); |
| |
| if (egl_image == EGL_NO_IMAGE_KHR) |
| return NULL; |
| |
| return new NativeImageBufferEGL(egl_display, egl_image); |
| } |
| |
| NativeImageBufferEGL::ClientInfo::ClientInfo(gfx::GLImage* client) |
| : client(client), needs_wait_before_read(true) {} |
| |
| NativeImageBufferEGL::ClientInfo::~ClientInfo() {} |
| |
| NativeImageBufferEGL::NativeImageBufferEGL(EGLDisplay display, |
| EGLImageKHR image) |
| : NativeImageBuffer(), |
| egl_display_(display), |
| egl_image_(image), |
| write_fence_(new gfx::GLFenceEGL(true)), |
| write_client_(NULL) { |
| DCHECK(egl_display_ != EGL_NO_DISPLAY); |
| DCHECK(egl_image_ != EGL_NO_IMAGE_KHR); |
| } |
| |
| NativeImageBufferEGL::~NativeImageBufferEGL() { |
| DCHECK(client_infos_.empty()); |
| if (egl_image_ != EGL_NO_IMAGE_KHR) |
| eglDestroyImageKHR(egl_display_, egl_image_); |
| } |
| |
| void NativeImageBufferEGL::AddClient(gfx::GLImage* client) { |
| base::AutoLock lock(lock_); |
| client_infos_.push_back(ClientInfo(client)); |
| } |
| |
| void NativeImageBufferEGL::RemoveClient(gfx::GLImage* client) { |
| base::AutoLock lock(lock_); |
| if (write_client_ == client) |
| write_client_ = NULL; |
| for (std::list<ClientInfo>::iterator it = client_infos_.begin(); |
| it != client_infos_.end(); |
| it++) { |
| if (it->client == client) { |
| client_infos_.erase(it); |
| return; |
| } |
| } |
| NOTREACHED(); |
| } |
| |
| bool NativeImageBufferEGL::IsClient(gfx::GLImage* client) { |
| base::AutoLock lock(lock_); |
| for (std::list<ClientInfo>::iterator it = client_infos_.begin(); |
| it != client_infos_.end(); |
| it++) { |
| if (it->client == client) |
| return true; |
| } |
| return false; |
| } |
| |
| void NativeImageBufferEGL::BindToTexture(GLenum target) { |
| DCHECK(egl_image_ != EGL_NO_IMAGE_KHR); |
| glEGLImageTargetTexture2DOES(target, egl_image_); |
| DCHECK_EQ(static_cast<EGLint>(EGL_SUCCESS), eglGetError()); |
| DCHECK_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError()); |
| } |
| |
| void NativeImageBufferEGL::WillRead(gfx::GLImage* client) { |
| base::AutoLock lock(lock_); |
| if (!write_fence_.get() || write_client_ == client) |
| return; |
| |
| for (std::list<ClientInfo>::iterator it = client_infos_.begin(); |
| it != client_infos_.end(); |
| it++) { |
| if (it->client == client) { |
| if (it->needs_wait_before_read) { |
| it->needs_wait_before_read = false; |
| write_fence_->ServerWait(); |
| } |
| return; |
| } |
| } |
| NOTREACHED(); |
| } |
| |
| void NativeImageBufferEGL::WillWrite(gfx::GLImage* client) { |
| base::AutoLock lock(lock_); |
| if (write_client_ != client) |
| write_fence_->ServerWait(); |
| |
| for (std::list<ClientInfo>::iterator it = client_infos_.begin(); |
| it != client_infos_.end(); |
| it++) { |
| if (it->read_fence.get() && it->client != client) |
| it->read_fence->ServerWait(); |
| } |
| } |
| |
| void NativeImageBufferEGL::DidRead(gfx::GLImage* client) { |
| base::AutoLock lock(lock_); |
| for (std::list<ClientInfo>::iterator it = client_infos_.begin(); |
| it != client_infos_.end(); |
| it++) { |
| if (it->client == client) { |
| it->read_fence = make_linked_ptr(new gfx::GLFenceEGL(true)); |
| return; |
| } |
| } |
| NOTREACHED(); |
| } |
| |
| void NativeImageBufferEGL::DidWrite(gfx::GLImage* client) { |
| base::AutoLock lock(lock_); |
| // Sharing semantics require the client to flush in order to make changes |
| // visible to other clients. |
| write_fence_.reset(new gfx::GLFenceEGL(false)); |
| write_client_ = client; |
| for (std::list<ClientInfo>::iterator it = client_infos_.begin(); |
| it != client_infos_.end(); |
| it++) { |
| it->needs_wait_before_read = true; |
| } |
| } |
| |
| #endif |
| |
| class NativeImageBufferStub : public NativeImageBuffer { |
| public: |
| NativeImageBufferStub() : NativeImageBuffer() {} |
| |
| private: |
| virtual ~NativeImageBufferStub() {} |
| virtual void AddClient(gfx::GLImage* client) OVERRIDE {} |
| virtual void RemoveClient(gfx::GLImage* client) OVERRIDE {} |
| virtual bool IsClient(gfx::GLImage* client) OVERRIDE { return true; } |
| virtual void BindToTexture(GLenum target) OVERRIDE {} |
| virtual void WillRead(gfx::GLImage* client) OVERRIDE {} |
| virtual void WillWrite(gfx::GLImage* client) OVERRIDE {} |
| virtual void DidRead(gfx::GLImage* client) OVERRIDE {} |
| virtual void DidWrite(gfx::GLImage* client) OVERRIDE {} |
| |
| DISALLOW_COPY_AND_ASSIGN(NativeImageBufferStub); |
| }; |
| |
| } // anonymous namespace |
| |
| // static |
| scoped_refptr<NativeImageBuffer> NativeImageBuffer::Create(GLuint texture_id) { |
| switch (gfx::GetGLImplementation()) { |
| #if !defined(OS_MACOSX) |
| case gfx::kGLImplementationEGLGLES2: |
| return NativeImageBufferEGL::Create(texture_id); |
| #endif |
| case gfx::kGLImplementationMockGL: |
| return new NativeImageBufferStub; |
| default: |
| NOTREACHED(); |
| return NULL; |
| } |
| } |
| |
| TextureDefinition::LevelInfo::LevelInfo(GLenum target, |
| GLenum internal_format, |
| GLsizei width, |
| GLsizei height, |
| GLsizei depth, |
| GLint border, |
| GLenum format, |
| GLenum type, |
| bool cleared) |
| : target(target), |
| internal_format(internal_format), |
| width(width), |
| height(height), |
| depth(depth), |
| border(border), |
| format(format), |
| type(type), |
| cleared(cleared) {} |
| |
| TextureDefinition::LevelInfo::~LevelInfo() {} |
| |
| TextureDefinition::TextureDefinition( |
| GLenum target, |
| Texture* texture, |
| unsigned int version, |
| const scoped_refptr<NativeImageBuffer>& image_buffer) |
| : version_(version), |
| target_(target), |
| image_buffer_(image_buffer ? image_buffer : NativeImageBuffer::Create( |
| texture->service_id())), |
| min_filter_(texture->min_filter()), |
| mag_filter_(texture->mag_filter()), |
| wrap_s_(texture->wrap_s()), |
| wrap_t_(texture->wrap_t()), |
| usage_(texture->usage()), |
| immutable_(texture->IsImmutable()) { |
| |
| // TODO |
| DCHECK(!texture->level_infos_.empty()); |
| DCHECK(!texture->level_infos_[0].empty()); |
| DCHECK(!texture->NeedsMips()); |
| DCHECK(texture->level_infos_[0][0].width); |
| DCHECK(texture->level_infos_[0][0].height); |
| |
| scoped_refptr<gfx::GLImage> gl_image( |
| new GLImageSync(image_buffer_, |
| gfx::Size(texture->level_infos_[0][0].width, |
| texture->level_infos_[0][0].height))); |
| texture->SetLevelImage(NULL, target, 0, gl_image); |
| |
| // TODO: all levels |
| level_infos_.clear(); |
| const Texture::LevelInfo& level = texture->level_infos_[0][0]; |
| LevelInfo info(level.target, |
| level.internal_format, |
| level.width, |
| level.height, |
| level.depth, |
| level.border, |
| level.format, |
| level.type, |
| level.cleared); |
| std::vector<LevelInfo> infos; |
| infos.push_back(info); |
| level_infos_.push_back(infos); |
| } |
| |
| TextureDefinition::~TextureDefinition() { |
| } |
| |
| Texture* TextureDefinition::CreateTexture() const { |
| if (!image_buffer_) |
| return NULL; |
| |
| GLuint texture_id; |
| glGenTextures(1, &texture_id); |
| |
| Texture* texture(new Texture(texture_id)); |
| UpdateTexture(texture); |
| |
| return texture; |
| } |
| |
| void TextureDefinition::UpdateTexture(Texture* texture) const { |
| gfx::ScopedTextureBinder texture_binder(target_, texture->service_id()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s_); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t_); |
| if (image_buffer_) |
| image_buffer_->BindToTexture(target_); |
| // We have to make sure the changes are visible to other clients in this share |
| // group. As far as the clients are concerned, the mailbox semantics only |
| // demand a single flush from the client after changes are first made, |
| // and it is not visible to them when another share group boundary is crossed. |
| // We could probably track this and be a bit smarter about when to flush |
| // though. |
| glFlush(); |
| |
| texture->level_infos_.resize(1); |
| for (size_t i = 0; i < level_infos_.size(); i++) { |
| const LevelInfo& base_info = level_infos_[i][0]; |
| const size_t levels_needed = TextureManager::ComputeMipMapCount( |
| base_info.target, base_info.width, base_info.height, base_info.depth); |
| DCHECK(level_infos_.size() <= levels_needed); |
| texture->level_infos_[0].resize(levels_needed); |
| for (size_t n = 0; n < level_infos_.size(); n++) { |
| const LevelInfo& info = level_infos_[i][n]; |
| texture->SetLevelInfo(NULL, |
| info.target, |
| i, |
| info.internal_format, |
| info.width, |
| info.height, |
| info.depth, |
| info.border, |
| info.format, |
| info.type, |
| info.cleared); |
| } |
| } |
| if (image_buffer_) { |
| texture->SetLevelImage( |
| NULL, |
| target_, |
| 0, |
| new GLImageSync( |
| image_buffer_, |
| gfx::Size(level_infos_[0][0].width, level_infos_[0][0].height))); |
| } |
| |
| texture->target_ = target_; |
| texture->SetImmutable(immutable_); |
| texture->min_filter_ = min_filter_; |
| texture->mag_filter_ = mag_filter_; |
| texture->wrap_s_ = wrap_s_; |
| texture->wrap_t_ = wrap_t_; |
| texture->usage_ = usage_; |
| } |
| |
| bool TextureDefinition::Matches(const Texture* texture) const { |
| DCHECK(target_ == texture->target()); |
| if (texture->min_filter_ != min_filter_ || |
| texture->mag_filter_ != mag_filter_ || |
| texture->wrap_s_ != wrap_s_ || |
| texture->wrap_t_ != wrap_t_) { |
| return false; |
| } |
| |
| // All structural changes should have orphaned the texture. |
| if (image_buffer_ && !texture->GetLevelImage(texture->target(), 0)) |
| return false; |
| |
| return true; |
| } |
| |
| } // namespace gles2 |
| } // namespace gpu |