1 // Copyright 2016 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 "ui/gl/yuv_to_rgb_converter.h"
6 
7 #include "base/strings/stringize_macros.h"
8 #include "base/strings/stringprintf.h"
9 #include "ui/gfx/color_transform.h"
10 #include "ui/gl/gl_helper.h"
11 #include "ui/gl/gl_version_info.h"
12 #include "ui/gl/scoped_binders.h"
13 
14 namespace gl {
15 namespace {
16 
17 const char kVertexHeaderES2[] =
18     "precision mediump float;\n"
19     "#define ATTRIBUTE attribute\n"
20     "#define VARYING varying\n";
21 
22 const char kVertexHeaderES3[] =
23     "#version 300 es\n"
24     "precision mediump float;\n"
25     "#define ATTRIBUTE in\n"
26     "#define VARYING out\n";
27 
28 const char kVertexHeaderCompatiblityProfile[] =
29     "#version 110\n"
30     "#define ATTRIBUTE attribute\n"
31     "#define VARYING varying\n";
32 
33 const char kVertexHeaderCoreProfile[] =
34     "#version 150\n"
35     "#define ATTRIBUTE in\n"
36     "#define VARYING out\n";
37 
38 const char kFragmentHeaderES22D[] =
39     "precision mediump float;\n"
40     "#define VARYING varying\n"
41     "#define FRAGCOLOR gl_FragColor\n"
42     "#define TEX texture2D\n";
43 
44 const char kFragmentHeaderES2Rect[] =
45     "#extension GL_ARB_texture_rectangle : require\n"
46     "precision mediump float;\n"
47     "#define VARYING varying\n"
48     "#define FRAGCOLOR gl_FragColor\n"
49     "#define TEX texture2DRect\n";
50 
51 const char kFragmentHeaderES3[] =
52     "#version 300 es\n"
53     "precision mediump float;\n"
54     "#define VARYING in\n"
55     "#define TEX texture\n"
56     "#define FRAGCOLOR frag_color\n"
57     "out vec4 FRAGCOLOR;\n";
58 
59 const char kFragmentHeaderCompatiblityProfile2D[] =
60     "#version 110\n"
61     "#define VARYING varying\n"
62     "#define FRAGCOLOR gl_FragColor\n"
63     "#define TEX texture2D\n";
64 
65 const char kFragmentHeaderCompatiblityProfileRect[] =
66     "#version 110\n"
67     "#extension GL_ARB_texture_rectangle : require\n"
68     "#define VARYING varying\n"
69     "#define FRAGCOLOR gl_FragColor\n"
70     "#define TEX texture2DRect\n";
71 
72 const char kFragmentHeaderCoreProfile[] =
73     "#version 150\n"
74     "#define VARYING in\n"
75     "#define TEX texture\n"
76     "#define FRAGCOLOR frag_color\n"
77     "out vec4 FRAGCOLOR;\n";
78 
79 // clang-format off
80 const char kVertexShader[] =
81 STRINGIZE(
82   ATTRIBUTE vec2 a_position;
83   uniform vec2 a_texScale;
84   VARYING vec2 v_texCoord;
85   void main() {
86     gl_Position = vec4(a_position, 0.0, 1.0);
87     v_texCoord = (a_position + vec2(1.0, 1.0)) * 0.5 * a_texScale;
88   }
89 );
90 
91 const char kFragmentShader2D[] =
92 STRINGIZE(
93   uniform sampler2D a_y_texture;
94   uniform sampler2D a_uv_texture;
95   VARYING vec2 v_texCoord;
96   void main() {
97     vec3 yuv = vec3(
98         TEX(a_y_texture, v_texCoord).r,
99         TEX(a_uv_texture, v_texCoord * 0.5).rg);
100     FRAGCOLOR = vec4(DoColorConversion(yuv), 1.0);
101   }
102 );
103 
104 const char kFragmentShaderRect[] =
105 STRINGIZE(
106   uniform sampler2DRect a_y_texture;
107   uniform sampler2DRect a_uv_texture;
108   VARYING vec2 v_texCoord;
109   void main() {
110     vec3 yuv = vec3(
111         TEX(a_y_texture, v_texCoord).r,
112         TEX(a_uv_texture, v_texCoord * 0.5).rg);
113     FRAGCOLOR = vec4(DoColorConversion(yuv), 1.0);
114   }
115 );
116 
117 // clang-format on
118 
119 }  // namespace
120 
YUVToRGBConverter(const GLVersionInfo & gl_version_info,const gfx::ColorSpace color_space)121 YUVToRGBConverter::YUVToRGBConverter(const GLVersionInfo& gl_version_info,
122                                      const gfx::ColorSpace color_space) {
123   std::unique_ptr<gfx::ColorTransform> color_transform =
124       gfx::ColorTransform::NewColorTransform(
125           color_space, color_space.GetAsFullRangeRGB(),
126           gfx::ColorTransform::Intent::INTENT_PERCEPTUAL);
127   std::string do_color_conversion = color_transform->GetShaderSource();
128 
129   // On MacOS, the default texture target for native GpuMemoryBuffers is
130   // GL_TEXTURE_RECTANGLE_ARB. This is due to CGL's requirements for creating
131   // a GL surface. However, when ANGLE is used on top of SwiftShader, it's
132   // necessary to use GL_TEXTURE_2D instead.
133   // TODO(crbug.com/1056312): The proper behavior is to check the config
134   // parameter set by the EGL_ANGLE_iosurface_client_buffer extension
135   bool is_rect = !gl_version_info.is_angle_swiftshader;
136   source_texture_target_ = (is_rect ? GL_TEXTURE_RECTANGLE_ARB : GL_TEXTURE_2D);
137 
138   const char* fragment_header = nullptr;
139   const char* vertex_header = nullptr;
140   if (gl_version_info.is_es2) {
141     vertex_header = kVertexHeaderES2;
142     fragment_header = (is_rect ? kFragmentHeaderES2Rect : kFragmentHeaderES22D);
143   } else if (gl_version_info.is_es3) {
144     vertex_header = kVertexHeaderES3;
145     fragment_header = kFragmentHeaderES3;
146   } else if (gl_version_info.is_desktop_core_profile) {
147     vertex_header = kVertexHeaderCoreProfile;
148     fragment_header = kFragmentHeaderCoreProfile;
149   } else {
150     DCHECK(!gl_version_info.is_es);
151     vertex_header = kVertexHeaderCompatiblityProfile;
152     fragment_header = (is_rect ? kFragmentHeaderCompatiblityProfileRect
153                                : kFragmentHeaderCompatiblityProfile2D);
154   }
155   DCHECK(vertex_header && fragment_header);
156 
157   glGenFramebuffersEXT(1, &framebuffer_);
158 
159   vertex_buffer_ = GLHelper::SetupQuadVertexBuffer();
160   vertex_shader_ = GLHelper::LoadShader(
161       GL_VERTEX_SHADER,
162       base::StringPrintf("%s\n%s", vertex_header, kVertexShader).c_str());
163   fragment_shader_ = GLHelper::LoadShader(
164       GL_FRAGMENT_SHADER,
165       base::StringPrintf("%s\n%s\n%s", fragment_header,
166                          do_color_conversion.c_str(),
167                          (is_rect ? kFragmentShaderRect : kFragmentShader2D))
168           .c_str());
169   program_ = GLHelper::SetupProgram(vertex_shader_, fragment_shader_);
170 
171   ScopedUseProgram use_program(program_);
172   size_location_ = glGetUniformLocation(program_, "a_texScale");
173   DCHECK_NE(-1, size_location_);
174   int y_sampler_location = glGetUniformLocation(program_, "a_y_texture");
175   DCHECK_NE(-1, y_sampler_location);
176   int uv_sampler_location = glGetUniformLocation(program_, "a_uv_texture");
177   DCHECK_NE(-1, uv_sampler_location);
178 
179   glGenTextures(1, &y_texture_);
180   glGenTextures(1, &uv_texture_);
181 
182   glUniform1i(y_sampler_location, 0);
183   glUniform1i(uv_sampler_location, 1);
184 
185   bool has_vertex_array_objects =
186       gl_version_info.is_es3 || gl_version_info.is_desktop_core_profile;
187   if (has_vertex_array_objects) {
188     glGenVertexArraysOES(1, &vertex_array_object_);
189   }
190 }
191 
~YUVToRGBConverter()192 YUVToRGBConverter::~YUVToRGBConverter() {
193   glDeleteTextures(1, &y_texture_);
194   glDeleteTextures(1, &uv_texture_);
195   glDeleteProgram(program_);
196   glDeleteShader(vertex_shader_);
197   glDeleteShader(fragment_shader_);
198   glDeleteBuffersARB(1, &vertex_buffer_);
199   glDeleteFramebuffersEXT(1, &framebuffer_);
200   if (vertex_array_object_) {
201     glDeleteVertexArraysOES(1, &vertex_array_object_);
202   }
203 }
204 
CopyYUV420ToRGB(unsigned target,const gfx::Size & size,unsigned rgb_texture)205 void YUVToRGBConverter::CopyYUV420ToRGB(unsigned target,
206                                         const gfx::Size& size,
207                                         unsigned rgb_texture) {
208   GLenum source_target_getter = 0;
209   switch (source_texture_target_) {
210     case GL_TEXTURE_2D:
211       source_target_getter = GL_TEXTURE_BINDING_2D;
212       break;
213     case GL_TEXTURE_RECTANGLE_ARB:
214       source_target_getter = GL_TEXTURE_BINDING_RECTANGLE_ARB;
215       break;
216     default:
217       NOTIMPLEMENTED() << " Target not supported.";
218       return;
219   }
220   // Note that state restoration is done explicitly instead of scoped binders to
221   // avoid https://crbug.com/601729.
222   GLint old_active_texture = -1;
223   glGetIntegerv(GL_ACTIVE_TEXTURE, &old_active_texture);
224   GLint old_texture0_binding = -1;
225   glActiveTexture(GL_TEXTURE0);
226   glGetIntegerv(source_target_getter, &old_texture0_binding);
227   GLint old_texture1_binding = -1;
228   glActiveTexture(GL_TEXTURE1);
229   glGetIntegerv(source_target_getter, &old_texture1_binding);
230 
231   // Allocate the rgb texture.
232   glActiveTexture(old_active_texture);
233   glBindTexture(target, rgb_texture);
234   glTexImage2D(target, 0, GL_RGB, size.width(), size.height(),
235                0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
236 
237   // Set up and issue the draw call.
238   glActiveTexture(GL_TEXTURE0);
239   glBindTexture(source_texture_target_, y_texture_);
240   glActiveTexture(GL_TEXTURE1);
241   glBindTexture(source_texture_target_, uv_texture_);
242   ScopedFramebufferBinder framebuffer_binder(framebuffer_);
243   ScopedViewport viewport(0, 0, size.width(), size.height());
244   glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
245                             target, rgb_texture, 0);
246   DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
247             glCheckFramebufferStatusEXT(GL_FRAMEBUFFER));
248   ScopedUseProgram use_program(program_);
249   glUniform2f(size_location_, size.width(), size.height());
250   // User code may have set up the other vertex attributes in the
251   // context in unexpected ways, including setting vertex attribute
252   // divisors which may otherwise cause GL_INVALID_OPERATION during
253   // glDrawArrays. Avoid interference by binding our own VAO during
254   // the draw call. crbug.com/930479
255   GLint old_vertex_array_object_ = 0;
256   if (vertex_array_object_) {
257     glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &old_vertex_array_object_);
258     glBindVertexArrayOES(vertex_array_object_);
259   }
260   GLHelper::DrawQuad(vertex_buffer_);
261   if (vertex_array_object_) {
262     glBindVertexArrayOES(old_vertex_array_object_);
263   }
264 
265   // Restore previous state.
266   glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
267                             target, 0, 0);
268   glActiveTexture(GL_TEXTURE0);
269   glBindTexture(source_texture_target_, old_texture0_binding);
270   glActiveTexture(GL_TEXTURE1);
271   glBindTexture(source_texture_target_, old_texture1_binding);
272   glActiveTexture(old_active_texture);
273 }
274 
275 }  // namespace gl
276