blob: 36f2b42bd6c2d038cba5ba192d7f7f966f789465 [file] [log] [blame]
// 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.
#include "gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h"
#include <string.h>
#include "base/basictypes.h"
#include "gpu/command_buffer/common/types.h"
#include "gpu/command_buffer/service/gl_utils.h"
#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
#define SHADER0(src) \
"#ifdef GL_ES\n"\
"precision mediump float;\n"\
"#endif\n"\
#src
#define SHADER(src) { false, SHADER0(src), }
#define SHADER_EXTERNAL_OES0(src) \
"#extension GL_OES_EGL_image_external : require\n"\
"#ifdef GL_ES\n"\
"precision mediump float;\n"\
"#endif\n"\
#src
#define SHADER_EXTERNAL_OES(src) { true, SHADER_EXTERNAL_OES0(src), }
namespace {
const GLfloat kQuadVertices[] = { -1.0f, -1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 1.0f };
enum ProgramId {
PROGRAM_COPY_TEXTURE,
PROGRAM_COPY_TEXTURE_FLIP_Y,
PROGRAM_COPY_TEXTURE_PREMULTIPLY_ALPHA,
PROGRAM_COPY_TEXTURE_UNPREMULTIPLY_ALPHA,
PROGRAM_COPY_TEXTURE_PREMULTIPLY_ALPHA_FLIPY,
PROGRAM_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_FLIPY,
PROGRAM_COPY_TEXTURE_OES,
PROGRAM_COPY_TEXTURE_OES_FLIP_Y,
PROGRAM_COPY_TEXTURE_OES_PREMULTIPLY_ALPHA,
PROGRAM_COPY_TEXTURE_OES_UNPREMULTIPLY_ALPHA,
PROGRAM_COPY_TEXTURE_OES_PREMULTIPLY_ALPHA_FLIPY,
PROGRAM_COPY_TEXTURE_OES_UNPREMULTIPLY_ALPHA_FLIPY,
};
struct ShaderInfo {
bool needs_egl_image_external;
const char* source;
};
const ShaderInfo shader_infos[] = {
// VERTEX_SHADER_POS_TEX
SHADER(
uniform mat4 u_matrix;
attribute vec4 a_position;
varying vec2 v_uv;
void main(void) {
gl_Position = u_matrix * a_position;
v_uv = a_position.xy * 0.5 + vec2(0.5, 0.5);
}),
// FRAGMENT_SHADER_TEX
SHADER(
uniform sampler2D u_texSampler;
varying vec2 v_uv;
void main(void) {
gl_FragColor = texture2D(u_texSampler, v_uv.st);
}),
// FRAGMENT_SHADER_TEX_FLIP_Y
SHADER(
uniform sampler2D u_texSampler;
varying vec2 v_uv;
void main(void) {
gl_FragColor = texture2D(u_texSampler, vec2(v_uv.s, 1.0 - v_uv.t));
}),
// FRAGMENT_SHADER_TEX_PREMULTIPLY_ALPHA
SHADER(
uniform sampler2D u_texSampler;
varying vec2 v_uv;
void main(void) {
gl_FragColor = texture2D(u_texSampler, v_uv.st);
gl_FragColor.rgb *= gl_FragColor.a;
}),
// FRAGMENT_SHADER_TEX_UNPREMULTIPLY_ALPHA
SHADER(
uniform sampler2D u_texSampler;
varying vec2 v_uv;
void main(void) {
gl_FragColor = texture2D(u_texSampler, v_uv.st);
if (gl_FragColor.a > 0.0)
gl_FragColor.rgb /= gl_FragColor.a;
}),
// FRAGMENT_SHADER_TEX_PREMULTIPLY_ALPHA_FLIP_Y
SHADER(
uniform sampler2D u_texSampler;
varying vec2 v_uv;
void main(void) {
gl_FragColor = texture2D(u_texSampler, vec2(v_uv.s, 1.0 - v_uv.t));
gl_FragColor.rgb *= gl_FragColor.a;
}),
// FRAGMENT_SHADER_TEX_UNPREMULTIPLY_ALPHA_FLIP_Y
SHADER(
uniform sampler2D u_texSampler;
varying vec2 v_uv;
void main(void) {
gl_FragColor = texture2D(u_texSampler, vec2(v_uv.s, 1.0 - v_uv.t));
if (gl_FragColor.a > 0.0)
gl_FragColor.rgb /= gl_FragColor.a;
}),
// FRAGMENT_SHADER_TEX_OES
SHADER_EXTERNAL_OES(
precision mediump float;
uniform samplerExternalOES u_texSampler;
varying vec2 v_uv;
void main(void) {
gl_FragColor = texture2D(u_texSampler, v_uv.st);
}),
// FRAGMENT_SHADER_TEX_OES_FLIP_Y
SHADER_EXTERNAL_OES(
precision mediump float;
uniform samplerExternalOES u_texSampler;
varying vec2 v_uv;
void main(void) {
gl_FragColor =
texture2D(u_texSampler, vec2(v_uv.s, 1.0 - v_uv.t));
}),
// FRAGMENT_SHADER_TEX_OES_PREMULTIPLY_ALPHA
SHADER_EXTERNAL_OES(
precision mediump float;
uniform samplerExternalOES u_texSampler;
varying vec2 v_uv;
void main(void) {
gl_FragColor = texture2D(u_texSampler, v_uv.st);
gl_FragColor.rgb *= gl_FragColor.a;
}),
// FRAGMENT_SHADER_TEX_OES_UNPREMULTIPLY_ALPHA
SHADER_EXTERNAL_OES(
precision mediump float;
uniform samplerExternalOES u_texSampler;
varying vec2 v_uv;
void main(void) {
gl_FragColor = texture2D(u_texSampler, v_uv.st);
if (gl_FragColor.a > 0.0)
gl_FragColor.rgb /= gl_FragColor.a;
}),
// FRAGMENT_SHADER_TEX_OES_PREMULTIPLY_ALPHA_FLIP_Y
SHADER_EXTERNAL_OES(
precision mediump float;
uniform samplerExternalOES u_texSampler;
varying vec2 v_uv;
void main(void) {
gl_FragColor =
texture2D(u_texSampler, vec2(v_uv.s, 1.0 - v_uv.t));
gl_FragColor.rgb *= gl_FragColor.a;
}),
// FRAGMENT_SHADER_TEX_OES_UNPREMULTIPLY_ALPHA_FLIP_Y
SHADER_EXTERNAL_OES(
precision mediump float;
uniform samplerExternalOES u_texSampler;
varying vec2 v_uv;
void main(void) {
gl_FragColor =
texture2D(u_texSampler, vec2(v_uv.s, 1.0 - v_uv.t));
if (gl_FragColor.a > 0.0)
gl_FragColor.rgb /= gl_FragColor.a;
}),
};
const int kNumShaders = arraysize(shader_infos);
// Returns the correct program to evaluate the copy operation for
// the CHROMIUM_flipy and premultiply alpha pixel store settings.
ProgramId GetProgram(
bool flip_y,
bool premultiply_alpha,
bool unpremultiply_alpha,
bool is_source_external_oes) {
// If both pre-multiply and unpremultiply are requested, then perform no
// alpha manipulation.
if (premultiply_alpha && unpremultiply_alpha) {
premultiply_alpha = false;
unpremultiply_alpha = false;
}
// bit 0: Flip_y
// bit 1: Premult
// bit 2: Unpremult
// bit 3: External_oes
static ProgramId program_ids[] = {
PROGRAM_COPY_TEXTURE,
PROGRAM_COPY_TEXTURE_FLIP_Y, // F
PROGRAM_COPY_TEXTURE_PREMULTIPLY_ALPHA, // P
PROGRAM_COPY_TEXTURE_PREMULTIPLY_ALPHA_FLIPY, // F P
PROGRAM_COPY_TEXTURE_UNPREMULTIPLY_ALPHA, // U
PROGRAM_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_FLIPY, // F U
PROGRAM_COPY_TEXTURE, // P U
PROGRAM_COPY_TEXTURE, // F P U
PROGRAM_COPY_TEXTURE_OES, // E
PROGRAM_COPY_TEXTURE_OES_FLIP_Y, // F E
PROGRAM_COPY_TEXTURE_OES_PREMULTIPLY_ALPHA, // P E
PROGRAM_COPY_TEXTURE_OES_PREMULTIPLY_ALPHA_FLIPY, // F P E
PROGRAM_COPY_TEXTURE_OES_UNPREMULTIPLY_ALPHA, // U E
PROGRAM_COPY_TEXTURE_OES_UNPREMULTIPLY_ALPHA_FLIPY, // F U E
PROGRAM_COPY_TEXTURE_OES, // P U E
PROGRAM_COPY_TEXTURE_OES, // F P U E
};
unsigned index = (flip_y ? (1 << 0) : 0) |
(premultiply_alpha ? (1 << 1) : 0) |
(unpremultiply_alpha ? (1 << 2) : 0) |
(is_source_external_oes ? (1 << 3) : 0);
return program_ids[index];
}
} // namespace
namespace gpu {
CopyTextureCHROMIUMResourceManager::CopyTextureCHROMIUMResourceManager()
: initialized_(false),
buffer_id_(0),
framebuffer_(0) {
for (int i = 0; i < kNumPrograms; ++i) {
programs_[i] = 0;
matrix_handle_[i] = 0;
sampler_locations_[i] = 0;
}
}
void CopyTextureCHROMIUMResourceManager::Initialize(
const gles2::GLES2Decoder* decoder) {
COMPILE_ASSERT(
kVertexPositionAttrib == 0u,
Position_attribs_must_be_0);
const char* extensions =
reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
bool have_egl_image_external = extensions &&
strstr(extensions, "GL_OES_EGL_image_external");
// Initialize all of the GPU resources required to perform the copy.
glGenBuffersARB(1, &buffer_id_);
glBindBuffer(GL_ARRAY_BUFFER, buffer_id_);
glBufferData(GL_ARRAY_BUFFER, sizeof(kQuadVertices), kQuadVertices,
GL_STATIC_DRAW);
glGenFramebuffersEXT(1, &framebuffer_);
// TODO(gman): Init these on demand.
GLuint shaders[kNumShaders];
for (int shader = 0; shader < kNumShaders; ++shader) {
shaders[shader] = glCreateShader(
shader == 0 ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER);
const ShaderInfo& info = shader_infos[shader];
if (info.needs_egl_image_external && !have_egl_image_external) {
continue;
}
const char* shader_source = shader_infos[shader].source;
glShaderSource(shaders[shader], 1, &shader_source, 0);
glCompileShader(shaders[shader]);
#ifndef NDEBUG
GLint compile_status;
glGetShaderiv(shaders[shader], GL_COMPILE_STATUS, &compile_status);
if (GL_TRUE != compile_status)
DLOG(ERROR) << "CopyTextureCHROMIUM: shader compilation failure.";
#endif
}
// TODO(gman): Init these on demand.
for (int program = 0; program < kNumPrograms; ++program) {
const ShaderInfo& info = shader_infos[program + 1];
if (info.needs_egl_image_external && !have_egl_image_external) {
continue;
}
programs_[program] = glCreateProgram();
glAttachShader(programs_[program], shaders[0]);
glAttachShader(programs_[program], shaders[program + 1]);
glBindAttribLocation(programs_[program], kVertexPositionAttrib,
"a_position");
glLinkProgram(programs_[program]);
#ifndef NDEBUG
GLint linked;
glGetProgramiv(programs_[program], GL_LINK_STATUS, &linked);
if (!linked)
DLOG(ERROR) << "CopyTextureCHROMIUM: program link failure.";
#endif
sampler_locations_[program] = glGetUniformLocation(programs_[program],
"u_texSampler");
matrix_handle_[program] = glGetUniformLocation(programs_[program],
"u_matrix");
}
for (int shader = 0; shader < kNumShaders; ++shader)
glDeleteShader(shaders[shader]);
decoder->RestoreBufferBindings();
initialized_ = true;
}
void CopyTextureCHROMIUMResourceManager::Destroy() {
if (!initialized_)
return;
glDeleteFramebuffersEXT(1, &framebuffer_);
for (int program = 0; program < kNumPrograms; ++program) {
if (programs_[program])
glDeleteProgram(programs_[program]);
}
glDeleteBuffersARB(1, &buffer_id_);
}
void CopyTextureCHROMIUMResourceManager::DoCopyTexture(
const gles2::GLES2Decoder* decoder,
GLenum source_target,
GLenum dest_target,
GLuint source_id,
GLuint dest_id,
GLint level,
GLsizei width,
GLsizei height,
bool flip_y,
bool premultiply_alpha,
bool unpremultiply_alpha) {
// Use default transform matrix if no transform passed in.
const static GLfloat default_matrix[16] = {1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
DoCopyTextureWithTransform(decoder, source_target, dest_target, source_id,
dest_id, level, width, height, flip_y, premultiply_alpha,
unpremultiply_alpha, default_matrix);
}
void CopyTextureCHROMIUMResourceManager::DoCopyTextureWithTransform(
const gles2::GLES2Decoder* decoder,
GLenum source_target,
GLenum dest_target,
GLuint source_id,
GLuint dest_id,
GLint level,
GLsizei width,
GLsizei height,
bool flip_y,
bool premultiply_alpha,
bool unpremultiply_alpha,
const GLfloat transform_matrix[16]) {
DCHECK(source_target == GL_TEXTURE_2D ||
source_target == GL_TEXTURE_EXTERNAL_OES);
if (!initialized_) {
DLOG(ERROR) << "CopyTextureCHROMIUM: Uninitialized manager.";
return;
}
GLuint program = GetProgram(
flip_y, premultiply_alpha, unpremultiply_alpha,
source_target == GL_TEXTURE_EXTERNAL_OES);
glUseProgram(programs_[program]);
#ifndef NDEBUG
glValidateProgram(programs_[program]);
GLint validation_status;
glGetProgramiv(programs_[program], GL_VALIDATE_STATUS, &validation_status);
if (GL_TRUE != validation_status) {
DLOG(ERROR) << "CopyTextureCHROMIUM: Invalid shader.";
return;
}
#endif
glUniformMatrix4fv(matrix_handle_[program], 1, GL_FALSE, transform_matrix);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, dest_id);
// NVidia drivers require texture settings to be a certain way
// or they won't report FRAMEBUFFER_COMPLETE.
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer_);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, dest_target,
dest_id, level);
#ifndef NDEBUG
GLenum fb_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
if (GL_FRAMEBUFFER_COMPLETE != fb_status) {
DLOG(ERROR) << "CopyTextureCHROMIUM: Incomplete framebuffer.";
} else
#endif
{
glEnableVertexAttribArray(kVertexPositionAttrib);
glBindBuffer(GL_ARRAY_BUFFER, buffer_id_);
glVertexAttribPointer(kVertexPositionAttrib, 4, GL_FLOAT, GL_FALSE,
4 * sizeof(GLfloat), 0);
glUniform1i(sampler_locations_[program], 0);
glBindTexture(source_target, source_id);
glTexParameterf(source_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(source_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(source_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(source_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_CULL_FACE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_FALSE);
glDisable(GL_BLEND);
glViewport(0, 0, width, height);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
decoder->RestoreAttribute(kVertexPositionAttrib);
decoder->RestoreTextureState(source_id);
decoder->RestoreTextureState(dest_id);
decoder->RestoreTextureUnitBindings(0);
decoder->RestoreActiveTexture();
decoder->RestoreProgramBindings();
decoder->RestoreBufferBindings();
decoder->RestoreFramebufferBindings();
decoder->RestoreGlobalState();
}
} // namespace