blob: 4c4ae200889d996f207c3b5f8e87e1289420fd14 [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.
#include "components/viz/common/gl_scaler.h"
#include <sstream>
#include <string>
#include <utility>
#include "base/logging.h"
#include "components/viz/common/gpu/context_provider.h"
#include "gpu/GLES2/gl2chromium.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/common/capabilities.h"
#include "ui/gfx/color_transform.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace viz {
GLScaler::GLScaler(scoped_refptr<ContextProvider> context_provider)
: context_provider_(std::move(context_provider)) {
if (context_provider_) {
DCHECK(context_provider_->ContextGL());
context_provider_->AddObserver(this);
}
}
GLScaler::~GLScaler() {
OnContextLost(); // Ensures destruction in dependency order.
}
bool GLScaler::SupportsPreciseColorManagement() const {
if (!context_provider_) {
return false;
}
const gpu::Capabilities& caps = context_provider_->ContextCapabilities();
return caps.texture_half_float_linear && caps.color_buffer_half_float_rgba;
}
int GLScaler::GetMaxDrawBuffersSupported() const {
if (!context_provider_) {
return 0;
}
if (max_draw_buffers_ < 0) {
// Query the GL context for the multiple draw buffers extension and, if
// present, the actual platform-supported maximum.
GLES2Interface* const gl = context_provider_->ContextGL();
DCHECK(gl);
if (const auto* extensions = gl->GetString(GL_EXTENSIONS)) {
const std::string extensions_string =
" " + std::string(reinterpret_cast<const char*>(extensions)) + " ";
if (extensions_string.find(" GL_EXT_draw_buffers ") !=
std::string::npos) {
gl->GetIntegerv(GL_MAX_DRAW_BUFFERS_EXT, &max_draw_buffers_);
}
}
if (max_draw_buffers_ < 1) {
max_draw_buffers_ = 1;
}
}
return max_draw_buffers_;
}
bool GLScaler::Configure(const Parameters& new_params) {
shader_programs_.clear();
if (!context_provider_) {
return false;
}
GLES2Interface* const gl = context_provider_->ContextGL();
DCHECK(gl);
params_ = new_params;
// Ensure the client has provided valid scaling vectors.
if (params_.scale_from.x() == 0 || params_.scale_from.y() == 0 ||
params_.scale_to.x() == 0 || params_.scale_to.y() == 0) {
// The caller computed invalid scale_from and/or scale_to values.
DVLOG(1) << __func__ << ": Invalid scaling vectors: scale_from="
<< params_.scale_from.ToString()
<< ", scale_to=" << params_.scale_to.ToString();
return false;
}
// Resolve the color spaces according to the rules described in the header
// file.
if (!params_.source_color_space.IsValid()) {
params_.source_color_space = gfx::ColorSpace::CreateSRGB();
}
if (!params_.output_color_space.IsValid()) {
params_.output_color_space = params_.source_color_space;
}
// Check that 16-bit half floats are supported if precise color management is
// being requested.
if (params_.enable_precise_color_management) {
if (!SupportsPreciseColorManagement()) {
DVLOG(1) << __func__
<< ": GL context does not support the half-floats "
"required for precise color management.";
return false;
}
}
// Check that MRT support is available if certain export formats were
// specified in the Parameters.
if (params_.export_format == Parameters::ExportFormat::NV61 ||
params_.export_format ==
Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE) {
if (GetMaxDrawBuffersSupported() < 2) {
DVLOG(1) << __func__ << ": GL context does not support 2+ draw buffers.";
return false;
}
}
// Color space transformation is meaningless when using the deinterleaver.
if (params_.export_format ==
Parameters::ExportFormat::DEINTERLEAVE_PAIRWISE &&
params_.source_color_space != params_.output_color_space) {
NOTIMPLEMENTED();
return false;
}
// Check that one of the two implemented output swizzles has been specified.
for (GLenum s : params_.swizzle) {
if (s != GL_RGBA && s != GL_BGRA_EXT) {
NOTIMPLEMENTED();
return false;
}
}
return true;
}
bool GLScaler::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) {
NOTIMPLEMENTED();
return false;
}
bool GLScaler::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 {
NOTIMPLEMENTED();
return false;
}
// static
bool GLScaler::ParametersHasSameScaleRatio(const GLScaler::Parameters& params,
const gfx::Vector2d& from,
const gfx::Vector2d& to) {
// Returns true iff a_num/a_denom == b_num/b_denom.
const auto AreRatiosEqual = [](int32_t a_num, int32_t a_denom, int32_t b_num,
int32_t b_denom) -> bool {
// The math (for each dimension):
// If: a_num/a_denom == b_num/b_denom
// Then: a_num*b_denom == b_num*a_denom
//
// ...and cast to int64_t to guarantee no overflow from the multiplications.
return (static_cast<int64_t>(a_num) * b_denom) ==
(static_cast<int64_t>(b_num) * a_denom);
};
return AreRatiosEqual(params.scale_from.x(), params.scale_to.x(), from.x(),
to.x()) &&
AreRatiosEqual(params.scale_from.y(), params.scale_to.y(), from.y(),
to.y());
}
void GLScaler::OnContextLost() {
// The destruction order here is important due to data dependencies.
shader_programs_.clear();
if (context_provider_) {
context_provider_->RemoveObserver(this);
context_provider_ = nullptr;
}
}
GLScaler::ShaderProgram* GLScaler::GetShaderProgram(
Shader shader,
GLint texture_format,
const gfx::ColorTransform* color_transform,
const GLenum swizzle[2]) {
const ShaderCacheKey key{
shader,
texture_format,
color_transform ? color_transform->GetSrcColorSpace() : gfx::ColorSpace(),
color_transform ? color_transform->GetDstColorSpace() : gfx::ColorSpace(),
swizzle[0],
swizzle[1]};
auto it = shader_programs_.find(key);
if (it == shader_programs_.end()) {
GLES2Interface* const gl = context_provider_->ContextGL();
DCHECK(gl);
it = shader_programs_
.emplace(std::piecewise_construct, std::forward_as_tuple(key),
std::forward_as_tuple(gl, shader, texture_format,
color_transform, swizzle))
.first;
}
return &it->second;
}
GLScaler::Parameters::Parameters() = default;
GLScaler::Parameters::~Parameters() = default;
// static
const GLfloat GLScaler::ShaderProgram::kVertexAttributes[16] = {
-1.0f, -1.0f, 0.0f, 0.0f, // vertex 0
1.0f, -1.0f, 1.0f, 0.0f, // vertex 1
-1.0f, 1.0f, 0.0f, 1.0f, // vertex 2
1.0f, 1.0f, 1.0f, 1.0f, // vertex 3
};
GLScaler::ShaderProgram::ShaderProgram(
gpu::gles2::GLES2Interface* gl,
GLScaler::Shader shader,
GLint texture_format,
const gfx::ColorTransform* color_transform,
const GLenum swizzle[2])
: gl_(gl),
shader_(shader),
texture_format_(texture_format),
program_(gl_->CreateProgram()) {
DCHECK(program_);
std::basic_ostringstream<GLchar> vertex_header;
std::basic_ostringstream<GLchar> fragment_directives;
std::basic_ostringstream<GLchar> fragment_header;
std::basic_ostringstream<GLchar> shared_variables;
std::basic_ostringstream<GLchar> vertex_main;
std::basic_ostringstream<GLchar> fragment_main;
vertex_header
<< ("precision highp float;\n"
"attribute vec2 a_position;\n"
"attribute vec2 a_texcoord;\n"
"uniform vec4 src_rect;\n");
fragment_header << "precision mediump float;\n";
if (texture_format_ == GL_RGBA16F_EXT) {
fragment_header << "precision mediump sampler2D;\n";
} else if (texture_format_ == GL_RGBA) {
fragment_header << "precision lowp sampler2D;\n";
} else {
NOTIMPLEMENTED();
}
fragment_header << "uniform sampler2D s_texture;\n";
if (color_transform && shader_ != Shader::PLANAR_CHANNEL_3) {
const std::string& source = color_transform->GetShaderSource();
// Assumption: gfx::ColorTransform::GetShaderSource() should provide a
// function named DoColorConversion() that takes a vec3 argument and returns
// a vec3.
DCHECK_NE(source.find("DoColorConversion"), std::string::npos);
fragment_header << source;
}
vertex_main
<< (" gl_Position = vec4(a_position, 0.0, 1.0);\n"
" vec2 texcoord = src_rect.xy + a_texcoord * src_rect.zw;\n");
switch (shader_) {
case Shader::BILINEAR:
shared_variables << "varying highp vec2 v_texcoord;\n";
vertex_main << " v_texcoord = texcoord;\n";
fragment_main << " vec4 sample = texture2D(s_texture, v_texcoord);\n";
if (color_transform) {
fragment_main << " sample.rgb = DoColorConversion(sample.rgb);\n";
}
if (swizzle[0] == GL_BGRA_EXT) {
fragment_main << " sample.rb = sample.br;\n";
}
fragment_main << " gl_FragColor = sample;\n";
break;
case Shader::BILINEAR2:
// This is equivialent to two passes of the BILINEAR shader above. It can
// be used to scale an image down 1.0x-2.0x in either dimension, or
// exactly 4x.
shared_variables << "varying highp vec4 v_texcoords;\n";
vertex_header << "uniform vec2 scaling_vector;\n";
vertex_main
<< (" vec2 step = scaling_vector / 4.0;\n"
" v_texcoords.xy = texcoord + step;\n"
" v_texcoords.zw = texcoord - step;\n");
fragment_main
<< (" vec4 blended = (texture2D(s_texture, v_texcoords.xy) +\n"
" texture2D(s_texture, v_texcoords.zw)) /\n"
" 2.0;\n");
if (color_transform) {
fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
}
if (swizzle[0] == GL_BGRA_EXT) {
fragment_main << " blended.rb = blended.br;\n";
}
fragment_main << " gl_FragColor = blended;\n";
break;
case Shader::BILINEAR3:
// This is kind of like doing 1.5 passes of the BILINEAR shader. It can be
// used to scale an image down 1.5x-3.0x, or exactly 6x.
shared_variables
<< ("varying highp vec4 v_texcoords0;\n"
"varying highp vec2 v_texcoords1;\n");
vertex_header << "uniform vec2 scaling_vector;\n";
vertex_main
<< (" vec2 step = scaling_vector / 3.0;\n"
" v_texcoords0.xy = texcoord + step;\n"
" v_texcoords0.zw = texcoord;\n"
" v_texcoords1 = texcoord - step;\n");
fragment_main
<< (" vec4 blended = (texture2D(s_texture, v_texcoords0.xy) +\n"
" texture2D(s_texture, v_texcoords0.zw) +\n"
" texture2D(s_texture, v_texcoords1)) / 3.0;\n");
if (color_transform) {
fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
}
if (swizzle[0] == GL_BGRA_EXT) {
fragment_main << " blended.rb = blended.br;\n";
}
fragment_main << " gl_FragColor = blended;\n";
break;
case Shader::BILINEAR4:
// This is equivialent to three passes of the BILINEAR shader above. It
// can be used to scale an image down 2.0x-4.0x or exactly 8x.
shared_variables << "varying highp vec4 v_texcoords[2];\n";
vertex_header << "uniform vec2 scaling_vector;\n";
vertex_main
<< (" vec2 step = scaling_vector / 8.0;\n"
" v_texcoords[0].xy = texcoord - step * 3.0;\n"
" v_texcoords[0].zw = texcoord - step;\n"
" v_texcoords[1].xy = texcoord + step;\n"
" v_texcoords[1].zw = texcoord + step * 3.0;\n");
fragment_main
<< (" vec4 blended = (\n"
" texture2D(s_texture, v_texcoords[0].xy) +\n"
" texture2D(s_texture, v_texcoords[0].zw) +\n"
" texture2D(s_texture, v_texcoords[1].xy) +\n"
" texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
if (color_transform) {
fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
}
if (swizzle[0] == GL_BGRA_EXT) {
fragment_main << " blended.rb = blended.br;\n";
}
fragment_main << " gl_FragColor = blended;\n";
break;
case Shader::BILINEAR2X2:
// This is equivialent to four passes of the BILINEAR shader above, two in
// each dimension. It can be used to scale an image down 1.0x-2.0x in both
// X and Y directions. Or, it could be used to scale an image down by
// exactly 4x in both dimensions.
shared_variables << "varying highp vec4 v_texcoords[2];\n";
vertex_header << "uniform vec2 scaling_vector;\n";
vertex_main
<< (" vec2 step = scaling_vector / 4.0;\n"
" v_texcoords[0].xy = texcoord + vec2(step.x, step.y);\n"
" v_texcoords[0].zw = texcoord + vec2(step.x, -step.y);\n"
" v_texcoords[1].xy = texcoord + vec2(-step.x, step.y);\n"
" v_texcoords[1].zw = texcoord + vec2(-step.x, -step.y);\n");
fragment_main
<< (" vec4 blended = (\n"
" texture2D(s_texture, v_texcoords[0].xy) +\n"
" texture2D(s_texture, v_texcoords[0].zw) +\n"
" texture2D(s_texture, v_texcoords[1].xy) +\n"
" texture2D(s_texture, v_texcoords[1].zw)) / 4.0;\n");
if (color_transform) {
fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
}
if (swizzle[0] == GL_BGRA_EXT) {
fragment_main << " blended.rb = blended.br;\n";
}
fragment_main << " gl_FragColor = blended;\n";
break;
case Shader::BICUBIC_UPSCALE:
// When scaling up, 4 texture reads are necessary. However, some
// instructions can be saved because the parameter passed to the bicubic
// function will be in a known range. Also, when sampling the bicubic
// function like this, the sum is always exactly one, so normalization can
// be skipped as well.
shared_variables << "varying highp vec2 v_texcoord;\n";
vertex_main << " v_texcoord = texcoord;\n";
fragment_header
<< ("uniform highp vec2 src_pixelsize;\n"
"uniform highp vec2 scaling_vector;\n"
"const float a = -0.5;\n"
// This function is equivialent to calling the bicubic
// function with x-1, x, 1-x and 2-x (assuming
// 0 <= x < 1)
"vec4 filt4(float x) {\n"
" return vec4(x * x * x, x * x, x, 1) *\n"
" mat4( a, -2.0 * a, a, 0.0,\n"
" a + 2.0, -a - 3.0, 0.0, 1.0,\n"
" -a - 2.0, 3.0 + 2.0 * a, -a, 0.0,\n"
" -a, a, 0.0, 0.0);\n"
"}\n"
"mat4 pixels_x(highp vec2 pos, highp vec2 step) {\n"
" return mat4(texture2D(s_texture, pos - step),\n"
" texture2D(s_texture, pos),\n"
" texture2D(s_texture, pos + step),\n"
" texture2D(s_texture, pos + step * 2.0));\n"
"}\n");
fragment_main
<< (" highp vec2 pixel_pos = v_texcoord * src_pixelsize - \n"
" scaling_vector / 2.0;\n"
" highp float frac = fract(dot(pixel_pos, scaling_vector));\n"
" highp vec2 base = \n"
" (floor(pixel_pos) + vec2(0.5)) / src_pixelsize;\n"
" highp vec2 step = scaling_vector / src_pixelsize;\n"
" vec4 blended = pixels_x(base, step) * filt4(frac);\n");
if (color_transform) {
fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
}
if (swizzle[0] == GL_BGRA_EXT) {
fragment_main << " blended.rb = blended.br;\n";
}
fragment_main << " gl_FragColor = blended;\n";
break;
case Shader::BICUBIC_HALF_1D:
// This scales down an image by exactly half in one dimension. The
// bilinear lookup reduces the number of texture reads from 8 to 4.
shared_variables << "varying highp vec4 v_texcoords[2];\n";
vertex_header
<< ("uniform vec2 scaling_vector;\n"
"const float CenterDist = 99.0 / 140.0;\n"
"const float LobeDist = 11.0 / 4.0;\n");
vertex_main
<< (" vec2 step = scaling_vector / 2.0;\n"
" v_texcoords[0].xy = texcoord - LobeDist * step;\n"
" v_texcoords[0].zw = texcoord - CenterDist * step;\n"
" v_texcoords[1].xy = texcoord + CenterDist * step;\n"
" v_texcoords[1].zw = texcoord + LobeDist * step;\n");
fragment_header
<< ("const float CenterWeight = 35.0 / 64.0;\n"
"const float LobeWeight = -3.0 / 64.0;\n");
fragment_main
<< (" vec4 blended = \n"
// Lobe pixels
" (texture2D(s_texture, v_texcoords[0].xy) +\n"
" texture2D(s_texture, v_texcoords[1].zw)) *\n"
" LobeWeight +\n"
// Center pixels
" (texture2D(s_texture, v_texcoords[0].zw) +\n"
" texture2D(s_texture, v_texcoords[1].xy)) *\n"
" CenterWeight;\n");
if (color_transform) {
fragment_main << " blended.rgb = DoColorConversion(blended.rgb);\n";
}
if (swizzle[0] == GL_BGRA_EXT) {
fragment_main << " blended.rb = blended.br;\n";
}
fragment_main << " gl_FragColor = blended;\n";
break;
case Shader::PLANAR_CHANNEL_0:
case Shader::PLANAR_CHANNEL_1:
case Shader::PLANAR_CHANNEL_2:
case Shader::PLANAR_CHANNEL_3: {
// Select one color channel, and pack 4 pixels into one output quad. See
// header file for diagram.
shared_variables << "varying highp vec4 v_texcoords[2];\n";
vertex_header << "uniform vec2 scaling_vector;\n";
vertex_main
<< (" vec2 step = scaling_vector / 4.0;\n"
" v_texcoords[0].xy = texcoord - step * 1.5;\n"
" v_texcoords[0].zw = texcoord - step * 0.5;\n"
" v_texcoords[1].xy = texcoord + step * 0.5;\n"
" v_texcoords[1].zw = texcoord + step * 1.5;\n");
std::basic_string<GLchar> convert_open;
std::basic_string<GLchar> convert_close;
if (color_transform && shader_ != Shader::PLANAR_CHANNEL_3) {
convert_open = "DoColorConversion(";
convert_close = ".rgb)";
}
const char selector = "rgba"[static_cast<int>(shader_) -
static_cast<int>(Shader::PLANAR_CHANNEL_0)];
fragment_main << " vec4 packed_quad = vec4(\n"
<< " " << convert_open
<< "texture2D(s_texture, v_texcoords[0].xy)"
<< convert_close << '.' << selector << ",\n"
<< " " << convert_open
<< "texture2D(s_texture, v_texcoords[0].zw)"
<< convert_close << '.' << selector << ",\n"
<< " " << convert_open
<< "texture2D(s_texture, v_texcoords[1].xy)"
<< convert_close << '.' << selector << ",\n"
<< " " << convert_open
<< "texture2D(s_texture, v_texcoords[1].zw)"
<< convert_close << '.' << selector << ");\n";
if (swizzle[0] == GL_BGRA_EXT) {
fragment_main << " packed_quad.rb = packed_quad.br;\n";
}
fragment_main << " gl_FragColor = packed_quad;\n";
break;
}
case Shader::I422_NV61_MRT:
// I422 sampling, delivered via two output textures (NV61 format). See
// header file for diagram.
shared_variables << "varying highp vec4 v_texcoords[2];\n";
vertex_header << "uniform vec2 scaling_vector;\n";
vertex_main
<< (" vec2 step = scaling_vector / 4.0;\n"
" v_texcoords[0].xy = texcoord - step * 1.5;\n"
" v_texcoords[0].zw = texcoord - step * 0.5;\n"
" v_texcoords[1].xy = texcoord + step * 0.5;\n"
" v_texcoords[1].zw = texcoord + step * 1.5;\n");
fragment_directives << "#extension GL_EXT_draw_buffers : enable\n";
fragment_main
<< (" vec3 pixel0 = texture2D(s_texture, v_texcoords[0].xy).rgb;\n"
" vec3 pixel1 = texture2D(s_texture, v_texcoords[0].zw).rgb;\n"
" vec3 pixel01 = (pixel0 + pixel1) / 2.0;\n"
" vec3 pixel2 = texture2D(s_texture, v_texcoords[1].xy).rgb;\n"
" vec3 pixel3 = texture2D(s_texture, v_texcoords[1].zw).rgb;\n"
" vec3 pixel23 = (pixel2 + pixel3) / 2.0;\n");
if (color_transform) {
fragment_main
<< (" pixel0 = DoColorConversion(pixel0);\n"
" pixel1 = DoColorConversion(pixel1);\n"
" pixel01 = DoColorConversion(pixel01);\n"
" pixel2 = DoColorConversion(pixel2);\n"
" pixel3 = DoColorConversion(pixel3);\n"
" pixel23 = DoColorConversion(pixel23);\n");
}
if (swizzle[0] == GL_BGRA_EXT) {
fragment_main
<< (" gl_FragData[0] = \n"
" vec4(pixel2.r, pixel1.r, pixel0.r, pixel3.r);\n");
} else {
fragment_main
<< (" gl_FragData[0] = \n"
" vec4(pixel0.r, pixel1.r, pixel2.r, pixel3.r);\n");
}
if (swizzle[1] == GL_BGRA_EXT) {
fragment_main
<< (" gl_FragData[1] = \n"
" vec4(pixel23.g, pixel01.b, pixel01.g, pixel23.b);\n");
} else {
fragment_main
<< (" gl_FragData[1] = \n"
" vec4(pixel01.g, pixel01.b, pixel23.g, pixel23.b);\n");
}
break;
case Shader::DEINTERLEAVE_PAIRWISE_MRT:
// Sample two pixels and unswizzle them. There's no need to do vertical
// scaling with math, since the bilinear interpolation in the sampler
// takes care of that.
shared_variables << "varying highp vec4 v_texcoords;\n";
vertex_header << "uniform vec2 scaling_vector;\n";
vertex_main
<< (" vec2 step = scaling_vector / 2.0;\n"
" v_texcoords.xy = texcoord - step * 0.5;\n"
" v_texcoords.zw = texcoord + step * 0.5;\n");
fragment_directives << "#extension GL_EXT_draw_buffers : enable\n";
DCHECK(!color_transform);
fragment_main
<< (" vec4 lo_uvuv = texture2D(s_texture, v_texcoords.xy);\n"
" vec4 hi_uvuv = texture2D(s_texture, v_texcoords.zw);\n"
" vec4 uuuu = vec4(lo_uvuv.rb, hi_uvuv.rb);\n"
" vec4 vvvv = vec4(lo_uvuv.ga, hi_uvuv.ga);\n");
if (swizzle[0] == GL_BGRA_EXT) {
fragment_main << " uuuu.rb = uuuu.br;\n";
}
fragment_main << " gl_FragData[0] = uuuu;\n";
if (swizzle[1] == GL_BGRA_EXT) {
fragment_main << " vvvv.rb = vvvv.br;\n";
}
fragment_main << " gl_FragData[1] = vvvv;\n";
break;
}
// Helper function to compile the shader source and log the GLSL compiler's
// results.
const auto CompileShaderFromSource =
[](GLES2Interface* gl, const std::basic_string<GLchar>& source,
GLenum type) -> GLuint {
VLOG(2) << __func__ << ": Compiling shader " << type
<< " with source:" << std::endl
<< source;
const GLuint shader = gl->CreateShader(type);
const GLchar* source_data = source.data();
const GLint length = base::checked_cast<GLint>(source.size());
gl->ShaderSource(shader, 1, &source_data, &length);
gl->CompileShader(shader);
GLint compile_status = GL_FALSE;
gl->GetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
// Fetch logs and forward them to the system logger. If compilation failed,
// clean-up and return 0 for error.
if (compile_status != GL_TRUE || VLOG_IS_ON(2)) {
GLint log_length = 0;
gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
std::string log;
if (log_length > 0) {
std::unique_ptr<GLchar[]> tmp(new GLchar[log_length]);
GLsizei returned_log_length = 0;
gl->GetShaderInfoLog(shader, log_length, &returned_log_length,
tmp.get());
log.assign(tmp.get(), returned_log_length);
}
if (log.empty()) {
log = "<<NO LOG>>";
}
if (compile_status != GL_TRUE) {
LOG(ERROR) << __func__ << ": Compilation of shader " << type
<< " failed:" << std::endl
<< log;
gl->DeleteShader(shader);
return 0;
}
VLOG(2) << __func__ << ": Compilation of shader " << type
<< " succeeded:" << std::endl
<< log;
}
return shader;
};
// Compile the vertex shader and attach it to the program.
const std::string shared_variables_str = shared_variables.str();
const GLuint vertex_shader =
CompileShaderFromSource(gl_,
vertex_header.str() + shared_variables_str +
"void main() {\n" + vertex_main.str() + "}\n",
GL_VERTEX_SHADER);
if (vertex_shader == 0) {
return;
}
gl_->AttachShader(program_, vertex_shader);
gl_->DeleteShader(vertex_shader);
// Compile the fragment shader and attach to |program_|.
const GLuint fragment_shader = CompileShaderFromSource(
gl_,
fragment_directives.str() + fragment_header.str() + shared_variables_str +
"void main() {\n" + fragment_main.str() + "}\n",
GL_FRAGMENT_SHADER);
if (fragment_shader == 0) {
return;
}
gl_->AttachShader(program_, fragment_shader);
gl_->DeleteShader(fragment_shader);
// Link the program.
gl_->LinkProgram(program_);
GLint link_status = GL_FALSE;
gl_->GetProgramiv(program_, GL_LINK_STATUS, &link_status);
if (link_status != GL_TRUE) {
LOG(ERROR) << "Failed to link shader program.";
return;
}
#define DCHECK_RESOLVED_LOCATION(member) \
DCHECK(member != -1 || gl_->GetGraphicsResetStatusKHR() != GL_NO_ERROR) \
<< "Failed to get " #member " in program, or GPU process crashed."
// Resolve the locations of the global variables.
position_location_ = gl_->GetAttribLocation(program_, "a_position");
DCHECK_RESOLVED_LOCATION(position_location_);
texcoord_location_ = gl_->GetAttribLocation(program_, "a_texcoord");
DCHECK_RESOLVED_LOCATION(texcoord_location_);
texture_location_ = gl_->GetUniformLocation(program_, "s_texture");
DCHECK_RESOLVED_LOCATION(texture_location_);
src_rect_location_ = gl_->GetUniformLocation(program_, "src_rect");
DCHECK_RESOLVED_LOCATION(src_rect_location_);
switch (shader_) {
case Shader::BILINEAR:
break;
case Shader::BILINEAR2:
case Shader::BILINEAR3:
case Shader::BILINEAR4:
case Shader::BILINEAR2X2:
case Shader::BICUBIC_HALF_1D:
case Shader::PLANAR_CHANNEL_0:
case Shader::PLANAR_CHANNEL_1:
case Shader::PLANAR_CHANNEL_2:
case Shader::PLANAR_CHANNEL_3:
case Shader::I422_NV61_MRT:
case Shader::DEINTERLEAVE_PAIRWISE_MRT:
scaling_vector_location_ =
gl_->GetUniformLocation(program_, "scaling_vector");
DCHECK_RESOLVED_LOCATION(scaling_vector_location_);
break;
case Shader::BICUBIC_UPSCALE:
src_pixelsize_location_ =
gl_->GetUniformLocation(program_, "src_pixelsize");
DCHECK_RESOLVED_LOCATION(src_pixelsize_location_);
scaling_vector_location_ =
gl_->GetUniformLocation(program_, "scaling_vector");
DCHECK_RESOLVED_LOCATION(scaling_vector_location_);
break;
}
#undef DCHECK_RESOLVED_LOCATION
}
GLScaler::ShaderProgram::~ShaderProgram() {
gl_->DeleteProgram(program_);
}
void GLScaler::ShaderProgram::UseProgram(const gfx::Size& src_texture_size,
const gfx::RectF& src_rect,
const gfx::Size& dst_size,
GLScaler::Axis primary_axis,
bool flip_y) {
gl_->UseProgram(program_);
// OpenGL defines the last parameter to VertexAttribPointer as type
// "const GLvoid*" even though it is actually an offset into the buffer
// object's data store and not a pointer to the client's address space.
const void* offsets[2] = {nullptr,
reinterpret_cast<const void*>(2 * sizeof(GLfloat))};
gl_->VertexAttribPointer(position_location_, 2, GL_FLOAT, GL_FALSE,
4 * sizeof(GLfloat), offsets[0]);
gl_->EnableVertexAttribArray(position_location_);
gl_->VertexAttribPointer(texcoord_location_, 2, GL_FLOAT, GL_FALSE,
4 * sizeof(GLfloat), offsets[1]);
gl_->EnableVertexAttribArray(texcoord_location_);
// Always sample from the first texture unit.
gl_->Uniform1i(texture_location_, 0);
// Convert |src_rect| from pixel coordinates to texture coordinates. The
// source texture coordinates are in the range [0.0,1.0] for each dimension,
// but the sampling rect may slightly "spill" outside that range (e.g., for
// scaler overscan).
GLfloat src_rect_texcoord[4] = {
src_rect.x() / src_texture_size.width(),
src_rect.y() / src_texture_size.height(),
src_rect.width() / src_texture_size.width(),
src_rect.height() / src_texture_size.height(),
};
if (flip_y) {
src_rect_texcoord[1] += src_rect_texcoord[3];
src_rect_texcoord[3] *= -1.0f;
}
gl_->Uniform4fv(src_rect_location_, 1, src_rect_texcoord);
// Set shader-specific uniform inputs. The |scaling_vector| is the ratio of
// the number of source pixels sampled per dest pixels output. It is used by
// the shader programs to locate distinct texels from the source texture, and
// sample them at the appropriate offset to produce each output texel.
switch (shader_) {
case Shader::BILINEAR:
break;
case Shader::BILINEAR2:
case Shader::BILINEAR3:
case Shader::BILINEAR4:
case Shader::BICUBIC_HALF_1D:
case Shader::PLANAR_CHANNEL_0:
case Shader::PLANAR_CHANNEL_1:
case Shader::PLANAR_CHANNEL_2:
case Shader::PLANAR_CHANNEL_3:
case Shader::I422_NV61_MRT:
case Shader::DEINTERLEAVE_PAIRWISE_MRT:
switch (primary_axis) {
case HORIZONTAL:
gl_->Uniform2f(scaling_vector_location_,
src_rect_texcoord[2] / dst_size.width(), 0.0);
break;
case VERTICAL:
gl_->Uniform2f(scaling_vector_location_, 0.0,
src_rect_texcoord[3] / dst_size.height());
break;
}
break;
case Shader::BILINEAR2X2:
gl_->Uniform2f(scaling_vector_location_,
src_rect_texcoord[2] / dst_size.width(),
src_rect_texcoord[3] / dst_size.height());
break;
case Shader::BICUBIC_UPSCALE:
gl_->Uniform2f(src_pixelsize_location_, src_texture_size.width(),
src_texture_size.height());
// For this shader program, the |scaling_vector| has an alternate meaning:
// It is only being used to select whether bicubic sampling is stepped in
// the X or the Y direction.
gl_->Uniform2f(scaling_vector_location_,
primary_axis == HORIZONTAL ? 1.0 : 0.0,
primary_axis == VERTICAL ? 1.0 : 0.0);
break;
}
}
} // namespace viz