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