From dde9e95fb0cd656c952906a630052d560d8a6753 Mon Sep 17 00:00:00 2001 From: ccameron Date: Mon, 18 Apr 2016 12:36:09 -0700 Subject: [PATCH] Attach YUV420 to RGB converter to GLContext This is less hacky than the global lookup table. Leave the IOSurface-specific parts of the conversion inside GLImageIOSurface. If this converter is needed on other platforms, this will make it easier to reuse. BUG= CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:linux_optional_gpu_tests_rel;tryserver.chromium.mac:mac_optional_gpu_tests_rel;tryserver.chromium.win:win_optional_gpu_tests_rel Review URL: https://codereview.chromium.org/1882953006 Cr-Commit-Position: refs/heads/master@{#387986} --- .../service/gl_context_virtual.cc | 4 + .../service/gl_context_virtual.h | 1 + ui/gl/BUILD.gn | 2 + ui/gl/gl.gyp | 2 + ui/gl/gl_context.cc | 4 + ui/gl/gl_context.h | 8 +- ui/gl/gl_context_cgl.cc | 12 + ui/gl/gl_context_cgl.h | 3 + ui/gl/gl_image_io_surface.h | 4 - ui/gl/gl_image_io_surface.mm | 284 ++++-------------- ui/gl/yuv_to_rgb_converter.cc | 166 ++++++++++ ui/gl/yuv_to_rgb_converter.h | 33 ++ 12 files changed, 286 insertions(+), 237 deletions(-) create mode 100644 ui/gl/yuv_to_rgb_converter.cc create mode 100644 ui/gl/yuv_to_rgb_converter.h diff --git a/gpu/command_buffer/service/gl_context_virtual.cc b/gpu/command_buffer/service/gl_context_virtual.cc index 998d553d69ae97..11138fe1d58168 100644 --- a/gpu/command_buffer/service/gl_context_virtual.cc +++ b/gpu/command_buffer/service/gl_context_virtual.cc @@ -106,6 +106,10 @@ void GLContextVirtual::SetUnbindFboOnMakeCurrent() { shared_context_->SetUnbindFboOnMakeCurrent(); } +gl::YUVToRGBConverter* GLContextVirtual::GetYUVToRGBConverter() { + return shared_context_->GetYUVToRGBConverter(); +} + GLContextVirtual::~GLContextVirtual() { Destroy(); } diff --git a/gpu/command_buffer/service/gl_context_virtual.h b/gpu/command_buffer/service/gl_context_virtual.h index ece0fc7da474ad..bedf253b7361dc 100644 --- a/gpu/command_buffer/service/gl_context_virtual.h +++ b/gpu/command_buffer/service/gl_context_virtual.h @@ -46,6 +46,7 @@ class GPU_EXPORT GLContextVirtual : public gfx::GLContext { void SetSafeToForceGpuSwitch() override; bool WasAllocatedUsingRobustnessExtension() override; void SetUnbindFboOnMakeCurrent() override; + gl::YUVToRGBConverter* GetYUVToRGBConverter() override; protected: ~GLContextVirtual() override; diff --git a/ui/gl/BUILD.gn b/ui/gl/BUILD.gn index 34cecb7b2833ab..ea3d06a925a817 100644 --- a/ui/gl/BUILD.gn +++ b/ui/gl/BUILD.gn @@ -230,6 +230,8 @@ component("gl") { "gl_image_io_surface.mm", "scoped_cgl.cc", "scoped_cgl.h", + "yuv_to_rgb_converter.cc", + "yuv_to_rgb_converter.h", ] libs = [ diff --git a/ui/gl/gl.gyp b/ui/gl/gl.gyp index 66420cec27aa5e..a1fce09a48bdd4 100644 --- a/ui/gl/gl.gyp +++ b/ui/gl/gl.gyp @@ -243,6 +243,8 @@ 'gl_image_io_surface.h', 'scoped_cgl.cc', 'scoped_cgl.h', + 'yuv_to_rgb_converter.cc', + 'yuv_to_rgb_converter.h', ], 'link_settings': { 'libraries': [ diff --git a/ui/gl/gl_context.cc b/ui/gl/gl_context.cc index 70cc852352d3b5..63b711a07f2b4f 100644 --- a/ui/gl/gl_context.cc +++ b/ui/gl/gl_context.cc @@ -92,6 +92,10 @@ std::string GLContext::GetGLRenderer() { return std::string(renderer ? renderer : ""); } +gl::YUVToRGBConverter* GLContext::GetYUVToRGBConverter() { + return nullptr; +} + bool GLContext::HasExtension(const char* name) { std::string extensions = GetExtensions(); extensions += " "; diff --git a/ui/gl/gl_context.h b/ui/gl/gl_context.h index 1b4c0406a9a487..6d1420ded49568 100644 --- a/ui/gl/gl_context.h +++ b/ui/gl/gl_context.h @@ -17,6 +17,10 @@ #include "ui/gl/gl_state_restorer.h" #include "ui/gl/gpu_preference.h" +namespace gl { +class YUVToRGBConverter; +} // namespace gl + namespace gpu { class GLContextVirtual; } // namespace gpu @@ -29,7 +33,6 @@ class GPUTimingClient; class VirtualGLApi; struct GLVersionInfo; - // Encapsulates an OpenGL context, hiding platform specific management. class GL_EXPORT GLContext : public base::RefCounted { public: @@ -127,6 +130,9 @@ class GL_EXPORT GLContext : public base::RefCounted { // Returns the GL renderer string. The context must be current. virtual std::string GetGLRenderer(); + // Returns a helper structure to convert YUV textures to RGB textures. + virtual gl::YUVToRGBConverter* GetYUVToRGBConverter(); + protected: virtual ~GLContext(); diff --git a/ui/gl/gl_context_cgl.cc b/ui/gl/gl_context_cgl.cc index 374b38e1bb6a7e..333f2f1af5a2b0 100644 --- a/ui/gl/gl_context_cgl.cc +++ b/ui/gl/gl_context_cgl.cc @@ -16,6 +16,8 @@ #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_surface.h" #include "ui/gl/gpu_switching_manager.h" +#include "ui/gl/scoped_cgl.h" +#include "ui/gl/yuv_to_rgb_converter.h" namespace gfx { @@ -136,6 +138,10 @@ bool GLContextCGL::Initialize(GLSurface* compatible_surface, } void GLContextCGL::Destroy() { + if (yuv_to_rgb_converter_) { + gfx::ScopedCGLSetCurrentContext(static_cast(context_)); + yuv_to_rgb_converter_.reset(); + } if (discrete_pixelformat_) { if (base::MessageLoop::current() != nullptr) { // Delay releasing the pixel format for 10 seconds to reduce the number of @@ -195,6 +201,12 @@ bool GLContextCGL::ForceGpuSwitchIfNeeded() { return true; } +gl::YUVToRGBConverter* GLContextCGL::GetYUVToRGBConverter() { + if (!yuv_to_rgb_converter_) + yuv_to_rgb_converter_.reset(new gl::YUVToRGBConverter); + return yuv_to_rgb_converter_.get(); +} + bool GLContextCGL::MakeCurrent(GLSurface* surface) { DCHECK(context_); diff --git a/ui/gl/gl_context_cgl.h b/ui/gl/gl_context_cgl.h index bb7a2415c76702..1a1016268f3294 100644 --- a/ui/gl/gl_context_cgl.h +++ b/ui/gl/gl_context_cgl.h @@ -8,6 +8,7 @@ #include #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "ui/gl/gl_context.h" namespace gfx { @@ -29,6 +30,7 @@ class GLContextCGL : public GLContextReal { void OnSetSwapInterval(int interval) override; void SetSafeToForceGpuSwitch() override; bool ForceGpuSwitchIfNeeded() override; + gl::YUVToRGBConverter* GetYUVToRGBConverter() override; protected: ~GLContextCGL() override; @@ -39,6 +41,7 @@ class GLContextCGL : public GLContextReal { void* context_; GpuPreference gpu_preference_; + scoped_ptr yuv_to_rgb_converter_; CGLPixelFormatObj discrete_pixelformat_; diff --git a/ui/gl/gl_image_io_surface.h b/ui/gl/gl_image_io_surface.h index a7d71bec4e7021..f62d4a218e0dfa 100644 --- a/ui/gl/gl_image_io_surface.h +++ b/ui/gl/gl_image_io_surface.h @@ -87,10 +87,6 @@ class GL_EXPORT GLImageIOSurface : public GLImage { gfx::GenericSharedMemoryId io_surface_id_; base::ThreadChecker thread_checker_; - // GL state to support 420v IOSurface conversion to RGB. This is retained - // to avoid re-creating the necessary GL programs every frame. - scoped_refptr rgb_converter_; - DISALLOW_COPY_AND_ASSIGN(GLImageIOSurface); }; diff --git a/ui/gl/gl_image_io_surface.mm b/ui/gl/gl_image_io_surface.mm index 1ae69b69c393e7..68bf3c0d1d226f 100644 --- a/ui/gl/gl_image_io_surface.mm +++ b/ui/gl/gl_image_io_surface.mm @@ -7,21 +7,16 @@ #include #include "base/callback_helpers.h" -#include "base/lazy_instance.h" #include "base/mac/bind_objc_block.h" #include "base/mac/foundation_util.h" -#include "base/strings/stringize_macros.h" -#include "base/strings/stringprintf.h" #include "base/trace_event/memory_allocator_dump.h" #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/process_memory_dump.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_context.h" -#include "ui/gl/gl_helper.h" -#include "ui/gl/gl_implementation.h" #include "ui/gl/scoped_api.h" #include "ui/gl/scoped_binders.h" -#include "ui/gl/scoped_cgl.h" +#include "ui/gl/yuv_to_rgb_converter.h" // Note that this must be included after gl_bindings.h to avoid conflicts. #include @@ -33,60 +28,6 @@ namespace gl { namespace { -const char kVertexHeaderCompatiblityProfile[] = - "#version 110\n" - "#define ATTRIBUTE attribute\n" - "#define VARYING varying\n"; - -const char kVertexHeaderCoreProfile[] = - "#version 150\n" - "#define ATTRIBUTE in\n" - "#define VARYING out\n"; - -const char kFragmentHeaderCompatiblityProfile[] = - "#version 110\n" - "#extension GL_ARB_texture_rectangle : require\n" - "#define VARYING varying\n" - "#define FRAGCOLOR gl_FragColor\n" - "#define TEX texture2DRect\n"; - -const char kFragmentHeaderCoreProfile[] = - "#version 150\n" - "#define VARYING in\n" - "#define TEX texture\n" - "#define FRAGCOLOR frag_color\n" - "out vec4 FRAGCOLOR;\n"; - -// clang-format off -const char kVertexShader[] = -STRINGIZE( - ATTRIBUTE vec2 a_position; - uniform vec2 a_texScale; - VARYING vec2 v_texCoord; - void main() { - gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0); - v_texCoord = (a_position + vec2(1.0, 1.0)) * 0.5 * a_texScale; - } -); - -const char kFragmentShader[] = -STRINGIZE( - uniform sampler2DRect a_y_texture; - uniform sampler2DRect a_uv_texture; - VARYING vec2 v_texCoord; - void main() { - vec3 yuv_adj = vec3(-0.0625, -0.5, -0.5); - mat3 yuv_matrix = mat3(vec3(1.164, 1.164, 1.164), - vec3(0.0, -.391, 2.018), - vec3(1.596, -.813, 0.0)); - vec3 yuv = vec3( - TEX(a_y_texture, v_texCoord).r, - TEX(a_uv_texture, v_texCoord * 0.5).rg); - FRAGCOLOR = vec4(yuv_matrix * (yuv + yuv_adj), 1.0); - } -); -// clang-format on - bool ValidInternalFormat(unsigned internalformat) { switch (internalformat) { case GL_RED: @@ -219,176 +160,6 @@ GLenum ConvertRequestedInternalFormat(GLenum internalformat) { } // namespace -class GLImageIOSurface::RGBConverter - : public base::RefCounted { - public: - static scoped_refptr GetForCurrentContext(); - bool CopyTexImage(IOSurfaceRef io_surface, const gfx::Size& size); - - private: - friend class base::RefCounted; - RGBConverter(CGLContextObj cgl_context); - ~RGBConverter(); - - unsigned framebuffer_ = 0; - unsigned vertex_shader_ = 0; - unsigned fragment_shader_ = 0; - unsigned program_ = 0; - int size_location_ = -1; - unsigned vertex_buffer_ = 0; - base::ScopedTypeRef cgl_context_; - - static base::LazyInstance< - std::map> - g_rgb_converters; - static base::LazyInstance - g_rgb_converters_thread_checker; -}; - -base::LazyInstance> - GLImageIOSurface::RGBConverter::g_rgb_converters; - -base::LazyInstance - GLImageIOSurface::RGBConverter::g_rgb_converters_thread_checker; - -scoped_refptr -GLImageIOSurface::RGBConverter::GetForCurrentContext() { - CGLContextObj current_context = CGLGetCurrentContext(); - DCHECK(current_context); - DCHECK(g_rgb_converters_thread_checker.Get().CalledOnValidThread()); - auto found = g_rgb_converters.Get().find(current_context); - if (found != g_rgb_converters.Get().end()) - return make_scoped_refptr(found->second); - return make_scoped_refptr(new RGBConverter(current_context)); -} - -GLImageIOSurface::RGBConverter::RGBConverter(CGLContextObj cgl_context) - : cgl_context_(cgl_context, base::scoped_policy::RETAIN) { - bool use_core_profile = - gfx::GetGLImplementation() == gfx::kGLImplementationDesktopGLCoreProfile; - gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; - glGenFramebuffersEXT(1, &framebuffer_); - vertex_buffer_ = gfx::GLHelper::SetupQuadVertexBuffer(); - vertex_shader_ = gfx::GLHelper::LoadShader( - GL_VERTEX_SHADER, - base::StringPrintf("%s\n%s", - use_core_profile ? kVertexHeaderCoreProfile - : kVertexHeaderCompatiblityProfile, - kVertexShader).c_str()); - fragment_shader_ = gfx::GLHelper::LoadShader( - GL_FRAGMENT_SHADER, - base::StringPrintf("%s\n%s", - use_core_profile ? kFragmentHeaderCoreProfile - : kFragmentHeaderCompatiblityProfile, - kFragmentShader).c_str()); - program_ = gfx::GLHelper::SetupProgram(vertex_shader_, fragment_shader_); - - gfx::ScopedUseProgram use_program(program_); - size_location_ = glGetUniformLocation(program_, "a_texScale"); - DCHECK_NE(-1, size_location_); - int y_sampler_location = glGetUniformLocation(program_, "a_y_texture"); - DCHECK_NE(-1, y_sampler_location); - int uv_sampler_location = glGetUniformLocation(program_, "a_uv_texture"); - DCHECK_NE(-1, uv_sampler_location); - - glUniform1i(y_sampler_location, 0); - glUniform1i(uv_sampler_location, 1); - - DCHECK(g_rgb_converters_thread_checker.Get().CalledOnValidThread()); - DCHECK(g_rgb_converters.Get().find(cgl_context) == - g_rgb_converters.Get().end()); - g_rgb_converters.Get()[cgl_context] = this; -} - -GLImageIOSurface::RGBConverter::~RGBConverter() { - DCHECK(g_rgb_converters_thread_checker.Get().CalledOnValidThread()); - DCHECK(g_rgb_converters.Get()[cgl_context_] == this); - g_rgb_converters.Get().erase(cgl_context_.get()); - { - gfx::ScopedCGLSetCurrentContext(cgl_context_.get()); - gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; - glDeleteProgram(program_); - glDeleteShader(vertex_shader_); - glDeleteShader(fragment_shader_); - glDeleteBuffersARB(1, &vertex_buffer_); - glDeleteFramebuffersEXT(1, &framebuffer_); - } - cgl_context_.reset(); -} - -bool GLImageIOSurface::RGBConverter::CopyTexImage(IOSurfaceRef io_surface, - const gfx::Size& size) { - gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; - DCHECK_EQ(CGLGetCurrentContext(), cgl_context_.get()); - glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, size.width(), size.height(), - 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); - GLint target_texture = 0; - glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &target_texture); - DCHECK(target_texture); - - // Note that state restoration is done explicitly in the ScopedClosureRunner - // instead of scoped binders to avoid https://crbug.com/601729. - GLint old_active_texture = -1; - glGetIntegerv(GL_ACTIVE_TEXTURE, &old_active_texture); - GLint old_texture0_binding = -1; - glActiveTexture(GL_TEXTURE0); - glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_texture0_binding); - GLint old_texture1_binding = -1; - glActiveTexture(GL_TEXTURE1); - glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_texture1_binding); - - unsigned y_texture = 0; - glGenTextures(1, &y_texture); - unsigned uv_texture = 0; - glGenTextures(1, &uv_texture); - - base::ScopedClosureRunner destroy_resources_runner(base::BindBlock(^{ - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, old_texture0_binding); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, old_texture1_binding); - glActiveTexture(old_active_texture); - - glDeleteTextures(1, &y_texture); - glDeleteTextures(1, &uv_texture); - })); - - CGLError cgl_error = kCGLNoError; - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, y_texture); - cgl_error = CGLTexImageIOSurface2D(cgl_context_, GL_TEXTURE_RECTANGLE_ARB, - GL_RED, size.width(), size.height(), - GL_RED, GL_UNSIGNED_BYTE, io_surface, 0); - if (cgl_error != kCGLNoError) { - LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the Y plane. " - << cgl_error; - return false; - } - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, uv_texture); - cgl_error = CGLTexImageIOSurface2D(cgl_context_, GL_TEXTURE_RECTANGLE_ARB, - GL_RG, size.width() / 2, size.height() / 2, - GL_RG, GL_UNSIGNED_BYTE, io_surface, 1); - if (cgl_error != kCGLNoError) { - LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the UV plane. " - << cgl_error; - return false; - } - - gfx::ScopedFrameBufferBinder framebuffer_binder(framebuffer_); - gfx::ScopedViewport viewport(0, 0, size.width(), size.height()); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_RECTANGLE_ARB, target_texture, 0); - DCHECK_EQ(static_cast(GL_FRAMEBUFFER_COMPLETE), - glCheckFramebufferStatusEXT(GL_FRAMEBUFFER)); - gfx::ScopedUseProgram use_program(program_); - glUniform2f(size_location_, size.width(), size.height()); - gfx::GLHelper::DrawQuad(vertex_buffer_); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_RECTANGLE_ARB, 0, 0); - return true; -} - GLImageIOSurface::GLImageIOSurface(const gfx::Size& size, unsigned internalformat) : size_(size), @@ -492,13 +263,62 @@ GLenum ConvertRequestedInternalFormat(GLenum internalformat) { if (format_ != BufferFormat::YUV_420_BIPLANAR) return false; + if (target != GL_TEXTURE_RECTANGLE_ARB) { LOG(ERROR) << "YUV_420_BIPLANAR requires GL_TEXTURE_RECTANGLE_ARB target"; return false; } - rgb_converter_ = RGBConverter::GetForCurrentContext(); - return rgb_converter_->CopyTexImage(io_surface_.get(), size_); + gfx::GLContext* gl_context = gfx::GLContext::GetCurrent(); + DCHECK(gl_context); + + gl::YUVToRGBConverter* yuv_to_rgb_converter = + gl_context->GetYUVToRGBConverter(); + DCHECK(yuv_to_rgb_converter); + + gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; + + // Note that state restoration is done explicitly instead of scoped binders to + // avoid https://crbug.com/601729. + GLint rgb_texture = 0; + GLuint y_texture = 0; + GLuint uv_texture = 0; + glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &rgb_texture); + glGenTextures(1, &y_texture); + glGenTextures(1, &uv_texture); + base::ScopedClosureRunner destroy_resources_runner(base::BindBlock(^{ + glDeleteTextures(1, &y_texture); + glDeleteTextures(1, &uv_texture); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, rgb_texture); + })); + + CGLContextObj cgl_context = CGLGetCurrentContext(); + { + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, y_texture); + CGLError cgl_error = CGLTexImageIOSurface2D( + cgl_context, GL_TEXTURE_RECTANGLE_ARB, GL_RED, size_.width(), + size_.height(), GL_RED, GL_UNSIGNED_BYTE, io_surface_, 0); + if (cgl_error != kCGLNoError) { + LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the Y plane. " + << cgl_error; + return false; + } + } + { + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, uv_texture); + CGLError cgl_error = CGLTexImageIOSurface2D( + cgl_context, GL_TEXTURE_RECTANGLE_ARB, GL_RG, size_.width() / 2, + size_.height() / 2, GL_RG, GL_UNSIGNED_BYTE, io_surface_, 1); + if (cgl_error != kCGLNoError) { + LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the UV plane. " + << cgl_error; + return false; + } + } + + yuv_to_rgb_converter->CopyYUV420ToRGB( + GL_TEXTURE_RECTANGLE_ARB, y_texture, uv_texture, size_, rgb_texture); + return true; } bool GLImageIOSurface::CopyTexSubImage(unsigned target, diff --git a/ui/gl/yuv_to_rgb_converter.cc b/ui/gl/yuv_to_rgb_converter.cc new file mode 100644 index 00000000000000..4c19b3685d0ceb --- /dev/null +++ b/ui/gl/yuv_to_rgb_converter.cc @@ -0,0 +1,166 @@ +// Copyright 2016 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/yuv_to_rgb_converter.h" + +#include "base/strings/stringize_macros.h" +#include "base/strings/stringprintf.h" +#include "ui/gl/gl_helper.h" +#include "ui/gl/gl_implementation.h" +#include "ui/gl/scoped_api.h" +#include "ui/gl/scoped_binders.h" + +namespace gl { +namespace { + +const char kVertexHeaderCompatiblityProfile[] = + "#version 110\n" + "#define ATTRIBUTE attribute\n" + "#define VARYING varying\n"; + +const char kVertexHeaderCoreProfile[] = + "#version 150\n" + "#define ATTRIBUTE in\n" + "#define VARYING out\n"; + +const char kFragmentHeaderCompatiblityProfile[] = + "#version 110\n" + "#extension GL_ARB_texture_rectangle : require\n" + "#define VARYING varying\n" + "#define FRAGCOLOR gl_FragColor\n" + "#define TEX texture2DRect\n"; + +const char kFragmentHeaderCoreProfile[] = + "#version 150\n" + "#define VARYING in\n" + "#define TEX texture\n" + "#define FRAGCOLOR frag_color\n" + "out vec4 FRAGCOLOR;\n"; + +// clang-format off +const char kVertexShader[] = +STRINGIZE( + ATTRIBUTE vec2 a_position; + uniform vec2 a_texScale; + VARYING vec2 v_texCoord; + void main() { + gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0); + v_texCoord = (a_position + vec2(1.0, 1.0)) * 0.5 * a_texScale; + } +); + +const char kFragmentShader[] = +STRINGIZE( + uniform sampler2DRect a_y_texture; + uniform sampler2DRect a_uv_texture; + VARYING vec2 v_texCoord; + void main() { + vec3 yuv_adj = vec3(-0.0625, -0.5, -0.5); + mat3 yuv_matrix = mat3(vec3(1.164, 1.164, 1.164), + vec3(0.0, -.391, 2.018), + vec3(1.596, -.813, 0.0)); + vec3 yuv = vec3( + TEX(a_y_texture, v_texCoord).r, + TEX(a_uv_texture, v_texCoord * 0.5).rg); + FRAGCOLOR = vec4(yuv_matrix * (yuv + yuv_adj), 1.0); + } +); +// clang-format on + +} // namespace + +YUVToRGBConverter::YUVToRGBConverter() { + bool use_core_profile = + gfx::GetGLImplementation() == gfx::kGLImplementationDesktopGLCoreProfile; + gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; + glGenFramebuffersEXT(1, &framebuffer_); + vertex_buffer_ = gfx::GLHelper::SetupQuadVertexBuffer(); + vertex_shader_ = gfx::GLHelper::LoadShader( + GL_VERTEX_SHADER, + base::StringPrintf("%s\n%s", + use_core_profile ? kVertexHeaderCoreProfile + : kVertexHeaderCompatiblityProfile, + kVertexShader) + .c_str()); + fragment_shader_ = gfx::GLHelper::LoadShader( + GL_FRAGMENT_SHADER, + base::StringPrintf("%s\n%s", + use_core_profile ? kFragmentHeaderCoreProfile + : kFragmentHeaderCompatiblityProfile, + kFragmentShader) + .c_str()); + program_ = gfx::GLHelper::SetupProgram(vertex_shader_, fragment_shader_); + + gfx::ScopedUseProgram use_program(program_); + size_location_ = glGetUniformLocation(program_, "a_texScale"); + DCHECK_NE(-1, size_location_); + int y_sampler_location = glGetUniformLocation(program_, "a_y_texture"); + DCHECK_NE(-1, y_sampler_location); + int uv_sampler_location = glGetUniformLocation(program_, "a_uv_texture"); + DCHECK_NE(-1, uv_sampler_location); + + glUniform1i(y_sampler_location, 0); + glUniform1i(uv_sampler_location, 1); +} + +YUVToRGBConverter::~YUVToRGBConverter() { + gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; + glDeleteProgram(program_); + glDeleteShader(vertex_shader_); + glDeleteShader(fragment_shader_); + glDeleteBuffersARB(1, &vertex_buffer_); + glDeleteFramebuffersEXT(1, &framebuffer_); +} + +void YUVToRGBConverter::CopyYUV420ToRGB(unsigned target, + unsigned y_texture, + unsigned uv_texture, + const gfx::Size& size, + unsigned rgb_texture) { + // Only support for rectangle targets exists so far. + DCHECK_EQ(static_cast(GL_TEXTURE_RECTANGLE_ARB), target); + gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; + + // Note that state restoration is done explicitly instead of scoped binders to + // avoid https://crbug.com/601729. + GLint old_active_texture = -1; + glGetIntegerv(GL_ACTIVE_TEXTURE, &old_active_texture); + GLint old_texture0_binding = -1; + glActiveTexture(GL_TEXTURE0); + glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_texture0_binding); + GLint old_texture1_binding = -1; + glActiveTexture(GL_TEXTURE1); + glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_texture1_binding); + + // Allocate the rgb texture. + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, rgb_texture); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, size.width(), size.height(), + 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); + + // Set up and issue the draw call. + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, y_texture); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, uv_texture); + gfx::ScopedFrameBufferBinder framebuffer_binder(framebuffer_); + gfx::ScopedViewport viewport(0, 0, size.width(), size.height()); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_RECTANGLE_ARB, rgb_texture, 0); + DCHECK_EQ(static_cast(GL_FRAMEBUFFER_COMPLETE), + glCheckFramebufferStatusEXT(GL_FRAMEBUFFER)); + gfx::ScopedUseProgram use_program(program_); + glUniform2f(size_location_, size.width(), size.height()); + gfx::GLHelper::DrawQuad(vertex_buffer_); + + // Restore previous state. + glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_RECTANGLE_ARB, 0, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, old_texture0_binding); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, old_texture1_binding); + glActiveTexture(old_active_texture); +} + +} // namespace gl diff --git a/ui/gl/yuv_to_rgb_converter.h b/ui/gl/yuv_to_rgb_converter.h new file mode 100644 index 00000000000000..794b61511a61b8 --- /dev/null +++ b/ui/gl/yuv_to_rgb_converter.h @@ -0,0 +1,33 @@ +// Copyright 2016 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. + +#ifndef UI_GL_YUV420_RGB_CONVERTER_H_ +#define UI_GL_YUV420_RGB_CONVERTER_H_ + +#include "ui/gfx/geometry/size.h" + +namespace gl { + +class YUVToRGBConverter { + public: + YUVToRGBConverter(); + ~YUVToRGBConverter(); + void CopyYUV420ToRGB(unsigned target, + unsigned y_texture, + unsigned uv_texture, + const gfx::Size& size, + unsigned rgb_texture); + + private: + unsigned framebuffer_ = 0; + unsigned vertex_shader_ = 0; + unsigned fragment_shader_ = 0; + unsigned program_ = 0; + int size_location_ = -1; + unsigned vertex_buffer_ = 0; +}; + +} // namespace gl + +#endif // UI_GL_YUV420_RGB_CONVERTER_H_