yuv_to_rgb_converter.cc 12 KB

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