// 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 "ui/gl/gl_image_memory.h" #include "base/debug/trace_event.h" #include "base/logging.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/scoped_binders.h" #if defined(OS_WIN) || defined(USE_X11) || defined(OS_ANDROID) || \ defined(USE_OZONE) #include "ui/gl/gl_surface_egl.h" #endif namespace gfx { namespace { bool ValidInternalFormat(unsigned internalformat) { switch (internalformat) { case GL_RGBA: return true; default: return false; } } bool ValidFormat(gfx::GpuMemoryBuffer::Format format) { switch (format) { case gfx::GpuMemoryBuffer::RGBA_8888: case gfx::GpuMemoryBuffer::BGRA_8888: return true; case gfx::GpuMemoryBuffer::RGBX_8888: return false; } NOTREACHED(); return false; } GLenum TextureFormat(gfx::GpuMemoryBuffer::Format format) { switch (format) { case gfx::GpuMemoryBuffer::RGBA_8888: return GL_RGBA; case gfx::GpuMemoryBuffer::BGRA_8888: return GL_BGRA_EXT; case gfx::GpuMemoryBuffer::RGBX_8888: NOTREACHED(); return 0; } NOTREACHED(); return 0; } GLenum DataFormat(gfx::GpuMemoryBuffer::Format format) { return TextureFormat(format); } GLenum DataType(gfx::GpuMemoryBuffer::Format format) { switch (format) { case gfx::GpuMemoryBuffer::RGBA_8888: case gfx::GpuMemoryBuffer::BGRA_8888: return GL_UNSIGNED_BYTE; case gfx::GpuMemoryBuffer::RGBX_8888: NOTREACHED(); return 0; } NOTREACHED(); return 0; } } // namespace GLImageMemory::GLImageMemory(const gfx::Size& size, unsigned internalformat) : size_(size), internalformat_(internalformat), memory_(NULL), format_(gfx::GpuMemoryBuffer::RGBA_8888), in_use_(false), target_(0), need_do_bind_tex_image_(false) #if defined(OS_WIN) || defined(USE_X11) || defined(OS_ANDROID) || \ defined(USE_OZONE) , egl_texture_id_(0u), egl_image_(EGL_NO_IMAGE_KHR) #endif { } GLImageMemory::~GLImageMemory() { #if defined(OS_WIN) || defined(USE_X11) || defined(OS_ANDROID) || \ defined(USE_OZONE) DCHECK_EQ(EGL_NO_IMAGE_KHR, egl_image_); DCHECK_EQ(0u, egl_texture_id_); #endif } // static size_t GLImageMemory::BytesPerPixel(gfx::GpuMemoryBuffer::Format format) { switch (format) { case gfx::GpuMemoryBuffer::RGBA_8888: case gfx::GpuMemoryBuffer::BGRA_8888: return 4; case gfx::GpuMemoryBuffer::RGBX_8888: NOTREACHED(); return 0; } NOTREACHED(); return 0; } bool GLImageMemory::Initialize(const unsigned char* memory, gfx::GpuMemoryBuffer::Format format) { if (!ValidInternalFormat(internalformat_)) { LOG(ERROR) << "Invalid internalformat: " << internalformat_; return false; } if (!ValidFormat(format)) { LOG(ERROR) << "Invalid format: " << format; return false; } DCHECK(memory); DCHECK(!memory_); memory_ = memory; format_ = format; return true; } void GLImageMemory::Destroy(bool have_context) { #if defined(OS_WIN) || defined(USE_X11) || defined(OS_ANDROID) || \ defined(USE_OZONE) if (egl_image_ != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(GLSurfaceEGL::GetHardwareDisplay(), egl_image_); egl_image_ = EGL_NO_IMAGE_KHR; } if (egl_texture_id_) { if (have_context) glDeleteTextures(1, &egl_texture_id_); egl_texture_id_ = 0u; } #endif memory_ = NULL; } gfx::Size GLImageMemory::GetSize() { return size_; } bool GLImageMemory::BindTexImage(unsigned target) { if (target_ && target_ != target) { LOG(ERROR) << "GLImage can only be bound to one target"; return false; } target_ = target; // Defer DoBindTexImage if not currently in use. if (!in_use_) { need_do_bind_tex_image_ = true; return true; } DoBindTexImage(target); return true; } bool GLImageMemory::CopyTexImage(unsigned target) { TRACE_EVENT0("gpu", "GLImageMemory::CopyTexImage"); // GL_TEXTURE_EXTERNAL_OES is not a supported CopyTexImage target. if (target == GL_TEXTURE_EXTERNAL_OES) return false; DCHECK(memory_); glTexSubImage2D(target, 0, // level 0, // x 0, // y size_.width(), size_.height(), DataFormat(format_), DataType(format_), memory_); return true; } void GLImageMemory::WillUseTexImage() { DCHECK(!in_use_); in_use_ = true; if (!need_do_bind_tex_image_) return; DCHECK(target_); DoBindTexImage(target_); } void GLImageMemory::DidUseTexImage() { DCHECK(in_use_); in_use_ = false; } bool GLImageMemory::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, int z_order, OverlayTransform transform, const Rect& bounds_rect, const RectF& crop_rect) { return false; } void GLImageMemory::DoBindTexImage(unsigned target) { TRACE_EVENT0("gpu", "GLImageMemory::DoBindTexImage"); DCHECK(need_do_bind_tex_image_); need_do_bind_tex_image_ = false; DCHECK(memory_); #if defined(OS_WIN) || defined(USE_X11) || defined(OS_ANDROID) || \ defined(USE_OZONE) if (target == GL_TEXTURE_EXTERNAL_OES) { if (egl_image_ == EGL_NO_IMAGE_KHR) { DCHECK_EQ(0u, egl_texture_id_); glGenTextures(1, &egl_texture_id_); { ScopedTextureBinder texture_binder(GL_TEXTURE_2D, egl_texture_id_); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, // mip level TextureFormat(format_), size_.width(), size_.height(), 0, // border DataFormat(format_), DataType(format_), memory_); } EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; // Need to pass current EGL rendering context to eglCreateImageKHR for // target type EGL_GL_TEXTURE_2D_KHR. egl_image_ = eglCreateImageKHR(GLSurfaceEGL::GetHardwareDisplay(), eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR, reinterpret_cast(egl_texture_id_), attrs); DCHECK_NE(EGL_NO_IMAGE_KHR, egl_image_) << "Error creating EGLImage: " << eglGetError(); } else { ScopedTextureBinder texture_binder(GL_TEXTURE_2D, egl_texture_id_); glTexSubImage2D(GL_TEXTURE_2D, 0, // mip level 0, // x-offset 0, // y-offset size_.width(), size_.height(), DataFormat(format_), DataType(format_), memory_); } glEGLImageTargetTexture2DOES(target, egl_image_); DCHECK_EQ(static_cast(GL_NO_ERROR), glGetError()); return; } #endif DCHECK_NE(static_cast(GL_TEXTURE_EXTERNAL_OES), target); glTexImage2D(target, 0, // mip level TextureFormat(format_), size_.width(), size_.height(), 0, // border DataFormat(format_), DataType(format_), memory_); } } // namespace gfx