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