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" | 10 #include "base/lazy_instance.h" |
11 #include "base/mac/bind_objc_block.h" | 11 #include "base/mac/bind_objc_block.h" |
12 #include "base/mac/foundation_util.h" | 12 #include "base/mac/foundation_util.h" |
13 #include "base/strings/stringize_macros.h" | 13 #include "base/strings/stringize_macros.h" |
14 #include "base/strings/stringprintf.h" | 14 #include "base/strings/stringprintf.h" |
15 #include "base/trace_event/memory_allocator_dump.h" | 15 #include "base/trace_event/memory_allocator_dump.h" |
16 #include "base/trace_event/memory_dump_manager.h" | 16 #include "base/trace_event/memory_dump_manager.h" |
17 #include "base/trace_event/process_memory_dump.h" | 17 #include "base/trace_event/process_memory_dump.h" |
18 #include "ui/gl/gl_bindings.h" | 18 #include "ui/gl/gl_bindings.h" |
19 #include "ui/gl/gl_context.h" | 19 #include "ui/gl/gl_context.h" |
20 #include "ui/gl/gl_helper.h" | 20 #include "ui/gl/gl_helper.h" |
| 21 #include "ui/gl/scoped_api.h" |
21 #include "ui/gl/scoped_binders.h" | 22 #include "ui/gl/scoped_binders.h" |
| 23 #include "ui/gl/scoped_cgl.h" |
22 | 24 |
23 // Note that this must be included after gl_bindings.h to avoid conflicts. | 25 // Note that this must be included after gl_bindings.h to avoid conflicts. |
24 #include <OpenGL/CGLIOSurface.h> | 26 #include <OpenGL/CGLIOSurface.h> |
25 #include <Quartz/Quartz.h> | 27 #include <Quartz/Quartz.h> |
26 #include <stddef.h> | 28 #include <stddef.h> |
27 | 29 |
28 using gfx::BufferFormat; | 30 using gfx::BufferFormat; |
29 | 31 |
30 namespace gl { | 32 namespace gl { |
31 namespace { | 33 namespace { |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
181 NOTREACHED(); | 183 NOTREACHED(); |
182 return 0; | 184 return 0; |
183 } | 185 } |
184 | 186 |
185 NOTREACHED(); | 187 NOTREACHED(); |
186 return 0; | 188 return 0; |
187 } | 189 } |
188 | 190 |
189 } // namespace | 191 } // namespace |
190 | 192 |
| 193 class GLImageIOSurface::RGBConverter |
| 194 : public base::RefCounted<GLImageIOSurface::RGBConverter> { |
| 195 public: |
| 196 static scoped_refptr<RGBConverter> GetForCurrentContext(); |
| 197 bool CopyTexImage(IOSurfaceRef io_surface, const gfx::Size& size); |
| 198 |
| 199 private: |
| 200 friend class base::RefCounted<RGBConverter>; |
| 201 RGBConverter(CGLContextObj cgl_context); |
| 202 ~RGBConverter(); |
| 203 |
| 204 unsigned framebuffer_ = 0; |
| 205 unsigned vertex_shader_ = 0; |
| 206 unsigned fragment_shader_ = 0; |
| 207 unsigned program_ = 0; |
| 208 int size_location_ = -1; |
| 209 unsigned vertex_buffer_ = 0; |
| 210 base::ScopedTypeRef<CGLContextObj> cgl_context_; |
| 211 |
| 212 static base::LazyInstance< |
| 213 std::map<CGLContextObj, GLImageIOSurface::RGBConverter*>> |
| 214 g_rgb_converters; |
| 215 static base::LazyInstance<base::ThreadChecker> |
| 216 g_rgb_converters_thread_checker; |
| 217 }; |
| 218 |
| 219 base::LazyInstance<std::map<CGLContextObj, GLImageIOSurface::RGBConverter*>> |
| 220 GLImageIOSurface::RGBConverter::g_rgb_converters; |
| 221 |
| 222 base::LazyInstance<base::ThreadChecker> |
| 223 GLImageIOSurface::RGBConverter::g_rgb_converters_thread_checker; |
| 224 |
| 225 scoped_refptr<GLImageIOSurface::RGBConverter> |
| 226 GLImageIOSurface::RGBConverter::GetForCurrentContext() { |
| 227 CGLContextObj current_context = CGLGetCurrentContext(); |
| 228 DCHECK(current_context); |
| 229 DCHECK(g_rgb_converters_thread_checker.Get().CalledOnValidThread()); |
| 230 auto found = g_rgb_converters.Get().find(current_context); |
| 231 if (found != g_rgb_converters.Get().end()) |
| 232 return make_scoped_refptr(found->second); |
| 233 return make_scoped_refptr(new RGBConverter(current_context)); |
| 234 } |
| 235 |
| 236 GLImageIOSurface::RGBConverter::RGBConverter(CGLContextObj cgl_context) |
| 237 : cgl_context_(cgl_context, base::scoped_policy::RETAIN) { |
| 238 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; |
| 239 glGenFramebuffersEXT(1, &framebuffer_); |
| 240 vertex_buffer_ = gfx::GLHelper::SetupQuadVertexBuffer(); |
| 241 vertex_shader_ = gfx::GLHelper::LoadShader( |
| 242 GL_VERTEX_SHADER, |
| 243 base::StringPrintf("%s\n%s", kGLSLVersion, kVertexShader).c_str()); |
| 244 fragment_shader_ = gfx::GLHelper::LoadShader( |
| 245 GL_FRAGMENT_SHADER, |
| 246 base::StringPrintf("%s\n%s\n%s", kGLSLVersion, kTextureRectangleRequired, |
| 247 kFragmentShader) |
| 248 .c_str()); |
| 249 program_ = gfx::GLHelper::SetupProgram(vertex_shader_, fragment_shader_); |
| 250 |
| 251 gfx::ScopedUseProgram use_program(program_); |
| 252 size_location_ = glGetUniformLocation(program_, "a_texScale"); |
| 253 DCHECK_NE(-1, size_location_); |
| 254 int y_sampler_location = glGetUniformLocation(program_, "a_y_texture"); |
| 255 DCHECK_NE(-1, y_sampler_location); |
| 256 int uv_sampler_location = glGetUniformLocation(program_, "a_uv_texture"); |
| 257 DCHECK_NE(-1, uv_sampler_location); |
| 258 |
| 259 glUniform1i(y_sampler_location, 0); |
| 260 glUniform1i(uv_sampler_location, 1); |
| 261 |
| 262 DCHECK(g_rgb_converters_thread_checker.Get().CalledOnValidThread()); |
| 263 DCHECK(g_rgb_converters.Get().find(cgl_context) == |
| 264 g_rgb_converters.Get().end()); |
| 265 g_rgb_converters.Get()[cgl_context] = this; |
| 266 } |
| 267 |
| 268 GLImageIOSurface::RGBConverter::~RGBConverter() { |
| 269 DCHECK(g_rgb_converters_thread_checker.Get().CalledOnValidThread()); |
| 270 DCHECK(g_rgb_converters.Get()[cgl_context_] == this); |
| 271 g_rgb_converters.Get().erase(cgl_context_.get()); |
| 272 { |
| 273 gfx::ScopedCGLSetCurrentContext(cgl_context_.get()); |
| 274 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; |
| 275 glDeleteProgram(program_); |
| 276 glDeleteShader(vertex_shader_); |
| 277 glDeleteShader(fragment_shader_); |
| 278 glDeleteBuffersARB(1, &vertex_buffer_); |
| 279 glDeleteFramebuffersEXT(1, &framebuffer_); |
| 280 } |
| 281 cgl_context_.reset(); |
| 282 } |
| 283 |
| 284 bool GLImageIOSurface::RGBConverter::CopyTexImage(IOSurfaceRef io_surface, |
| 285 const gfx::Size& size) { |
| 286 gfx::ScopedSetGLToRealGLApi scoped_set_gl_api; |
| 287 DCHECK_EQ(CGLGetCurrentContext(), cgl_context_.get()); |
| 288 glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, size.width(), size.height(), |
| 289 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); |
| 290 GLint target_texture = 0; |
| 291 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &target_texture); |
| 292 DCHECK(target_texture); |
| 293 |
| 294 // Note that state restoration is done explicitly in the ScopedClosureRunner |
| 295 // instead of scoped binders to avoid https://crbug.com/601729. |
| 296 GLint old_active_texture = -1; |
| 297 glGetIntegerv(GL_ACTIVE_TEXTURE, &old_active_texture); |
| 298 GLint old_texture0_binding = -1; |
| 299 glActiveTexture(GL_TEXTURE0); |
| 300 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_texture0_binding); |
| 301 GLint old_texture1_binding = -1; |
| 302 glActiveTexture(GL_TEXTURE1); |
| 303 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_texture1_binding); |
| 304 |
| 305 unsigned y_texture = 0; |
| 306 glGenTextures(1, &y_texture); |
| 307 unsigned uv_texture = 0; |
| 308 glGenTextures(1, &uv_texture); |
| 309 |
| 310 base::ScopedClosureRunner destroy_resources_runner(base::BindBlock(^{ |
| 311 glActiveTexture(GL_TEXTURE0); |
| 312 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, old_texture0_binding); |
| 313 glActiveTexture(GL_TEXTURE1); |
| 314 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, old_texture1_binding); |
| 315 glActiveTexture(old_active_texture); |
| 316 |
| 317 glDeleteTextures(1, &y_texture); |
| 318 glDeleteTextures(1, &uv_texture); |
| 319 })); |
| 320 |
| 321 CGLError cgl_error = kCGLNoError; |
| 322 glActiveTexture(GL_TEXTURE0); |
| 323 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, y_texture); |
| 324 cgl_error = CGLTexImageIOSurface2D(cgl_context_, GL_TEXTURE_RECTANGLE_ARB, |
| 325 GL_RED, size.width(), size.height(), |
| 326 GL_RED, GL_UNSIGNED_BYTE, io_surface, 0); |
| 327 if (cgl_error != kCGLNoError) { |
| 328 LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the Y plane. " |
| 329 << cgl_error; |
| 330 return false; |
| 331 } |
| 332 glActiveTexture(GL_TEXTURE1); |
| 333 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, uv_texture); |
| 334 cgl_error = CGLTexImageIOSurface2D(cgl_context_, GL_TEXTURE_RECTANGLE_ARB, |
| 335 GL_RG, size.width() / 2, size.height() / 2, |
| 336 GL_RG, GL_UNSIGNED_BYTE, io_surface, 1); |
| 337 if (cgl_error != kCGLNoError) { |
| 338 LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the UV plane. " |
| 339 << cgl_error; |
| 340 return false; |
| 341 } |
| 342 |
| 343 gfx::ScopedFrameBufferBinder framebuffer_binder(framebuffer_); |
| 344 gfx::ScopedViewport viewport(0, 0, size.width(), size.height()); |
| 345 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| 346 GL_TEXTURE_RECTANGLE_ARB, target_texture, 0); |
| 347 DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), |
| 348 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER)); |
| 349 gfx::ScopedUseProgram use_program(program_); |
| 350 glUniform2f(size_location_, size.width(), size.height()); |
| 351 gfx::GLHelper::DrawQuad(vertex_buffer_); |
| 352 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| 353 GL_TEXTURE_RECTANGLE_ARB, 0, 0); |
| 354 return true; |
| 355 } |
| 356 |
191 GLImageIOSurface::GLImageIOSurface(const gfx::Size& size, | 357 GLImageIOSurface::GLImageIOSurface(const gfx::Size& size, |
192 unsigned internalformat) | 358 unsigned internalformat) |
193 : size_(size), | 359 : size_(size), |
194 internalformat_(internalformat), | 360 internalformat_(internalformat), |
195 format_(BufferFormat::RGBA_8888) {} | 361 format_(BufferFormat::RGBA_8888) {} |
196 | 362 |
197 GLImageIOSurface::~GLImageIOSurface() { | 363 GLImageIOSurface::~GLImageIOSurface() { |
198 DCHECK(thread_checker_.CalledOnValidThread()); | 364 DCHECK(thread_checker_.CalledOnValidThread()); |
199 DCHECK(!io_surface_); | 365 DCHECK(!io_surface_); |
200 } | 366 } |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
233 | 399 |
234 if (!Initialize(io_surface, io_surface_id, format)) | 400 if (!Initialize(io_surface, io_surface_id, format)) |
235 return false; | 401 return false; |
236 | 402 |
237 cv_pixel_buffer_.reset(cv_pixel_buffer, base::scoped_policy::RETAIN); | 403 cv_pixel_buffer_.reset(cv_pixel_buffer, base::scoped_policy::RETAIN); |
238 return true; | 404 return true; |
239 } | 405 } |
240 | 406 |
241 void GLImageIOSurface::Destroy(bool have_context) { | 407 void GLImageIOSurface::Destroy(bool have_context) { |
242 DCHECK(thread_checker_.CalledOnValidThread()); | 408 DCHECK(thread_checker_.CalledOnValidThread()); |
243 if (have_context && framebuffer_) { | |
244 glDeleteProgram(program_); | |
245 glDeleteShader(vertex_shader_); | |
246 glDeleteShader(fragment_shader_); | |
247 glDeleteBuffersARB(1, &vertex_buffer_); | |
248 glDeleteFramebuffersEXT(1, &framebuffer_); | |
249 } | |
250 io_surface_.reset(); | 409 io_surface_.reset(); |
251 cv_pixel_buffer_.reset(); | 410 cv_pixel_buffer_.reset(); |
252 } | 411 } |
253 | 412 |
254 gfx::Size GLImageIOSurface::GetSize() { | 413 gfx::Size GLImageIOSurface::GetSize() { |
255 return size_; | 414 return size_; |
256 } | 415 } |
257 | 416 |
258 unsigned GLImageIOSurface::GetInternalFormat() { | 417 unsigned GLImageIOSurface::GetInternalFormat() { |
259 return internalformat_; | 418 return internalformat_; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
294 bool GLImageIOSurface::CopyTexImage(unsigned target) { | 453 bool GLImageIOSurface::CopyTexImage(unsigned target) { |
295 DCHECK(thread_checker_.CalledOnValidThread()); | 454 DCHECK(thread_checker_.CalledOnValidThread()); |
296 | 455 |
297 if (format_ != BufferFormat::YUV_420_BIPLANAR) | 456 if (format_ != BufferFormat::YUV_420_BIPLANAR) |
298 return false; | 457 return false; |
299 if (target != GL_TEXTURE_RECTANGLE_ARB) { | 458 if (target != GL_TEXTURE_RECTANGLE_ARB) { |
300 LOG(ERROR) << "YUV_420_BIPLANAR requires GL_TEXTURE_RECTANGLE_ARB target"; | 459 LOG(ERROR) << "YUV_420_BIPLANAR requires GL_TEXTURE_RECTANGLE_ARB target"; |
301 return false; | 460 return false; |
302 } | 461 } |
303 | 462 |
304 // Ensure that all textures bound to IOSurfaces be destroyed before the | 463 rgb_converter_ = RGBConverter::GetForCurrentContext(); |
305 // function exits. If they are not destroyed they may cause deadlocks between | 464 return rgb_converter_->CopyTexImage(io_surface_.get(), size_); |
306 // VTDecompressionSession at CGLContextDestroy. | |
307 // https://crbug.com/598388 | |
308 unsigned y_texture = 0; | |
309 glGenTextures(1, &y_texture); | |
310 unsigned uv_texture = 0; | |
311 glGenTextures(1, &uv_texture); | |
312 base::ScopedClosureRunner destroy_resources_runner(base::BindBlock(^{ | |
313 glDeleteTextures(1, &y_texture); | |
314 glDeleteTextures(1, &uv_texture); | |
315 })); | |
316 | |
317 if (!framebuffer_) { | |
318 glGenFramebuffersEXT(1, &framebuffer_); | |
319 vertex_buffer_ = gfx::GLHelper::SetupQuadVertexBuffer(); | |
320 vertex_shader_ = gfx::GLHelper::LoadShader( | |
321 GL_VERTEX_SHADER, | |
322 base::StringPrintf("%s\n%s", kGLSLVersion, kVertexShader).c_str()); | |
323 fragment_shader_ = gfx::GLHelper::LoadShader( | |
324 GL_FRAGMENT_SHADER, | |
325 base::StringPrintf("%s\n%s\n%s", kGLSLVersion, | |
326 kTextureRectangleRequired, kFragmentShader) | |
327 .c_str()); | |
328 program_ = gfx::GLHelper::SetupProgram(vertex_shader_, fragment_shader_); | |
329 gfx::ScopedUseProgram use_program(program_); | |
330 | |
331 size_location_ = glGetUniformLocation(program_, "a_texScale"); | |
332 DCHECK_NE(-1, size_location_); | |
333 int y_sampler_location = glGetUniformLocation(program_, "a_y_texture"); | |
334 DCHECK_NE(-1, y_sampler_location); | |
335 int uv_sampler_location = glGetUniformLocation(program_, "a_uv_texture"); | |
336 DCHECK_NE(-1, uv_sampler_location); | |
337 | |
338 glUniform1i(y_sampler_location, 0); | |
339 glUniform1i(uv_sampler_location, 1); | |
340 } | |
341 | |
342 CGLContextObj cgl_context = | |
343 static_cast<CGLContextObj>(gfx::GLContext::GetCurrent()->GetHandle()); | |
344 | |
345 GLint target_texture = 0; | |
346 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &target_texture); | |
347 DCHECK(target_texture); | |
348 glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGB, size_.width(), | |
349 size_.height(), 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); | |
350 | |
351 CGLError cgl_error = kCGLNoError; | |
352 { | |
353 DCHECK(io_surface_); | |
354 | |
355 gfx::ScopedActiveTexture active_texture0(GL_TEXTURE0); | |
356 gfx::ScopedTextureBinder texture_y_binder(GL_TEXTURE_RECTANGLE_ARB, | |
357 y_texture); | |
358 cgl_error = CGLTexImageIOSurface2D( | |
359 cgl_context, GL_TEXTURE_RECTANGLE_ARB, GL_RED, size_.width(), | |
360 size_.height(), GL_RED, GL_UNSIGNED_BYTE, io_surface_.get(), 0); | |
361 if (cgl_error != kCGLNoError) { | |
362 LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the Y plane. " | |
363 << cgl_error; | |
364 return false; | |
365 } | |
366 { | |
367 gfx::ScopedActiveTexture active_texture1(GL_TEXTURE1); | |
368 gfx::ScopedTextureBinder texture_uv_binder(GL_TEXTURE_RECTANGLE_ARB, | |
369 uv_texture); | |
370 cgl_error = CGLTexImageIOSurface2D( | |
371 cgl_context, GL_TEXTURE_RECTANGLE_ARB, GL_RG, size_.width() / 2, | |
372 size_.height() / 2, GL_RG, GL_UNSIGNED_BYTE, io_surface_.get(), 1); | |
373 if (cgl_error != kCGLNoError) { | |
374 LOG(ERROR) << "Error in CGLTexImageIOSurface2D for the UV plane. " | |
375 << cgl_error; | |
376 return false; | |
377 } | |
378 | |
379 gfx::ScopedFrameBufferBinder framebuffer_binder(framebuffer_); | |
380 gfx::ScopedViewport viewport(0, 0, size_.width(), size_.height()); | |
381 glViewport(0, 0, size_.width(), size_.height()); | |
382 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
383 GL_TEXTURE_RECTANGLE_ARB, target_texture, 0); | |
384 DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE), | |
385 glCheckFramebufferStatusEXT(GL_FRAMEBUFFER)); | |
386 | |
387 gfx::ScopedUseProgram use_program(program_); | |
388 glUniform2f(size_location_, size_.width(), size_.height()); | |
389 | |
390 gfx::GLHelper::DrawQuad(vertex_buffer_); | |
391 // Detach the output texture from the fbo. | |
392 glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, | |
393 GL_TEXTURE_RECTANGLE_ARB, 0, 0); | |
394 } | |
395 } | |
396 return true; | |
397 } | 465 } |
398 | 466 |
399 bool GLImageIOSurface::CopyTexSubImage(unsigned target, | 467 bool GLImageIOSurface::CopyTexSubImage(unsigned target, |
400 const gfx::Point& offset, | 468 const gfx::Point& offset, |
401 const gfx::Rect& rect) { | 469 const gfx::Rect& rect) { |
402 return false; | 470 return false; |
403 } | 471 } |
404 | 472 |
405 bool GLImageIOSurface::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, | 473 bool GLImageIOSurface::ScheduleOverlayPlane(gfx::AcceleratedWidget widget, |
406 int z_order, | 474 int z_order, |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
438 return cv_pixel_buffer_; | 506 return cv_pixel_buffer_; |
439 } | 507 } |
440 | 508 |
441 // static | 509 // static |
442 unsigned GLImageIOSurface::GetInternalFormatForTesting( | 510 unsigned GLImageIOSurface::GetInternalFormatForTesting( |
443 gfx::BufferFormat format) { | 511 gfx::BufferFormat format) { |
444 DCHECK(ValidFormat(format)); | 512 DCHECK(ValidFormat(format)); |
445 return TextureFormat(format); | 513 return TextureFormat(format); |
446 } | 514 } |
447 } // namespace gl | 515 } // namespace gl |
OLD | NEW |