1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306
  307
  308
  309
  310
  311
  312
  313
  314
  315
  316
  317
  318
  319
  320
  321
  322
  323
  324
  325
  326
  327
  328
  329
  330
  331
  332
  333
  334
  335
  336
  337
  338
  339
  340
  341
  342
  343
  344
  345
  346
  347
  348
  349
  350
  351
  352
  353
  354
  355
  356
  357
  358
  359
  360
  361
  362
  363
  364
  365
  366
  367
  368
  369
  370
  371
  372
  373
  374
  375
  376
  377
  378
  379
  380
  381
  382
  383
  384
  385
  386
  387
  388
  389
  390
  391
  392
  393
  394
  395
  396
  397
  398
  399
  400
  401
  402
  403
  404
  405
  406
  407
  408
  409
  410
  411
  412
  413
  414
  415
  416
  417
  418
  419
  420
  421
  422
  423
  424
  425
  426
  427
  428
  429
  430
  431
  432
  433
  434
  435
  436
  437
  438
  439
  440
  441
  442
  443
  444
  445
  446
  447
  448
  449
  450
  451
  452
  453
  454
  455
  456
  457
  458
  459
  460
  461
  462
  463
  464
  465
  466
  467
  468
  469
  470
  471
  472

gpu / command_buffer / client / gl_helper.h [blame]

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef GPU_COMMAND_BUFFER_CLIENT_GL_HELPER_H_
#define GPU_COMMAND_BUFFER_CLIENT_GL_HELPER_H_

#include <memory>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/gpu_export.h"
#include "ui/gfx/geometry/size.h"

namespace gfx {
class Point;
class Rect;
class Vector2d;
class Vector2dF;
}  // namespace gfx

namespace gpu {

class ContextSupport;
class GLHelperScaling;

class ScopedGLuint {
 public:
  typedef void (gles2::GLES2Interface::*GenFunc)(GLsizei n, GLuint* ids);
  typedef void (gles2::GLES2Interface::*DeleteFunc)(GLsizei n,
                                                    const GLuint* ids);
  ScopedGLuint(gles2::GLES2Interface* gl,
               GenFunc gen_func,
               DeleteFunc delete_func)
      : gl_(gl), id_(0u), delete_func_(delete_func) {
    (gl_->*gen_func)(1, &id_);
  }

  operator GLuint() const { return id_; }

  GLuint id() const { return id_; }

  ScopedGLuint(const ScopedGLuint&) = delete;
  ScopedGLuint& operator=(const ScopedGLuint&) = delete;

  ~ScopedGLuint() {
    if (id_ != 0) {
      (gl_->*delete_func_)(1, &id_);
    }
  }

 private:
  raw_ptr<gles2::GLES2Interface> gl_;
  GLuint id_;
  DeleteFunc delete_func_;
};

class ScopedBuffer : public ScopedGLuint {
 public:
  explicit ScopedBuffer(gles2::GLES2Interface* gl)
      : ScopedGLuint(gl,
                     &gles2::GLES2Interface::GenBuffers,
                     &gles2::GLES2Interface::DeleteBuffers) {}
};

class ScopedFramebuffer : public ScopedGLuint {
 public:
  explicit ScopedFramebuffer(gles2::GLES2Interface* gl)
      : ScopedGLuint(gl,
                     &gles2::GLES2Interface::GenFramebuffers,
                     &gles2::GLES2Interface::DeleteFramebuffers) {}
};

class ScopedTexture : public ScopedGLuint {
 public:
  explicit ScopedTexture(gles2::GLES2Interface* gl)
      : ScopedGLuint(gl,
                     &gles2::GLES2Interface::GenTextures,
                     &gles2::GLES2Interface::DeleteTextures) {}
};

template <GLenum Target>
class ScopedBinder {
 public:
  typedef void (gles2::GLES2Interface::*BindFunc)(GLenum target, GLuint id);
  ScopedBinder(gles2::GLES2Interface* gl, GLuint id, BindFunc bind_func)
      : gl_(gl), bind_func_(bind_func) {
    (gl_->*bind_func_)(Target, id);
  }

