blob: 30529bfb2f28460e717980657318b4852dbd0ecd [file] [log] [blame]
// 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 "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/vector2d.h"
namespace gfx {
class Vector2dF;
class Rect;
class Size;
} // 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::Vector2dF& 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::Vector2dF& src_offset,
GLuint dest_texture_0,
GLuint dest_texture_1,
const gfx::Rect& output_rect) WARN_UNUSED_RESULT;
// 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. Returns true to indicate success, or
// false if this GLScaler is not valid.
//
// 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.
bool 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
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:
using GLES2Interface = gpu::gles2::GLES2Interface;
// ContextLostObserver implementation.
void OnContextLost() final;
// 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;
DISALLOW_COPY_AND_ASSIGN(GLScaler);
};
} // namespace viz
#endif // COMPONENTS_VIZ_COMMON_GL_SCALER_H_