Home | History | Annotate | Download | only in service
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/basictypes.h"
     10 #include "gpu/command_buffer/service/gl_utils.h"
     11 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
     12 
     13 #define SHADER(src)                     \
     14   "#ifdef GL_ES\n"                      \
     15   "precision mediump float;\n"          \
     16   "#define TexCoordPrecision mediump\n" \
     17   "#else\n"                             \
     18   "#define TexCoordPrecision\n"         \
     19   "#endif\n" #src
     20 #define SHADER_2D(src)              \
     21   "#define SamplerType sampler2D\n" \
     22   "#define TextureLookup texture2D\n" SHADER(src)
     23 #define SHADER_RECTANGLE_ARB(src)     \
     24   "#define SamplerType samplerRect\n" \
     25   "#define TextureLookup textureRect\n" SHADER(src)
     26 #define SHADER_EXTERNAL_OES(src)                     \
     27   "#extension GL_OES_EGL_image_external : require\n" \
     28   "#define SamplerType samplerExternalOES\n"         \
     29   "#define TextureLookup texture2D\n" SHADER(src)
     30 #define FRAGMENT_SHADERS(src) \
     31   SHADER_2D(src), SHADER_RECTANGLE_ARB(src), SHADER_EXTERNAL_OES(src)
     32 
     33 namespace {
     34 
     35 enum VertexShaderId {
     36   VERTEX_SHADER_COPY_TEXTURE,
     37   VERTEX_SHADER_COPY_TEXTURE_FLIP_Y,
     38   NUM_VERTEX_SHADERS,
     39 };
     40 
     41 enum FragmentShaderId {
     42   FRAGMENT_SHADER_COPY_TEXTURE_2D,
     43   FRAGMENT_SHADER_COPY_TEXTURE_RECTANGLE_ARB,
     44   FRAGMENT_SHADER_COPY_TEXTURE_EXTERNAL_OES,
     45   FRAGMENT_SHADER_COPY_TEXTURE_PREMULTIPLY_ALPHA_2D,
     46   FRAGMENT_SHADER_COPY_TEXTURE_PREMULTIPLY_ALPHA_RECTANGLE_ARB,
     47   FRAGMENT_SHADER_COPY_TEXTURE_PREMULTIPLY_ALPHA_EXTERNAL_OES,
     48   FRAGMENT_SHADER_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_2D,
     49   FRAGMENT_SHADER_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_RECTANGLE_ARB,
     50   FRAGMENT_SHADER_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_EXTERNAL_OES,
     51   NUM_FRAGMENT_SHADERS,
     52 };
     53 
     54 const char* vertex_shader_source[NUM_VERTEX_SHADERS] = {
     55   // VERTEX_SHADER_COPY_TEXTURE
     56   SHADER(
     57     uniform mat4 u_matrix;
     58     uniform vec2 u_half_size;
     59     attribute vec4 a_position;
     60     varying TexCoordPrecision vec2 v_uv;
     61     void main(void) {
     62       gl_Position = u_matrix * a_position;
     63       v_uv = a_position.xy * vec2(u_half_size.s, u_half_size.t) +
     64              vec2(u_half_size.s, u_half_size.t);
     65     }),
     66   // VERTEX_SHADER_COPY_TEXTURE_FLIP_Y
     67   SHADER(
     68     uniform mat4 u_matrix;
     69     uniform vec2 u_half_size;
     70     attribute vec4 a_position;
     71     varying TexCoordPrecision vec2 v_uv;
     72     void main(void) {
     73       gl_Position = u_matrix * a_position;
     74       v_uv = a_position.xy * vec2(u_half_size.s, -u_half_size.t) +
     75              vec2(u_half_size.s, u_half_size.t);
     76     }),
     77 };
     78 
     79 const char* fragment_shader_source[NUM_FRAGMENT_SHADERS] = {
     80   // FRAGMENT_SHADER_COPY_TEXTURE_*
     81   FRAGMENT_SHADERS(
     82     uniform SamplerType u_sampler;
     83     varying TexCoordPrecision vec2 v_uv;
     84     void main(void) {
     85       gl_FragColor = TextureLookup(u_sampler, v_uv.st);
     86     }),
     87   // FRAGMENT_SHADER_COPY_TEXTURE_PREMULTIPLY_ALPHA_*
     88   FRAGMENT_SHADERS(
     89     uniform SamplerType u_sampler;
     90     varying TexCoordPrecision vec2 v_uv;
     91     void main(void) {
     92       gl_FragColor = TextureLookup(u_sampler, v_uv.st);
     93       gl_FragColor.rgb *= gl_FragColor.a;
     94     }),
     95   // FRAGMENT_SHADER_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_*
     96   FRAGMENT_SHADERS(
     97     uniform SamplerType u_sampler;
     98     varying TexCoordPrecision vec2 v_uv;
     99     void main(void) {
    100       gl_FragColor = TextureLookup(u_sampler, v_uv.st);
    101       if (gl_FragColor.a > 0.0)
    102         gl_FragColor.rgb /= gl_FragColor.a;
    103     }),
    104 };
    105 
    106 // Returns the correct vertex shader id to evaluate the copy operation for
    107 // the CHROMIUM_flipy setting.
    108 VertexShaderId GetVertexShaderId(bool flip_y) {
    109   // bit 0: flip y
    110   static VertexShaderId shader_ids[] = {
    111       VERTEX_SHADER_COPY_TEXTURE,
    112       VERTEX_SHADER_COPY_TEXTURE_FLIP_Y,
    113   };
    114 
    115   unsigned index = flip_y ? 1 : 0;
    116   return shader_ids[index];
    117 }
    118 
    119 // Returns the correct fragment shader id to evaluate the copy operation for
    120 // the premultiply alpha pixel store settings and target.
    121 FragmentShaderId GetFragmentShaderId(bool premultiply_alpha,
    122                                      bool unpremultiply_alpha,
    123                                      GLenum target) {
    124   enum {
    125     SAMPLER_2D,
    126     SAMPLER_RECTANGLE_ARB,
    127     SAMPLER_EXTERNAL_OES,
    128     NUM_SAMPLERS
    129   };
    130 
    131   // bit 0: premultiply alpha
    132   // bit 1: unpremultiply alpha
    133   static FragmentShaderId shader_ids[][NUM_SAMPLERS] = {
    134       {
    135        FRAGMENT_SHADER_COPY_TEXTURE_2D,
    136        FRAGMENT_SHADER_COPY_TEXTURE_RECTANGLE_ARB,
    137        FRAGMENT_SHADER_COPY_TEXTURE_EXTERNAL_OES,
    138       },
    139       {
    140        FRAGMENT_SHADER_COPY_TEXTURE_PREMULTIPLY_ALPHA_2D,
    141        FRAGMENT_SHADER_COPY_TEXTURE_PREMULTIPLY_ALPHA_RECTANGLE_ARB,
    142        FRAGMENT_SHADER_COPY_TEXTURE_PREMULTIPLY_ALPHA_EXTERNAL_OES,
    143       },
    144       {
    145        FRAGMENT_SHADER_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_2D,
    146        FRAGMENT_SHADER_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_RECTANGLE_ARB,
    147        FRAGMENT_SHADER_COPY_TEXTURE_UNPREMULTIPLY_ALPHA_EXTERNAL_OES,
    148       },
    149       {
    150        FRAGMENT_SHADER_COPY_TEXTURE_2D,
    151        FRAGMENT_SHADER_COPY_TEXTURE_RECTANGLE_ARB,
    152        FRAGMENT_SHADER_COPY_TEXTURE_EXTERNAL_OES,
    153       }};
    154 
    155   unsigned index = (premultiply_alpha   ? (1 << 0) : 0) |
    156                    (unpremultiply_alpha ? (1 << 1) : 0);
    157 
    158   switch (target) {
    159     case GL_TEXTURE_2D:
    160       return shader_ids[index][SAMPLER_2D];
    161     case GL_TEXTURE_RECTANGLE_ARB:
    162       return shader_ids[index][SAMPLER_RECTANGLE_ARB];
    163     case GL_TEXTURE_EXTERNAL_OES:
    164       return shader_ids[index][SAMPLER_EXTERNAL_OES];
    165     default:
    166       break;
    167   }
    168 
    169   NOTREACHED();
    170   return shader_ids[0][SAMPLER_2D];
    171 }
    172 
    173 void CompileShader(GLuint shader, const char* shader_source) {
    174   glShaderSource(shader, 1, &shader_source, 0);
    175   glCompileShader(shader);
    176 #ifndef NDEBUG
    177   GLint compile_status;
    178   glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
    179   if (GL_TRUE != compile_status)
    180     DLOG(ERROR) << "CopyTextureCHROMIUM: shader compilation failure.";
    181 #endif
    182 }
    183 
    184 void DeleteShader(GLuint shader) {
    185   if (shader)
    186     glDeleteShader(shader);
    187 }
    188 
    189 bool BindFramebufferTexture2D(GLenum target,
    190                               GLuint texture_id,
    191                               GLint level,
    192                               GLuint framebuffer) {
    193   DCHECK(target == GL_TEXTURE_2D || target == GL_TEXTURE_RECTANGLE_ARB);
    194   glActiveTexture(GL_TEXTURE0);
    195   glBindTexture(target, texture_id);
    196   // NVidia drivers require texture settings to be a certain way
    197   // or they won't report FRAMEBUFFER_COMPLETE.
    198   glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    199   glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    200   glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    201   glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    202   glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
    203   glFramebufferTexture2DEXT(
    204       GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, texture_id, level);
    205 
    206 #ifndef NDEBUG
    207   GLenum fb_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
    208   if (GL_FRAMEBUFFER_COMPLETE != fb_status) {
    209     DLOG(ERROR) << "CopyTextureCHROMIUM: Incomplete framebuffer.";
    210     return false;
    211   }
    212 #endif
    213   return true;
    214 }
    215 
    216 void DoCopyTexImage2D(const gpu::gles2::GLES2Decoder* decoder,
    217                       GLenum source_target,
    218                       GLuint source_id,
    219                       GLuint dest_id,
    220                       GLint dest_level,
    221                       GLenum dest_internal_format,
    222                       GLsizei width,
    223                       GLsizei height,
    224                       GLuint framebuffer) {
    225   DCHECK(source_target == GL_TEXTURE_2D ||
    226          source_target == GL_TEXTURE_RECTANGLE_ARB);
    227   if (BindFramebufferTexture2D(
    228           source_target, source_id, 0 /* level */, framebuffer)) {
    229     glBindTexture(GL_TEXTURE_2D, dest_id);
    230     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    231     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    232     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    233     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    234     glCopyTexImage2D(GL_TEXTURE_2D,
    235                      dest_level,
    236                      dest_internal_format,
    237                      0 /* x */,
    238                      0 /* y */,
    239                      width,
    240                      height,
    241                      0 /* border */);
    242   }
    243 
    244   decoder->RestoreTextureState(source_id);
    245   decoder->RestoreTextureState(dest_id);
    246   decoder->RestoreTextureUnitBindings(0);
    247   decoder->RestoreActiveTexture();
    248   decoder->RestoreFramebufferBindings();
    249 }
    250 
    251 }  // namespace
    252 
    253 namespace gpu {
    254 
    255 CopyTextureCHROMIUMResourceManager::CopyTextureCHROMIUMResourceManager()
    256     : initialized_(false),
    257       vertex_shaders_(NUM_VERTEX_SHADERS, 0u),
    258       fragment_shaders_(NUM_FRAGMENT_SHADERS, 0u),
    259       buffer_id_(0u),
    260       framebuffer_(0u) {}
    261 
    262 CopyTextureCHROMIUMResourceManager::~CopyTextureCHROMIUMResourceManager() {
    263   DCHECK(!buffer_id_);
    264   DCHECK(!framebuffer_);
    265 }
    266 
    267 void CopyTextureCHROMIUMResourceManager::Initialize(
    268     const gles2::GLES2Decoder* decoder) {
    269   COMPILE_ASSERT(
    270       kVertexPositionAttrib == 0u,
    271       Position_attribs_must_be_0);
    272   DCHECK(!buffer_id_);
    273   DCHECK(!framebuffer_);
    274   DCHECK(programs_.empty());
    275 
    276   // Initialize all of the GPU resources required to perform the copy.
    277   glGenBuffersARB(1, &buffer_id_);
    278   glBindBuffer(GL_ARRAY_BUFFER, buffer_id_);
    279   const GLfloat kQuadVertices[] = {-1.0f, -1.0f,
    280                                     1.0f, -1.0f,
    281                                     1.0f,  1.0f,
    282                                    -1.0f,  1.0f};
    283   glBufferData(
    284       GL_ARRAY_BUFFER, sizeof(kQuadVertices), kQuadVertices, GL_STATIC_DRAW);
    285 
    286   glGenFramebuffersEXT(1, &framebuffer_);
    287 
    288   decoder->RestoreBufferBindings();
    289 
    290   initialized_ = true;
    291 }
    292 
    293 void CopyTextureCHROMIUMResourceManager::Destroy() {
    294   if (!initialized_)
    295     return;
    296 
    297   glDeleteFramebuffersEXT(1, &framebuffer_);
    298   framebuffer_ = 0;
    299 
    300   std::for_each(vertex_shaders_.begin(), vertex_shaders_.end(), DeleteShader);
    301   std::for_each(
    302       fragment_shaders_.begin(), fragment_shaders_.end(), DeleteShader);
    303 
    304   for (ProgramMap::const_iterator it = programs_.begin(); it != programs_.end();
    305        ++it) {
    306     const ProgramInfo& info = it->second;
    307     glDeleteProgram(info.program);
    308   }
    309 
    310   glDeleteBuffersARB(1, &buffer_id_);
    311   buffer_id_ = 0;
    312 }
    313 
    314 void CopyTextureCHROMIUMResourceManager::DoCopyTexture(
    315     const gles2::GLES2Decoder* decoder,
    316     GLenum source_target,
    317     GLuint source_id,
    318     GLenum source_internal_format,
    319     GLuint dest_id,
    320     GLint dest_level,
    321     GLenum dest_internal_format,
    322     GLsizei width,
    323     GLsizei height,
    324     bool flip_y,
    325     bool premultiply_alpha,
    326     bool unpremultiply_alpha) {
    327   bool premultiply_alpha_change = premultiply_alpha ^ unpremultiply_alpha;
    328   // GL_INVALID_OPERATION is generated if the currently bound framebuffer's
    329   // format does not contain a superset of the components required by the base
    330   // format of internalformat.
    331   // https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCopyTexImage2D.xml
    332   bool source_format_contain_superset_of_dest_format =
    333       source_internal_format == dest_internal_format ||
    334       (source_internal_format == GL_RGBA && dest_internal_format == GL_RGB);
    335   // GL_TEXTURE_RECTANGLE_ARB on FBO is supported by OpenGL, not GLES2,
    336   // so restrict this to GL_TEXTURE_2D.
    337   if (source_target == GL_TEXTURE_2D && !flip_y && !premultiply_alpha_change &&
    338       source_format_contain_superset_of_dest_format) {
    339     DoCopyTexImage2D(decoder,
    340                      source_target,
    341                      source_id,
    342                      dest_id,
    343                      dest_level,
    344                      dest_internal_format,
    345                      width,
    346                      height,
    347                      framebuffer_);
    348     return;
    349   }
    350 
    351   // Use default transform matrix if no transform passed in.
    352   const static GLfloat default_matrix[16] = {1.0f, 0.0f, 0.0f, 0.0f,
    353                                              0.0f, 1.0f, 0.0f, 0.0f,
    354                                              0.0f, 0.0f, 1.0f, 0.0f,
    355                                              0.0f, 0.0f, 0.0f, 1.0f};
    356   DoCopyTextureWithTransform(decoder,
    357                              source_target,
    358                              source_id,
    359                              dest_id,
    360                              dest_level,
    361                              width,
    362                              height,
    363                              flip_y,
    364                              premultiply_alpha,
    365                              unpremultiply_alpha,
    366                              default_matrix);
    367 }
    368 
    369 void CopyTextureCHROMIUMResourceManager::DoCopyTextureWithTransform(
    370     const gles2::GLES2Decoder* decoder,
    371     GLenum source_target,
    372     GLuint source_id,
    373     GLuint dest_id,
    374     GLint dest_level,
    375     GLsizei width,
    376     GLsizei height,
    377     bool flip_y,
    378     bool premultiply_alpha,
    379     bool unpremultiply_alpha,
    380     const GLfloat transform_matrix[16]) {
    381   DCHECK(source_target == GL_TEXTURE_2D ||
    382          source_target == GL_TEXTURE_RECTANGLE_ARB ||
    383          source_target == GL_TEXTURE_EXTERNAL_OES);
    384   if (!initialized_) {
    385     DLOG(ERROR) << "CopyTextureCHROMIUM: Uninitialized manager.";
    386     return;
    387   }
    388 
    389   VertexShaderId vertex_shader_id = GetVertexShaderId(flip_y);
    390   DCHECK_LT(static_cast<size_t>(vertex_shader_id), vertex_shaders_.size());
    391   FragmentShaderId fragment_shader_id = GetFragmentShaderId(
    392       premultiply_alpha, unpremultiply_alpha, source_target);
    393   DCHECK_LT(static_cast<size_t>(fragment_shader_id), fragment_shaders_.size());
    394 
    395   ProgramMapKey key(vertex_shader_id, fragment_shader_id);
    396   ProgramInfo* info = &programs_[key];
    397   // Create program if necessary.
    398   if (!info->program) {
    399     info->program = glCreateProgram();
    400     GLuint* vertex_shader = &vertex_shaders_[vertex_shader_id];
    401     if (!*vertex_shader) {
    402       *vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    403       CompileShader(*vertex_shader, vertex_shader_source[vertex_shader_id]);
    404     }
    405     glAttachShader(info->program, *vertex_shader);
    406     GLuint* fragment_shader = &fragment_shaders_[fragment_shader_id];
    407     if (!*fragment_shader) {
    408       *fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    409       CompileShader(*fragment_shader,
    410                     fragment_shader_source[fragment_shader_id]);
    411     }
    412     glAttachShader(info->program, *fragment_shader);
    413     glBindAttribLocation(info->program, kVertexPositionAttrib, "a_position");
    414     glLinkProgram(info->program);
    415 #ifndef NDEBUG
    416     GLint linked;
    417     glGetProgramiv(info->program, GL_LINK_STATUS, &linked);
    418     if (!linked)
    419       DLOG(ERROR) << "CopyTextureCHROMIUM: program link failure.";
    420 #endif
    421     info->matrix_handle = glGetUniformLocation(info->program, "u_matrix");
    422     info->half_size_handle = glGetUniformLocation(info->program, "u_half_size");
    423     info->sampler_handle = glGetUniformLocation(info->program, "u_sampler");
    424   }
    425   glUseProgram(info->program);
    426 
    427 #ifndef NDEBUG
    428   glValidateProgram(info->program);
    429   GLint validation_status;
    430   glGetProgramiv(info->program, GL_VALIDATE_STATUS, &validation_status);
    431   if (GL_TRUE != validation_status) {
    432     DLOG(ERROR) << "CopyTextureCHROMIUM: Invalid shader.";
    433     return;
    434   }
    435 #endif
    436 
    437   glUniformMatrix4fv(info->matrix_handle, 1, GL_FALSE, transform_matrix);
    438   if (source_target == GL_TEXTURE_RECTANGLE_ARB)
    439     glUniform2f(info->half_size_handle, width / 2.0f, height / 2.0f);
    440   else
    441     glUniform2f(info->half_size_handle, 0.5f, 0.5f);
    442 
    443   if (BindFramebufferTexture2D(
    444           GL_TEXTURE_2D, dest_id, dest_level, framebuffer_)) {
    445     decoder->ClearAllAttributes();
    446     glEnableVertexAttribArray(kVertexPositionAttrib);
    447 
    448     glBindBuffer(GL_ARRAY_BUFFER, buffer_id_);
    449     glVertexAttribPointer(kVertexPositionAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
    450 
    451     glUniform1i(info->sampler_handle, 0);
    452 
    453     glBindTexture(source_target, source_id);
    454     glTexParameterf(source_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    455     glTexParameterf(source_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    456     glTexParameteri(source_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    457     glTexParameteri(source_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    458 
    459     glDisable(GL_DEPTH_TEST);
    460     glDisable(GL_SCISSOR_TEST);
    461     glDisable(GL_STENCIL_TEST);
    462     glDisable(GL_CULL_FACE);
    463     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    464     glDepthMask(GL_FALSE);
    465     glDisable(GL_BLEND);
    466 
    467     glViewport(0, 0, width, height);
    468     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    469   }
    470 
    471   decoder->RestoreAllAttributes();
    472   decoder->RestoreTextureState(source_id);
    473   decoder->RestoreTextureState(dest_id);
    474   decoder->RestoreTextureUnitBindings(0);
    475   decoder->RestoreActiveTexture();
    476   decoder->RestoreProgramBindings();
    477   decoder->RestoreBufferBindings();
    478   decoder->RestoreFramebufferBindings();
    479   decoder->RestoreGlobalState();
    480 }
    481 
    482 }  // namespace gpu
    483