| // Copyright 2018 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_SCALER_H_ |
| #define COMPONENTS_VIZ_COMMON_GL_SCALER_H_ |
| |
| #include <stdint.h> |
| |
| #include <map> |
| #include <memory> |
| #include <tuple> |
| #include <utility> |
| |
| #include "base/compiler_specific.h" |
| #include "base/macros.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "components/viz/common/gpu/context_lost_observer.h" |
| #include "components/viz/common/viz_common_export.h" |
| #include "gpu/command_buffer/client/gles2_interface.h" |
| #include "ui/gfx/color_space.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/rect_f.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/geometry/vector2d.h" |
| |
| namespace gfx { |
| class ColorTransform; |
| } // namespace gfx |
| |
| namespace viz { |
| |
| class ContextProvider; |
| |
| // A high-performance texture scaler for use with an OpenGL ES 2.0 context. It |
| // can be configured to operate at different quality levels, manages/converts |
| // color spaces, and optionally re-arranges/formats data in output textures for |
| // use with more-efficient texture readback pipelines. |
| class VIZ_COMMON_EXPORT GLScaler : public ContextLostObserver { |
| public: |
| struct VIZ_COMMON_EXPORT Parameters { |
| // Relative scale from/to factors. Both of these must be non-zero. |
| gfx::Vector2d scale_from = gfx::Vector2d(1, 1); |
| gfx::Vector2d scale_to = gfx::Vector2d(1, 1); |
| |
| // The color space of the source texture and the desired color space of the |
| // output texture. If |source_color_space| is not set (or invalid), sRGB is |
| // assumed. If |output_color_space| is not set (or invalid), the source |
| // color space is assumed. |
| gfx::ColorSpace source_color_space; |
| gfx::ColorSpace output_color_space; |
| |
| // Enable color management heuristics, using higher precision texture and |
| // gamma-aware scaling? |
| // |
| // When disabled, the gamma of the source color space and other concerns are |
| // ignored and 8-bit precision is used. |
| // |
| // When enabled, scaling occurs in a linear color space with 16-bit floats. |
| // This produces excellent results for virtually all color spaces while |
| // typically requiring twice the memory and execution resources. The caller |
| // must ensure the GL context supports the use of GL_RGBA16F format |
| // textures. |
| // |
| // Relevant reading: http://www.ericbrasseur.org/gamma.html |
| bool enable_precise_color_management = false; |
| |
| // Selects the trade-off between quality and speed. |
| enum class Quality : int8_t { |
| // Bilinear single pass. Fastest possible. Do not use this unless the GL |
| // implementation is so slow that the other quality options won't work. |
| FAST, |
| |
| // Bilinear upscale + N * 50% bilinear downscales. This is still fast |
| // enough for general-purpose use, and image quality is nearly as good as |
| // BEST when downscaling. |
| GOOD, |
| |
| // Bicubic upscale + N * 50% bicubic downscales. Produces very good |
| // quality scaled images, but it's 2-8x slower than the "GOOD" quality. |
| BEST, |
| } quality = Quality::GOOD; |
| |
| // Is the source texture Y-flipped (i.e., the origin is the lower-left |
| // corner and not the upper-left corner)? Most GL textures are Y-flipped. |
| // This information is required so that the scaler can correctly compute the |
| // sampling region. |
| bool is_flipped_source = true; |
| |
| // Should the output be vertically flipped? Usually, this is used when the |
| // source is not Y-flipped, but the destination texture needs to be. Or, it |
| // can be used to draw the final output upside-down to avoid having to copy |
| // the rows in reverse order after a glReadPixels(). |
| bool flip_output = false; |
| |
| // Optionally rearrange the image data for export. Generally, this is used |
| // to make later readback steps more efficient (e.g., using glReadPixels() |
| // will produce the raw bytes in their correct locations). |
| // |
| // Output textures are assumed to be using one of the 4-channel RGBA |
| // formats. While it may be more "proper" to use a single-component texture |
| // format for the planar-oriented image data, not all GL implementations |
| // support the use of those formats. However, all must support at least |
| // GL_RGBA. Therefore, each RGBA pixel is treated as a generic "vec4" (a |
| // quad of values). |
| // |
| // When using this feature, it is usually necessary to adjust the |
| // |output_rect| passed to Scale() or ScaleToMultipleOutputs(). See notes |
| // below. |
| enum class ExportFormat : int8_t { |
| // Do not rearrange the image data: |
| // |
| // (interleaved quads) (interleaved quads) |
| // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA |
| // RGBA RGBA RGBA RGBA --> RGBA RGBA RGBA RGBA |
| // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA |
| INTERLEAVED_QUADS, |
| |
| // Select one color channel, packing each of 4 pixels' values into the 4 |
| // elements of one output quad. |
| // |
| // For example, for CHANNEL_0: |
| // |
| // (interleaved quads) (channel 0) |
| // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA RRRR RRRR |
| // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA --> RRRR RRRR |
| // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA RRRR RRRR |
| // |
| // Note: Because of this packing, the horizontal coordinates of the |
| // |output_rect| used with Scale() should be divided by 4. |
| CHANNEL_0, |
| CHANNEL_1, |
| CHANNEL_2, |
| CHANNEL_3, |
| |
| // I422 sampling, delivered via two output textures (NV61 format): The |
| // first texture is produced the same as CHANNEL_0, while the second |
| // texture contains CHANNEL_1 and CHANNEL_2 at half-width interleaved and |
| // full-height. For example, if this is combined with RGB→YUV color space |
| // conversion: |
| // |
| // (interleaved quads) |
| // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA |
| // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA |
| // RGBA RGBA RGBA RGBA RGBA RGBA RGBA RGBA |
| // | |
| // | (luma plane) (chroma, interleaved) |
| // | YYYY YYYY UVUV UVUV |
| // +---> { YYYY YYYY + UVUV UVUV } |
| // YYYY YYYY UVUV UVUV |
| // |
| // Note: Because of this packing, the horizontal coordinates of the |
| // |output_rect| used with ScaleToMultipleOutputs() should be divided by |
| // 4. |
| // Note 2: This requires a GL context that supports multiple render |
| // targets with at least two draw buffers. |
| NV61, |
| |
| // Deinterleave into two output textures. |
| // |
| // UVUV UVUV UUUU VVVV |
| // UVUV UVUV --> { UUUU + VVVV } |
| // UVUV UVUV UUUU VVVV |
| // |
| // Note: Because of this packing, the horizontal coordinates of the |
| // |output_rect| used with ScaleToMultipleOutputs() should be divided by |
| // 2. |
| // Note 2: This requires a GL context that supports multiple render |
| // targets with at least two draw buffers. |
| DEINTERLEAVE_PAIRWISE, |
| } export_format = ExportFormat::INTERLEAVED_QUADS; |
| |
| // Optionally swizzle the ordering of the values in each output quad. If the |
| // output of the scaler is not going to be read back (e.g., used with |
| // glReadPixels()), simply leave these unchanged. Otherwise, changing this |
| // allows a read-back pipeline to use the native format of the platform to |
| // avoid having to perform extra "BGRA⇄RGBA swizzle" memcpy's. Usually, this |
| // should match the format to be used with glReadPixels(), and that should |
| // match the GL_IMPLEMENTATION_COLOR_READ_FORMAT. |
| GLenum swizzle[2] = { |
| GL_RGBA, // For |dest_texture_0|. |
| GL_RGBA, // For |dest_texture_1|. |
| }; |
| |
| Parameters(); |
| ~Parameters(); |
| }; |
| |
| explicit GLScaler(scoped_refptr<ContextProvider> context_provider); |
| |
| ~GLScaler() final; |
| |
| // Returns true if the GL context provides the necessary support for enabling |
| // precise color management (see Parameters::enable_precise_color_management). |
| bool SupportsPreciseColorManagement() const; |
| |
| // Returns the maximum number of simultaneous drawing buffers supported by the |
| // GL context. Certain Parameters can only be used when this is more than 1. |
| int GetMaxDrawBuffersSupported() const; |
| |
| // [Re]Configure the scaler with the given |new_params|. Returns true on |
| // success, or false on failure. |
| bool Configure(const Parameters& new_params) WARN_UNUSED_RESULT; |
| |
| // Returns the currently-configured and resolved Parameters. Note that these |
| // Parameters might not be exactly the same as those that were passed to |
| // Configure() because some properties (e.g., color spaces) are auto-resolved. |
| // Results are undefined if Configure() has never been called successfully. |
| const Parameters& params() const { return params_; } |
| |
| // Scales a portion of |src_texture| and draws the result into |dest_texture| |
| // at offset (0, 0). Returns true to indicate success, or false if this |
| // GLScaler is not valid. |
| // |
| // |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. |
| // |
| // |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. |
| bool Scale(GLuint src_texture, |
| const gfx::Size& src_texture_size, |
| const gfx::Vector2d& src_offset, |
| GLuint dest_texture, |
| const gfx::Rect& output_rect) WARN_UNUSED_RESULT { |
| return ScaleToMultipleOutputs(src_texture, src_texture_size, src_offset, |
| dest_texture, 0, output_rect); |
| } |
| |
| // Same as above, but for use cases where there are two output textures drawn |
| // (see Parameters::ExportFormat). |
| bool ScaleToMultipleOutputs(GLuint src_texture, |
| const gfx::Size& src_texture_size, |
| const gfx::Vector2d& src_offset, |
| GLuint dest_texture_0, |
| GLuint dest_texture_1, |
| const gfx::Rect& output_rect) WARN_UNUSED_RESULT; |
| |
| // Returns true if from:to represent the same scale ratio as that specified in |
| // |params|. |
| static bool ParametersHasSameScaleRatio(const Parameters& params, |
| const gfx::Vector2d& from, |
| const gfx::Vector2d& to); |
| |
| private: |
| friend class GLScalerOverscanPixelTest; |
| friend class GLScalerShaderPixelTest; |
| |
| using GLES2Interface = gpu::gles2::GLES2Interface; |
| |
| enum Axis { HORIZONTAL, VERTICAL }; |
| |
| // The shaders used by each stage in the scaling pipeline. |
| enum class Shader : int8_t { |
| BILINEAR, |
| BILINEAR2, |
| BILINEAR3, |
| BILINEAR4, |
| BILINEAR2X2, |
| BICUBIC_UPSCALE, |
| BICUBIC_HALF_1D, |
| PLANAR_CHANNEL_0, |
| PLANAR_CHANNEL_1, |
| PLANAR_CHANNEL_2, |
| PLANAR_CHANNEL_3, |
| I422_NV61_MRT, |
| DEINTERLEAVE_PAIRWISE_MRT, |
| }; |
| |
| // A cached, re-usable shader program that performs one step in the scaling |
| // pipeline. |
| class VIZ_COMMON_EXPORT ShaderProgram { |
| public: |
| ShaderProgram(GLES2Interface* gl, |
| Shader shader, |
| GLint texture_format, |
| const gfx::ColorTransform* color_transform, |
| const GLenum swizzle[2]); |
| ~ShaderProgram(); |
| |
| Shader shader() const { return shader_; } |
| GLint texture_format() const { return texture_format_; } |
| |
| // UseProgram must be called with GL_ARRAY_BUFFER bound to a vertex |
| // attribute buffer. |src_texture_size| is the size of the entire source |
| // texture, regardless of which region is to be sampled. |src_rect| is the |
| // source region, not including overscan pixels past the edges. |
| // |primary_axis| determines whether multiple texture samplings occur in one |
| // direction or the other (for some shaders). Note that this cannot |
| // necessarily be determined by just comparing the src and dst sizes. |
| // |flip_y| causes the |src_rect| to be scanned upside-down, to produce a |
| // vertically-flipped result. |
| void UseProgram(const gfx::Size& src_texture_size, |
| const gfx::RectF& src_rect, |
| const gfx::Size& dst_size, |
| Axis primary_axis, |
| bool flip_y); |
| |
| // GL_ARRAY_BUFFER data that must be bound when drawing with a |
| // ShaderProgram. These are the vertex attributes that will sweep the entire |
| // source area when executing the program. They represent triangle strip |
| // coordinates: The first two columns are (x,y) values interpolated to |
| // produce the vertex coordinates in object space, while the latter two |
| // columns are (s,t) values interpolated to produce the texture coordinates |
| // that correspond to the vertex coordinates. |
| static const GLfloat kVertexAttributes[16]; |
| |
| private: |
| GLES2Interface* const gl_; |
| const Shader shader_; |
| const GLint texture_format_; |
| |
| // A program for copying a source texture into a destination texture. |
| const GLuint program_; |
| |
| // The location of the position in the program. |
| GLint position_location_ = -1; |
| // The location of the texture coordinate in the program. |
| GLint texcoord_location_ = -1; |
| // The location of the source texture in the program. |
| GLint texture_location_ = -1; |
| // The location of the texture coordinate of the source rectangle in the |
| // program. |
| GLint src_rect_location_ = -1; |
| // Location of size of source image in pixels. |
| GLint src_pixelsize_location_ = -1; |
| // Location of vector for scaling ratio between source and dest textures. |
| GLint scaling_vector_location_ = -1; |
| |
| DISALLOW_COPY_AND_ASSIGN(ShaderProgram); |
| }; |
| |
| // One scaling stage in a chain of scaler pipeline stages. Each ScalerStage |
| // owns the previous ScalerStage in the chain: At execution time, a "working |
| // backwards" approach is used: The previous "input" stage renders an |
| // intermediate result that will be used as input for the current stage. |
| // |
| // Each ScalerStage caches textures and framebuffers to avoid reallocating |
| // them for each separate image scaling, which can be expensive on some |
| // platforms/drivers. |
| class VIZ_COMMON_EXPORT ScalerStage { |
| public: |
| ScalerStage(GLES2Interface* gl, |
| Shader shader, |
| Axis primary_axis, |
| const gfx::Vector2d& scale_from, |
| const gfx::Vector2d& scale_to); |
| ~ScalerStage(); |
| |
| Shader shader() const { return shader_; } |
| const gfx::Vector2d& scale_from() const { return scale_from_; } |
| const gfx::Vector2d& scale_to() const { return scale_to_; } |
| |
| ScalerStage* input_stage() const { return input_stage_.get(); } |
| void set_input_stage(std::unique_ptr<ScalerStage> stage) { |
| input_stage_ = std::move(stage); |
| } |
| |
| void set_shader_program(ShaderProgram* program) { program_ = program; } |
| |
| bool is_flipped_source() const { return is_flipped_source_; } |
| void set_is_flipped_source(bool flipped) { is_flipped_source_ = flipped; } |
| |
| bool flip_output() const { return flip_output_; } |
| void set_flip_output(bool flip) { flip_output_ = flip; } |
| |
| void ScaleToMultipleOutputs(GLuint src_texture, |
| gfx::Size src_texture_size, |
| const gfx::Vector2d& src_offset, |
| GLuint dest_texture_0, |
| GLuint dest_texture_1, |
| const gfx::Rect& output_rect); |
| |
| private: |
| friend class GLScalerOverscanPixelTest; |
| |
| // Returns the given |output_rect| mapped to the input stage's coordinate |
| // system. |
| gfx::RectF ToSourceRect(const gfx::Rect& output_rect) const; |
| |
| // Returns the given |source_rect| padded to include the overscan pixels the |
| // shader program will access. |
| gfx::Rect ToInputRect(gfx::RectF source_rect) const; |
| |
| // Generates the intermediate texture and/or re-defines it if its size has |
| // changed. |
| void EnsureIntermediateTextureDefined(const gfx::Size& size); |
| |
| GLES2Interface* const gl_; |
| const Shader shader_; |
| const Axis primary_axis_; |
| const gfx::Vector2d scale_from_; |
| const gfx::Vector2d scale_to_; |
| |
| std::unique_ptr<ScalerStage> input_stage_; |
| ShaderProgram* program_ = nullptr; |
| bool is_flipped_source_ = false; |
| bool flip_output_ = false; |
| |
| GLuint intermediate_texture_ = 0; |
| gfx::Size intermediate_texture_size_; |
| GLuint dest_framebuffer_ = 0; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScalerStage); |
| }; |
| |
| // ContextLostObserver implementation. |
| void OnContextLost() final; |
| |
| // Returns a cached ShaderProgram, creating one on-demand if necessary. |
| ShaderProgram* GetShaderProgram(Shader shader, |
| GLint texture_format, |
| const gfx::ColorTransform* color_transform, |
| const GLenum swizzle[2]); |
| |
| // The provider of the GL context. This is non-null while the GL context is |
| // valid and GLScaler is observing for context loss. |
| scoped_refptr<ContextProvider> context_provider_; |
| |
| // Set by Configure() to the resolved set of Parameters. |
| Parameters params_; |
| |
| // The maximum number of simultaneous draw buffers, lazy initialized by |
| // GetMaxDrawBuffersSupported(). -1 means "not yet known." |
| mutable int max_draw_buffers_ = -1; |
| |
| // Cache of ShaderPrograms. The cache key consists of fields that correspond |
| // to the arguments of GetShaderProgram(): the shader, the texture format, the |
| // source and output color spaces (color transform), and the two swizzles. |
| using ShaderCacheKey = std:: |
| tuple<Shader, GLint, gfx::ColorSpace, gfx::ColorSpace, GLenum, GLenum>; |
| std::map<ShaderCacheKey, ShaderProgram> shader_programs_; |
| |
| // The GL_ARRAY_BUFFER that holds the vertices and the texture coordinates |
| // data for sweeping the source area when a ScalerStage draws a quad (to |
| // execute its shader program). |
| GLuint vertex_attributes_buffer_ = 0; |
| |
| // The chain of ScalerStages. |
| std::unique_ptr<ScalerStage> chain_; |
| |
| DISALLOW_COPY_AND_ASSIGN(GLScaler); |
| }; |
| |
| } // namespace viz |
| |
| #endif // COMPONENTS_VIZ_COMMON_GL_SCALER_H_ |