#include "gpu/command_buffer/service/gles2_cmd_srgb_converter.h"
#include "gpu/command_buffer/service/shader_manager.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "ui/gl/gl_version_info.h"
namespace gpu {
namespace gles2 {
SRGBConverter::SRGBConverter(
const gles2::FeatureInfo* feature_info)
: feature_info_(feature_info) {
}
SRGBConverter::~SRGBConverter() = default;
void SRGBConverter::InitializeSRGBConverterProgram() {
if (srgb_converter_program_) {
return;
}
srgb_converter_program_ = glCreateProgram();
const char* kShaderPreamble =
"#ifdef GL_ES\n"
"precision mediump float;\n"
"#define TexCoordPrecision mediump\n"
"#else\n"
"#define TexCoordPrecision\n"
"#endif\n";
std::string vs_source;
if (feature_info_->gl_version_info().is_es) {
if (feature_info_->gl_version_info().is_es3) {
vs_source += "#version 300 es\n";
vs_source +=
"#define ATTRIBUTE in\n"
"#define VARYING out\n";
} else {
vs_source +=
"#define ATTRIBUTE attribute\n"
"#define VARYING varying\n";
}
} else {
vs_source += "#version 150\n";
vs_source +=
"#define ATTRIBUTE in\n"
"#define VARYING out\n";
}
vs_source += kShaderPreamble;
vs_source +=
"VARYING TexCoordPrecision vec2 v_texcoord;\n"
"\n"
"void main()\n"
"{\n"
" const vec2 quad_positions[6] = vec2[6]\n"
" (\n"
" vec2(0.0f, 0.0f),\n"
" vec2(0.0f, 1.0f),\n"
" vec2(1.0f, 0.0f),\n"
"\n"
" vec2(0.0f, 1.0f),\n"
" vec2(1.0f, 0.0f),\n"
" vec2(1.0f, 1.0f)\n"
" );\n"
"\n"
" vec2 xy = vec2((quad_positions[gl_VertexID] * 2.0) - 1.0);\n"
" gl_Position = vec4(xy, 0.0, 1.0);\n"
" v_texcoord = quad_positions[gl_VertexID];\n"
"}\n";
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
CompileShaderWithLog(vs, vs_source.c_str());
glAttachShader(srgb_converter_program_, vs);
glDeleteShader(vs);
std::string fs_source;
if (feature_info_->gl_version_info().is_es) {
if (feature_info_->gl_version_info().is_es3) {
fs_source += "#version 300 es\n";
}
} else {
fs_source += "#version 150\n";
}
fs_source += kShaderPreamble;
if (feature_info_->gl_version_info().is_es) {
if (feature_info_->gl_version_info().is_es3) {
fs_source +=
"#define VARYING in\n"
"out vec4 frag_color;\n"
"#define FRAGCOLOR frag_color\n"
"#define TextureLookup texture\n";
} else {
fs_source +=
"#define VARYING varying\n"
"#define FRAGCOLOR gl_FragColor\n"
"#define TextureLookup texture2D\n";
}
} else {
fs_source +=
"#define VARYING in\n"
"out vec4 frag_color;\n"
"#define FRAGCOLOR frag_color\n"
"#define TextureLookup texture\n";
}
fs_source +=
"uniform mediump sampler2D u_source_texture;\n"
"VARYING TexCoordPrecision vec2 v_texcoord;\n"
"\n"
"void main()\n"
"{\n"
" vec4 c = TextureLookup(u_source_texture, v_texcoord);\n"
" FRAGCOLOR = c;\n"
"}\n";
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
CompileShaderWithLog(fs, fs_source.c_str());
glAttachShader(srgb_converter_program_, fs);
glDeleteShader(fs);
glLinkProgram(srgb_converter_program_);
#ifndef NDEBUG
GLint linked = 0;
glGetProgramiv(srgb_converter_program_, GL_LINK_STATUS, &linked);
if (!linked) {
DLOG(ERROR) << "BlitFramebuffer: program link failure.";
}
#endif
GLuint texture_uniform =
glGetUniformLocation(srgb_converter_program_, "u_source_texture");
glUseProgram(srgb_converter_program_);
glUniform1i(texture_uniform, 0);
}
void SRGBConverter::InitializeSRGBConverter(
const gles2::GLES2Decoder* decoder) {
if (srgb_converter_initialized_) {
return;
}
InitializeSRGBConverterProgram();
glGenTextures(
srgb_converter_textures_.size(), srgb_converter_textures_.data());
glActiveTexture(GL_TEXTURE0);
for (auto srgb_converter_texture : srgb_converter_textures_) {
glBindTexture(GL_TEXTURE_2D, srgb_converter_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
glGenFramebuffersEXT(1, &srgb_decoder_fbo_);
glGenFramebuffersEXT(1, &srgb_encoder_fbo_);
glGenVertexArraysOES(1, &srgb_converter_vao_);
decoder->RestoreTextureUnitBindings(0);
decoder->RestoreActiveTexture();
decoder->RestoreProgramBindings();
srgb_converter_initialized_ = true;
}
void SRGBConverter::Destroy() {
if (srgb_converter_initialized_) {
glDeleteTextures(srgb_converter_textures_.size(),
srgb_converter_textures_.data());
srgb_converter_textures_.fill(0);
glDeleteFramebuffersEXT(1, &srgb_decoder_fbo_);
srgb_decoder_fbo_ = 0;
glDeleteFramebuffersEXT(1, &srgb_encoder_fbo_);
srgb_encoder_fbo_ = 0;
glDeleteVertexArraysOES(1, &srgb_converter_vao_);
srgb_converter_vao_ = 0;
glDeleteProgram(srgb_converter_program_);
srgb_converter_program_ = 0;
srgb_converter_initialized_ = false;
}
}
void SRGBConverter::Blit(
const gles2::GLES2Decoder* decoder,
GLint srcX0,
GLint srcY0,
GLint srcX1,
GLint srcY1,
GLint dstX0,
GLint dstY0,
GLint dstX1,
GLint dstY1,
GLbitfield mask,
GLenum filter,
const gfx::Size& framebuffer_size,
GLuint src_framebuffer,
GLenum src_framebuffer_internal_format,
GLenum src_framebuffer_format,
GLenum src_framebuffer_type,
GLuint dst_framebuffer,
bool decode,
bool encode,
bool enable_scissor_test) {
DCHECK(srgb_converter_initialized_);
DCHECK(!feature_info_->gl_version_info().is_es);
glActiveTexture(GL_TEXTURE0);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_CULL_FACE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_FALSE);
glDisable(GL_BLEND);
glDisable(GL_DITHER);
if (decoder->GetFeatureInfo()->feature_flags().ext_window_rectangles) {
glWindowRectanglesEXT(GL_EXCLUSIVE_EXT, 0, nullptr);
}
GLuint width_read = 0, height_read = 0, xoffset = 0, yoffset = 0;
if (decode) {
glBindFramebufferEXT(GL_FRAMEBUFFER, src_framebuffer);
glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[0]);
GLuint x = srcX1 > srcX0 ? srcX0 : srcX1;
GLuint y = srcY1 > srcY0 ? srcY0 : srcY1;
width_read = srcX1 > srcX0 ? srcX1 - srcX0 : srcX0 - srcX1;
height_read = srcY1 > srcY0 ? srcY1 - srcY0 : srcY0 - srcY1;
gfx::Rect c(0, 0, framebuffer_size.width(), framebuffer_size.height());
c.Intersect(gfx::Rect(x, y, width_read, height_read));
xoffset = c.x() - x;
yoffset = c.y() - y;
glCopyTexImage2D(GL_TEXTURE_2D, 0, src_framebuffer_internal_format,
c.x(), c.y(), c.width(), c.height(), 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, c.width(), c.height(), 0,
GL_RGBA, GL_FLOAT, nullptr);
glBindFramebufferEXT(GL_FRAMEBUFFER, srgb_decoder_fbo_);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, srgb_converter_textures_[1], 0);
glUseProgram(srgb_converter_program_);
glViewport(0, 0, width_read, height_read);
glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[0]);
glBindVertexArrayOES(srgb_converter_vao_);
glDrawArrays(GL_TRIANGLES, 0, 6);
} else {
glBindFramebufferEXT(GL_READ_FRAMEBUFFER, src_framebuffer);
}
GLuint width_draw = 0, height_draw = 0;
if (encode) {
glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[0]);
width_draw = dstX1 > dstX0 ? dstX1 - dstX0 : dstX0 - dstX1;
height_draw = dstY1 > dstY0 ? dstY1 - dstY0 : dstY0 - dstY1;
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glTexImage2D(
GL_TEXTURE_2D, 0, decode ? GL_RGBA32F : src_framebuffer_internal_format,
width_draw, height_draw, 0, decode ? GL_RGBA : src_framebuffer_format,
decode ? GL_FLOAT : src_framebuffer_type, nullptr);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, srgb_encoder_fbo_);
glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, srgb_converter_textures_[0], 0);
} else {
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer);
if (enable_scissor_test) {
glEnable(GL_SCISSOR_TEST);
}
}
glBlitFramebuffer(
decode ? (srcX0 < srcX1 ? 0 - xoffset : width_read - xoffset) : srcX0,
decode ? (srcY0 < srcY1 ? 0 - yoffset : height_read - yoffset) : srcY0,
decode ? (srcX0 < srcX1 ? width_read - xoffset : 0 - xoffset) : srcX1,
decode ? (srcY0 < srcY1 ? height_read - yoffset : 0 - yoffset) : srcY1,
encode ? (dstX0 < dstX1 ? 0 : width_draw) : dstX0,
encode ? (dstY0 < dstY1 ? 0 : height_draw) : dstY0,
encode ? (dstX0 < dstX1 ? width_draw : 0) : dstX1,
encode ? (dstY0 < dstY1 ? height_draw : 0) : dstY1,
mask, filter);
if (encode) {
GLuint xstart = dstX0 < dstX1 ? dstX0 : dstX1;
GLuint ystart = dstY0 < dstY1 ? dstY0 : dstY1;
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, dst_framebuffer);
glUseProgram(srgb_converter_program_);
glViewport(xstart, ystart, width_draw, height_draw);
glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[0]);
glBindVertexArrayOES(srgb_converter_vao_);
if (enable_scissor_test) {
glEnable(GL_SCISSOR_TEST);
}
glDrawArrays(GL_TRIANGLES, 0, 6);
}
decoder->RestoreAllAttributes();
decoder->RestoreTextureUnitBindings(0);
decoder->RestoreActiveTexture();
decoder->RestoreProgramBindings();
decoder->RestoreBufferBindings();
decoder->RestoreFramebufferBindings();
decoder->RestoreGlobalState();
}
void SRGBConverter::GenerateMipmap(gles2::GLES2Decoder* decoder,
Texture* tex,
GLenum target) {
DCHECK(srgb_converter_initialized_);
GLsizei width;
GLsizei height;
GLsizei depth;
GLenum type = 0;
GLenum internal_format = 0;
GLenum format = 0;
GLsizei base_level = tex->base_level();
GLsizei max_level = tex->max_level();
tex->GetLevelSize(target, base_level, &width, &height, &depth);
tex->GetLevelType(target, base_level, &type, &internal_format);
format = TextureManager::ExtractFormatFromStorageFormat(internal_format);
GLint mipmap_levels;
if (tex->IsImmutable()) {
mipmap_levels = tex->GetImmutableLevels();
} else {
mipmap_levels =
TextureManager::ComputeMipMapCount(target, width, height, depth);
}
GLint max_mipmap_available_level;
base::CheckedNumeric<GLint> max = base_level;
max = max - 1 + mipmap_levels;
if (!max.IsValid() || max.ValueOrDie() > max_level) {
max_mipmap_available_level = max_level;
} else {
max_mipmap_available_level = max.ValueOrDie();
}
glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[1]);
if (feature_info_->ext_color_buffer_float_available() &&
feature_info_->oes_texture_float_linear_available()) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA,
GL_FLOAT, nullptr);
} else {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
}
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, srgb_decoder_fbo_);
glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, srgb_converter_textures_[1], 0);
glUseProgram(srgb_converter_program_);
glViewport(0, 0, width, height);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_CULL_FACE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_FALSE);
glDisable(GL_BLEND);
glDisable(GL_DITHER);
if (decoder->GetFeatureInfo()->feature_flags().ext_window_rectangles) {
glWindowRectanglesEXT(GL_EXCLUSIVE_EXT, 0, nullptr);
}
glBindVertexArrayOES(srgb_converter_vao_);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex->service_id());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[1]);
glGenerateMipmapEXT(GL_TEXTURE_2D);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, srgb_encoder_fbo_);
glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST_MIPMAP_NEAREST);
width = (width == 1) ? 1 : width >> 1;
height = (height == 1) ? 1 : height >> 1;
base::CheckedNumeric<GLint> level = base_level;
level += 1;
if (!tex->IsImmutable()) {
glBindTexture(GL_TEXTURE_2D, tex->service_id());
GLsizei level_width = width;
GLsizei level_height = height;
for (base::CheckedNumeric<GLint> i = level;
i.IsValid() && i.ValueOrDie() <= max_mipmap_available_level; ++i) {
glTexImage2D(GL_TEXTURE_2D, i.ValueOrDie(), internal_format, level_width,
level_height, 0, format, type, nullptr);
level_width = (level_width == 1) ? 1 : level_width >> 1;
level_height = (level_height == 1) ? 1 : level_height >> 1;
}
}
glBindTexture(GL_TEXTURE_2D, srgb_converter_textures_[1]);
for (base::CheckedNumeric<GLint> i = level;
i.IsValid() && i.ValueOrDie() <= max_mipmap_available_level; ++i) {
glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, tex->service_id(), i.ValueOrDie());
DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
glCheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER));
glViewport(0, 0, width, height);
glDrawArrays(GL_TRIANGLES, 0, 6);
width = (width == 1) ? 1 : width >> 1;
height = (height == 1) ? 1 : height >> 1;
}
decoder->RestoreAllAttributes();
decoder->RestoreTextureUnitBindings(0);
decoder->RestoreActiveTexture();
decoder->RestoreProgramBindings();
decoder->RestoreBufferBindings();
decoder->RestoreFramebufferBindings();
decoder->RestoreGlobalState();
decoder->RestoreTextureState(tex->service_id());
}
}
}