| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/gl/gl_image_io_surface.h" | 5 #include "ui/gl/gl_image_io_surface.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 | 8 |
| 9 #include "base/callback_helpers.h" | 9 #include "base/callback_helpers.h" |
| 10 #include "base/lazy_instance.h" | |
| 11 #include "base/mac/bind_objc_block.h" | 10 #include "base/mac/bind_objc_block.h" |
| 12 #include "base/mac/foundation_util.h" | 11 #include "base/mac/foundation_util.h" |
| 13 #include "base/strings/stringize_macros.h" | |
| 14 #include "base/strings/stringprintf.h" | |
| 15 #include "base/trace_event/memory_allocator_dump.h" | 12 #include "base/trace_event/memory_allocator_dump.h" |
| 16 #include "base/trace_event/memory_dump_manager.h" | 13 #include "base/trace_event/memory_dump_manager.h" |
| 17 #include "base/trace_event/process_memory_dump.h" | 14 #include "base/trace_event/process_memory_dump.h" |
| 18 #include "ui/gl/gl_bindings.h" | 15 #include "ui/gl/gl_bindings.h" |
| 19 #include "ui/gl/gl_context.h" | 16 #include "ui/gl/gl_context.h" |
| 20 #include "ui/gl/gl_helper.h" | |
| 21 #include "ui/gl/gl_implementation.h" | |
| 22 #include "ui/gl/scoped_api.h" | 17 #include "ui/gl/scoped_api.h" |
| 23 #include "ui/gl/scoped_binders.h" | 18 #include "ui/gl/scoped_binders.h" |
| 24 #include "ui/gl/scoped_cgl.h" | 19 #include "ui/gl/yuv_to_rgb_converter.h" |
| 25 | 20 |
| 26 // Note that this must be included after gl_bindings.h to avoid conflicts. | 21 // Note that this must be included after gl_bindings.h to avoid conflicts. |
| 27 #include <OpenGL/CGLIOSurface.h> | 22 #include <OpenGL/CGLIOSurface.h> |
| 28 #include <Quartz/Quartz.h> | 23 #include <Quartz/Quartz.h> |
| 29 #include <stddef.h> | 24 #include <stddef.h> |
| 30 | 25 |
| 31 using gfx::BufferFormat; | 26 using gfx::BufferFormat; |
| 32 | 27 |
| 33 namespace gl { | 28 namespace gl { |
| 34 namespace { | 29 namespace { |
| 35 | 30 |
| 36 const char kVertexHeaderCompatiblityProfile[] = | |
| 37 "#version 110\n" | |
| 38 "#define ATTRIBUTE attribute\n" | |
| 39 "#define VARYING varying\n"; | |
| 40 | |
| 41 const char kVertexHeaderCoreProfile[] = | |
| 42 "#version 150\n" | |
| 43 "#define ATTRIBUTE in\n" | |
| 44 "#define VARYING out\n"; | |
| 45 | |
| 46 const char kFragmentHeaderCompatiblityProfile[] = | |
| 47 "#version 110\n" | |
| 48 "#extension GL_ARB_texture_rectangle : require\n" | |
| 49 "#define VARYING varying\n" | |
| 50 "#define FRAGCOLOR gl_FragColor\n" | |
| 51 "#define TEX texture2DRect\n"; | |
| 52 | |
| 53 const char kFragmentHeaderCoreProfile[] = | |
| 54 "#version 150\n" | |
| 55 "#define VARYING in\n" | |
| 56 "#define TEX texture\n" | |
| 57 "#define FRAGCOLOR frag_color\n" | |
| 58 "out vec4 FRAGCOLOR;\n"; | |
| 59 | |
| 60 // clang-format off | |
| 61 const char kVertexShader[] = | |
| 62 STRINGIZE( | |
| 63 ATTRIBUTE vec2 a_position; | |
| 64 uniform vec2 a_texScale; | |
| 65 VARYING vec2 v_texCoord; | |
| 66 void main() { | |
| 67 gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0); | |
| 68 v_texCoord = (a_position + vec2(1.0, 1.0)) * 0.5 * a_texScale; | |
| 69 } | |
| 70 ); | |
| 71 | |
| 72 const char kFragmentShader[] = | |
| 73 STRINGIZE( | |
| 74 uniform sampler2DRect a_y_texture; | |
| 75 uniform sampler2DRect a_uv_texture; | |
| 76 VARYING vec2 v_texCoord; | |
| 77 void main() { | |
| 78 vec3 yuv_adj = vec3(-0.0625, -0.5, -0.5); | |
| 79 mat3 yuv_matrix = mat3(vec3(1.164, 1.164, 1.164), | |
| 80 vec3(0.0, -.391, 2.018), | |
| 81 vec3(1.596, -.813, 0.0)); | |
| 82 vec3 yuv = vec3( | |
| 83 TEX(a_y_texture, v_texCoord).r, | |
| 84 TEX(a_uv_texture, v_texCoord * 0.5).rg); | |
| 85 FRAGCOLOR = vec4(yuv_matrix * (yuv + yuv_adj), 1.0); | |
| 86 } | |
| 87 ); | |
| 88 // clang-format on | |
| 89 | |
| 90 bool ValidInternalFormat(unsigned internalformat) { | 31 bool ValidInternalFormat(unsigned internalformat) { |
| 91 switch (internalformat) { | 32 switch (internalformat) { |
| 92 case GL_RED: | 33 case GL_RED: |
| 93 case GL_BGRA_EXT: | 34 case GL_BGRA_EXT: |
| 94 case GL_RGB: | 35 case GL_RGB: |
| 95 case GL_RGB_YCBCR_420V_CHROMIUM: | 36 case GL_RGB_YCBCR_420V_CHROMIUM: |
| 96 case GL_RGB_YCBCR_422_CHROMIUM: | 37 case GL_RGB_YCBCR_422_CHROMIUM: |
| 97 case GL_RGBA: | 38 case GL_RGBA: |
| 98 return true; | 39 return true; |
| 99 default: | 40 default: |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 203 NOTREACHED(); | 144 NOTREACHED(); |
| 204 return 0; | 145 return 0; |
| 205 } | 146 } |
| 206 | 147 |
| 207 NOTREACHED(); | 148 NOTREACHED(); |
| 208 return 0; | 149 return 0; |
| 209 } | 150 } |
| 210 | 151 |
| 211 } // namespace | 152 } // namespace |
| 212 | 153 |
| 213 class GLImageIOSurface::RGBConverter | |
| 214 : public base::RefCounted<GLImageIOSurface::RGBConverter> { | |
| 215 public: | |
| 216 static scoped_refptr<RGBConverter> GetForCurrentContext(); | |
| 217 bool CopyTexImage(IOSurfaceRef io_surface, const gfx::Size& size); | |
| 218 | |
| 219 private: | |
| 220 friend class base::RefCounted<RGBConverter>; | |
| 221 RGBConverter(CGLContextObj cgl_context); | |
| 222 ~RGBConverter(); | |
| 223 | |
| 224 unsigned framebuffer_ = 0; | |
| 225 unsigned vertex_shader_ = 0; | |
| 226 unsigned fragment_shader_ = 0; | |
| 227 unsigned program_ = 0; | |
| 228 int size_location_ = -1; | |
| 229 unsigned vertex_buffer_ = 0; | |
| 230 base::ScopedTypeRef<CGLContextObj> cgl_context_; | |
| 231 | |
| 232 static base::LazyInstance< | |
| 233 std::map<CGLContextObj, GLImageIOSurface::RGBConverter*>> | |
| 234 g_rgb_converters; | |
| 235 static base::LazyInstance<base::ThreadChecker> | |
| 236 g_rgb_converters_thread_checker; | |
| 237 }; | |
| 238 | |
| 239 base::LazyInstance<std::map<CGLContextObj, GLImageIOSurface::RGBConverter*>> | |
| 240 GLImageIOSurface::RGBConverter::g_rgb_converters; | |
| 241 | |
| 242 base::LazyInstance<base::ThreadChecker> | |
| 243 GLImageIOSurface::RGBConverter::g_rgb_converters_thread_checker; | |
| 244 | |
| 245 scoped_refptr<GLImageIOSurface::RGBConverter> | |
| 246 GLImageIOSurface::RGBConverter::GetForCurrentContext() { | |
| 247 CGLContextObj current_context = CGLGetCurrentContext(); | |
| 248 DCHECK(current_context); | |
| 249 DCHECK(g_rgb_converters_thread_checker.Get().CalledOnValidThread()); | |
| 250 auto found = g_rgb_converters.Get().find(current_context); | |
| 251 if (found != g_rgb_converters.Get().end()) | |
| 252 return make_scoped_refptr(found->second); | |
| 253 return make_scoped_refptr(new RGBConverter(current_context)); | |
| 254 } | |
| 255 | |
| 256 GLImageIOSurface::RGBConverter::RGBConverter(CGLContextObj cgl_context) | |
| 257 : cgl_context_(cgl_context, base::scoped_policy::RETAIN) { | |
| 258 bool use_core_profile = | |
| 259 gfx::GetGLImplementation() == gfx::kGLImplementationDesktopGLCoreProfile; | |
| 260 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; | |
| 261 glGenFramebuffersEXT(1, &framebuffer_); | |
| 262 vertex_buffer_ = gfx::GLHelper::SetupQuadVertexBuffer(); | |
| 263 vertex_shader_ = gfx::GLHelper::LoadShader( | |
| 264 GL_VERTEX_SHADER, | |
| 265 base::StringPrintf("%s\n%s", | |
| 266 use_core_profile ? kVertexHeaderCoreProfile | |
| 267 : kVertexHeaderCompatiblityProfile, | |
| 268 kVertexShader).c_str()); | |
| 269 fragment_shader_ = gfx::GLHelper::LoadShader( | |
| 270 GL_FRAGMENT_SHADER, | |
| 271 base::StringPrintf("%s\n%s", | |
| 272 use_core_profile ? kFragmentHeaderCoreProfile | |
| 273 : kFragmentHeaderCompatiblityProfile, | |
| 274 kFragmentShader).c_str()); | |
| 275 program_ = gfx::GLHelper::SetupProgram(vertex_shader_, fragment_shader_); | |
| 276 | |
| 277 gfx::ScopedUseProgram use_program(program_); | |
| 278 size_location_ = glGetUniformLocation(program_, "a_texScale"); | |
| 279 DCHECK_NE(-1, size_location_); | |
| 280 int y_sampler_location = glGetUniformLocation(program_, "a_y_texture"); | |
| 281 DCHECK_NE(-1, y_sampler_location); | |
| 282 int uv_sampler_location = glGetUniformLocation(program_, "a_uv_texture"); | |
| 283 DCHECK_NE(-1, uv_sampler_location); | |
| 284 | |
| 285 glUniform1i(y_sampler_location, 0); | |
| 286 glUniform1i(uv_sampler_location, 1); | |
| 287 | |
| 288 DCHECK(g_rgb_converters_thread_checker.Get().CalledOnValidThread()); | |
| 289 DCHECK(g_rgb_converters.Get().find(cgl_context) == | |
| 290 g_rgb_converters.Get().end()); | |
| 291 g_rgb_converters.Get()[cgl_context] = this; | |
| 292 } | |
| 293 | |
| 294 GLImageIOSurface::RGBConverter::~RGBConverter() { | |
| 295 DCHECK(g_rgb_converters_thread_checker.Get().CalledOnValidThread()); | |
| 296 DCHECK(g_rgb_converters.Get()[cgl_context_] == this); | |
| 297 g_rgb_converters.Get().erase(cgl_context_.get()); | |
| 298 { | |
| 299 gfx::ScopedCGLSetCurrentContext(cgl_context_.get()); | |
| 300 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; | |
| 301 glDeleteProgram(program_); | |
| 302 glDeleteShader(vertex_shader_); | |
| 303 glDeleteShader(fragment_shader_); | |
| 304 glDeleteBuffersARB(1, &vertex_buffer_); | |
| 305 glDeleteFramebuffersEXT(1, &framebuffer_); | |
| 306 } | |
| 307 cgl_context_.reset(); | |
| 308 } | |
| 309 | |
| 310 bool GLImageIOSurface::RGBConverter::CopyTexImage(IOSurfaceRef io_surface, | |
| 311 const gfx::Size& size) { | |
| 312 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; | |
| 313 DCHECK_EQ(CGLGetCurrentContext(), cgl_context_.get()); | |
| 314 glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, size.width(), size.height(), | |
| 315 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); | |
| 316 GLint target_texture = 0; | |
| 317 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &target_texture); | |
| 318 DCHECK(target_texture); | |
| 319 | |
| 320 // Note that state restoration is done explicitly in the ScopedClosureRunner | |
| 321 // instead of scoped binders to avoid https://crbug.com/601729. | |
| 322 GLint old_active_texture = -1; | |
| 323 glGetIntegerv(GL_ACTIVE_TEXTURE, &old_active_texture); | |
| 324 GLint old_texture0_binding = -1; | |
| 325 glActiveTexture(GL_TEXTURE0); | |
| 326 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_texture0_binding); | |
| 327 GLint old_texture1_binding = -1; | |
| 328 glActiveTexture(GL_TEXTURE1); | |
| 329 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_texture1_binding); | |
| 330 | |
| 331 unsigned y_texture = 0; | |
| 332 glGenTextures(1, &y_texture); | |
| 333 unsigned uv_texture = 0; | |
| 334 glGenTextures(1, &uv_texture); | |
| 335 | |
| 336 base::ScopedClosureRunner destroy_resources_runner(base::BindBlock(^{ | |
| 337 glActiveTexture(GL_TEXTURE0); | |
| 338 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, old_texture0_binding); | |
| 339 glActiveTexture(GL_TEXTURE1); | |
| 340 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, old_texture1_binding); | |
| 341 glActiveTexture(old_active_texture); | |
| 342 | |
| 343 glDeleteTextures(1, &y_texture); | |
| 344 glDeleteTextures(1, &uv_texture); | |
| 345 })); | |
| 346 | |
| 347 CGLError cgl_error = kCGLNoError; | |
| 348 glActiveTexture(GL_TEXTURE0); | |
| 349 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, y_texture); | |
| 350 cgl_error = CGLTexImageIOSurface2D(cgl_context_, GL_TEXTURE_RECTANGLE_ARB, | |
| 351 GL_RED, size.width(), size.height(), | |
| 352 GL_RED, GL_UNSIGNED_BYTE, io_surface, 0); | |
| 353 if (cgl_error != kCGLNoError) { | |
| 354 LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the Y plane. " | |
| 355 << cgl_error; | |
| 356 return false; | |
| 357 } | |
| 358 glActiveTexture(GL_TEXTURE1); | |
| 359 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, uv_texture); | |
| 360 cgl_error = CGLTexImageIOSurface2D(cgl_context_, GL_TEXTURE_RECTANGLE_ARB, | |
| 361 GL_RG, size.width() / 2, size.height() / 2, | |
| 362 GL_RG, GL_UNSIGNED_BYTE, io_surface, 1); | |
| 363 if (cgl_error != kCGLNoError) { | |
| 364 LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the UV plane. " | |
| 365 << cgl_error; | |
| 366 return false; | |
| 367 } | |
| 368 | |
| 369 gfx::ScopedFrameBufferBinder framebuffer_binder(framebuffer_); | |
| 370 gfx::ScopedViewport viewport(0, 0, size.width(), size.height()); | |
| 371 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
| 372 GL_TEXTURE_RECTANGLE_ARB, target_texture, 0); | |
| 373 DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), | |
| 374 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER)); | |
| 375 gfx::ScopedUseProgram use_program(program_); | |
| 376 glUniform2f(size_location_, size.width(), size.height()); | |
| 377 gfx::GLHelper::DrawQuad(vertex_buffer_); | |
| 378 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
| 379 GL_TEXTURE_RECTANGLE_ARB, 0, 0); | |
| 380 return true; | |
| 381 } | |
| 382 | |
| 383 GLImageIOSurface::GLImageIOSurface(const gfx::Size& size, | 154 GLImageIOSurface::GLImageIOSurface(const gfx::Size& size, |
| 384 unsigned internalformat) | 155 unsigned internalformat) |
| 385 : size_(size), | 156 : size_(size), |
| 386 internalformat_(internalformat), | 157 internalformat_(internalformat), |
| 387 format_(BufferFormat::RGBA_8888) {} | 158 format_(BufferFormat::RGBA_8888) {} |
| 388 | 159 |
| 389 GLImageIOSurface::~GLImageIOSurface() { | 160 GLImageIOSurface::~GLImageIOSurface() { |
| 390 DCHECK(thread_checker_.CalledOnValidThread()); | 161 DCHECK(thread_checker_.CalledOnValidThread()); |
| 391 DCHECK(!io_surface_); | 162 DCHECK(!io_surface_); |
| 392 } | 163 } |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 474 } | 245 } |
| 475 | 246 |
| 476 return true; | 247 return true; |
| 477 } | 248 } |
| 478 | 249 |
| 479 bool GLImageIOSurface::CopyTexImage(unsigned target) { | 250 bool GLImageIOSurface::CopyTexImage(unsigned target) { |
| 480 DCHECK(thread_checker_.CalledOnValidThread()); | 251 DCHECK(thread_checker_.CalledOnValidThread()); |
| 481 | 252 |
| 482 if (format_ != BufferFormat::YUV_420_BIPLANAR) | 253 if (format_ != BufferFormat::YUV_420_BIPLANAR) |
| 483 return false; | 254 return false; |
| 255 |
| 484 if (target != GL_TEXTURE_RECTANGLE_ARB) { | 256 if (target != GL_TEXTURE_RECTANGLE_ARB) { |
| 485 LOG(ERROR) << "YUV_420_BIPLANAR requires GL_TEXTURE_RECTANGLE_ARB target"; | 257 LOG(ERROR) << "YUV_420_BIPLANAR requires GL_TEXTURE_RECTANGLE_ARB target"; |
| 486 return false; | 258 return false; |
| 487 } | 259 } |
| 488 | 260 |
| 489 rgb_converter_ = RGBConverter::GetForCurrentContext(); | 261 gfx::GLContext* gl_context = gfx::GLContext::GetCurrent(); |
| 490 return rgb_converter_->CopyTexImage(io_surface_.get(), size_); | 262 DCHECK(gl_context); |
| 263 |
| 264 gl::YUVToRGBConverter* yuv_to_rgb_converter = |
| 265 gl_context->GetYUVToRGBConverter(); |
| 266 DCHECK(yuv_to_rgb_converter); |
| 267 |
| 268 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; |
| 269 |
| 270 // Note that state restoration is done explicitly instead of scoped binders to |
| 271 // avoid https://crbug.com/601729. |
| 272 GLint rgb_texture = 0; |
| 273 GLuint y_texture = 0; |
| 274 GLuint uv_texture = 0; |
| 275 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &rgb_texture); |
| 276 glGenTextures(1, &y_texture); |
| 277 glGenTextures(1, &uv_texture); |
| 278 base::ScopedClosureRunner destroy_resources_runner(base::BindBlock(^{ |
| 279 glDeleteTextures(1, &y_texture); |
| 280 glDeleteTextures(1, &uv_texture); |
| 281 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, rgb_texture); |
| 282 })); |
| 283 |
| 284 CGLContextObj cgl_context = CGLGetCurrentContext(); |
| 285 { |
| 286 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, y_texture); |
| 287 CGLError cgl_error = CGLTexImageIOSurface2D( |
| 288 cgl_context, GL_TEXTURE_RECTANGLE_ARB, GL_RED, size_.width(), |
| 289 size_.height(), GL_RED, GL_UNSIGNED_BYTE, io_surface_, 0); |
| 290 if (cgl_error != kCGLNoError) { |
| 291 LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the Y plane. " |
| 292 << cgl_error; |
| 293 return false; |
| 294 } |
| 295 } |
| 296 { |
| 297 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, uv_texture); |
| 298 CGLError cgl_error = CGLTexImageIOSurface2D( |
| 299 cgl_context, GL_TEXTURE_RECTANGLE_ARB, GL_RG, size_.width() / 2, |
| 300 size_.height() / 2, GL_RG, GL_UNSIGNED_BYTE, io_surface_, 1); |
| 301 if (cgl_error != kCGLNoError) { |
| 302 LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the UV plane. " |
| 303 << cgl_error; |
| 304 return false; |
| 305 } |
| 306 } |
| 307 |
| 308 yuv_to_rgb_converter->CopyYUV420ToRGB( |
| 309 GL_TEXTURE_RECTANGLE_ARB, y_texture, uv_texture, size_, rgb_texture); |
| 310 return true; |
| 491 } | 311 } |
| 492 | 312 |
| 493 bool GLImageIOSurface::CopyTexSubImage(unsigned target, | 313 bool GLImageIOSurface::CopyTexSubImage(unsigned target, |
| 494 const gfx::Point& offset, | 314 const gfx::Point& offset, |
| 495 const gfx::Rect& rect) { | 315 const gfx::Rect& rect) { |
| 496 return false; | 316 return false; |
| 497 } | 317 } |
| 498 | 318 |
| 499 bool GLImageIOSurface::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, | 319 bool GLImageIOSurface::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, |
| 500 int z_order, | 320 int z_order, |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 532 return cv_pixel_buffer_; | 352 return cv_pixel_buffer_; |
| 533 } | 353 } |
| 534 | 354 |
| 535 // static | 355 // static |
| 536 unsigned GLImageIOSurface::GetInternalFormatForTesting( | 356 unsigned GLImageIOSurface::GetInternalFormatForTesting( |
| 537 gfx::BufferFormat format) { | 357 gfx::BufferFormat format) { |
| 538 DCHECK(ValidFormat(format)); | 358 DCHECK(ValidFormat(format)); |
| 539 return TextureFormat(format); | 359 return TextureFormat(format); |
| 540 } | 360 } |
| 541 } // namespace gl | 361 } // namespace gl |
| OLD | NEW |