root/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetProgram
  2. framebuffer_
  3. Initialize
  4. Destroy
  5. DoCopyTexture
  6. DoCopyTextureWithTransform

// 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
  {
    decoder->ClearAllAttributes();
    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->RestoreAllAttributes();
  decoder->RestoreTextureState(source_id);
  decoder->RestoreTextureState(dest_id);
  decoder->RestoreTextureUnitBindings(0);
  decoder->RestoreActiveTexture();
  decoder->RestoreProgramBindings();
  decoder->RestoreBufferBindings();
  decoder->RestoreFramebufferBindings();
  decoder->RestoreGlobalState();
}

}  // namespace


/* [<][>][^][v][top][bottom][index][help] */