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 "gpu/command_buffer/client/gl_helper.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <string>
11 #include <utility>
12 
13 #include "base/bind.h"
14 #include "base/bind_helpers.h"
15 #include "base/containers/queue.h"
16 #include "base/lazy_instance.h"
17 #include "base/logging.h"
18 #include "base/macros.h"
19 #include "base/memory/ref_counted.h"
20 #include "base/numerics/safe_conversions.h"
21 #include "base/strings/string_util.h"
22 #include "base/time/time.h"
23 #include "base/trace_event/trace_event.h"
24 #include "gpu/GLES2/gl2extchromium.h"
25 #include "gpu/command_buffer/client/context_support.h"
26 #include "gpu/command_buffer/client/gl_helper_scaling.h"
27 #include "ui/gfx/geometry/point.h"
28 #include "ui/gfx/geometry/rect.h"
29 #include "ui/gfx/geometry/size.h"
30 #include "ui/gfx/geometry/vector2d.h"
31 
32 namespace gpu {
33 
34 using gles2::GLES2Interface;
35 
36 namespace {
37 
38 class ScopedFlush {
39  public:
ScopedFlush(gles2::GLES2Interface * gl)40   explicit ScopedFlush(gles2::GLES2Interface* gl) : gl_(gl) {}
41 
~ScopedFlush()42   ~ScopedFlush() { gl_->Flush(); }
43 
44  private:
45   gles2::GLES2Interface* gl_;
46 
47   DISALLOW_COPY_AND_ASSIGN(ScopedFlush);
48 };
49 
50 // Helper class for allocating and holding an RGBA texture of a given
51 // size.
52 class TextureHolder {
53  public:
TextureHolder(GLES2Interface * gl,gfx::Size size)54   TextureHolder(GLES2Interface* gl, gfx::Size size)
55       : texture_(gl), size_(size) {
56     ScopedTextureBinder<GL_TEXTURE_2D> texture_binder(gl, texture_);
57     gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0,
58                    GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
59   }
60 
texture() const61   GLuint texture() const { return texture_.id(); }
size() const62   gfx::Size size() const { return size_; }
63 
64  private:
65   ScopedTexture texture_;
66   gfx::Size size_;
67 
68   DISALLOW_COPY_AND_ASSIGN(TextureHolder);
69 };
70 
71 class I420ConverterImpl : public I420Converter {
72  public:
73   I420ConverterImpl(GLES2Interface* gl,
74                     GLHelperScaling* scaler_impl,
75                     bool flipped_source,
76                     bool flip_output,
77                     bool swizzle,
78                     bool use_mrt);
79 
80   ~I420ConverterImpl() override;
81 
82   void Convert(GLuint src_texture,
83                const gfx::Size& src_texture_size,
84                const gfx::Vector2dF& src_offset,
85                GLHelper::ScalerInterface* optional_scaler,
86                const gfx::Rect& output_rect,
87                GLuint y_plane_texture,
88                GLuint u_plane_texture,
89                GLuint v_plane_texture) override;
90 
91   bool IsSamplingFlippedSource() const override;
92   bool IsFlippingOutput() const override;
93   GLenum GetReadbackFormat() const override;
94 
95  protected:
96   // Returns true if the planerizer should use the faster, two-pass shaders
97   // to generate the YUV planar outputs. If false, the source will be
98   // scanned three times, once for each Y/U/V plane.
use_mrt() const99   bool use_mrt() const { return !v_planerizer_; }
100 
101   // Reallocates the intermediate and plane textures, if needed.
102   void EnsureTexturesSizedFor(const gfx::Size& scaler_output_size,
103                               const gfx::Size& y_texture_size,
104                               const gfx::Size& chroma_texture_size,
105                               GLuint y_plane_texture,
106                               GLuint u_plane_texture,
107                               GLuint v_plane_texture);
108 
109   GLES2Interface* const gl_;
110 
111  private:
112   // These generate the Y/U/V planes. If MRT is being used, |y_planerizer_|
113   // generates the Y and interim UV plane, |u_planerizer_| generates the
114   // final U and V planes, and |v_planerizer_| is unused. If MRT is not
115   // being used, each of these generates only one of the Y/U/V planes.
116   const std::unique_ptr<GLHelper::ScalerInterface> y_planerizer_;
117   const std::unique_ptr<GLHelper::ScalerInterface> u_planerizer_;
118   const std::unique_ptr<GLHelper::ScalerInterface> v_planerizer_;
119 
120   // Intermediate texture, holding the scaler's output.
121   base::Optional<TextureHolder> intermediate_;
122 
123   // Intermediate texture, holding the UV interim output (if the MRT shader
124   // is being used).
125   base::Optional<ScopedTexture> uv_;
126 
127   DISALLOW_COPY_AND_ASSIGN(I420ConverterImpl);
128 };
129 
130 }  // namespace
131 
132 // Implements texture consumption/readback and encapsulates
133 // the data needed for it.
134 class GLHelper::CopyTextureToImpl
135     : public base::SupportsWeakPtr<GLHelper::CopyTextureToImpl> {
136  public:
CopyTextureToImpl(GLES2Interface * gl,ContextSupport * context_support,GLHelper * helper)137   CopyTextureToImpl(GLES2Interface* gl,
138                     ContextSupport* context_support,
139                     GLHelper* helper)
140       : gl_(gl),
141         context_support_(context_support),
142         helper_(helper),
143         flush_(gl) {}
~CopyTextureToImpl()144   ~CopyTextureToImpl() { CancelRequests(); }
145 
146   void ReadbackTextureAsync(GLuint texture,
147                             GLenum texture_target,
148                             const gfx::Size& dst_size,
149                             unsigned char* out,
150                             GLenum format,
151                             base::OnceCallback<void(bool)> callback);
152 
153   // Reads back bytes from the currently bound frame buffer.
154   // Note that dst_size is specified in bytes, not pixels.
155   void ReadbackAsync(const gfx::Size& dst_size,
156                      size_t bytes_per_row,     // generally dst_size.width() * 4
157                      size_t row_stride_bytes,  // generally dst_size.width() * 4
158                      unsigned char* out,
159                      GLenum format,
160                      GLenum type,
161                      size_t bytes_per_pixel,
162                      base::OnceCallback<void(bool)> callback);
163 
164   void ReadbackPlane(const gfx::Size& texture_size,
165                      int row_stride_bytes,
166                      unsigned char* data,
167                      int size_shift,
168                      const gfx::Rect& paste_rect,
169                      ReadbackSwizzle swizzle,
170                      base::OnceCallback<void(bool)> callback);
171 
172   std::unique_ptr<ReadbackYUVInterface> CreateReadbackPipelineYUV(
173       bool flip_vertically,
174       bool use_mrt);
175 
176  private:
177   // Represents the state of a single readback request.
178   // The main thread can cancel the request, before it's handled by the helper
179   // thread, by resetting the texture and pixels fields. Alternatively, the
180   // thread marks that it handles the request by resetting the pixels field
181   // (meaning it guarantees that the callback with be called).
182   // In either case, the callback must be called exactly once, and the texture
183   // must be deleted by the main thread gl.
184   struct Request {
Requestgpu::GLHelper::CopyTextureToImpl::Request185     Request(const gfx::Size& size_,
186             size_t bytes_per_row_,
187             size_t row_stride_bytes_,
188             unsigned char* pixels_,
189             base::OnceCallback<void(bool)> callback_)
190         : done(false),
191           size(size_),
192           bytes_per_row(bytes_per_row_),
193           row_stride_bytes(row_stride_bytes_),
194           pixels(pixels_),
195           callback(std::move(callback_)),
196           buffer(0),
197           query(0) {}
198 
199     bool done;
200     bool result;
201     gfx::Size size;
202     size_t bytes_per_row;
203     size_t row_stride_bytes;
204     unsigned char* pixels;
205     base::OnceCallback<void(bool)> callback;
206     GLuint buffer;
207     GLuint query;
208   };
209 
210   // We must take care to call the callbacks last, as they may
211   // end up destroying the gl_helper and make *this invalid.
212   // We stick the finished requests in a stack object that calls
213   // the callbacks when it goes out of scope.
214   class FinishRequestHelper {
215    public:
FinishRequestHelper()216     FinishRequestHelper() {}
~FinishRequestHelper()217     ~FinishRequestHelper() {
218       while (!requests_.empty()) {
219         Request* request = requests_.front();
220         requests_.pop();
221         std::move(request->callback).Run(request->result);
222         delete request;
223       }
224     }
Add(Request * r)225     void Add(Request* r) { requests_.push(r); }
226 
227    private:
228     base::queue<Request*> requests_;
229     DISALLOW_COPY_AND_ASSIGN(FinishRequestHelper);
230   };
231 
232   // A readback pipeline that also converts the data to YUV before
233   // reading it back.
234   class ReadbackYUVImpl : public I420ConverterImpl,
235                           public ReadbackYUVInterface {
236    public:
237     ReadbackYUVImpl(GLES2Interface* gl,
238                     CopyTextureToImpl* copy_impl,
239                     GLHelperScaling* scaler_impl,
240                     bool flip_vertically,
241                     ReadbackSwizzle swizzle,
242                     bool use_mrt);
243 
244     ~ReadbackYUVImpl() override;
245 
246     void SetScaler(std::unique_ptr<GLHelper::ScalerInterface> scaler) override;
247 
248     GLHelper::ScalerInterface* scaler() const override;
249 
250     bool IsFlippingOutput() const override;
251 
252     void ReadbackYUV(GLuint texture,
253                      const gfx::Size& src_texture_size,
254                      const gfx::Rect& output_rect,
255                      int y_plane_row_stride_bytes,
256                      unsigned char* y_plane_data,
257                      int u_plane_row_stride_bytes,
258                      unsigned char* u_plane_data,
259                      int v_plane_row_stride_bytes,
260                      unsigned char* v_plane_data,
261                      const gfx::Point& paste_location,
262                      base::OnceCallback<void(bool)> callback) override;
263 
264    private:
265     GLES2Interface* gl_;
266     CopyTextureToImpl* copy_impl_;
267     ReadbackSwizzle swizzle_;
268 
269     // May be null if no scaling is required. This can be changed between
270     // calls to ReadbackYUV().
271     std::unique_ptr<GLHelper::ScalerInterface> scaler_;
272 
273     // These are the output textures for each Y/U/V plane.
274     ScopedTexture y_;
275     ScopedTexture u_;
276     ScopedTexture v_;
277 
278     // Framebuffers used by ReadbackPlane(). They are cached here so as to not
279     // be re-allocated for every frame of video.
280     ScopedFramebuffer y_readback_framebuffer_;
281     ScopedFramebuffer u_readback_framebuffer_;
282     ScopedFramebuffer v_readback_framebuffer_;
283 
284     DISALLOW_COPY_AND_ASSIGN(ReadbackYUVImpl);
285   };
286 
287   void ReadbackDone(Request* request, size_t bytes_per_pixel);
288   void FinishRequest(Request* request,
289                      bool result,
290                      FinishRequestHelper* helper);
291   void CancelRequests();
292 
293   bool IsBGRAReadbackSupported();
294 
295   GLES2Interface* gl_;
296   ContextSupport* context_support_;
297   GLHelper* helper_;
298 
299   // A scoped flush that will ensure all resource deletions are flushed when
300   // this object is destroyed. Must be declared before other Scoped* fields.
301   ScopedFlush flush_;
302 
303   base::queue<Request*> request_queue_;
304 
305   // Lazily set by IsBGRAReadbackSupported().
306   enum {
307     BGRA_SUPPORT_UNKNOWN,
308     BGRA_SUPPORTED,
309     BGRA_NOT_SUPPORTED
310   } bgra_support_ = BGRA_SUPPORT_UNKNOWN;
311 
312   // A run-once test is lazy executed in CreateReadbackPipelineYUV(), to
313   // determine whether the GL_BGRA_EXT format is preferred for readback.
314   enum {
315     BGRA_PREFERENCE_UNKNOWN,
316     BGRA_PREFERRED,
317     BGRA_NOT_PREFERRED
318   } bgra_preference_ = BGRA_PREFERENCE_UNKNOWN;
319 };
320 
CreateScaler(ScalerQuality quality,const gfx::Vector2d & scale_from,const gfx::Vector2d & scale_to,bool flipped_source,bool flip_output,bool swizzle)321 std::unique_ptr<GLHelper::ScalerInterface> GLHelper::CreateScaler(
322     ScalerQuality quality,
323     const gfx::Vector2d& scale_from,
324     const gfx::Vector2d& scale_to,
325     bool flipped_source,
326     bool flip_output,
327     bool swizzle) {
328   InitScalerImpl();
329   return scaler_impl_->CreateScaler(quality, scale_from, scale_to,
330                                     flipped_source, flip_output, swizzle);
331 }
332 
ReadbackAsync(const gfx::Size & dst_size,size_t bytes_per_row,size_t row_stride_bytes,unsigned char * out,GLenum format,GLenum type,size_t bytes_per_pixel,base::OnceCallback<void (bool)> callback)333 void GLHelper::CopyTextureToImpl::ReadbackAsync(
334     const gfx::Size& dst_size,
335     size_t bytes_per_row,
336     size_t row_stride_bytes,
337     unsigned char* out,
338     GLenum format,
339     GLenum type,
340     size_t bytes_per_pixel,
341     base::OnceCallback<void(bool)> callback) {
342   TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::ReadbackAsync");
343   Request* request = new Request(dst_size, bytes_per_row, row_stride_bytes, out,
344                                  std::move(callback));
345   request_queue_.push(request);
346   request->buffer = 0u;
347 
348   gl_->GenBuffers(1, &request->buffer);
349   gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer);
350   gl_->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
351                   bytes_per_pixel * dst_size.GetArea(), nullptr,
352                   GL_STREAM_READ);
353 
354   request->query = 0u;
355   gl_->GenQueriesEXT(1, &request->query);
356   gl_->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, request->query);
357   gl_->ReadPixels(0, 0, dst_size.width(), dst_size.height(), format, type,
358                   nullptr);
359   gl_->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM);
360   gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
361   context_support_->SignalQuery(
362       request->query, base::BindOnce(&CopyTextureToImpl::ReadbackDone,
363                                      AsWeakPtr(), request, bytes_per_pixel));
364 }
365 
ReadbackTextureAsync(GLuint texture,GLenum texture_target,const gfx::Size & dst_size,unsigned char * out,GLenum format,base::OnceCallback<void (bool)> callback)366 void GLHelper::CopyTextureToImpl::ReadbackTextureAsync(
367     GLuint texture,
368     GLenum texture_target,
369     const gfx::Size& dst_size,
370     unsigned char* out,
371     GLenum format,
372     base::OnceCallback<void(bool)> callback) {
373   constexpr size_t kBytesPerPixel = 4;
374 
375   // Note: It's possible the GL implementation supports other readback
376   // types. However, as of this writing, no caller of this method will
377   // request a different |color_type| (i.e., requiring using some other GL
378   // format).
379   if (format != GL_RGBA &&
380       (format != GL_BGRA_EXT || !IsBGRAReadbackSupported())) {
381     std::move(callback).Run(false);
382     return;
383   }
384 
385   ScopedFramebuffer dst_framebuffer(gl_);
386   ScopedFramebufferBinder<GL_FRAMEBUFFER> framebuffer_binder(gl_,
387                                                              dst_framebuffer);
388   gl_->BindTexture(texture_target, texture);
389   gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
390                             texture_target, texture, 0);
391   ReadbackAsync(dst_size, dst_size.width() * kBytesPerPixel,
392                 dst_size.width() * kBytesPerPixel, out, format,
393                 GL_UNSIGNED_BYTE, kBytesPerPixel, std::move(callback));
394   gl_->BindTexture(texture_target, 0);
395 }
396 
ReadbackDone(Request * finished_request,size_t bytes_per_pixel)397 void GLHelper::CopyTextureToImpl::ReadbackDone(Request* finished_request,
398                                                size_t bytes_per_pixel) {
399   TRACE_EVENT0("gpu.capture",
400                "GLHelper::CopyTextureToImpl::CheckReadbackFramebufferComplete");
401   finished_request->done = true;
402 
403   FinishRequestHelper finish_request_helper;
404 
405   // We process transfer requests in the order they were received, regardless
406   // of the order we get the callbacks in.
407   while (!request_queue_.empty()) {
408     Request* request = request_queue_.front();
409     if (!request->done) {
410       break;
411     }
412 
413     bool result = false;
414     if (request->buffer != 0) {
415       gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, request->buffer);
416       unsigned char* data = static_cast<unsigned char*>(gl_->MapBufferCHROMIUM(
417           GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY));
418       if (data) {
419         result = true;
420         if (request->bytes_per_row == request->size.width() * bytes_per_pixel &&
421             request->bytes_per_row == request->row_stride_bytes) {
422           memcpy(request->pixels, data,
423                  request->size.GetArea() * bytes_per_pixel);
424         } else {
425           unsigned char* out = request->pixels;
426           for (int y = 0; y < request->size.height(); y++) {
427             memcpy(out, data, request->bytes_per_row);
428             out += request->row_stride_bytes;
429             data += request->size.width() * bytes_per_pixel;
430           }
431         }
432         gl_->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM);
433       }
434       gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0);
435     }
436     FinishRequest(request, result, &finish_request_helper);
437   }
438 }
439 
FinishRequest(Request * request,bool result,FinishRequestHelper * finish_request_helper)440 void GLHelper::CopyTextureToImpl::FinishRequest(
441     Request* request,
442     bool result,
443     FinishRequestHelper* finish_request_helper) {
444   TRACE_EVENT0("gpu.capture", "GLHelper::CopyTextureToImpl::FinishRequest");
445   DCHECK(request_queue_.front() == request);
446   request_queue_.pop();
447   request->result = result;
448   ScopedFlush flush(gl_);
449   if (request->query != 0) {
450     gl_->DeleteQueriesEXT(1, &request->query);
451     request->query = 0;
452   }
453   if (request->buffer != 0) {
454     gl_->DeleteBuffers(1, &request->buffer);
455     request->buffer = 0;
456   }
457   finish_request_helper->Add(request);
458 }
459 
CancelRequests()460 void GLHelper::CopyTextureToImpl::CancelRequests() {
461   FinishRequestHelper finish_request_helper;
462   while (!request_queue_.empty()) {
463     Request* request = request_queue_.front();
464     FinishRequest(request, false, &finish_request_helper);
465   }
466 }
467 
IsBGRAReadbackSupported()468 bool GLHelper::CopyTextureToImpl::IsBGRAReadbackSupported() {
469   if (bgra_support_ == BGRA_PREFERENCE_UNKNOWN) {
470     bgra_support_ = BGRA_NOT_SUPPORTED;
471     if (auto* extensions = gl_->GetString(GL_EXTENSIONS)) {
472       const std::string extensions_string =
473           " " + std::string(reinterpret_cast<const char*>(extensions)) + " ";
474       if (extensions_string.find(" GL_EXT_read_format_bgra ") !=
475           std::string::npos) {
476         bgra_support_ = BGRA_SUPPORTED;
477       }
478     }
479   }
480 
481   return bgra_support_ == BGRA_SUPPORTED;
482 }
483 
GLHelper(GLES2Interface * gl,ContextSupport * context_support)484 GLHelper::GLHelper(GLES2Interface* gl, ContextSupport* context_support)
485     : gl_(gl), context_support_(context_support) {}
486 
~GLHelper()487 GLHelper::~GLHelper() {}
488 
ReadbackTextureAsync(GLuint texture,GLenum texture_target,const gfx::Size & dst_size,unsigned char * out,GLenum format,base::OnceCallback<void (bool)> callback)489 void GLHelper::ReadbackTextureAsync(GLuint texture,
490                                     GLenum texture_target,
491                                     const gfx::Size& dst_size,
492                                     unsigned char* out,
493                                     GLenum format,
494                                     base::OnceCallback<void(bool)> callback) {
495   InitCopyTextToImpl();
496   copy_texture_to_impl_->ReadbackTextureAsync(texture, texture_target, dst_size,
497                                               out, format, std::move(callback));
498 }
499 
InitCopyTextToImpl()500 void GLHelper::InitCopyTextToImpl() {
501   // Lazily initialize |copy_texture_to_impl_|
502   if (!copy_texture_to_impl_)
503     copy_texture_to_impl_.reset(
504         new CopyTextureToImpl(gl_, context_support_, this));
505 }
506 
InitScalerImpl()507 void GLHelper::InitScalerImpl() {
508   // Lazily initialize |scaler_impl_|
509   if (!scaler_impl_)
510     scaler_impl_.reset(new GLHelperScaling(gl_, this));
511 }
512 
MaxDrawBuffers()513 GLint GLHelper::MaxDrawBuffers() {
514   if (max_draw_buffers_ < 0) {
515     max_draw_buffers_ = 0;
516     const GLubyte* extensions = gl_->GetString(GL_EXTENSIONS);
517     if (extensions) {
518       const std::string extensions_string =
519           " " + std::string(reinterpret_cast<const char*>(extensions)) + " ";
520       if (extensions_string.find(" GL_EXT_draw_buffers ") !=
521           std::string::npos) {
522         gl_->GetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &max_draw_buffers_);
523         DCHECK_GE(max_draw_buffers_, 0);
524       }
525     }
526   }
527 
528   return max_draw_buffers_;
529 }
530 
ReadbackPlane(const gfx::Size & texture_size,int row_stride_bytes,unsigned char * data,int size_shift,const gfx::Rect & paste_rect,ReadbackSwizzle swizzle,base::OnceCallback<void (bool)> callback)531 void GLHelper::CopyTextureToImpl::ReadbackPlane(
532     const gfx::Size& texture_size,
533     int row_stride_bytes,
534     unsigned char* data,
535     int size_shift,
536     const gfx::Rect& paste_rect,
537     ReadbackSwizzle swizzle,
538     base::OnceCallback<void(bool)> callback) {
539   const size_t offset = row_stride_bytes * (paste_rect.y() >> size_shift) +
540                         (paste_rect.x() >> size_shift);
541   ReadbackAsync(texture_size, paste_rect.width() >> size_shift,
542                 row_stride_bytes, data + offset,
543                 (swizzle == kSwizzleBGRA) ? GL_BGRA_EXT : GL_RGBA,
544                 GL_UNSIGNED_BYTE, 4, std::move(callback));
545 }
546 
547 I420Converter::I420Converter() = default;
548 I420Converter::~I420Converter() = default;
549 
550 // static
GetYPlaneTextureSize(const gfx::Size & output_size)551 gfx::Size I420Converter::GetYPlaneTextureSize(const gfx::Size& output_size) {
552   return gfx::Size((output_size.width() + 3) / 4, output_size.height());
553 }
554 
555 // static
GetChromaPlaneTextureSize(const gfx::Size & output_size)556 gfx::Size I420Converter::GetChromaPlaneTextureSize(
557     const gfx::Size& output_size) {
558   return gfx::Size((output_size.width() + 7) / 8,
559                    (output_size.height() + 1) / 2);
560 }
561 
562 namespace {
563 
I420ConverterImpl(GLES2Interface * gl,GLHelperScaling * scaler_impl,bool flipped_source,bool flip_output,bool swizzle,bool use_mrt)564 I420ConverterImpl::I420ConverterImpl(GLES2Interface* gl,
565                                      GLHelperScaling* scaler_impl,
566                                      bool flipped_source,
567                                      bool flip_output,
568                                      bool swizzle,
569                                      bool use_mrt)
570     : gl_(gl),
571       y_planerizer_(
572           use_mrt ? scaler_impl->CreateI420MrtPass1Planerizer(flipped_source,
573                                                               flip_output,
574                                                               swizzle)
575                   : scaler_impl->CreateI420Planerizer(0,
576                                                       flipped_source,
577                                                       flip_output,
578                                                       swizzle)),
579       u_planerizer_(use_mrt ? scaler_impl->CreateI420MrtPass2Planerizer(swizzle)
580                             : scaler_impl->CreateI420Planerizer(1,
581                                                                 flipped_source,
582                                                                 flip_output,
583                                                                 swizzle)),
584       v_planerizer_(use_mrt ? nullptr
585                             : scaler_impl->CreateI420Planerizer(2,
586                                                                 flipped_source,
587                                                                 flip_output,
588                                                                 swizzle)) {}
589 
590 I420ConverterImpl::~I420ConverterImpl() = default;
591 
Convert(GLuint src_texture,const gfx::Size & src_texture_size,const gfx::Vector2dF & src_offset,GLHelper::ScalerInterface * optional_scaler,const gfx::Rect & output_rect,GLuint y_plane_texture,GLuint u_plane_texture,GLuint v_plane_texture)592 void I420ConverterImpl::Convert(GLuint src_texture,
593                                 const gfx::Size& src_texture_size,
594                                 const gfx::Vector2dF& src_offset,
595                                 GLHelper::ScalerInterface* optional_scaler,
596                                 const gfx::Rect& output_rect,
597                                 GLuint y_plane_texture,
598                                 GLuint u_plane_texture,
599                                 GLuint v_plane_texture) {
600   const gfx::Size scaler_output_size =
601       optional_scaler ? output_rect.size() : gfx::Size();
602   const gfx::Size y_texture_size = GetYPlaneTextureSize(output_rect.size());
603   const gfx::Size chroma_texture_size =
604       GetChromaPlaneTextureSize(output_rect.size());
605   EnsureTexturesSizedFor(scaler_output_size, y_texture_size,
606                          chroma_texture_size, y_plane_texture, u_plane_texture,
607                          v_plane_texture);
608 
609   // Scale first, if needed.
610   if (optional_scaler) {
611     // The scaler should not be configured to do any swizzling.
612     DCHECK_EQ(optional_scaler->GetReadbackFormat(),
613               static_cast<GLenum>(GL_RGBA));
614     optional_scaler->Scale(src_texture, src_texture_size, src_offset,
615                            intermediate_->texture(), output_rect);
616   }
617 
618   // Convert the intermediate (or source) texture into Y, U and V planes.
619   const GLuint texture =
620       optional_scaler ? intermediate_->texture() : src_texture;
621   const gfx::Size texture_size =
622       optional_scaler ? intermediate_->size() : src_texture_size;
623   const gfx::Vector2dF offset = optional_scaler ? gfx::Vector2dF() : src_offset;
624   if (use_mrt()) {
625     y_planerizer_->ScaleToMultipleOutputs(texture, texture_size, offset,
626                                           y_plane_texture, uv_->id(),
627                                           gfx::Rect(y_texture_size));
628     u_planerizer_->ScaleToMultipleOutputs(
629         uv_->id(), y_texture_size, gfx::Vector2dF(), u_plane_texture,
630         v_plane_texture, gfx::Rect(chroma_texture_size));
631   } else {
632     y_planerizer_->Scale(texture, texture_size, offset, y_plane_texture,
633                          gfx::Rect(y_texture_size));
634     u_planerizer_->Scale(texture, texture_size, offset, u_plane_texture,
635                          gfx::Rect(chroma_texture_size));
636     v_planerizer_->Scale(texture, texture_size, offset, v_plane_texture,
637                          gfx::Rect(chroma_texture_size));
638   }
639 }
640 
IsSamplingFlippedSource() const641 bool I420ConverterImpl::IsSamplingFlippedSource() const {
642   return y_planerizer_->IsSamplingFlippedSource();
643 }
644 
IsFlippingOutput() const645 bool I420ConverterImpl::IsFlippingOutput() const {
646   return y_planerizer_->IsFlippingOutput();
647 }
648 
GetReadbackFormat() const649 GLenum I420ConverterImpl::GetReadbackFormat() const {
650   return y_planerizer_->GetReadbackFormat();
651 }
652 
EnsureTexturesSizedFor(const gfx::Size & scaler_output_size,const gfx::Size & y_texture_size,const gfx::Size & chroma_texture_size,GLuint y_plane_texture,GLuint u_plane_texture,GLuint v_plane_texture)653 void I420ConverterImpl::EnsureTexturesSizedFor(
654     const gfx::Size& scaler_output_size,
655     const gfx::Size& y_texture_size,
656     const gfx::Size& chroma_texture_size,
657     GLuint y_plane_texture,
658     GLuint u_plane_texture,
659     GLuint v_plane_texture) {
660   // Reallocate the intermediate texture, if needed.
661   if (!scaler_output_size.IsEmpty()) {
662     if (!intermediate_ || intermediate_->size() != scaler_output_size)
663       intermediate_.emplace(gl_, scaler_output_size);
664   } else {
665     intermediate_ = base::nullopt;
666   }
667 
668   // Size the interim UV plane and the three output planes.
669   const auto SetRGBATextureSize = [this](const gfx::Size& size) {
670     gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0,
671                     GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
672   };
673   if (use_mrt()) {
674     uv_.emplace(gl_);
675     gl_->BindTexture(GL_TEXTURE_2D, uv_->id());
676     SetRGBATextureSize(y_texture_size);
677   }
678   gl_->BindTexture(GL_TEXTURE_2D, y_plane_texture);
679   SetRGBATextureSize(y_texture_size);
680   gl_->BindTexture(GL_TEXTURE_2D, u_plane_texture);
681   SetRGBATextureSize(chroma_texture_size);
682   gl_->BindTexture(GL_TEXTURE_2D, v_plane_texture);
683   SetRGBATextureSize(chroma_texture_size);
684 }
685 
686 }  // namespace
687 
ReadbackYUVImpl(GLES2Interface * gl,CopyTextureToImpl * copy_impl,GLHelperScaling * scaler_impl,bool flip_vertically,ReadbackSwizzle swizzle,bool use_mrt)688 GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUVImpl(
689     GLES2Interface* gl,
690     CopyTextureToImpl* copy_impl,
691     GLHelperScaling* scaler_impl,
692     bool flip_vertically,
693     ReadbackSwizzle swizzle,
694     bool use_mrt)
695     : I420ConverterImpl(gl,
696                         scaler_impl,
697                         false,
698                         flip_vertically,
699                         swizzle == kSwizzleBGRA,
700                         use_mrt),
701       gl_(gl),
702       copy_impl_(copy_impl),
703       swizzle_(swizzle),
704       y_(gl_),
705       u_(gl_),
706       v_(gl_),
707       y_readback_framebuffer_(gl_),
708       u_readback_framebuffer_(gl_),
709       v_readback_framebuffer_(gl_) {}
710 
711 GLHelper::CopyTextureToImpl::ReadbackYUVImpl::~ReadbackYUVImpl() = default;
712 
SetScaler(std::unique_ptr<GLHelper::ScalerInterface> scaler)713 void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::SetScaler(
714     std::unique_ptr<GLHelper::ScalerInterface> scaler) {
715   scaler_ = std::move(scaler);
716 }
717 
718 GLHelper::ScalerInterface*
scaler() const719 GLHelper::CopyTextureToImpl::ReadbackYUVImpl::scaler() const {
720   return scaler_.get();
721 }
722 
IsFlippingOutput() const723 bool GLHelper::CopyTextureToImpl::ReadbackYUVImpl::IsFlippingOutput() const {
724   return I420ConverterImpl::IsFlippingOutput();
725 }
726 
ReadbackYUV(GLuint texture,const gfx::Size & src_texture_size,const gfx::Rect & output_rect,int y_plane_row_stride_bytes,unsigned char * y_plane_data,int u_plane_row_stride_bytes,unsigned char * u_plane_data,int v_plane_row_stride_bytes,unsigned char * v_plane_data,const gfx::Point & paste_location,base::OnceCallback<void (bool)> callback)727 void GLHelper::CopyTextureToImpl::ReadbackYUVImpl::ReadbackYUV(
728     GLuint texture,
729     const gfx::Size& src_texture_size,
730     const gfx::Rect& output_rect,
731     int y_plane_row_stride_bytes,
732     unsigned char* y_plane_data,
733     int u_plane_row_stride_bytes,
734     unsigned char* u_plane_data,
735     int v_plane_row_stride_bytes,
736     unsigned char* v_plane_data,
737     const gfx::Point& paste_location,
738     base::OnceCallback<void(bool)> callback) {
739   DCHECK(!(paste_location.x() & 1));
740   DCHECK(!(paste_location.y() & 1));
741 
742   I420ConverterImpl::Convert(texture, src_texture_size, gfx::Vector2dF(),
743                              scaler_.get(), output_rect, y_, u_, v_);
744 
745   // Read back planes, one at a time. Keep the video frame alive while doing
746   // the readback.
747   const gfx::Rect paste_rect(paste_location, output_rect.size());
748   const auto SetUpAndBindFramebuffer = [this](GLuint framebuffer,
749                                               GLuint texture) {
750     gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
751     gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
752                               GL_TEXTURE_2D, texture, 0);
753   };
754   SetUpAndBindFramebuffer(y_readback_framebuffer_, y_);
755   copy_impl_->ReadbackPlane(
756       GetYPlaneTextureSize(output_rect.size()), y_plane_row_stride_bytes,
757       y_plane_data, 0, paste_rect, swizzle_, base::DoNothing::Once<bool>());
758   SetUpAndBindFramebuffer(u_readback_framebuffer_, u_);
759   const gfx::Size chroma_texture_size =
760       GetChromaPlaneTextureSize(output_rect.size());
761   copy_impl_->ReadbackPlane(chroma_texture_size, u_plane_row_stride_bytes,
762                             u_plane_data, 1, paste_rect, swizzle_,
763                             base::DoNothing::Once<bool>());
764   SetUpAndBindFramebuffer(v_readback_framebuffer_, v_);
765   copy_impl_->ReadbackPlane(chroma_texture_size, v_plane_row_stride_bytes,
766                             v_plane_data, 1, paste_rect, swizzle_,
767                             std::move(callback));
768   gl_->BindFramebuffer(GL_FRAMEBUFFER, 0);
769 }
770 
CreateI420Converter(bool flipped_source,bool flip_output,bool swizzle,bool use_mrt)771 std::unique_ptr<I420Converter> GLHelper::CreateI420Converter(
772     bool flipped_source,
773     bool flip_output,
774     bool swizzle,
775     bool use_mrt) {
776   InitCopyTextToImpl();
777   InitScalerImpl();
778   return std::make_unique<I420ConverterImpl>(
779       gl_, scaler_impl_.get(), flipped_source, flip_output, swizzle,
780       use_mrt && (MaxDrawBuffers() >= 2));
781 }
782 
783 std::unique_ptr<ReadbackYUVInterface>
CreateReadbackPipelineYUV(bool flip_vertically,bool use_mrt)784 GLHelper::CopyTextureToImpl::CreateReadbackPipelineYUV(bool flip_vertically,
785                                                        bool use_mrt) {
786   helper_->InitScalerImpl();
787 
788   if (bgra_preference_ == BGRA_PREFERENCE_UNKNOWN) {
789     if (IsBGRAReadbackSupported()) {
790       // Test whether GL_BRGA_EXT is preferred for readback by creating a test
791       // texture, binding it to a framebuffer as a color attachment, and then
792       // querying the implementation for the framebuffer's readback format.
793       constexpr int kTestSize = 64;
794       GLuint texture = 0;
795       gl_->GenTextures(1, &texture);
796       gl_->BindTexture(GL_TEXTURE_2D, texture);
797       gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
798       gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
799       gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
800       gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
801       gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTestSize, kTestSize, 0,
802                       GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
803       GLuint framebuffer = 0;
804       gl_->GenFramebuffers(1, &framebuffer);
805       gl_->BindFramebuffer(GL_FRAMEBUFFER, framebuffer);
806       gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
807                                 GL_TEXTURE_2D, texture, 0);
808       GLint readback_format = 0;
809       GLint readback_type = 0;
810       gl_->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &readback_format);
811       gl_->GetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &readback_type);
812       if (readback_format == GL_BGRA_EXT && readback_type == GL_UNSIGNED_BYTE) {
813         bgra_preference_ = BGRA_PREFERRED;
814       } else {
815         bgra_preference_ = BGRA_NOT_PREFERRED;
816       }
817       if (framebuffer != 0)
818         gl_->DeleteFramebuffers(1, &framebuffer);
819       if (texture != 0)
820         gl_->DeleteTextures(1, &texture);
821     } else {
822       bgra_preference_ = BGRA_NOT_PREFERRED;
823     }
824   }
825 
826   const ReadbackSwizzle swizzle =
827       (bgra_preference_ == BGRA_PREFERRED) ? kSwizzleBGRA : kSwizzleNone;
828   return std::make_unique<ReadbackYUVImpl>(
829       gl_, this, helper_->scaler_impl_.get(), flip_vertically, swizzle,
830       use_mrt && (helper_->MaxDrawBuffers() >= 2));
831 }
832 
CreateReadbackPipelineYUV(bool flip_vertically,bool use_mrt)833 std::unique_ptr<ReadbackYUVInterface> GLHelper::CreateReadbackPipelineYUV(
834     bool flip_vertically,
835     bool use_mrt) {
836   InitCopyTextToImpl();
837   return copy_texture_to_impl_->CreateReadbackPipelineYUV(flip_vertically,
838                                                           use_mrt);
839 }
840 
GetReadbackPipelineYUV(bool vertically_flip_texture)841 ReadbackYUVInterface* GLHelper::GetReadbackPipelineYUV(
842     bool vertically_flip_texture) {
843   ReadbackYUVInterface* yuv_reader = nullptr;
844   if (vertically_flip_texture) {
845     if (!shared_readback_yuv_flip_) {
846       shared_readback_yuv_flip_ = CreateReadbackPipelineYUV(
847           vertically_flip_texture, true /* use_mrt */);
848     }
849     yuv_reader = shared_readback_yuv_flip_.get();
850   } else {
851     if (!shared_readback_yuv_noflip_) {
852       shared_readback_yuv_noflip_ = CreateReadbackPipelineYUV(
853           vertically_flip_texture, true /* use_mrt */);
854     }
855     yuv_reader = shared_readback_yuv_noflip_.get();
856   }
857   DCHECK(!yuv_reader->scaler());
858   return yuv_reader;
859 }
860 
861 }  // namespace gpu
862