  ScopedBinder(const ScopedBinder&) = delete;
  ScopedBinder& operator=(const ScopedBinder&) = delete;

  virtual ~ScopedBinder() { (gl_->*bind_func_)(Target, 0); }

 private:
  raw_ptr<gles2::GLES2Interface> gl_;
  BindFunc bind_func_;
};

template <GLenum Target>
class ScopedBufferBinder : ScopedBinder<Target> {
 public:
  ScopedBufferBinder(gles2::GLES2Interface* gl, GLuint id)
      : ScopedBinder<Target>(gl, id, &gles2::GLES2Interface::BindBuffer) {}
};

template <GLenum Target>
class ScopedFramebufferBinder : ScopedBinder<Target> {
 public:
  ScopedFramebufferBinder(gles2::GLES2Interface* gl, GLuint id)
      : ScopedBinder<Target>(gl, id, &gles2::GLES2Interface::BindFramebuffer) {}
};

template <GLenum Target>
class ScopedTextureBinder : ScopedBinder<Target> {
 public:
  ScopedTextureBinder(gles2::GLES2Interface* gl, GLuint id)
      : ScopedBinder<Target>(gl, id, &gles2::GLES2Interface::BindTexture) {}
};

class I420Converter;
class ReadbackYUVInterface;

// Provides higher level operations on top of the gles2::GLES2Interface
// interfaces.
//
// TODO(crbug.com/41405483): DEPRECATED. Please contact the crbug owner before
// adding any new dependencies on this code.
class GPU_EXPORT GLHelper {
 public:
  GLHelper(gles2::GLES2Interface* gl, ContextSupport* context_support);

  GLHelper(const GLHelper&) = delete;
  GLHelper& operator=(const GLHelper&) = delete;

  ~GLHelper();

  enum ScalerQuality {
    // Bilinear single pass, fastest possible.
    SCALER_QUALITY_FAST = 1,

    // Bilinear upscale + N * 50% bilinear downscales.
    // This is still fast enough for most purposes and
    // Image quality is nearly as good as the BEST option.
    SCALER_QUALITY_GOOD = 2,

    // Bicubic upscale + N * 50% bicubic downscales.
    // Produces very good quality scaled images, but it's
    // 2-8x slower than the "GOOD" quality, so it's not always
    // worth it.
    SCALER_QUALITY_BEST = 3,
  };

  // Copies the texture data out of |texture| into |out|.
  // |src_starting_point| an origin point of the rectangle fragment of the
  // texture to copy, |dst_size| - size of the rectangle to copy.
  // No post processing is applied to the pixels.  The
  // texture is assumed to have a format of GL_RGBA or GL_BGRA_EXT with a pixel
  // type of GL_UNSIGNED_BYTE.
  //
  // TODO(crbug.com/41405483): DEPRECATED. This will be moved to be closer to
  // its one caller soon.
  void ReadbackTextureAsync(GLuint texture,
                            GLenum texture_target,
                            const gfx::Point& src_starting_point,
                            const gfx::Size& dst_size,
                            unsigned char* out,
                            size_t row_stride_bytes,
                            bool flip_y,
                            GLenum format,
                            base::OnceCallback<void(bool)> callback);

  // Caches all intermediate textures and programs needed to scale any subset of
  // a source texture at a fixed scaling ratio.
  class ScalerInterface {
   public:
    ScalerInterface(const ScalerInterface&) = delete;
    ScalerInterface& operator=(const ScalerInterface&) = delete;

    virtual ~ScalerInterface() {}

    // Scales a portion of |src_texture| and draws the result into
    // |dest_texture| at offset (0, 0).
    //
    // |src_texture_size| is the full, allocated size of the |src_texture|. This
    // is required for computing texture coordinate transforms (and only because
    // the OpenGL ES 2.0 API lacks the ability to query this info).
    //
    // |src_offset| is the offset in the source texture corresponding to point
    // (0,0) in the source/output coordinate spaces. This prevents the need for
    // extra texture copies just to re-position the source coordinate system.
    // TODO(crbug.com/41350322): This must be set to whole-numbered values for
    // now, until the implementation is modified to handle fractional offsets.
    //
    // |output_rect| selects the region to draw (in the scaled, not the source,
    // coordinate space). This is used to save work in cases where only a
    // portion needs to be re-scaled. The implementation will back-compute,
    // internally, to determine the region of the |src_texture| to sample.
    //
    // WARNING: The output will always be placed at (0, 0) in the
    // |dest_texture|, and not at |output_rect.origin()|.
    //
    // Note that the src_texture will have the min/mag filter set to GL_LINEAR
    // and wrap_s/t set to CLAMP_TO_EDGE in this call.
    void Scale(GLuint src_texture,
               const gfx::Size& src_texture_size,
               const gfx::Vector2dF& src_offset,
               GLuint dest_texture,
               const gfx::Rect& output_rect) {
      ScaleToMultipleOutputs(src_texture, src_texture_size, src_offset,
                             dest_texture, 0, output_rect);
    }

    // Same as above, but for shaders that output to two textures at once.
    virtual void ScaleToMultipleOutputs(GLuint src_texture,
                                        const gfx::Size& src_texture_size,
                                        const gfx::Vector2dF& src_offset,
                                        GLuint dest_texture_0,
                                        GLuint dest_texture_1,
                                        const gfx::Rect& output_rect) = 0;

    // Given the |src_texture_size|, |src_offset| and |output_rect| arguments
    // that would be passed to Scale(), compute the region of pixels in the
    // source texture that would be sampled to produce a scaled result. The
    // result is stored in |sampling_rect|, along with the |offset| to the (0,0)
    // point relative to |sampling_rect|'s origin.
    //
    // This is used by clients that need to know the minimal portion of a source
    // buffer that must be copied without affecting Scale()'s results. This
    // method also accounts for vertical flipping.
    virtual void ComputeRegionOfInfluence(const gfx::Size& src_texture_size,
                                          const gfx::Vector2dF& src_offset,
                                          const gfx::Rect& output_rect,
                                          gfx::Rect* sampling_rect,
                                          gfx::Vector2dF* offset) const = 0;

    // Returns true if from:to represent the same scale ratio as that provided
    // by this scaler.
    virtual bool IsSameScaleRatio(const gfx::Vector2d& from,
                                  const gfx::Vector2d& to) const = 0;

    // Returns true if the scaler is assuming the source texture's content is
    // vertically flipped.
    virtual bool IsSamplingFlippedSource() const = 0;

    // Returns true if the scaler will vertically-flip the output. Note that if
    // both this method and IsSamplingFlippedSource() return true, then the
    // scaler output will be right-side up.
    virtual bool IsFlippingOutput() const = 0;

    // Returns the format to use when calling glReadPixels() to read-back the
    // output texture(s). This indicates whether the 0th and 2nd bytes in each
    // RGBA quad have been swapped. If no swapping has occurred, this will
    // return GL_RGBA. Otherwise, it will return GL_BGRA_EXT.
    virtual GLenum GetReadbackFormat() const = 0;

   protected:
    ScalerInterface() {}
  };

  // Create a scaler that upscales or downscales at the given ratio
  // (scale_from:scale_to). Returns null on invalid arguments.
  //
  // If |flipped_source| is true, then the scaler will assume the content of the
  // source texture is vertically-flipped. This is required so that the scaler
  // can correctly compute the sampling region.
  //
  // If |flip_output| is true, then the scaler will vertically-flip its output
  // result. This is used when the output texture will be read-back into system
  // memory, so that the rows do not have to be copied in reverse.
  //
  // If |swizzle| is true, the 0th and 2nd elements in each RGBA quad will be
  // swapped. This is beneficial for optimizing read-back into system memory.
  //
  // WARNING: The returned scaler assumes both this GLHelper and its
  // GLES2Interface/ContextSupport will outlive it!
  std::unique_ptr<ScalerInterface> CreateScaler(ScalerQuality quality,
                                                const gfx::Vector2d& scale_from,
                                                const gfx::Vector2d& scale_to,
                                                bool flipped_source,
                                                bool flip_output,
                                                bool swizzle);

  // Create a pipeline that will (optionally) scale a source texture, and then
  // convert it to I420 (YUV) planar form, delivering results in three separate
  // output textures (one for each plane; see I420Converter::Convert()).
  //
  // Due to limitations in the OpenGL ES 2.0 API, the output textures will have
  // a format of GL_RGBA. However, each RGBA "pixel" in these textures actually
  // carries 4 consecutive pixels for the single-color-channel result plane.
  // Therefore, when using the OpenGL APIs to read-back the image into system
  // memory, note that a width 1/4 the actual |output_rect.width()| must be
  // used.
  //
  // |flipped_source|, |flip_output|, and |swizzle| have the same meaning as
  // that explained in the method comments for CreateScaler().
  //
  // If |use_mrt| is true, the pipeline will try to optimize the YUV conversion
  // using the multi-render-target extension, if the platform is capable.
  // |use_mrt| should only be set to false for testing.
  //
  // The benefit of using this pipeline is seen when these output textures are
  // read back from GPU to CPU memory: The I420 format reduces the amount of
  // data read back by a factor of ~2.6 (32bpp → 12bpp) which can greatly
  // improve performance, for things like video screen capture, on platforms
  // with slow GPU read-back performance.
  //
  // WARNING: The returned I420Converter instance assumes both this GLHelper and
  // its GLES2Interface/ContextSupport will outlive it!
  std::unique_ptr<I420Converter> CreateI420Converter(bool flipped_source,
                                                     bool flip_output,
                                                     bool swizzle,
                                                     bool use_mrt);

  // Create a readback pipeline that will (optionally) scale a source texture,
  // then convert it to YUV420 planar form, and finally read back that. This
  // reduces the amount of memory read from GPU to CPU memory by a factor of 2.6
  // (32bpp → 12bpp), which can be quite handy since readbacks have very limited
  // speed on some platforms.
  //
  // If |use_mrt| is true, the pipeline will try to optimize the YUV conversion
  // using the multi-render-target extension, if the platform is capable.
  // |use_mrt| should only be set to false for testing.
  //
  // WARNING: The returned ReadbackYUVInterface instance assumes both this
  // GLHelper and its GLES2Interface/ContextSupport will outlive it!
  //
  // TODO(crbug.com/41405483): DEPRECATED. This will be removed soon, in favor
  // of CreateI420Converter().
  std::unique_ptr<ReadbackYUVInterface> CreateReadbackPipelineYUV(
      bool vertically_flip_texture,
      bool use_mrt);

  // Returns a ReadbackYUVInterface instance that is lazily created and owned by
  // this class. |use_mrt| is always true for these instances.
  //
  // TODO(crbug.com/41405483): DEPRECATED. This will be moved to be closer to
  // its one caller soon.
  ReadbackYUVInterface* GetReadbackPipelineYUV(bool vertically_flip_texture);

  // Returns the maximum number of draw buffers available,
  // 0 if GL_EXT_draw_buffers is not available.
  GLint MaxDrawBuffers();

 protected:
  class CopyTextureToImpl;

  // Creates |copy_texture_to_impl_| if nullptr.
  void InitCopyTextToImpl();
  // Creates |scaler_impl_| if nullptr.
  void InitScalerImpl();

  enum ReadbackSwizzle { kSwizzleNone = 0, kSwizzleBGRA };

  raw_ptr<gles2::GLES2Interface> gl_;
  raw_ptr<ContextSupport> context_support_;
  std::unique_ptr<CopyTextureToImpl> copy_texture_to_impl_;
  std::unique_ptr<GLHelperScaling> scaler_impl_;
  std::unique_ptr<ReadbackYUVInterface> shared_readback_yuv_flip_;
  std::unique_ptr<ReadbackYUVInterface> shared_readback_yuv_noflip_;

