gl_helper.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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.h"
16#include "base/strings/string_util.h"
17#include "base/time/time.h"
18#include "cc/resources/sync_point_helper.h"
19#include "content/common/gpu/client/gl_helper_scaling.h"
20#include "gpu/command_buffer/common/mailbox.h"
21#include "media/base/video_frame.h"
22#include "media/base/video_util.h"
23#include "third_party/WebKit/public/platform/WebCString.h"
24#include "third_party/skia/include/core/SkRegion.h"
25#include "ui/gfx/rect.h"
26#include "ui/gfx/size.h"
27#include "ui/gl/gl_bindings.h"
28
29using WebKit::WebGLId;
30using WebKit::WebGraphicsContext3D;
31
32namespace {
33
34// Helper class for allocating and holding an RGBA texture of a given
35// size and an associated framebuffer.
36class TextureFrameBufferPair {
37 public:
38  TextureFrameBufferPair(WebGraphicsContext3D* context,
39                         gfx::Size size)
40      : texture_(context, context->createTexture()),
41        framebuffer_(context, context->createFramebuffer()),
42        size_(size) {
43    content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context,
44                                                               texture_);
45    context->texImage2D(GL_TEXTURE_2D,
46                        0,
47                        GL_RGBA,
48                        size.width(),
49                        size.height(),
50                        0,
51                        GL_RGBA,
52                        GL_UNSIGNED_BYTE,
53                        NULL);
54    content::ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(
55        context,
56        framebuffer_);
57    context->framebufferTexture2D(GL_FRAMEBUFFER,
58                                  GL_COLOR_ATTACHMENT0,
59                                  GL_TEXTURE_2D,
60                                  texture_,
61                                  0);
62  }
63
64  WebGLId texture() const { return texture_.id(); }
65  WebGLId framebuffer() const { return framebuffer_.id(); }
66  gfx::Size size() const { return size_; }
67
68 private:
69  content::ScopedTexture texture_;
70  content::ScopedFramebuffer framebuffer_;
71  gfx::Size size_;
72
73  DISALLOW_COPY_AND_ASSIGN(TextureFrameBufferPair);
74};
75
76// Helper class for holding a scaler, a texture for the output of that
77// scaler and an associated frame buffer. This is inteded to be used
78// when the output of a scaler is to be sent to a readback.
79class ScalerHolder {
80 public:
81  ScalerHolder(WebGraphicsContext3D* context,
82               content::GLHelper::ScalerInterface *scaler)
83      : texture_and_framebuffer_(context, scaler->DstSize()),
84        scaler_(scaler) {
85  }
86
87  void Scale(WebKit::WebGLId src_texture) {
88    scaler_->Scale(src_texture, texture_and_framebuffer_.texture());
89  }
90
91  content::GLHelper::ScalerInterface* scaler() const { return scaler_.get(); }
92  TextureFrameBufferPair *texture_and_framebuffer() {
93    return &texture_and_framebuffer_;
94  }
95  WebGLId texture() const { return texture_and_framebuffer_.texture(); }
96
97 private:
98  TextureFrameBufferPair texture_and_framebuffer_;
99  scoped_ptr<content::GLHelper::ScalerInterface> scaler_;
100
101  DISALLOW_COPY_AND_ASSIGN(ScalerHolder);
102};
103
104}  // namespace
105
106namespace content {
107
108// Implements GLHelper::CropScaleReadbackAndCleanTexture and encapsulates
109// the data needed for it.
110class GLHelper::CopyTextureToImpl :
111      public base::SupportsWeakPtr<GLHelper::CopyTextureToImpl> {
112 public:
113  CopyTextureToImpl(WebGraphicsContext3D* context,
114                    GLHelper* helper)
115      : context_(context),
116        helper_(helper),
117        flush_(context),
118        max_draw_buffers_(0) {
119    std::string extensions_string = " " +
120        UTF16ToASCII(context_->getString(GL_EXTENSIONS)) + " ";
121    if (extensions_string.find(" GL_EXT_draw_buffers ") != std::string::npos) {
122      context_->getIntegerv(GL_MAX_DRAW_BUFFERS, &max_draw_buffers_);
123    }
124  }
125  ~CopyTextureToImpl() {
126    CancelRequests();
127  }
128
129  WebGLId ConsumeMailboxToTexture(const gpu::Mailbox& mailbox,
130                                  uint32 sync_point) {
131    return helper_->ConsumeMailboxToTexture(mailbox, sync_point);
132  }
133
134  void CropScaleReadbackAndCleanTexture(
135      WebGLId src_texture,
136      const gfx::Size& src_size,
137      const gfx::Rect& src_subrect,
138      const gfx::Size& dst_size,
139      unsigned char* out,
140      const base::Callback<void(bool)>& callback,
141      GLHelper::ScalerQuality quality);
142
143  void ReadbackTextureSync(WebGLId texture,
144                           const gfx::Rect& src_rect,
145                           unsigned char* out);
146
147  // Reads back bytes from the currently bound frame buffer.
148  // Note that dst_size is specified in bytes, not pixels.
149  void ReadbackAsync(
150      const gfx::Size& dst_size,
151      int32 bytes_per_row,     // generally dst_size.width() * 4
152      int32 row_stride_bytes,  // generally dst_size.width() * 4
153      unsigned char* out,
154      const base::Callback<void(bool)>& callback);
155
156  void ReadbackPlane(TextureFrameBufferPair* source,
157                     media::VideoFrame* target,
158                     int plane,
159                     int size_shift,
160                     const gfx::Rect& dst_subrect,
161                     const base::Callback<void(bool)>& callback);
162
163  WebKit::WebGLId CopyAndScaleTexture(WebGLId texture,
164                                      const gfx::Size& src_size,
165                                      const gfx::Size& dst_size,
166                                      bool vertically_flip_texture,
167                                      GLHelper::ScalerQuality quality);
168
169  ReadbackYUVInterface* CreateReadbackPipelineYUV(
170      GLHelper::ScalerQuality quality,
171      const gfx::Size& src_size,
172      const gfx::Rect& src_subrect,
173      const gfx::Size& dst_size,
174      const gfx::Rect& dst_subrect,
175      bool flip_vertically,
176      bool use_mrt);
177
178  // Returns the maximum number of draw buffers available,
179  // 0 if GL_EXT_draw_buffers is not available.
180  WebKit::WGC3Dint MaxDrawBuffers() const {
181    return max_draw_buffers_;
182  }
183
184 private:
185  // A single request to CropScaleReadbackAndCleanTexture.
186  // The main thread can cancel the request, before it's handled by the helper
187  // thread, by resetting the texture and pixels fields. Alternatively, the
188  // thread marks that it handles the request by resetting the pixels field
189  // (meaning it guarantees that the callback with be called).
190  // In either case, the callback must be called exactly once, and the texture
191  // must be deleted by the main thread context.
192  struct Request {
193    Request(const gfx::Size& size_,
194            int32 bytes_per_row_,
195            int32 row_stride_bytes_,
196            unsigned char* pixels_,
197            const base::Callback<void(bool)>& callback_)
198        : size(size_),
199          bytes_per_row(bytes_per_row_),
200          row_stride_bytes(row_stride_bytes_),
201          pixels(pixels_),
202          callback(callback_),
203          buffer(0) {
204    }
205
206    gfx::Size size;
207    int bytes_per_row;
208    int row_stride_bytes;
209    unsigned char* pixels;
210    base::Callback<void(bool)> callback;
211    GLuint buffer;
212  };
213
214  // A readback pipeline that also converts the data to YUV before
215  // reading it back.
216  class ReadbackYUVImpl : public ReadbackYUVInterface {
217   public:
218    ReadbackYUVImpl(WebGraphicsContext3D* context,
219                    CopyTextureToImpl* copy_impl,
220                    GLHelperScaling* scaler_impl,
221                    GLHelper::ScalerQuality quality,
222                    const gfx::Size& src_size,
223                    const gfx::Rect& src_subrect,
224                    const gfx::Size& dst_size,
225                    const gfx::Rect& dst_subrect,
226                    bool flip_vertically);
227
228    virtual void ReadbackYUV(
229        const gpu::Mailbox& mailbox,
230        uint32 sync_point,
231        media::VideoFrame* target,
232        const base::Callback<void(bool)>& callback) OVERRIDE;
233
234    virtual ScalerInterface* scaler() OVERRIDE {
235      return scaler_.scaler();
236    }
237
238   private:
239    WebGraphicsContext3D* context_;
240    CopyTextureToImpl* copy_impl_;
241    gfx::Size dst_size_;
242    gfx::Rect dst_subrect_;
243    ScalerHolder scaler_;
244    ScalerHolder y_;
245    ScalerHolder u_;
246    ScalerHolder v_;
247
248    DISALLOW_COPY_AND_ASSIGN(ReadbackYUVImpl);
249  };
250
251  // A readback pipeline that also converts the data to YUV before
252  // reading it back. This one uses Multiple Render Targets, which
253  // may not be supported on all platforms.
254  class ReadbackYUV_MRT : public ReadbackYUVInterface {
255   public:
256    ReadbackYUV_MRT(WebGraphicsContext3D* context,
257                    CopyTextureToImpl* copy_impl,
258                    GLHelperScaling* scaler_impl,
259                    GLHelper::ScalerQuality quality,
260                    const gfx::Size& src_size,
261                    const gfx::Rect& src_subrect,
262                    const gfx::Size& dst_size,
263                    const gfx::Rect& dst_subrect,
264                    bool flip_vertically);
265
266    virtual void ReadbackYUV(
267        const gpu::Mailbox& mailbox,
268        uint32 sync_point,
269        media::VideoFrame* target,
270        const base::Callback<void(bool)>& callback) OVERRIDE;
271
272    virtual ScalerInterface* scaler() OVERRIDE {
273      return scaler_.scaler();
274    }
275
276   private:
277    WebGraphicsContext3D* context_;
278    CopyTextureToImpl* copy_impl_;
279    gfx::Size dst_size_;
280    gfx::Rect dst_subrect_;
281    ScalerHolder scaler_;
282    scoped_ptr<content::GLHelperScaling::ShaderInterface> pass1_shader_;
283    scoped_ptr<content::GLHelperScaling::ShaderInterface> pass2_shader_;
284    TextureFrameBufferPair y_;
285    ScopedTexture uv_;
286    TextureFrameBufferPair u_;
287    TextureFrameBufferPair v_;
288
289    DISALLOW_COPY_AND_ASSIGN(ReadbackYUV_MRT);
290  };
291
292  // Copies the block of pixels specified with |src_subrect| from |src_texture|,
293  // scales it to |dst_size|, writes it into a texture, and returns its ID.
294  // |src_size| is the size of |src_texture|.
295  WebGLId ScaleTexture(WebGLId src_texture,
296                       const gfx::Size& src_size,
297                       const gfx::Rect& src_subrect,
298                       const gfx::Size& dst_size,
299                       bool vertically_flip_texture,
300                       bool swizzle,
301                       GLHelper::ScalerQuality quality);
302
303  static void nullcallback(bool success) {}
304  void ReadbackDone(Request* request);
305  void FinishRequest(Request* request, bool result);
306  void CancelRequests();
307
308  static const float kRGBtoYColorWeights[];
309  static const float kRGBtoUColorWeights[];
310  static const float kRGBtoVColorWeights[];
311
312  WebGraphicsContext3D* context_;
313  GLHelper* helper_;
314
315  // A scoped flush that will ensure all resource deletions are flushed when
316  // this object is destroyed. Must be declared before other Scoped* fields.
317  ScopedFlush flush_;
318
319  std::queue<Request*> request_queue_;
320  WebKit::WGC3Dint max_draw_buffers_;
321};
322
323GLHelper::ScalerInterface* GLHelper::CreateScaler(
324    ScalerQuality quality,
325    const gfx::Size& src_size,
326    const gfx::Rect& src_subrect,
327    const gfx::Size& dst_size,
328    bool vertically_flip_texture,
329    bool swizzle) {
330  InitScalerImpl();
331  return scaler_impl_->CreateScaler(quality,
332                                    src_size,
333                                    src_subrect,
334                                    dst_size,
335                                    vertically_flip_texture,
336                                    swizzle);
337}
338
339WebGLId GLHelper::CopyTextureToImpl::ScaleTexture(
340    WebGLId src_texture,
341    const gfx::Size& src_size,
342    const gfx::Rect& src_subrect,
343    const gfx::Size& dst_size,
344    bool vertically_flip_texture,
345    bool swizzle,
346    GLHelper::ScalerQuality quality) {
347  scoped_ptr<ScalerInterface> scaler(
348      helper_->CreateScaler(quality,
349                            src_size,
350                            src_subrect,
351                            dst_size,
352                            vertically_flip_texture,
353                            swizzle));
354
355  WebGLId dst_texture = context_->createTexture();
356  {
357    ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, dst_texture);
358    context_->texImage2D(GL_TEXTURE_2D,
359                         0,
360                         GL_RGBA,
361                         dst_size.width(),
362                         dst_size.height(),
363                         0,
364                         GL_RGBA,
365                         GL_UNSIGNED_BYTE,
366                         NULL);
367  }
368  scaler->Scale(src_texture, dst_texture);
369  return dst_texture;
370}
371
372void GLHelper::CopyTextureToImpl::ReadbackAsync(
373    const gfx::Size& dst_size,
374    int32 bytes_per_row,
375    int32 row_stride_bytes,
376    unsigned char* out,
377    const base::Callback<void(bool)>& callback) {
378  Request* request = new Request(dst_size,
379                                 bytes_per_row,
380                                 row_stride_bytes,
381                                 out,
382                                 callback);
383  request_queue_.push(request);
384  request->buffer = context_->createBuffer();
385  context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
386                       request->buffer);
387  context_->bufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
388                       4 * dst_size.GetArea(),
389                       NULL,
390                       GL_STREAM_READ);
391
392  context_->readPixels(0, 0, dst_size.width(), dst_size.height(),
393                       GL_RGBA, GL_UNSIGNED_BYTE, NULL);
394  context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
395  cc::SyncPointHelper::SignalSyncPoint(
396      context_,
397      context_->insertSyncPoint(),
398      base::Bind(&CopyTextureToImpl::ReadbackDone, AsWeakPtr(), request));
399}
400
401
402void GLHelper::CopyTextureToImpl::CropScaleReadbackAndCleanTexture(
403    WebGLId src_texture,
404    const gfx::Size& src_size,
405    const gfx::Rect& src_subrect,
406    const gfx::Size& dst_size,
407    unsigned char* out,
408    const base::Callback<void(bool)>& callback,
409    GLHelper::ScalerQuality quality) {
410  WebGLId texture = ScaleTexture(src_texture,
411                                 src_size,
412                                 src_subrect,
413                                 dst_size,
414                                 true,
415#if (SK_R32_SHIFT == 16) && !SK_B32_SHIFT
416                                 true,
417#else
418                                 false,
419#endif
420                                 quality);
421  ScopedFramebuffer dst_framebuffer(context_, context_->createFramebuffer());
422  ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(context_,
423                                                             dst_framebuffer);
424  ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
425  context_->framebufferTexture2D(GL_FRAMEBUFFER,
426                                 GL_COLOR_ATTACHMENT0,
427                                 GL_TEXTURE_2D,
428                                 texture,
429                                 0);
430  ReadbackAsync(dst_size,
431                dst_size.width() * 4,
432                dst_size.width() * 4,
433                out,
434                callback);
435  context_->deleteTexture(texture);
436}
437
438void GLHelper::CopyTextureToImpl::ReadbackTextureSync(
439    WebGLId texture,
440    const gfx::Rect& src_rect,
441    unsigned char* out) {
442  ScopedFramebuffer dst_framebuffer(context_, context_->createFramebuffer());
443  ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(context_,
444                                                             dst_framebuffer);
445  ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
446  context_->framebufferTexture2D(GL_FRAMEBUFFER,
447                                 GL_COLOR_ATTACHMENT0,
448                                 GL_TEXTURE_2D,
449                                 texture,
450                                 0);
451  context_->readPixels(src_rect.x(),
452                       src_rect.y(),
453                       src_rect.width(),
454                       src_rect.height(),
455                       GL_RGBA,
456                       GL_UNSIGNED_BYTE,
457                       out);
458}
459
460WebKit::WebGLId GLHelper::CopyTextureToImpl::CopyAndScaleTexture(
461    WebGLId src_texture,
462    const gfx::Size& src_size,
463    const gfx::Size& dst_size,
464    bool vertically_flip_texture,
465    GLHelper::ScalerQuality quality) {
466  return ScaleTexture(src_texture,
467                      src_size,
468                      gfx::Rect(src_size),
469                      dst_size,
470                      vertically_flip_texture,
471                      false,
472                      quality);
473}
474
475void GLHelper::CopyTextureToImpl::ReadbackDone(Request* request) {
476  TRACE_EVENT0("mirror",
477               "GLHelper::CopyTextureToImpl::CheckReadbackFramebufferComplete");
478  DCHECK(request == request_queue_.front());
479
480  bool result = false;
481  if (request->buffer != 0) {
482    context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
483                         request->buffer);
484    unsigned char* data = static_cast<unsigned char *>(
485        context_->mapBufferCHROMIUM(
486            GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY));
487    if (data) {
488      result = true;
489      if (request->bytes_per_row == request->size.width() * 4 &&
490          request->bytes_per_row == request->row_stride_bytes) {
491        memcpy(request->pixels, data, request->size.GetArea() * 4);
492      } else {
493        unsigned char* out = request->pixels;
494        for (int y = 0; y < request->size.height(); y++) {
495          memcpy(out, data, request->bytes_per_row);
496          out += request->row_stride_bytes;
497          data += request->size.width() * 4;
498        }
499      }
500      context_->unmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM);
501    }
502    context_->bindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
503  }
504
505  FinishRequest(request, result);
506}
507
508void GLHelper::CopyTextureToImpl::FinishRequest(Request* request,
509                                                bool result) {
510  DCHECK(request_queue_.front() == request);
511  request_queue_.pop();
512  request->callback.Run(result);
513  ScopedFlush flush(context_);
514  if (request->buffer != 0) {
515    context_->deleteBuffer(request->buffer);
516    request->buffer = 0;
517  }
518  delete request;
519}
520
521void GLHelper::CopyTextureToImpl::CancelRequests() {
522  while (!request_queue_.empty()) {
523    Request* request = request_queue_.front();
524    FinishRequest(request, false);
525  }
526}
527
528GLHelper::GLHelper(WebKit::WebGraphicsContext3D* context)
529    : context_(context) {
530}
531
532GLHelper::~GLHelper() {
533}
534
535void GLHelper::CropScaleReadbackAndCleanTexture(
536    WebGLId src_texture,
537    const gfx::Size& src_size,
538    const gfx::Rect& src_subrect,
539    const gfx::Size& dst_size,
540    unsigned char* out,
541    const base::Callback<void(bool)>& callback) {
542  InitCopyTextToImpl();
543  copy_texture_to_impl_->CropScaleReadbackAndCleanTexture(
544      src_texture,
545      src_size,
546      src_subrect,
547      dst_size,
548      out,
549      callback,
550      GLHelper::SCALER_QUALITY_FAST);
551}
552
553void GLHelper::CropScaleReadbackAndCleanMailbox(
554    const gpu::Mailbox& src_mailbox,
555    uint32 sync_point,
556    const gfx::Size& src_size,
557    const gfx::Rect& src_subrect,
558    const gfx::Size& dst_size,
559    unsigned char* out,
560    const base::Callback<void(bool)>& callback) {
561  WebGLId mailbox_texture = ConsumeMailboxToTexture(src_mailbox, sync_point);
562  CropScaleReadbackAndCleanTexture(
563      mailbox_texture, src_size, src_subrect, dst_size, out, callback);
564  context_->deleteTexture(mailbox_texture);
565}
566
567void GLHelper::ReadbackTextureSync(WebKit::WebGLId texture,
568                                   const gfx::Rect& src_rect,
569                                   unsigned char* out) {
570  InitCopyTextToImpl();
571  copy_texture_to_impl_->ReadbackTextureSync(texture,
572                                             src_rect,
573                                             out);
574}
575
576WebKit::WebGLId GLHelper::CopyTexture(WebKit::WebGLId texture,
577                                      const gfx::Size& size) {
578  InitCopyTextToImpl();
579  return copy_texture_to_impl_->CopyAndScaleTexture(
580      texture,
581      size,
582      size,
583      false,
584      GLHelper::SCALER_QUALITY_FAST);
585}
586
587WebKit::WebGLId GLHelper::CopyAndScaleTexture(
588    WebKit::WebGLId texture,
589    const gfx::Size& src_size,
590    const gfx::Size& dst_size,
591    bool vertically_flip_texture,
592    ScalerQuality quality) {
593  InitCopyTextToImpl();
594  return copy_texture_to_impl_->CopyAndScaleTexture(texture,
595                                                    src_size,
596                                                    dst_size,
597                                                    vertically_flip_texture,
598                                                    quality);
599}
600
601WebGLId GLHelper::CompileShaderFromSource(
602    const WebKit::WGC3Dchar* source,
603    WebKit::WGC3Denum type) {
604  ScopedShader shader(context_, context_->createShader(type));
605  context_->shaderSource(shader, source);
606  context_->compileShader(shader);
607  WebKit::WGC3Dint compile_status = 0;
608  context_->getShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
609  if (!compile_status) {
610    LOG(ERROR) << std::string(context_->getShaderInfoLog(shader).utf8());
611    return 0;
612  }
613  return shader.Detach();
614}
615
616void GLHelper::InitCopyTextToImpl() {
617  // Lazily initialize |copy_texture_to_impl_|
618  if (!copy_texture_to_impl_)
619    copy_texture_to_impl_.reset(new CopyTextureToImpl(context_, this));
620}
621
622void GLHelper::InitScalerImpl() {
623  // Lazily initialize |scaler_impl_|
624  if (!scaler_impl_)
625    scaler_impl_.reset(new GLHelperScaling(context_, this));
626}
627
628WebKit::WGC3Dint GLHelper::MaxDrawBuffers() {
629  InitCopyTextToImpl();
630  return copy_texture_to_impl_->MaxDrawBuffers();
631}
632
633void GLHelper::CopySubBufferDamage(WebKit::WebGLId texture,
634                                   WebKit::WebGLId previous_texture,
635                                   const SkRegion& new_damage,
636                                   const SkRegion& old_damage) {
637  SkRegion region(old_damage);
638  if (region.op(new_damage, SkRegion::kDifference_Op)) {
639    ScopedFramebuffer dst_framebuffer(context_,
640                                      context_->createFramebuffer());
641    ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(context_,
642                                                               dst_framebuffer);
643    ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
644    context_->framebufferTexture2D(GL_FRAMEBUFFER,
645                                   GL_COLOR_ATTACHMENT0,
646                                   GL_TEXTURE_2D,
647                                   previous_texture,
648                                   0);
649    for (SkRegion::Iterator it(region); !it.done(); it.next()) {
650      const SkIRect& rect = it.rect();
651      context_->copyTexSubImage2D(GL_TEXTURE_2D, 0,
652                                  rect.x(), rect.y(),
653                                  rect.x(), rect.y(),
654                                  rect.width(), rect.height());
655    }
656    context_->flush();
657  }
658}
659
660WebKit::WebGLId GLHelper::CreateTexture() {
661  WebKit::WebGLId texture = context_->createTexture();
662  content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_,
663                                                             texture);
664  context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
665  context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
666  context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
667  context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
668  return texture;
669}
670
671WebKit::WebGLId GLHelper::ConsumeMailboxToTexture(const gpu::Mailbox& mailbox,
672                                                  uint32 sync_point) {
673  if (mailbox.IsZero())
674    return 0;
675  if (sync_point)
676    context_->waitSyncPoint(sync_point);
677  WebKit::WebGLId texture = CreateTexture();
678  content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_,
679                                                             texture);
680  context_->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
681  return texture;
682}
683
684void GLHelper::ResizeTexture(WebKit::WebGLId texture, const gfx::Size& size) {
685  content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
686  context_->texImage2D(GL_TEXTURE_2D, 0, GL_RGB,
687                       size.width(), size.height(), 0,
688                       GL_RGB, GL_UNSIGNED_BYTE, NULL);
689}
690
691void GLHelper::CopyTextureSubImage(WebKit::WebGLId texture,
692                                   const gfx::Rect& rect) {
693  content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
694  context_->copyTexSubImage2D(GL_TEXTURE_2D, 0,
695                              rect.x(), rect.y(),
696                              rect.x(), rect.y(), rect.width(), rect.height());
697}
698
699void GLHelper::CopyTextureFullImage(WebKit::WebGLId texture,
700                                    const gfx::Size& size) {
701  content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context_, texture);
702  context_->copyTexImage2D(GL_TEXTURE_2D, 0,
703                           GL_RGB,
704                           0, 0,
705                           size.width(), size.height(), 0);
706}
707
708void GLHelper::CopyTextureToImpl::ReadbackPlane(
709    TextureFrameBufferPair* source,
710    media::VideoFrame* target,
711    int plane,
712    int size_shift,
713    const gfx::Rect& dst_subrect,
714    const base::Callback<void(bool)>& callback) {
715  context_->bindFramebuffer(GL_FRAMEBUFFER, source->framebuffer());
716  size_t offset = target->stride(plane) * (dst_subrect.y() >> size_shift) +
717      (dst_subrect.x() >> size_shift);
718  ReadbackAsync(
719      source->size(),
720      dst_subrect.width() >> size_shift,
721      target->stride(plane),
722      target->data(plane) + offset,
723      callback);
724}
725
726const float GLHelper::CopyTextureToImpl::kRGBtoYColorWeights[] = {
727  0.257f, 0.504f, 0.098f, 0.0625f
728};
729const float GLHelper::CopyTextureToImpl::kRGBtoUColorWeights[] = {
730  -0.148f, -0.291f, 0.439f, 0.5f
731};
732const float GLHelper::CopyTextureToImpl::kRGBtoVColorWeights[] = {
733  0.439f, -0.368f, -0.071f, 0.5f
734};
735
736// YUV readback constructors. Initiates the main scaler pipeline and
737// one planar scaler for each of the Y, U and V planes.
738GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUVImpl(
739    WebGraphicsContext3D* context,
740    CopyTextureToImpl* copy_impl,
741    GLHelperScaling* scaler_impl,
742    GLHelper::ScalerQuality quality,
743    const gfx::Size& src_size,
744    const gfx::Rect& src_subrect,
745    const gfx::Size& dst_size,
746    const gfx::Rect& dst_subrect,
747    bool flip_vertically)
748    : context_(context),
749      copy_impl_(copy_impl),
750      dst_size_(dst_size),
751      dst_subrect_(dst_subrect),
752      scaler_(context, scaler_impl->CreateScaler(
753          quality,
754          src_size,
755          src_subrect,
756          dst_subrect.size(),
757          flip_vertically,
758          false)),
759      y_(context, scaler_impl->CreatePlanarScaler(
760          dst_subrect.size(),
761          gfx::Rect(0, 0,
762                    (dst_subrect.width() + 3) & ~3,
763                    dst_subrect.height()),
764          gfx::Size((dst_subrect.width() + 3) / 4,
765                    dst_subrect.height()),
766          false,
767          kRGBtoYColorWeights)),
768      u_(context, scaler_impl->CreatePlanarScaler(
769          dst_subrect.size(),
770          gfx::Rect(0, 0,
771                    (dst_subrect.width() + 7) & ~7,
772                    (dst_subrect.height() + 1) & ~1),
773          gfx::Size((dst_subrect.width() + 7) / 8,
774                    (dst_subrect.height() + 1) / 2),
775          false,
776          kRGBtoUColorWeights)),
777      v_(context, scaler_impl->CreatePlanarScaler(
778          dst_subrect.size(),
779          gfx::Rect(0, 0,
780                    (dst_subrect.width() + 7) & ~7,
781                    (dst_subrect.height() + 1) & ~1),
782          gfx::Size((dst_subrect.width() + 7) / 8,
783                    (dst_subrect.height() + 1) / 2),
784          false,
785          kRGBtoVColorWeights)) {
786  DCHECK(!(dst_size.width() & 1));
787  DCHECK(!(dst_size.height() & 1));
788  DCHECK(!(dst_subrect.width() & 1));
789  DCHECK(!(dst_subrect.height() & 1));
790  DCHECK(!(dst_subrect.x() & 1));
791  DCHECK(!(dst_subrect.y() & 1));
792}
793
794
795void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUV(
796    const gpu::Mailbox& mailbox,
797    uint32 sync_point,
798    media::VideoFrame *target,
799    const base::Callback<void(bool)>& callback) {
800  WebGLId mailbox_texture =
801      copy_impl_->ConsumeMailboxToTexture(mailbox, sync_point);
802
803  // Scale texture to right size.
804  scaler_.Scale(mailbox_texture);
805  context_->deleteTexture(mailbox_texture);
806
807  // Convert the scaled texture in to Y, U and V planes.
808  y_.Scale(scaler_.texture());
809  u_.Scale(scaler_.texture());
810  v_.Scale(scaler_.texture());
811
812  if (target->coded_size() != dst_size_) {
813    DCHECK(target->coded_size() == dst_size_);
814    LOG(ERROR) << "ReadbackYUV size error!";
815    callback.Run(false);
816    return;
817  }
818
819  // Read back planes, one at a time.
820  copy_impl_->ReadbackPlane(y_.texture_and_framebuffer(),
821                            target,
822                            media::VideoFrame::kYPlane,
823                            0,
824                            dst_subrect_,
825                            base::Bind(&nullcallback));
826  copy_impl_->ReadbackPlane(u_.texture_and_framebuffer(),
827                            target,
828                            media::VideoFrame::kUPlane,
829                            1,
830                            dst_subrect_,
831                            base::Bind(&nullcallback));
832  copy_impl_->ReadbackPlane(v_.texture_and_framebuffer(),
833                            target,
834                            media::VideoFrame::kVPlane,
835                            1,
836                            dst_subrect_,
837                            callback);
838  context_->bindFramebuffer(GL_FRAMEBUFFER, 0);
839  media::LetterboxYUV(target, dst_subrect_);
840}
841
842// YUV readback constructors. Initiates the main scaler pipeline and
843// one planar scaler for each of the Y, U and V planes.
844GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV_MRT(
845    WebGraphicsContext3D* context,
846    CopyTextureToImpl* copy_impl,
847    GLHelperScaling* scaler_impl,
848    GLHelper::ScalerQuality quality,
849    const gfx::Size& src_size,
850    const gfx::Rect& src_subrect,
851    const gfx::Size& dst_size,
852    const gfx::Rect& dst_subrect,
853    bool flip_vertically)
854    : context_(context),
855      copy_impl_(copy_impl),
856      dst_size_(dst_size),
857      dst_subrect_(dst_subrect),
858      scaler_(context, scaler_impl->CreateScaler(
859          quality,
860          src_size,
861          src_subrect,
862          dst_subrect.size(),
863          flip_vertically,
864          false)),
865      pass1_shader_(scaler_impl->CreateYuvMrtShader(
866          dst_subrect.size(),
867          gfx::Rect(0, 0,
868                    (dst_subrect.width() + 3) & ~3,
869                    dst_subrect.height()),
870          gfx::Size((dst_subrect.width() + 3) / 4,
871                    dst_subrect.height()),
872          false,
873          GLHelperScaling::SHADER_YUV_MRT_PASS1)),
874      pass2_shader_(scaler_impl->CreateYuvMrtShader(
875          gfx::Size((dst_subrect.width() + 3) / 4,
876                    dst_subrect.height()),
877          gfx::Rect(0, 0,
878                    (dst_subrect.width() + 7) / 8 * 2,
879                    dst_subrect.height()),
880          gfx::Size((dst_subrect.width() + 7) / 8,
881                    (dst_subrect.height() + 1) / 2),
882          false,
883          GLHelperScaling::SHADER_YUV_MRT_PASS2)),
884      y_(context, gfx::Size((dst_subrect.width() + 3) / 4,
885                            dst_subrect.height())),
886      uv_(context, context->createTexture()),
887      u_(context, gfx::Size((dst_subrect.width() + 7) / 8,
888                            (dst_subrect.height() + 1) / 2)),
889      v_(context, gfx::Size((dst_subrect.width() + 7) / 8,
890                            (dst_subrect.height() + 1) / 2)) {
891
892  content::ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(context, uv_);
893  context->texImage2D(GL_TEXTURE_2D,
894                      0,
895                      GL_RGBA,
896                      (dst_subrect.width() + 3) / 4,
897                      dst_subrect.height(),
898                      0,
899                      GL_RGBA,
900                      GL_UNSIGNED_BYTE,
901                      NULL);
902
903  DCHECK(!(dst_size.width() & 1));
904  DCHECK(!(dst_size.height() & 1));
905  DCHECK(!(dst_subrect.width() & 1));
906  DCHECK(!(dst_subrect.height() & 1));
907  DCHECK(!(dst_subrect.x() & 1));
908  DCHECK(!(dst_subrect.y() & 1));
909}
910
911void GLHelper::CopyTextureToImpl::ReadbackYUV_MRT::ReadbackYUV(
912    const gpu::Mailbox& mailbox,
913    uint32 sync_point,
914    media::VideoFrame *target,
915    const base::Callback<void(bool)>& callback) {
916  WebGLId mailbox_texture =
917      copy_impl_->ConsumeMailboxToTexture(mailbox, sync_point);
918
919  // Scale texture to right size.
920  scaler_.Scale(mailbox_texture);
921  context_->deleteTexture(mailbox_texture);
922
923  std::vector<WebKit::WebGLId> outputs(2);
924  // Convert the scaled texture in to Y, U and V planes.
925  outputs[0] = y_.texture();
926  outputs[1] = uv_;
927  pass1_shader_->Execute(scaler_.texture(), outputs);
928  outputs[0] = u_.texture();
929  outputs[1] = v_.texture();
930  pass2_shader_->Execute(uv_, outputs);
931
932  if (target->coded_size() != dst_size_) {
933    DCHECK(target->coded_size() == dst_size_);
934    LOG(ERROR) << "ReadbackYUV size error!";
935    callback.Run(false);
936    return;
937  }
938
939  // Read back planes, one at a time.
940  copy_impl_->ReadbackPlane(&y_,
941                            target,
942                            media::VideoFrame::kYPlane,
943                            0,
944                            dst_subrect_,
945                            base::Bind(&nullcallback));
946  copy_impl_->ReadbackPlane(&u_,
947                            target,
948                            media::VideoFrame::kUPlane,
949                            1,
950                            dst_subrect_,
951                            base::Bind(&nullcallback));
952  copy_impl_->ReadbackPlane(&v_,
953                            target,
954                            media::VideoFrame::kVPlane,
955                            1,
956                            dst_subrect_,
957                            callback);
958  context_->bindFramebuffer(GL_FRAMEBUFFER, 0);
959  media::LetterboxYUV(target, dst_subrect_);
960}
961
962ReadbackYUVInterface*
963GLHelper::CopyTextureToImpl::CreateReadbackPipelineYUV(
964    GLHelper::ScalerQuality quality,
965    const gfx::Size& src_size,
966    const gfx::Rect& src_subrect,
967    const gfx::Size& dst_size,
968    const gfx::Rect& dst_subrect,
969    bool flip_vertically,
970    bool use_mrt) {
971  helper_->InitScalerImpl();
972  if (max_draw_buffers_ >= 2 && use_mrt) {
973    return new ReadbackYUV_MRT(
974        context_,
975        this,
976        helper_->scaler_impl_.get(),
977        quality,
978        src_size,
979        src_subrect,
980        dst_size,
981        dst_subrect,
982        flip_vertically);
983  }
984  return new ReadbackYUVImpl(
985      context_,
986      this,
987      helper_->scaler_impl_.get(),
988      quality,
989      src_size,
990      src_subrect,
991      dst_size,
992      dst_subrect,
993      flip_vertically);
994}
995
996ReadbackYUVInterface*
997GLHelper::CreateReadbackPipelineYUV(
998    ScalerQuality quality,
999    const gfx::Size& src_size,
1000    const gfx::Rect& src_subrect,
1001    const gfx::Size& dst_size,
1002    const gfx::Rect& dst_subrect,
1003    bool flip_vertically,
1004    bool use_mrt) {
1005  InitCopyTextToImpl();
1006  return copy_texture_to_impl_->CreateReadbackPipelineYUV(
1007      quality,
1008      src_size,
1009      src_subrect,
1010      dst_size,
1011      dst_subrect,
1012      flip_vertically,
1013      use_mrt);
1014}
1015
1016}  // namespace content
1017