| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef COMPONENTS_VIZ_COMMON_GL_HELPER_H_ |
| #define COMPONENTS_VIZ_COMMON_GL_HELPER_H_ |
| |
| #include <memory> |
| |
| #include "base/callback.h" |
| #include "base/macros.h" |
| #include "components/viz/common/viz_common_export.h" |
| #include "gpu/command_buffer/client/gles2_interface.h" |
| #include "gpu/command_buffer/common/mailbox_holder.h" |
| #include "third_party/skia/include/core/SkImageInfo.h" |
| #include "ui/gfx/geometry/size.h" |
| |
| namespace gfx { |
| class Point; |
| class Rect; |
| class Vector2d; |
| class Vector2dF; |
| } // namespace gfx |
| |
| namespace gpu { |
| class ContextSupport; |
| struct Mailbox; |
| } // namespace gpu |
| |
| namespace viz { |
| |
| class GLHelperScaling; |
| |
| class ScopedGLuint { |
| public: |
| typedef void (gpu::gles2::GLES2Interface::*GenFunc)(GLsizei n, GLuint* ids); |
| typedef void (gpu::gles2::GLES2Interface::*DeleteFunc)(GLsizei n, |
| const GLuint* ids); |
| ScopedGLuint(gpu::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() { |
| if (id_ != 0) { |
| (gl_->*delete_func_)(1, &id_); |
| } |
| } |
| |
| private: |
| gpu::gles2::GLES2Interface* gl_; |
| GLuint id_; |
| DeleteFunc delete_func_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScopedGLuint); |
| }; |
| |
| class ScopedBuffer : public ScopedGLuint { |
| public: |
| explicit ScopedBuffer(gpu::gles2::GLES2Interface* gl) |
| : ScopedGLuint(gl, |
| &gpu::gles2::GLES2Interface::GenBuffers, |
| &gpu::gles2::GLES2Interface::DeleteBuffers) {} |
| }; |
| |
| class ScopedFramebuffer : public ScopedGLuint { |
| public: |
| explicit ScopedFramebuffer(gpu::gles2::GLES2Interface* gl) |
| : ScopedGLuint(gl, |
| &gpu::gles2::GLES2Interface::GenFramebuffers, |
| &gpu::gles2::GLES2Interface::DeleteFramebuffers) {} |
| }; |
| |
| class ScopedTexture : public ScopedGLuint { |
| public: |
| explicit ScopedTexture(gpu::gles2::GLES2Interface* gl) |
| : ScopedGLuint(gl, |
| &gpu::gles2::GLES2Interface::GenTextures, |
| &gpu::gles2::GLES2Interface::DeleteTextures) {} |
| }; |
| |
| template <GLenum Target> |
| class ScopedBinder { |
| public: |
| typedef void (gpu::gles2::GLES2Interface::*BindFunc)(GLenum target, |
| GLuint id); |
| ScopedBinder(gpu::gles2::GLES2Interface* gl, GLuint id, BindFunc bind_func) |
| : gl_(gl), bind_func_(bind_func) { |
| (gl_->*bind_func_)(Target, id); |
| } |
| |
| virtual ~ScopedBinder() { (gl_->*bind_func_)(Target, 0); } |
| |
| private: |
| gpu::gles2::GLES2Interface* gl_; |
| BindFunc bind_func_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScopedBinder); |
| }; |
| |
| template <GLenum Target> |
| class ScopedBufferBinder : ScopedBinder<Target> { |
| public: |
| ScopedBufferBinder(gpu::gles2::GLES2Interface* gl, GLuint id) |
| : ScopedBinder<Target>(gl, id, &gpu::gles2::GLES2Interface::BindBuffer) {} |
| }; |
| |
| template <GLenum Target> |
| class ScopedFramebufferBinder : ScopedBinder<Target> { |
| public: |
| ScopedFramebufferBinder(gpu::gles2::GLES2Interface* gl, GLuint id) |
| : ScopedBinder<Target>(gl, |
| id, |
| &gpu::gles2::GLES2Interface::BindFramebuffer) {} |
| }; |
| |
| template <GLenum Target> |
| class ScopedTextureBinder : ScopedBinder<Target> { |
| public: |
| ScopedTextureBinder(gpu::gles2::GLES2Interface* gl, GLuint id) |
| : ScopedBinder<Target>(gl, id, &gpu::gles2::GLES2Interface::BindTexture) { |
| } |
| }; |
| |
| class I420Converter; |
| class ReadbackYUVInterface; |
| |
| // Provides higher level operations on top of the gpu::gles2::GLES2Interface |
| // interfaces. |
| // |
| // TODO(crbug.com/870036): DEPRECATED. Please contact the crbug owner before |
| // adding any new dependencies on this code. |
| class VIZ_COMMON_EXPORT GLHelper { |
| public: |
| GLHelper(gpu::gles2::GLES2Interface* gl, |
| gpu::ContextSupport* context_support); |
| ~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|. |dst_size| is the |
| // size of the texture. 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/870036): DEPRECATED. This will be moved to be closer to its |
| // one caller soon. |
| void ReadbackTextureAsync(GLuint texture, |
| const gfx::Size& dst_size, |
| unsigned char* out, |
| SkColorType color_type, |
| base::OnceCallback<void(bool)> callback); |
| |
| // Creates a mailbox holder that is attached to the given texture id, with a |
| // sync point to wait on before using the mailbox. Returns a holder with an |
| // empty mailbox on failure. |
| // Note the texture is assumed to be GL_TEXTURE_2D. |
| // |
| // TODO(crbug.com/870036): DEPRECATED. This will be moved to be closer to its |
| // one caller soon. |
| gpu::MailboxHolder ProduceMailboxHolderFromTexture(GLuint texture_id); |
| |
| // Creates a texture and consumes a mailbox into it. Returns 0 on failure. |
| // Note the mailbox is assumed to be GL_TEXTURE_2D. |
| // |
| // TODO(crbug.com/870036): DEPRECATED. This will be moved to be closer to its |
| // one caller soon. |
| GLuint ConsumeMailboxToTexture(const gpu::Mailbox& mailbox, |
| const gpu::SyncToken& sync_token); |
| |
| // Caches all intermediate textures and programs needed to scale any subset of |
| // a source texture at a fixed scaling ratio. |
| class ScalerInterface { |
| public: |
| 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/775740): 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() {} |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(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/870036): 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/870036): 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 NULL. |
| void InitCopyTextToImpl(); |
| // Creates |scaler_impl_| if NULL. |
| void InitScalerImpl(); |
| |
| enum ReadbackSwizzle { kSwizzleNone = 0, kSwizzleBGRA }; |
| |
| gpu::gles2::GLES2Interface* gl_; |
| gpu::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; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(GLHelper); |
| }; |
| |
| // 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 VIZ_COMMON_EXPORT I420Converter { |
| public: |
| I420Converter(); |
| 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); |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(I420Converter); |
| }; |
| |
| // 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/870036): DEPRECATED. This will be removed soon, in favor of |
| // I420Converter and readback implementation in GLRendererCopier. |
| class VIZ_COMMON_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. Sync-wait and then consume and take ownership of the source texture |
| // provided by |mailbox|. |
| // 2. Scale the source texture to an intermediate texture. |
| // 3. Planarize, producing textures containing the Y, U, and V planes. |
| // 4. 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()). |
| // 5. Run |callback| with true on success, false on failure (with no output |
| // modified). |
| virtual void ReadbackYUV(const gpu::Mailbox& mailbox, |
| const gpu::SyncToken& sync_token, |
| 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 viz |
| |
| #endif // COMPONENTS_VIZ_COMMON_GL_HELPER_H_ |