  // Memoized result for MaxDrawBuffers(), if >= 0. Otherwise, MaxDrawBuffers()
  // will need to query the GL implementation.
  GLint max_draw_buffers_ = -1;
};

// Splits an RGBA source texture's image into separate Y, U, and V planes. The U
// and V planes are half-width and half-height, according to the I420 standard.
class GPU_EXPORT I420Converter {
 public:
  I420Converter();

  I420Converter(const I420Converter&) = delete;
  I420Converter& operator=(const I420Converter&) = delete;

  virtual ~I420Converter();

  // Transforms a RGBA |src_texture| into three textures, each containing bytes
  // in I420 planar form. See the GLHelper::ScalerInterface::Scale() method
  // comments for the meaning/semantics of |src_texture_size|, |src_offset| and
  // |output_rect|. If |optional_scaler| is not null, it will first be used to
  // scale the source texture into an intermediate texture before generating the
  // Y+U+V planes.
  //
  // See notes for CreateI420Converter() regarding the semantics of the output
  // textures.
  virtual void 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) = 0;

  // Returns true if the converter is assuming the source texture's content is
  // vertically flipped.
  virtual bool IsSamplingFlippedSource() const = 0;

  // Returns true if the converter will vertically-flip the output.
  virtual bool IsFlippingOutput() const = 0;

  // Returns the format to use when calling glReadPixels() to read-back the
  // output textures. This indicates whether the 0th and 2nd bytes in each RGBA
  // quad have been swapped. If no swapping has occurred, this will return
  // GL_RGBA. Otherwise, it will return GL_BGRA_EXT.
  virtual GLenum GetReadbackFormat() const = 0;

  // Returns the texture size of the Y plane texture, based on the size of the
  // |output_rect| that was given to Convert(). This will have a width of
  // CEIL(output_rect_size.width() / 4), and the same height.
  static gfx::Size GetYPlaneTextureSize(const gfx::Size& output_rect_size);

  // Like GetYPlaneTextureSize(), except the returned size will have a width of
  // CEIL(output_rect_size.width() / 8), and a height of
  // CEIL(output_rect_size.height() / 2); because the chroma planes are half-
  // length in both dimensions in the I420 format.
  static gfx::Size GetChromaPlaneTextureSize(const gfx::Size& output_rect_size);
};

// Similar to a ScalerInterface, a YUV readback pipeline will cache a scaler and
// all intermediate textures and frame buffers needed to scale, crop, letterbox
// and read back a texture from the GPU into CPU-accessible RAM. A single
// readback pipeline can handle multiple outstanding readbacks at the same time.
//
// TODO(crbug.com/41405483): DEPRECATED. This will be removed soon in favor of
// I420Converter.
class GPU_EXPORT ReadbackYUVInterface {
 public:
  ReadbackYUVInterface() {}
  virtual ~ReadbackYUVInterface() {}

  // Optional behavior: This sets a scaler to use to scale the inputs before
  // planarizing. If null (or never called), then no scaling is performed.
  virtual void SetScaler(std::unique_ptr<GLHelper::ScalerInterface> scaler) = 0;

  // Returns the currently-set scaler, or null.
  virtual GLHelper::ScalerInterface* scaler() const = 0;

  // Returns true if the converter will vertically-flip the output.
  virtual bool IsFlippingOutput() const = 0;

  // Transforms a RGBA texture into I420 planar form, and then reads it back
  // from the GPU into system memory. See the GLHelper::ScalerInterface::Scale()
  // method comments for the meaning/semantics of |src_texture_size| and
  // |output_rect|. The process is:
  //
  //   1. Scale the source texture to an intermediate texture.
  //   2. Planarize, producing textures containing the Y, U, and V planes.
  //   3. Read-back the planar data, copying it into the given output
  //      destination. |paste_location| specifies the where to place the output
  //      pixels: Rect(paste_location.origin(), output_rect.size()).
  //   4. Run |callback| with true on success, false on failure (with no output
  //      modified).
  virtual void 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) = 0;
};

}  // namespace gpu

#endif  // GPU_COMMAND_BUFFER_CLIENT_GL_HELPER_H_