Commit 20c7724e authored by Alexis Hetu's avatar Alexis Hetu Committed by Commit Bot

GLImageIOSurfaceEGL::BindTexImageImpl implementation

This function is required for YUV decoding in Chromium
on MacOS using EGL. Fixes WebGL conformance test:
conformance/extensions/oes-texture-float-with-video.html

Bug: chromium:757974
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_vr;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: I143d69d30251b361daad13b4c0190e13f66795f8
Reviewed-on: https://chromium-review.googlesource.com/956247Reviewed-by: default avatarKenneth Russell <kbr@chromium.org>
Commit-Queue: Alexis Hétu <sugoi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#545667}
parent 921ee41b
...@@ -224,6 +224,12 @@ component("gl") { ...@@ -224,6 +224,12 @@ component("gl") {
] ]
} }
} }
if (is_mac || use_egl) {
sources += [
"yuv_to_rgb_converter.cc",
"yuv_to_rgb_converter.h",
]
}
if (is_android || is_linux || is_fuchsia) { if (is_android || is_linux || is_fuchsia) {
sources += [ sources += [
"gl_implementation_osmesa.cc", "gl_implementation_osmesa.cc",
...@@ -313,8 +319,6 @@ component("gl") { ...@@ -313,8 +319,6 @@ component("gl") {
"gl_image_io_surface.mm", "gl_image_io_surface.mm",
"scoped_cgl.cc", "scoped_cgl.cc",
"scoped_cgl.h", "scoped_cgl.h",
"yuv_to_rgb_converter.cc",
"yuv_to_rgb_converter.h",
] ]
libs = [ libs = [
......
...@@ -156,7 +156,8 @@ void GLContextCGL::Destroy() { ...@@ -156,7 +156,8 @@ void GLContextCGL::Destroy() {
SetCurrentGL(GetCurrentGL()); SetCurrentGL(GetCurrentGL());
} }
ScopedCGLSetCurrentContext(static_cast<CGLContextObj>(context_)); ScopedCGLSetCurrentContext scoped_set_current(
static_cast<CGLContextObj>(context_));
yuv_to_rgb_converters_.clear(); yuv_to_rgb_converters_.clear();
// Rebind the current context's API if needed. // Rebind the current context's API if needed.
...@@ -228,8 +229,8 @@ YUVToRGBConverter* GLContextCGL::GetYUVToRGBConverter( ...@@ -228,8 +229,8 @@ YUVToRGBConverter* GLContextCGL::GetYUVToRGBConverter(
std::unique_ptr<YUVToRGBConverter>& yuv_to_rgb_converter = std::unique_ptr<YUVToRGBConverter>& yuv_to_rgb_converter =
yuv_to_rgb_converters_[color_space]; yuv_to_rgb_converters_[color_space];
if (!yuv_to_rgb_converter) if (!yuv_to_rgb_converter)
yuv_to_rgb_converter.reset( yuv_to_rgb_converter =
new YUVToRGBConverter(*GetVersionInfo(), color_space)); std::make_unique<YUVToRGBConverter>(*GetVersionInfo(), color_space);
return yuv_to_rgb_converter.get(); return yuv_to_rgb_converter.get();
} }
......
...@@ -21,8 +21,9 @@ ...@@ -21,8 +21,9 @@
#include "third_party/khronos/EGL/eglext.h" #include "third_party/khronos/EGL/eglext.h"
#include "ui/gl/egl_util.h" #include "ui/gl/egl_util.h"
#include "ui/gl/gl_bindings.h" #include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_gl_api_implementation.h"
#include "ui/gl/gl_surface_egl.h" #include "ui/gl/gl_surface_egl.h"
#include "ui/gl/yuv_to_rgb_converter.h"
#ifndef EGL_CHROMIUM_create_context_bind_generates_resource #ifndef EGL_CHROMIUM_create_context_bind_generates_resource
#define EGL_CHROMIUM_create_context_bind_generates_resource 1 #define EGL_CHROMIUM_create_context_bind_generates_resource 1
...@@ -62,8 +63,8 @@ namespace gl { ...@@ -62,8 +63,8 @@ namespace gl {
GLContextEGL::GLContextEGL(GLShareGroup* share_group) GLContextEGL::GLContextEGL(GLShareGroup* share_group)
: GLContextReal(share_group), : GLContextReal(share_group),
context_(nullptr), context_(EGL_NO_CONTEXT),
display_(nullptr), display_(EGL_NO_DISPLAY),
config_(nullptr), config_(nullptr),
unbind_fbo_on_makecurrent_(false) {} unbind_fbo_on_makecurrent_(false) {}
...@@ -204,6 +205,7 @@ bool GLContextEGL::Initialize(GLSurface* compatible_surface, ...@@ -204,6 +205,7 @@ bool GLContextEGL::Initialize(GLSurface* compatible_surface,
} }
void GLContextEGL::Destroy() { void GLContextEGL::Destroy() {
ReleaseYUVToRGBConverters();
if (context_) { if (context_) {
if (!eglDestroyContext(display_, context_)) { if (!eglDestroyContext(display_, context_)) {
LOG(ERROR) << "eglDestroyContext failed with error " LOG(ERROR) << "eglDestroyContext failed with error "
...@@ -214,6 +216,58 @@ void GLContextEGL::Destroy() { ...@@ -214,6 +216,58 @@ void GLContextEGL::Destroy() {
} }
} }
YUVToRGBConverter* GLContextEGL::GetYUVToRGBConverter(
const gfx::ColorSpace& color_space) {
std::unique_ptr<YUVToRGBConverter>& yuv_to_rgb_converter =
yuv_to_rgb_converters_[color_space];
if (!yuv_to_rgb_converter) {
yuv_to_rgb_converter =
std::make_unique<YUVToRGBConverter>(*GetVersionInfo(), color_space);
}
return yuv_to_rgb_converter.get();
}
void GLContextEGL::ReleaseYUVToRGBConverters() {
if (!yuv_to_rgb_converters_.empty()) {
// If this context is not current, bind this context's API so that the YUV
// converter can safely destruct
GLContext* current_context = GetRealCurrent();
if (current_context != this) {
SetCurrentGL(GetCurrentGL());
}
EGLContext current_egl_context = eglGetCurrentContext();
EGLSurface current_draw_surface = EGL_NO_SURFACE;
EGLSurface current_read_surface = EGL_NO_SURFACE;
if (context_ != current_egl_context) {
current_draw_surface = eglGetCurrentSurface(EGL_DRAW);
current_read_surface = eglGetCurrentSurface(EGL_READ);
// This call relies on the fact that yuv_to_rgb_converters_ are only ever
// allocated in GLImageIOSurfaceEGL::CopyTexImage, which is only on
// MacOS, where surfaceless EGL contexts are always supported.
if (!eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, context_)) {
DVLOG(1) << "eglMakeCurrent failed with error "
<< GetLastEGLErrorString();
}
}
yuv_to_rgb_converters_.clear();
// Rebind the current context's API if needed.
if (current_context && current_context != this) {
SetCurrentGL(current_context->GetCurrentGL());
}
if (context_ != current_egl_context) {
if (!eglMakeCurrent(display_, current_draw_surface, current_read_surface,
current_egl_context)) {
DVLOG(1) << "eglMakeCurrent failed with error "
<< GetLastEGLErrorString();
}
}
}
}
bool GLContextEGL::MakeCurrent(GLSurface* surface) { bool GLContextEGL::MakeCurrent(GLSurface* surface) {
DCHECK(context_); DCHECK(context_);
if (IsCurrent(surface)) if (IsCurrent(surface))
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef UI_GL_GL_CONTEXT_EGL_H_ #ifndef UI_GL_GL_CONTEXT_EGL_H_
#define UI_GL_GL_CONTEXT_EGL_H_ #define UI_GL_GL_CONTEXT_EGL_H_
#include <map>
#include <string> #include <string>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
...@@ -35,17 +36,22 @@ class GL_EXPORT GLContextEGL : public GLContextReal { ...@@ -35,17 +36,22 @@ class GL_EXPORT GLContextEGL : public GLContextReal {
void OnSetSwapInterval(int interval) override; void OnSetSwapInterval(int interval) override;
bool WasAllocatedUsingRobustnessExtension() override; bool WasAllocatedUsingRobustnessExtension() override;
void SetUnbindFboOnMakeCurrent() override; void SetUnbindFboOnMakeCurrent() override;
YUVToRGBConverter* GetYUVToRGBConverter(
const gfx::ColorSpace& color_space) override;
protected: protected:
~GLContextEGL() override; ~GLContextEGL() override;
private: private:
void Destroy(); void Destroy();
void ReleaseYUVToRGBConverters();
EGLContext context_; EGLContext context_;
EGLDisplay display_; EGLDisplay display_;
EGLConfig config_; EGLConfig config_;
bool unbind_fbo_on_makecurrent_; bool unbind_fbo_on_makecurrent_;
std::map<gfx::ColorSpace, std::unique_ptr<YUVToRGBConverter>>
yuv_to_rgb_converters_;
DISALLOW_COPY_AND_ASSIGN(GLContextEGL); DISALLOW_COPY_AND_ASSIGN(GLContextEGL);
}; };
......
...@@ -22,6 +22,7 @@ class GL_EXPORT GLImageIOSurfaceEGL : public GLImageIOSurface { ...@@ -22,6 +22,7 @@ class GL_EXPORT GLImageIOSurfaceEGL : public GLImageIOSurface {
protected: protected:
~GLImageIOSurfaceEGL() override; ~GLImageIOSurfaceEGL() override;
bool BindTexImageImpl(unsigned internalformat) override; bool BindTexImageImpl(unsigned internalformat) override;
bool CopyTexImage(unsigned target) override;
private: private:
EGLDisplay display_; EGLDisplay display_;
......
...@@ -4,7 +4,12 @@ ...@@ -4,7 +4,12 @@
#include "ui/gl/gl_image_io_surface_egl.h" #include "ui/gl/gl_image_io_surface_egl.h"
#include "base/callback_helpers.h"
#include "base/mac/bind_objc_block.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_surface_egl.h" #include "ui/gl/gl_surface_egl.h"
#include "ui/gl/scoped_binders.h"
#include "ui/gl/yuv_to_rgb_converter.h"
// Enums for the EGL_ANGLE_iosurface_client_buffer extension // Enums for the EGL_ANGLE_iosurface_client_buffer extension
#define EGL_IOSURFACE_ANGLE 0x3454 #define EGL_IOSURFACE_ANGLE 0x3454
...@@ -128,7 +133,7 @@ bool GLImageIOSurfaceEGL::BindTexImageImpl(unsigned internalformat) { ...@@ -128,7 +133,7 @@ bool GLImageIOSurfaceEGL::BindTexImageImpl(unsigned internalformat) {
EGL_TEXTURE_TYPE_ANGLE, formatType.type, EGL_TEXTURE_TYPE_ANGLE, formatType.type,
EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE,
}; };
// clang-format off // clang-format on
pbuffer_ = eglCreatePbufferFromClientBuffer(display_, EGL_IOSURFACE_ANGLE, pbuffer_ = eglCreatePbufferFromClientBuffer(display_, EGL_IOSURFACE_ANGLE,
io_surface_.get(), dummy_config_, attribs); io_surface_.get(), dummy_config_, attribs);
...@@ -152,4 +157,138 @@ bool GLImageIOSurfaceEGL::BindTexImageImpl(unsigned internalformat) { ...@@ -152,4 +157,138 @@ bool GLImageIOSurfaceEGL::BindTexImageImpl(unsigned internalformat) {
return true; return true;
} }
bool GLImageIOSurfaceEGL::CopyTexImage(unsigned target) {
DCHECK(thread_checker_.CalledOnValidThread());
if (format_ != gfx::BufferFormat::YUV_420_BIPLANAR)
return false;
GLContext* gl_context = GLContext::GetCurrent();
DCHECK(gl_context);
YUVToRGBConverter* yuv_to_rgb_converter =
gl_context->GetYUVToRGBConverter(color_space_for_yuv_to_rgb_);
DCHECK(yuv_to_rgb_converter);
// Note that state restoration is done explicitly instead of scoped binders to
// avoid https://crbug.com/601729.
GLint rgb_texture = 0;
GLenum target_getter = 0;
switch (target) {
case GL_TEXTURE_2D:
target_getter = GL_TEXTURE_BINDING_2D;
break;
case GL_TEXTURE_CUBE_MAP:
target_getter = GL_TEXTURE_BINDING_CUBE_MAP;
break;
case GL_TEXTURE_EXTERNAL_OES:
target_getter = GL_TEXTURE_BINDING_EXTERNAL_OES;
break;
case GL_TEXTURE_RECTANGLE_ARB:
target_getter = GL_TEXTURE_BINDING_RECTANGLE_ARB;
break;
default:
NOTIMPLEMENTED() << " Target not supported.";
return false;
}
EGLSurface y_surface = EGL_NO_SURFACE;
EGLSurface uv_surface = EGL_NO_SURFACE;
glGetIntegerv(target_getter, &rgb_texture);
base::ScopedClosureRunner destroy_resources_runner(base::BindBlock(^{
if (y_surface != EGL_NO_SURFACE) {
EGLBoolean result =
eglReleaseTexImage(display_, y_surface, EGL_BACK_BUFFER);
DCHECK(result == EGL_TRUE);
result = eglDestroySurface(display_, y_surface);
DCHECK(result == EGL_TRUE);
}
if (uv_surface != EGL_NO_SURFACE) {
EGLBoolean result =
eglReleaseTexImage(display_, uv_surface, EGL_BACK_BUFFER);
DCHECK(result == EGL_TRUE);
result = eglDestroySurface(display_, uv_surface);
DCHECK(result == EGL_TRUE);
}
glBindTexture(target, rgb_texture);
}));
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, yuv_to_rgb_converter->y_texture());
if (glGetError() != GL_NO_ERROR) {
LOG(ERROR) << "Can't bind Y texture";
return false;
}
// clang-format off
const EGLint yAttribs[] = {
EGL_WIDTH, size_.width(),
EGL_HEIGHT, size_.height(),
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_RED,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
y_surface = eglCreatePbufferFromClientBuffer(display_, EGL_IOSURFACE_ANGLE,
io_surface_.get(), dummy_config_,
yAttribs);
if (y_surface == EGL_NO_SURFACE) {
LOG(ERROR) << "eglCreatePbufferFromClientBuffer failed, EGL error is "
<< eglGetError();
return false;
}
EGLBoolean result = eglBindTexImage(display_, y_surface, EGL_BACK_BUFFER);
if (result != EGL_TRUE) {
LOG(ERROR) << "eglBindTexImage failed, EGL error is " << eglGetError();
return false;
}
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, yuv_to_rgb_converter->uv_texture());
if (glGetError() != GL_NO_ERROR) {
LOG(ERROR) << "Can't bind UV texture";
return false;
}
// clang-format off
const EGLint uvAttribs[] = {
EGL_WIDTH, size_.width() / 2,
EGL_HEIGHT, size_.height() / 2,
EGL_IOSURFACE_PLANE_ANGLE, 1,
EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_RG,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
uv_surface = eglCreatePbufferFromClientBuffer(display_, EGL_IOSURFACE_ANGLE,
io_surface_.get(),
dummy_config_, uvAttribs);
if (uv_surface == EGL_NO_SURFACE) {
LOG(ERROR) << "eglCreatePbufferFromClientBuffer failed, EGL error is "
<< eglGetError();
return false;
}
result = eglBindTexImage(display_, uv_surface, EGL_BACK_BUFFER);
if (result != EGL_TRUE) {
LOG(ERROR) << "eglBindTexImage failed, EGL error is " << eglGetError();
return false;
}
yuv_to_rgb_converter->CopyYUV420ToRGB(target, size_, rgb_texture);
if (glGetError() != GL_NO_ERROR) {
LOG(ERROR) << "Failed converting from YUV to RGB";
return false;
}
return true;
}
} // namespace gl } // namespace gl
...@@ -14,6 +14,12 @@ ...@@ -14,6 +14,12 @@
namespace gl { namespace gl {
namespace { namespace {
const char kVertexHeaderES3[] =
"#version 300 es\n"
"precision mediump float;\n"
"#define ATTRIBUTE in\n"
"#define VARYING out\n";
const char kVertexHeaderCompatiblityProfile[] = const char kVertexHeaderCompatiblityProfile[] =
"#version 110\n" "#version 110\n"
"#define ATTRIBUTE attribute\n" "#define ATTRIBUTE attribute\n"
...@@ -24,6 +30,14 @@ const char kVertexHeaderCoreProfile[] = ...@@ -24,6 +30,14 @@ const char kVertexHeaderCoreProfile[] =
"#define ATTRIBUTE in\n" "#define ATTRIBUTE in\n"
"#define VARYING out\n"; "#define VARYING out\n";
const char kFragmentHeaderES3[] =
"#version 300 es\n"
"precision mediump float;\n"
"#define VARYING in\n"
"#define TEX texture\n"
"#define FRAGCOLOR frag_color\n"
"out vec4 FRAGCOLOR;\n";
const char kFragmentHeaderCompatiblityProfile[] = const char kFragmentHeaderCompatiblityProfile[] =
"#version 110\n" "#version 110\n"
"#extension GL_ARB_texture_rectangle : require\n" "#extension GL_ARB_texture_rectangle : require\n"
...@@ -45,7 +59,7 @@ STRINGIZE( ...@@ -45,7 +59,7 @@ STRINGIZE(
uniform vec2 a_texScale; uniform vec2 a_texScale;
VARYING vec2 v_texCoord; VARYING vec2 v_texCoord;
void main() { void main() {
gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0); gl_Position = vec4(a_position, 0.0, 1.0);
v_texCoord = (a_position + vec2(1.0, 1.0)) * 0.5 * a_texScale; v_texCoord = (a_position + vec2(1.0, 1.0)) * 0.5 * a_texScale;
} }
); );
...@@ -75,22 +89,27 @@ YUVToRGBConverter::YUVToRGBConverter(const GLVersionInfo& gl_version_info, ...@@ -75,22 +89,27 @@ YUVToRGBConverter::YUVToRGBConverter(const GLVersionInfo& gl_version_info,
DCHECK(color_transform->CanGetShaderSource()); DCHECK(color_transform->CanGetShaderSource());
std::string do_color_conversion = color_transform->GetShaderSource(); std::string do_color_conversion = color_transform->GetShaderSource();
bool use_es3 = gl_version_info.is_es3;
bool use_core_profile = gl_version_info.is_desktop_core_profile; bool use_core_profile = gl_version_info.is_desktop_core_profile;
glGenFramebuffersEXT(1, &framebuffer_); glGenFramebuffersEXT(1, &framebuffer_);
vertex_buffer_ = GLHelper::SetupQuadVertexBuffer(); vertex_buffer_ = GLHelper::SetupQuadVertexBuffer();
vertex_shader_ = GLHelper::LoadShader( vertex_shader_ = GLHelper::LoadShader(
GL_VERTEX_SHADER, GL_VERTEX_SHADER,
base::StringPrintf("%s\n%s", base::StringPrintf(
use_core_profile ? kVertexHeaderCoreProfile "%s\n%s",
: kVertexHeaderCompatiblityProfile, use_es3 ? kVertexHeaderES3
kVertexShader) : (use_core_profile ? kVertexHeaderCoreProfile
: kVertexHeaderCompatiblityProfile),
kVertexShader)
.c_str()); .c_str());
fragment_shader_ = GLHelper::LoadShader( fragment_shader_ = GLHelper::LoadShader(
GL_FRAGMENT_SHADER, GL_FRAGMENT_SHADER,
base::StringPrintf("%s\n%s\n%s", base::StringPrintf(
use_core_profile ? kFragmentHeaderCoreProfile "%s\n%s\n%s",
: kFragmentHeaderCompatiblityProfile, use_es3 ? kFragmentHeaderES3
do_color_conversion.c_str(), kFragmentShader) : (use_core_profile ? kFragmentHeaderCoreProfile
: kFragmentHeaderCompatiblityProfile),
do_color_conversion.c_str(), kFragmentShader)
.c_str()); .c_str());
program_ = GLHelper::SetupProgram(vertex_shader_, fragment_shader_); program_ = GLHelper::SetupProgram(vertex_shader_, fragment_shader_);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment