1 // Copyright (c) 2012 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 "content/common/gpu/client/gl_helper.h" 6 7 #include <queue> 8 #include <string> 9 10 #include "base/bind.h" 11 #include "base/debug/trace_event.h" 12 #include "base/lazy_instance.h" 13 #include "base/logging.h" 14 #include "base/memory/ref_counted.h" 15 #include "base/message_loop/message_loop.h" 16 #include "base/strings/string_util.h" 17 #include "base/time/time.h" 18 #include "content/common/gpu/client/gl_helper_readback_support.h" 19 #include "content/common/gpu/client/gl_helper_scaling.h" 20 #include "gpu/GLES2/gl2extchromium.h" 21 #include "gpu/command_buffer/client/context_support.h" 22 #include "gpu/command_buffer/common/mailbox.h" 23 #include "gpu/command_buffer/common/mailbox_holder.h" 24 #include "media/base/video_frame.h" 25 #include "media/base/video_util.h" 26 #include "third_party/skia/include/core/SkRegion.h" 27 #include "ui/gfx/rect.h" 28 #include "ui/gfx/size.h" 29 30 using gpu::gles2::GLES2Interface; 31 32 namespace { 33 34 class ScopedFlush { 35 public: 36 explicit ScopedFlush(gpu::gles2::GLES2Interface* gl) : gl_(gl) {} 37 38 ~ScopedFlush() { gl_->Flush(); } 39 40 private: 41 gpu::gles2::GLES2Interface* gl_; 42 43 DISALLOW_COPY_AND_ASSIGN(ScopedFlush); 44 }; 45 46 // Helper class for allocating and holding an RGBA texture of a given 47 // size and an associated framebuffer. 48 class TextureFrameBufferPair { 49 public: 50 TextureFrameBufferPair(GLES2Interface* gl, gfx::Size size) 51 : texture_(gl), framebuffer_(gl), size_(size) { 52 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, texture_); 53 gl->TexImage2D(GL_TEXTURE_2D, 54 0, 55 GL_RGBA, 56 size.width(), 57 size.height(), 58 0, 59 GL_RGBA, 60 GL_UNSIGNED_BYTE, 61 NULL); 62 content::ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder( 63 gl, framebuffer_); 64 gl->FramebufferTexture2D( 65 GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_, 0); 66 } 67 68 GLuint texture() const { return texture_.id(); } 69 GLuint framebuffer() const { return framebuffer_.id(); } 70 gfx::Size size() const { return size_; } 71 72 private: 73 content::ScopedTexture texture_; 74 content::ScopedFramebuffer framebuffer_; 75 gfx::Size size_; 76 77 DISALLOW_COPY_AND_ASSIGN(TextureFrameBufferPair); 78 }; 79 80 // Helper class for holding a scaler, a texture for the output of that 81 // scaler and an associated frame buffer. This is inteded to be used 82 // when the output of a scaler is to be sent to a readback. 83 class ScalerHolder { 84 public: 85 ScalerHolder(GLES2Interface* gl, content::GLHelper::ScalerInterface* scaler) 86 : texture_and_framebuffer_(gl, scaler->DstSize()), scaler_(scaler) {} 87 88 void Scale(GLuint src_texture) { 89 scaler_->Scale(src_texture, texture_and_framebuffer_.texture()); 90 } 91 92 content::GLHelper::ScalerInterface* scaler() const { return scaler_.get(); } 93 TextureFrameBufferPair* texture_and_framebuffer() { 94 return &texture_and_framebuffer_; 95 } 96 GLuint texture() const { return texture_and_framebuffer_.texture(); } 97 98 private: 99 TextureFrameBufferPair texture_and_framebuffer_; 100 scoped_ptr<content::GLHelper::ScalerInterface> scaler_; 101 102 DISALLOW_COPY_AND_ASSIGN(ScalerHolder); 103 }; 104 105 } // namespace 106 107 namespace content { 108 109 // Implements GLHelper::CropScaleReadbackAndCleanTexture and encapsulates 110 // the data needed for it. 111 class GLHelper::CopyTextureToImpl 112 : public base::SupportsWeakPtr<GLHelper::CopyTextureToImpl> { 113 public: 114 CopyTextureToImpl(GLES2Interface* gl, 115 gpu::ContextSupport* context_support, 116 GLHelper* helper) 117 : gl_(gl), 118 context_support_(context_support), 119 helper_(helper), 120 flush_(gl), 121 max_draw_buffers_(0) { 122 const GLubyte* extensions = gl_->GetString(GL_EXTENSIONS); 123 if (!extensions) 124 return; 125 std::string extensions_string = 126 " " + std::string(reinterpret_cast<const char*>(extensions)) + " "; 127 if (extensions_string.find(" GL_EXT_draw_buffers ") != std::string::npos) { 128 gl_->GetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &max_draw_buffers_); 129 } 130 } 131 ~CopyTextureToImpl() { CancelRequests(); } 132 133 GLuint ConsumeMailboxToTexture(const gpu::Mailbox& mailbox, 134 uint32 sync_point) { 135 return helper_->ConsumeMailboxToTexture(mailbox, sync_point); 136 } 137 138 void CropScaleReadbackAndCleanTexture( 139 GLuint src_texture, 140 const gfx::Size& src_size, 141 const gfx::Rect& src_subrect, 142 const gfx::Size& dst_size, 143 unsigned char* out, 144 const SkBitmap::Config config, 145 const base::Callback<void(bool)>& callback, 146 GLHelper::ScalerQuality quality); 147 148 void ReadbackTextureSync(GLuint texture, 149 const gfx::Rect& src_rect, 150 unsigned char* out, 151 SkBitmap::Config format); 152 153 void ReadbackTextureAsync(GLuint texture, 154 const gfx::Size& dst_size, 155 unsigned char* out, 156 SkBitmap::Config config, 157 const base::Callback<void(bool)>& callback); 158 159 // Reads back bytes from the currently bound frame buffer. 160 // Note that dst_size is specified in bytes, not pixels. 161 void ReadbackAsync(const gfx::Size& dst_size, 162 int32 bytes_per_row, // generally dst_size.width() * 4 163 int32 row_stride_bytes, // generally dst_size.width() * 4 164 unsigned char* out, 165 const SkBitmap::Config config, 166 ReadbackSwizzle swizzle, 167 const base::Callback<void(bool)>& callback); 168 169 void ReadbackPlane(TextureFrameBufferPair* source, 170 const scoped_refptr<media::VideoFrame>& target, 171 int plane, 172 int size_shift, 173 const gfx::Rect& dst_subrect, 174 ReadbackSwizzle swizzle, 175 const base::Callback<void(bool)>& callback); 176 177 GLuint CopyAndScaleTexture(GLuint texture, 178 const gfx::Size& src_size, 179 const gfx::Size& dst_size, 180 bool vertically_flip_texture, 181 GLHelper::ScalerQuality quality); 182 183 ReadbackYUVInterface* CreateReadbackPipelineYUV( 184 GLHelper::ScalerQuality quality, 185 const gfx::Size& src_size, 186 const gfx::Rect& src_subrect, 187 const gfx::Size& dst_size, 188 const gfx::Rect& dst_subrect, 189 bool flip_vertically, 190 bool use_mrt); 191 192 // Returns the maximum number of draw buffers available, 193 // 0 if GL_EXT_draw_buffers is not available. 194 GLint MaxDrawBuffers() const { return max_draw_buffers_; } 195 196 bool IsReadbackConfigSupported(SkBitmap::Config bitmap_config); 197 198 private: 199 // A single request to CropScaleReadbackAndCleanTexture. 200 // The main thread can cancel the request, before it's handled by the helper 201 // thread, by resetting the texture and pixels fields. Alternatively, the 202 // thread marks that it handles the request by resetting the pixels field 203 // (meaning it guarantees that the callback with be called). 204 // In either case, the callback must be called exactly once, and the texture 205 // must be deleted by the main thread gl. 206 struct Request { 207 Request(const gfx::Size& size_, 208 int32 bytes_per_row_, 209 int32 row_stride_bytes_, 210 unsigned char* pixels_, 211 const base::Callback<void(bool)>& callback_) 212 : done(false), 213 size(size_), 214 bytes_per_row(bytes_per_row_), 215 row_stride_bytes(row_stride_bytes_), 216 pixels(pixels_), 217 callback(callback_), 218 buffer(0), 219 query(0) {} 220 221 bool done; 222 gfx::Size size; 223 int bytes_per_row; 224 int row_stride_bytes; 225 unsigned char* pixels; 226 base::Callback<void(bool)> callback; 227 GLuint buffer; 228 GLuint query; 229 }; 230 231 // A readback pipeline that also converts the data to YUV before 232 // reading it back. 233 class ReadbackYUVImpl : public ReadbackYUVInterface { 234 public: 235 ReadbackYUVImpl(GLES2Interface* gl, 236 CopyTextureToImpl* copy_impl, 237 GLHelperScaling* scaler_impl, 238 GLHelper::ScalerQuality quality, 239 const gfx::Size& src_size, 240 const gfx::Rect& src_subrect, 241 const gfx::Size& dst_size, 242 const gfx::Rect& dst_subrect, 243 bool flip_vertically, 244 ReadbackSwizzle swizzle); 245 246 virtual void ReadbackYUV(const gpu::Mailbox& mailbox, 247 uint32 sync_point, 248 const scoped_refptr<media::VideoFrame>& target, 249 const base::Callback<void(bool)>& callback) 250 OVERRIDE; 251 252 virtual ScalerInterface* scaler() OVERRIDE { return scaler_.scaler(); } 253 254 private: 255 GLES2Interface* gl_; 256 CopyTextureToImpl* copy_impl_; 257 gfx::Size dst_size_; 258 gfx::Rect dst_subrect_; 259 ReadbackSwizzle swizzle_; 260 ScalerHolder scaler_; 261 ScalerHolder y_; 262 ScalerHolder u_; 263 ScalerHolder v_; 264 265 DISALLOW_COPY_AND_ASSIGN(ReadbackYUVImpl); 266 }; 267 268 // A readback pipeline that also converts the data to YUV before 269 // reading it back. This one uses Multiple Render Targets, which 270 // may not be supported on all platforms. 271 class ReadbackYUV_MRT : public ReadbackYUVInterface { 272 public: 273 ReadbackYUV_MRT(GLES2Interface* gl, 274 CopyTextureToImpl* copy_impl, 275 GLHelperScaling* scaler_impl, 276 GLHelper::ScalerQuality quality, 277 const gfx::Size& src_size, 278 const gfx::Rect& src_subrect, 279 const gfx::Size& dst_size, 280 const gfx::Rect& dst_subrect, 281 bool flip_vertically, 282 ReadbackSwizzle swizzle); 283 284 virtual void ReadbackYUV(const gpu::Mailbox& mailbox, 285 uint32 sync_point, 286 const scoped_refptr<media::VideoFrame>& target, 287 const base::Callback<void(bool)>& callback) 288 OVERRIDE; 289 290 virtual ScalerInterface* scaler() OVERRIDE { return scaler_.scaler(); } 291 292 private: 293 GLES2Interface* gl_; 294 CopyTextureToImpl* copy_impl_; 295 gfx::Size dst_size_; 296 gfx::Rect dst_subrect_; 297 GLHelper::ScalerQuality quality_; 298 ReadbackSwizzle swizzle_; 299 ScalerHolder scaler_; 300 scoped_ptr<content::GLHelperScaling::ShaderInterface> pass1_shader_; 301 scoped_ptr<content::GLHelperScaling::ShaderInterface> pass2_shader_; 302 TextureFrameBufferPair y_; 303 ScopedTexture uv_; 304 TextureFrameBufferPair u_; 305 TextureFrameBufferPair v_; 306 307 DISALLOW_COPY_AND_ASSIGN(ReadbackYUV_MRT); 308 }; 309 310 // Copies the block of pixels specified with |src_subrect| from |src_texture|, 311 // scales it to |dst_size|, writes it into a texture, and returns its ID. 312 // |src_size| is the size of |src_texture|. 313 GLuint ScaleTexture(GLuint src_texture, 314 const gfx::Size& src_size, 315 const gfx::Rect& src_subrect, 316 const gfx::Size& dst_size, 317 bool vertically_flip_texture, 318 bool swizzle, 319 SkBitmap::Config config, 320 GLHelper::ScalerQuality quality); 321 322 static void nullcallback(bool success) {} 323 void ReadbackDone(Request *request, int bytes_per_pixel); 324 void FinishRequest(Request* request, bool result); 325 void CancelRequests(); 326 327 static const float kRGBtoYColorWeights[]; 328 static const float kRGBtoUColorWeights[]; 329 static const float kRGBtoVColorWeights[]; 330 331 GLES2Interface* gl_; 332 gpu::ContextSupport* context_support_; 333 GLHelper* helper_; 334 335 // A scoped flush that will ensure all resource deletions are flushed when 336 // this object is destroyed. Must be declared before other Scoped* fields. 337 ScopedFlush flush_; 338 339 std::queue<Request*> request_queue_; 340 GLint max_draw_buffers_; 341 }; 342 343 GLHelper::ScalerInterface* GLHelper::CreateScaler(ScalerQuality quality, 344 const gfx::Size& src_size, 345 const gfx::Rect& src_subrect, 346 const gfx::Size& dst_size, 347 bool vertically_flip_texture, 348 bool swizzle) { 349 InitScalerImpl(); 350 return scaler_impl_->CreateScaler(quality, 351 src_size, 352 src_subrect, 353 dst_size, 354 vertically_flip_texture, 355 swizzle); 356 } 357 358 GLuint GLHelper::CopyTextureToImpl::ScaleTexture( 359 GLuint src_texture, 360 const gfx::Size& src_size, 361 const gfx::Rect& src_subrect, 362 const gfx::Size& dst_size, 363 bool vertically_flip_texture, 364 bool swizzle, 365 SkBitmap::Config bitmap_config, 366 GLHelper::ScalerQuality quality) { 367 if (!IsReadbackConfigSupported(bitmap_config)) 368 return 0; 369 370 scoped_ptr<ScalerInterface> scaler( 371 helper_->CreateScaler(quality, 372 src_size, 373 src_subrect, 374 dst_size, 375 vertically_flip_texture, 376 swizzle)); 377 GLuint dst_texture = 0u; 378 // Start with ARGB8888 params as any other format which is not 379 // supported is already asserted above. 380 GLenum format = GL_RGBA , type = GL_UNSIGNED_BYTE; 381 gl_->GenTextures(1, &dst_texture); 382 { 383 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, dst_texture); 384 switch (bitmap_config) { 385 case SkBitmap::kARGB_8888_Config: 386 // Do nothing params already set. 387 break; 388 case SkBitmap::kRGB_565_Config: 389 format = GL_RGB; 390 type = GL_UNSIGNED_SHORT_5_6_5; 391 break; 392 default: 393 NOTREACHED(); 394 break; 395 } 396 gl_->TexImage2D(GL_TEXTURE_2D, 397 0, 398 format, 399 dst_size.width(), 400 dst_size.height(), 401 0, 402 format, 403 type, 404 NULL); 405 } 406 scaler->Scale(src_texture, dst_texture); 407 return dst_texture; 408 } 409 410 void GLHelper::CopyTextureToImpl::ReadbackAsync( 411 const gfx::Size& dst_size, 412 int32 bytes_per_row, 413 int32 row_stride_bytes, 414 unsigned char* out, 415 const SkBitmap::Config bitmap_config, 416 ReadbackSwizzle swizzle, 417 const base::Callback<void(bool)>& callback) { 418 if (!IsReadbackConfigSupported(bitmap_config)) { 419 callback.Run(false); 420 return; 421 } 422 Request* request = 423 new Request(dst_size, bytes_per_row, row_stride_bytes, out, callback); 424 request_queue_.push(request); 425 request->buffer = 0u; 426 // Start with ARGB8888 params as any other format which is not 427 // supported is already asserted above. 428 GLenum format = GL_RGBA, type = GL_UNSIGNED_BYTE; 429 int bytes_per_pixel = 4; 430 431 switch (bitmap_config) { 432 case SkBitmap::kARGB_8888_Config: 433 if (swizzle == kSwizzleBGRA) 434 format = GL_BGRA_EXT; 435 break; 436 case SkBitmap::kRGB_565_Config: 437 format = GL_RGB; 438 type = GL_UNSIGNED_SHORT_5_6_5; 439 bytes_per_pixel = 2; 440 break; 441 default: 442 NOTREACHED(); 443 break; 444 } 445 gl_->GenBuffers(1, &request->buffer); 446 gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer); 447 gl_->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 448 bytes_per_pixel * dst_size.GetArea(), 449 NULL, 450 GL_STREAM_READ); 451 452 request->query = 0u; 453 gl_->GenQueriesEXT(1, &request->query); 454 gl_->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, request->query); 455 gl_->ReadPixels(0, 456 0, 457 dst_size.width(), 458 dst_size.height(), 459 format, 460 type, 461 NULL); 462 gl_->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM); 463 gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); 464 context_support_->SignalQuery( 465 request->query, 466 base::Bind(&CopyTextureToImpl::ReadbackDone, AsWeakPtr(), 467 request, bytes_per_pixel)); 468 } 469 void GLHelper::CopyTextureToImpl::CropScaleReadbackAndCleanTexture( 470 GLuint src_texture, 471 const gfx::Size& src_size, 472 const gfx::Rect& src_subrect, 473 const gfx::Size& dst_size, 474 unsigned char* out, 475 const SkBitmap::Config bitmap_config, 476 const base::Callback<void(bool)>& callback, 477 GLHelper::ScalerQuality quality) { 478 if (!IsReadbackConfigSupported(bitmap_config)) { 479 callback.Run(false); 480 return; 481 } 482 GLuint texture = ScaleTexture(src_texture, 483 src_size, 484 src_subrect, 485 dst_size, 486 true, 487 #if (SK_R32_SHIFT == 16) && !SK_B32_SHIFT 488 true, 489 #else 490 false, 491 #endif 492 bitmap_config, 493 quality); 494 DCHECK(texture); 495 ScopedFramebuffer dst_framebuffer(gl_); 496 ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_, 497 dst_framebuffer); 498 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); 499 gl_->FramebufferTexture2D(GL_FRAMEBUFFER, 500 GL_COLOR_ATTACHMENT0, 501 GL_TEXTURE_2D, 502 texture, 503 0); 504 int bytes_per_pixel = 4; 505 switch (bitmap_config) { 506 case SkBitmap::kARGB_8888_Config: 507 // Do nothing params already set. 508 break; 509 case SkBitmap::kRGB_565_Config: 510 bytes_per_pixel = 2; 511 break; 512 default: 513 NOTREACHED(); 514 break; 515 } 516 ReadbackAsync(dst_size, 517 dst_size.width() * bytes_per_pixel, 518 dst_size.width() * bytes_per_pixel, 519 out, 520 bitmap_config, 521 kSwizzleNone, 522 callback); 523 gl_->DeleteTextures(1, &texture); 524 } 525 526 void GLHelper::CopyTextureToImpl::ReadbackTextureSync( 527 GLuint texture, 528 const gfx::Rect& src_rect, 529 unsigned char* out, 530 SkBitmap::Config bitmap_config) { 531 if (!IsReadbackConfigSupported(bitmap_config)) 532 return; 533 534 ScopedFramebuffer dst_framebuffer(gl_); 535 ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_, 536 dst_framebuffer); 537 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); 538 gl_->FramebufferTexture2D( 539 GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); 540 GLenum format = 541 (bitmap_config == SkBitmap::kRGB_565_Config) ? GL_RGB : GL_RGBA; 542 GLenum type = (bitmap_config == SkBitmap::kRGB_565_Config) 543 ? GL_UNSIGNED_SHORT_5_6_5 544 : GL_UNSIGNED_BYTE; 545 gl_->ReadPixels(src_rect.x(), 546 src_rect.y(), 547 src_rect.width(), 548 src_rect.height(), 549 format, 550 type, 551 out); 552 } 553 554 void GLHelper::CopyTextureToImpl::ReadbackTextureAsync( 555 GLuint texture, 556 const gfx::Size& dst_size, 557 unsigned char* out, 558 SkBitmap::Config bitmap_config, 559 const base::Callback<void(bool)>& callback) { 560 if (!IsReadbackConfigSupported(bitmap_config)) 561 return; 562 563 ScopedFramebuffer dst_framebuffer(gl_); 564 ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_, 565 dst_framebuffer); 566 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); 567 gl_->FramebufferTexture2D(GL_FRAMEBUFFER, 568 GL_COLOR_ATTACHMENT0, 569 GL_TEXTURE_2D, 570 texture, 571 0); 572 int bytes_per_pixel = (bitmap_config == SkBitmap::kRGB_565_Config) ? 2 : 4; 573 ReadbackAsync(dst_size, 574 dst_size.width() * bytes_per_pixel, 575 dst_size.width() * bytes_per_pixel, 576 out, 577 bitmap_config, 578 kSwizzleNone, 579 callback); 580 } 581 582 GLuint GLHelper::CopyTextureToImpl::CopyAndScaleTexture( 583 GLuint src_texture, 584 const gfx::Size& src_size, 585 const gfx::Size& dst_size, 586 bool vertically_flip_texture, 587 GLHelper::ScalerQuality quality) { 588 return ScaleTexture(src_texture, 589 src_size, 590 gfx::Rect(src_size), 591 dst_size, 592 vertically_flip_texture, 593 false, 594 SkBitmap::kARGB_8888_Config, 595 quality); 596 } 597 598 bool GLHelper::CopyTextureToImpl::IsReadbackConfigSupported( 599 SkBitmap::Config bitmap_config) { 600 if (!helper_) { 601 DCHECK(helper_); 602 return false; 603 } 604 return helper_->IsReadbackConfigSupported(bitmap_config); 605 } 606 607 void GLHelper::CopyTextureToImpl::ReadbackDone(Request* finished_request, 608 int bytes_per_pixel) { 609 TRACE_EVENT0("mirror", 610 "GLHelper::CopyTextureToImpl::CheckReadbackFramebufferComplete"); 611 finished_request->done = true; 612 613 // We process transfer requests in the order they were received, regardless 614 // of the order we get the callbacks in. 615 while (!request_queue_.empty()) { 616 Request* request = request_queue_.front(); 617 if (!request->done) { 618 break; 619 } 620 621 bool result = false; 622 if (request->buffer != 0) { 623 gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer); 624 unsigned char* data = static_cast<unsigned char*>(gl_->MapBufferCHROMIUM( 625 GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY)); 626 if (data) { 627 result = true; 628 if (request->bytes_per_row == request->size.width() * bytes_per_pixel && 629 request->bytes_per_row == request->row_stride_bytes) { 630 memcpy(request->pixels, data, 631 request->size.GetArea() * bytes_per_pixel); 632 } else { 633 unsigned char* out = request->pixels; 634 for (int y = 0; y < request->size.height(); y++) { 635 memcpy(out, data, request->bytes_per_row); 636 out += request->row_stride_bytes; 637 data += request->size.width() * bytes_per_pixel; 638 } 639 } 640 gl_->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM); 641 } 642 gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0); 643 } 644 FinishRequest(request, result); 645 } 646 } 647 648 void GLHelper::CopyTextureToImpl::FinishRequest(Request* request, bool result) { 649 TRACE_EVENT0("mirror", "GLHelper::CopyTextureToImpl::FinishRequest"); 650 DCHECK(request_queue_.front() == request); 651 request_queue_.pop(); 652 request->callback.Run(result); 653 ScopedFlush flush(gl_); 654 if (request->query != 0) { 655 gl_->DeleteQueriesEXT(1, &request->query); 656 request->query = 0; 657 } 658 if (request->buffer != 0) { 659 gl_->DeleteBuffers(1, &request->buffer); 660 request->buffer = 0; 661 } 662 delete request; 663 } 664 665 void GLHelper::CopyTextureToImpl::CancelRequests() { 666 while (!request_queue_.empty()) { 667 Request* request = request_queue_.front(); 668 FinishRequest(request, false); 669 } 670 } 671 672 GLHelper::GLHelper(GLES2Interface* gl, gpu::ContextSupport* context_support) 673 : gl_(gl), 674 context_support_(context_support), 675 readback_support_(new GLHelperReadbackSupport(gl)) {} 676 677 GLHelper::~GLHelper() {} 678 679 void GLHelper::CropScaleReadbackAndCleanTexture( 680 GLuint src_texture, 681 const gfx::Size& src_size, 682 const gfx::Rect& src_subrect, 683 const gfx::Size& dst_size, 684 unsigned char* out, 685 const SkBitmap::Config config, 686 const base::Callback<void(bool)>& callback, 687 GLHelper::ScalerQuality quality) { 688 InitCopyTextToImpl(); 689 copy_texture_to_impl_->CropScaleReadbackAndCleanTexture( 690 src_texture, 691 src_size, 692 src_subrect, 693 dst_size, 694 out, 695 config, 696 callback, 697 quality); 698 } 699 700 void GLHelper::CropScaleReadbackAndCleanMailbox( 701 const gpu::Mailbox& src_mailbox, 702 uint32 sync_point, 703 const gfx::Size& src_size, 704 const gfx::Rect& src_subrect, 705 const gfx::Size& dst_size, 706 unsigned char* out, 707 const SkBitmap::Config bitmap_config, 708 const base::Callback<void(bool)>& callback, 709 GLHelper::ScalerQuality quality) { 710 GLuint mailbox_texture = ConsumeMailboxToTexture(src_mailbox, sync_point); 711 CropScaleReadbackAndCleanTexture( 712 mailbox_texture, src_size, src_subrect, dst_size, out, 713 bitmap_config, 714 callback, 715 quality); 716 gl_->DeleteTextures(1, &mailbox_texture); 717 } 718 719 void GLHelper::ReadbackTextureSync(GLuint texture, 720 const gfx::Rect& src_rect, 721 unsigned char* out, 722 SkBitmap::Config format) { 723 InitCopyTextToImpl(); 724 copy_texture_to_impl_->ReadbackTextureSync(texture, src_rect, out, format); 725 } 726 727 void GLHelper::ReadbackTextureAsync( 728 GLuint texture, 729 const gfx::Size& dst_size, 730 unsigned char* out, 731 SkBitmap::Config config, 732 const base::Callback<void(bool)>& callback) { 733 InitCopyTextToImpl(); 734 copy_texture_to_impl_->ReadbackTextureAsync(texture, 735 dst_size, 736 out, 737 config, 738 callback); 739 } 740 741 GLuint GLHelper::CopyTexture(GLuint texture, const gfx::Size& size) { 742 InitCopyTextToImpl(); 743 return copy_texture_to_impl_->CopyAndScaleTexture( 744 texture, size, size, false, GLHelper::SCALER_QUALITY_FAST); 745 } 746 747 GLuint GLHelper::CopyAndScaleTexture(GLuint texture, 748 const gfx::Size& src_size, 749 const gfx::Size& dst_size, 750 bool vertically_flip_texture, 751 ScalerQuality quality) { 752 InitCopyTextToImpl(); 753 return copy_texture_to_impl_->CopyAndScaleTexture( 754 texture, src_size, dst_size, vertically_flip_texture, quality); 755 } 756 757 GLuint GLHelper::CompileShaderFromSource(const GLchar* source, GLenum type) { 758 GLuint shader = gl_->CreateShader(type); 759 GLint length = strlen(source); 760 gl_->ShaderSource(shader, 1, &source, &length); 761 gl_->CompileShader(shader); 762 GLint compile_status = 0; 763 gl_->GetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); 764 if (!compile_status) { 765 GLint log_length = 0; 766 gl_->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); 767 if (log_length) { 768 scoped_ptr<GLchar[]> log(new GLchar[log_length]); 769 GLsizei returned_log_length = 0; 770 gl_->GetShaderInfoLog( 771 shader, log_length, &returned_log_length, log.get()); 772 LOG(ERROR) << std::string(log.get(), returned_log_length); 773 } 774 gl_->DeleteShader(shader); 775 return 0; 776 } 777 return shader; 778 } 779 780 void GLHelper::InitCopyTextToImpl() { 781 // Lazily initialize |copy_texture_to_impl_| 782 if (!copy_texture_to_impl_) 783 copy_texture_to_impl_.reset( 784 new CopyTextureToImpl(gl_, context_support_, this)); 785 } 786 787 void GLHelper::InitScalerImpl() { 788 // Lazily initialize |scaler_impl_| 789 if (!scaler_impl_) 790 scaler_impl_.reset(new GLHelperScaling(gl_, this)); 791 } 792 793 GLint GLHelper::MaxDrawBuffers() { 794 InitCopyTextToImpl(); 795 return copy_texture_to_impl_->MaxDrawBuffers(); 796 } 797 798 void GLHelper::CopySubBufferDamage(GLuint texture, 799 GLuint previous_texture, 800 const SkRegion& new_damage, 801 const SkRegion& old_damage) { 802 SkRegion region(old_damage); 803 if (region.op(new_damage, SkRegion::kDifference_Op)) { 804 ScopedFramebuffer dst_framebuffer(gl_); 805 ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_, 806 dst_framebuffer); 807 ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); 808 gl_->FramebufferTexture2D(GL_FRAMEBUFFER, 809 GL_COLOR_ATTACHMENT0, 810 GL_TEXTURE_2D, 811 previous_texture, 812 0); 813 for (SkRegion::Iterator it(region); !it.done(); it.next()) { 814 const SkIRect& rect = it.rect(); 815 gl_->CopyTexSubImage2D(GL_TEXTURE_2D, 816 0, 817 rect.x(), 818 rect.y(), 819 rect.x(), 820 rect.y(), 821 rect.width(), 822 rect.height()); 823 } 824 gl_->Flush(); 825 } 826 } 827 828 GLuint GLHelper::CreateTexture() { 829 GLuint texture = 0u; 830 gl_->GenTextures(1, &texture); 831 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); 832 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 833 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 834 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 835 gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 836 return texture; 837 } 838 839 void GLHelper::DeleteTexture(GLuint texture_id) { 840 gl_->DeleteTextures(1, &texture_id); 841 } 842 843 uint32 GLHelper::InsertSyncPoint() { return gl_->InsertSyncPointCHROMIUM(); } 844 845 void GLHelper::WaitSyncPoint(uint32 sync_point) { 846 gl_->WaitSyncPointCHROMIUM(sync_point); 847 } 848 849 gpu::MailboxHolder GLHelper::ProduceMailboxHolderFromTexture( 850 GLuint texture_id) { 851 gpu::Mailbox mailbox; 852 gl_->GenMailboxCHROMIUM(mailbox.name); 853 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture_id); 854 gl_->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); 855 return gpu::MailboxHolder(mailbox, GL_TEXTURE_2D, InsertSyncPoint()); 856 } 857 858 GLuint GLHelper::ConsumeMailboxToTexture(const gpu::Mailbox& mailbox, 859 uint32 sync_point) { 860 if (mailbox.IsZero()) 861 return 0; 862 if (sync_point) 863 WaitSyncPoint(sync_point); 864 GLuint texture = CreateTexture(); 865 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); 866 gl_->ConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name); 867 return texture; 868 } 869 870 void GLHelper::ResizeTexture(GLuint texture, const gfx::Size& size) { 871 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); 872 gl_->TexImage2D(GL_TEXTURE_2D, 873 0, 874 GL_RGB, 875 size.width(), 876 size.height(), 877 0, 878 GL_RGB, 879 GL_UNSIGNED_BYTE, 880 NULL); 881 } 882 883 void GLHelper::CopyTextureSubImage(GLuint texture, const gfx::Rect& rect) { 884 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); 885 gl_->CopyTexSubImage2D(GL_TEXTURE_2D, 886 0, 887 rect.x(), 888 rect.y(), 889 rect.x(), 890 rect.y(), 891 rect.width(), 892 rect.height()); 893 } 894 895 void GLHelper::CopyTextureFullImage(GLuint texture, const gfx::Size& size) { 896 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl_, texture); 897 gl_->CopyTexImage2D( 898 GL_TEXTURE_2D, 0, GL_RGB, 0, 0, size.width(), size.height(), 0); 899 } 900 901 void GLHelper::Flush() { 902 gl_->Flush(); 903 } 904 905 void GLHelper::CopyTextureToImpl::ReadbackPlane( 906 TextureFrameBufferPair* source, 907 const scoped_refptr<media::VideoFrame>& target, 908 int plane, 909 int size_shift, 910 const gfx::Rect& dst_subrect, 911 ReadbackSwizzle swizzle, 912 const base::Callback<void(bool)>& callback) { 913 gl_->BindFramebuffer(GL_FRAMEBUFFER, source->framebuffer()); 914 size_t offset = target->stride(plane) * (dst_subrect.y() >> size_shift) + 915 (dst_subrect.x() >> size_shift); 916 ReadbackAsync(source->size(), 917 dst_subrect.width() >> size_shift, 918 target->stride(plane), 919 target->data(plane) + offset, 920 SkBitmap::kARGB_8888_Config, 921 swizzle, 922 callback); 923 } 924 925 const float GLHelper::CopyTextureToImpl::kRGBtoYColorWeights[] = { 926 0.257f, 0.504f, 0.098f, 0.0625f}; 927 const float GLHelper::CopyTextureToImpl::kRGBtoUColorWeights[] = { 928 -0.148f, -0.291f, 0.439f, 0.5f}; 929 const float GLHelper::CopyTextureToImpl::kRGBtoVColorWeights[] = { 930 0.439f, -0.368f, -0.071f, 0.5f}; 931 932 // YUV readback constructors. Initiates the main scaler pipeline and 933 // one planar scaler for each of the Y, U and V planes. 934 GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUVImpl( 935 GLES2Interface* gl, 936 CopyTextureToImpl* copy_impl, 937 GLHelperScaling* scaler_impl, 938 GLHelper::ScalerQuality quality, 939 const gfx::Size& src_size, 940 const gfx::Rect& src_subrect, 941 const gfx::Size& dst_size, 942 const gfx::Rect& dst_subrect, 943 bool flip_vertically, 944 ReadbackSwizzle swizzle) 945 : gl_(gl), 946 copy_impl_(copy_impl), 947 dst_size_(dst_size), 948 dst_subrect_(dst_subrect), 949 swizzle_(swizzle), 950 scaler_(gl, 951 scaler_impl->CreateScaler(quality, 952 src_size, 953 src_subrect, 954 dst_subrect.size(), 955 flip_vertically, 956 false)), 957 y_(gl, 958 scaler_impl->CreatePlanarScaler( 959 dst_subrect.size(), 960 gfx::Rect(0, 961 0, 962 (dst_subrect.width() + 3) & ~3, 963 dst_subrect.height()), 964 gfx::Size((dst_subrect.width() + 3) / 4, dst_subrect.height()), 965 false, 966 (swizzle == kSwizzleBGRA), 967 kRGBtoYColorWeights)), 968 u_(gl, 969 scaler_impl->CreatePlanarScaler( 970 dst_subrect.size(), 971 gfx::Rect(0, 972 0, 973 (dst_subrect.width() + 7) & ~7, 974 (dst_subrect.height() + 1) & ~1), 975 gfx::Size((dst_subrect.width() + 7) / 8, 976 (dst_subrect.height() + 1) / 2), 977 false, 978 (swizzle == kSwizzleBGRA), 979 kRGBtoUColorWeights)), 980 v_(gl, 981 scaler_impl->CreatePlanarScaler( 982 dst_subrect.size(), 983 gfx::Rect(0, 984 0, 985 (dst_subrect.width() + 7) & ~7, 986 (dst_subrect.height() + 1) & ~1), 987 gfx::Size((dst_subrect.width() + 7) / 8, 988 (dst_subrect.height() + 1) / 2), 989 false, 990 (swizzle == kSwizzleBGRA), 991 kRGBtoVColorWeights)) { 992 DCHECK(!(dst_size.width() & 1)); 993 DCHECK(!(dst_size.height() & 1)); 994 DCHECK(!(dst_subrect.width() & 1)); 995 DCHECK(!(dst_subrect.height() & 1)); 996 DCHECK(!(dst_subrect.x() & 1)); 997 DCHECK(!(dst_subrect.y() & 1)); 998 } 999 1000 static void CallbackKeepingVideoFrameAlive( 1001 scoped_refptr<media::VideoFrame> video_frame, 1002 const base::Callback<void(bool)>& callback, 1003 bool success) { 1004 callback.Run(success); 1005 } 1006 1007 void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUV( 1008 const gpu::Mailbox& mailbox, 1009 uint32 sync_point, 1010 const scoped_refptr<media::VideoFrame>& target, 1011 const base::Callback<void(bool)>& callback) { 1012 GLuint mailbox_texture = 1013 copy_impl_->ConsumeMailboxToTexture(mailbox, sync_point); 1014 1015 // Scale texture to right size. 1016 scaler_.Scale(mailbox_texture); 1017 gl_->DeleteTextures(1, &mailbox_texture); 1018 1019 // Convert the scaled texture in to Y, U and V planes. 1020 y_.Scale(scaler_.texture()); 1021 u_.Scale(scaler_.texture()); 1022 v_.Scale(scaler_.texture()); 1023 1024 if (target->coded_size() != dst_size_) { 1025 DCHECK(target->coded_size() == dst_size_); 1026 LOG(ERROR) << "ReadbackYUV size error!"; 1027 callback.Run(false); 1028 return; 1029 } 1030 1031 // Read back planes, one at a time. Keep the video frame alive while doing the 1032 // readback. 1033 copy_impl_->ReadbackPlane(y_.texture_and_framebuffer(), 1034 target, 1035 media::VideoFrame::kYPlane, 1036 0, 1037 dst_subrect_, 1038 swizzle_, 1039 base::Bind(&nullcallback)); 1040 copy_impl_->ReadbackPlane(u_.texture_and_framebuffer(), 1041 target, 1042 media::VideoFrame::kUPlane, 1043 1, 1044 dst_subrect_, 1045 swizzle_, 1046 base::Bind(&nullcallback)); 1047 copy_impl_->ReadbackPlane( 1048 v_.texture_and_framebuffer(), 1049 target, 1050 media::VideoFrame::kVPlane, 1051 1, 1052 dst_subrect_, 1053 swizzle_, 1054 base::Bind(&CallbackKeepingVideoFrameAlive, target, callback)); 1055 gl_->BindFramebuffer(GL_FRAMEBUFFER, 0); 1056 media::LetterboxYUV(target, dst_subrect_); 1057 } 1058 1059 // YUV readback constructors. Initiates the main scaler pipeline and 1060 // one planar scaler for each of the Y, U and V planes. 1061 GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV_MRT( 1062 GLES2Interface* gl, 1063 CopyTextureToImpl* copy_impl, 1064 GLHelperScaling* scaler_impl, 1065 GLHelper::ScalerQuality quality, 1066 const gfx::Size& src_size, 1067 const gfx::Rect& src_subrect, 1068 const gfx::Size& dst_size, 1069 const gfx::Rect& dst_subrect, 1070 bool flip_vertically, 1071 ReadbackSwizzle swizzle) 1072 : gl_(gl), 1073 copy_impl_(copy_impl), 1074 dst_size_(dst_size), 1075 dst_subrect_(dst_subrect), 1076 quality_(quality), 1077 swizzle_(swizzle), 1078 scaler_(gl, 1079 scaler_impl->CreateScaler(quality, 1080 src_size, 1081 src_subrect, 1082 dst_subrect.size(), 1083 false, 1084 false)), 1085 pass1_shader_(scaler_impl->CreateYuvMrtShader( 1086 dst_subrect.size(), 1087 gfx::Rect(0, 0, (dst_subrect.width() + 3) & ~3, dst_subrect.height()), 1088 gfx::Size((dst_subrect.width() + 3) / 4, dst_subrect.height()), 1089 flip_vertically, 1090 (swizzle == kSwizzleBGRA), 1091 GLHelperScaling::SHADER_YUV_MRT_PASS1)), 1092 pass2_shader_(scaler_impl->CreateYuvMrtShader( 1093 gfx::Size((dst_subrect.width() + 3) / 4, dst_subrect.height()), 1094 gfx::Rect(0, 1095 0, 1096 (dst_subrect.width() + 7) / 8 * 2, 1097 dst_subrect.height()), 1098 gfx::Size((dst_subrect.width() + 7) / 8, 1099 (dst_subrect.height() + 1) / 2), 1100 false, 1101 (swizzle == kSwizzleBGRA), 1102 GLHelperScaling::SHADER_YUV_MRT_PASS2)), 1103 y_(gl, gfx::Size((dst_subrect.width() + 3) / 4, dst_subrect.height())), 1104 uv_(gl), 1105 u_(gl, 1106 gfx::Size((dst_subrect.width() + 7) / 8, 1107 (dst_subrect.height() + 1) / 2)), 1108 v_(gl, 1109 gfx::Size((dst_subrect.width() + 7) / 8, 1110 (dst_subrect.height() + 1) / 2)) { 1111 1112 content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, uv_); 1113 gl->TexImage2D(GL_TEXTURE_2D, 1114 0, 1115 GL_RGBA, 1116 (dst_subrect.width() + 3) / 4, 1117 dst_subrect.height(), 1118 0, 1119 GL_RGBA, 1120 GL_UNSIGNED_BYTE, 1121 NULL); 1122 1123 DCHECK(!(dst_size.width() & 1)); 1124 DCHECK(!(dst_size.height() & 1)); 1125 DCHECK(!(dst_subrect.width() & 1)); 1126 DCHECK(!(dst_subrect.height() & 1)); 1127 DCHECK(!(dst_subrect.x() & 1)); 1128 DCHECK(!(dst_subrect.y() & 1)); 1129 } 1130 1131 void GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV( 1132 const gpu::Mailbox& mailbox, 1133 uint32 sync_point, 1134 const scoped_refptr<media::VideoFrame>& target, 1135 const base::Callback<void(bool)>& callback) { 1136 GLuint mailbox_texture = 1137 copy_impl_->ConsumeMailboxToTexture(mailbox, sync_point); 1138 1139 GLuint texture; 1140 if (quality_ == GLHelper::SCALER_QUALITY_FAST) { 1141 // Optimization: SCALER_QUALITY_FAST is just a single bilinear 1142 // pass, which pass1_shader_ can do just as well, so let's skip 1143 // the actual scaling in that case. 1144 texture = mailbox_texture; 1145 } else { 1146 // Scale texture to right size. 1147 scaler_.Scale(mailbox_texture); 1148 texture = scaler_.texture(); 1149 } 1150 1151 std::vector<GLuint> outputs(2); 1152 // Convert the scaled texture in to Y, U and V planes. 1153 outputs[0] = y_.texture(); 1154 outputs[1] = uv_; 1155 pass1_shader_->Execute(texture, outputs); 1156 1157 gl_->DeleteTextures(1, &mailbox_texture); 1158 1159 outputs[0] = u_.texture(); 1160 outputs[1] = v_.texture(); 1161 pass2_shader_->Execute(uv_, outputs); 1162 1163 if (target->coded_size() != dst_size_) { 1164 DCHECK(target->coded_size() == dst_size_); 1165 LOG(ERROR) << "ReadbackYUV size error!"; 1166 callback.Run(false); 1167 return; 1168 } 1169 1170 // Read back planes, one at a time. 1171 copy_impl_->ReadbackPlane(&y_, 1172 target, 1173 media::VideoFrame::kYPlane, 1174 0, 1175 dst_subrect_, 1176 swizzle_, 1177 base::Bind(&nullcallback)); 1178 copy_impl_->ReadbackPlane(&u_, 1179 target, 1180 media::VideoFrame::kUPlane, 1181 1, 1182 dst_subrect_, 1183 swizzle_, 1184 base::Bind(&nullcallback)); 1185 copy_impl_->ReadbackPlane( 1186 &v_, 1187 target, 1188 media::VideoFrame::kVPlane, 1189 1, 1190 dst_subrect_, 1191 swizzle_, 1192 base::Bind(&CallbackKeepingVideoFrameAlive, target, callback)); 1193 gl_->BindFramebuffer(GL_FRAMEBUFFER, 0); 1194 media::LetterboxYUV(target, dst_subrect_); 1195 } 1196 1197 bool GLHelper::IsReadbackConfigSupported(SkBitmap::Config texture_format) { 1198 DCHECK(readback_support_.get()); 1199 return readback_support_.get()->IsReadbackConfigSupported(texture_format); 1200 } 1201 1202 ReadbackYUVInterface* GLHelper::CopyTextureToImpl::CreateReadbackPipelineYUV( 1203 GLHelper::ScalerQuality quality, 1204 const gfx::Size& src_size, 1205 const gfx::Rect& src_subrect, 1206 const gfx::Size& dst_size, 1207 const gfx::Rect& dst_subrect, 1208 bool flip_vertically, 1209 bool use_mrt) { 1210 helper_->InitScalerImpl(); 1211 // Query preferred format for glReadPixels, if is is GL_BGRA then use that 1212 // and trigger the appropriate swizzle in the YUV shaders. 1213 GLint format = 0, type = 0; 1214 ReadbackSwizzle swizzle = kSwizzleNone; 1215 helper_->readback_support_.get()->GetAdditionalFormat(GL_RGBA, 1216 GL_UNSIGNED_BYTE, 1217 &format, &type); 1218 if (format == GL_BGRA_EXT && type == GL_UNSIGNED_BYTE) 1219 swizzle = kSwizzleBGRA; 1220 if (max_draw_buffers_ >= 2 && use_mrt) { 1221 return new ReadbackYUV_MRT(gl_, 1222 this, 1223 helper_->scaler_impl_.get(), 1224 quality, 1225 src_size, 1226 src_subrect, 1227 dst_size, 1228 dst_subrect, 1229 flip_vertically, 1230 swizzle); 1231 } 1232 return new ReadbackYUVImpl(gl_, 1233 this, 1234 helper_->scaler_impl_.get(), 1235 quality, 1236 src_size, 1237 src_subrect, 1238 dst_size, 1239 dst_subrect, 1240 flip_vertically, 1241 swizzle); 1242 } 1243 1244 ReadbackYUVInterface* GLHelper::CreateReadbackPipelineYUV( 1245 ScalerQuality quality, 1246 const gfx::Size& src_size, 1247 const gfx::Rect& src_subrect, 1248 const gfx::Size& dst_size, 1249 const gfx::Rect& dst_subrect, 1250 bool flip_vertically, 1251 bool use_mrt) { 1252 InitCopyTextToImpl(); 1253 return copy_texture_to_impl_->CreateReadbackPipelineYUV(quality, 1254 src_size, 1255 src_subrect, 1256 dst_size, 1257 dst_subrect, 1258 flip_vertically, 1259 use_mrt); 1260 } 1261 1262 } // namespace content 1263