// 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 "webkit/gpu/webgraphicscontext3d_in_process_impl.h"

#include <string.h>

#include <algorithm>
#include <string>
#include <vector>

#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/string_split.h"
#include "base/synchronization/lock.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_bindings_skia_in_process.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_surface.h"

namespace webkit {
namespace gpu {

enum {
  MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB,
  MAX_VARYING_VECTORS = 0x8DFC,
  MAX_FRAGMENT_UNIFORM_VECTORS = 0x8DFD
};

struct WebGraphicsContext3DInProcessImpl::ShaderSourceEntry {
  explicit ShaderSourceEntry(WGC3Denum shader_type)
      : type(shader_type),
        is_valid(false) {
  }

  WGC3Denum type;
  scoped_array<char> source;
  scoped_array<char> log;
  scoped_array<char> translated_source;
  bool is_valid;
};

WebGraphicsContext3DInProcessImpl::WebGraphicsContext3DInProcessImpl(
    gfx::GLSurface* surface,
    gfx::GLContext* context,
    bool render_directly_to_web_view)
    : initialized_(false),
      render_directly_to_web_view_(render_directly_to_web_view),
      is_gles2_(false),
      have_ext_framebuffer_object_(false),
      have_ext_framebuffer_multisample_(false),
      have_angle_framebuffer_multisample_(false),
      texture_(0),
      fbo_(0),
      depth_stencil_buffer_(0),
      cached_width_(0),
      cached_height_(0),
      multisample_fbo_(0),
      multisample_depth_stencil_buffer_(0),
      multisample_color_buffer_(0),
      bound_fbo_(0),
      bound_texture_(0),
#ifdef FLIP_FRAMEBUFFER_VERTICALLY
      scanline_(0),
#endif
      gl_context_(context),
      gl_surface_(surface),
      fragment_compiler_(0),
      vertex_compiler_(0) {
}

// All instances in a process that share resources are in the same share group.
static base::LazyInstance<
    std::set<WebGraphicsContext3DInProcessImpl*> >
        g_all_shared_contexts = LAZY_INSTANCE_INITIALIZER;
static base::LazyInstance<base::Lock>::Leaky
    g_all_shared_contexts_lock = LAZY_INSTANCE_INITIALIZER;

WebGraphicsContext3DInProcessImpl::~WebGraphicsContext3DInProcessImpl() {
  base::AutoLock a(g_all_shared_contexts_lock.Get());
  g_all_shared_contexts.Pointer()->erase(this);

  if (!initialized_)
    return;

  makeContextCurrent();

  if (attributes_.antialias) {
    glDeleteRenderbuffersEXT(1, &multisample_color_buffer_);
    if (attributes_.depth || attributes_.stencil)
      glDeleteRenderbuffersEXT(1, &multisample_depth_stencil_buffer_);
    glDeleteFramebuffersEXT(1, &multisample_fbo_);
  } else {
    if (attributes_.depth || attributes_.stencil)
      glDeleteRenderbuffersEXT(1, &depth_stencil_buffer_);
  }
  glDeleteTextures(1, &texture_);
#ifdef FLIP_FRAMEBUFFER_VERTICALLY
  if (scanline_)
    delete[] scanline_;
#endif
  glDeleteFramebuffersEXT(1, &fbo_);

  gl_context_->ReleaseCurrent(gl_surface_.get());
  gl_context_->Destroy();
  gl_surface_->Destroy();

  for (ShaderSourceMap::iterator ii = shader_source_map_.begin();
       ii != shader_source_map_.end(); ++ii) {
    if (ii->second)
      delete ii->second;
  }
  AngleDestroyCompilers();
}

WebGraphicsContext3DInProcessImpl*
WebGraphicsContext3DInProcessImpl::CreateForWebView(
    WebGraphicsContext3D::Attributes attributes,
    bool render_directly_to_web_view) {
  if (!gfx::GLSurface::InitializeOneOff())
    return NULL;

  gfx::GLShareGroup* share_group = NULL;

  if (attributes.shareResources) {
    WebGraphicsContext3DInProcessImpl* context_impl =
      g_all_shared_contexts.Pointer()->empty() ?
        NULL : *g_all_shared_contexts.Pointer()->begin();
    if (context_impl)
        share_group = context_impl->gl_context_->share_group();
  }

  // This implementation always renders offscreen regardless of whether
  // render_directly_to_web_view is true. Both DumpRenderTree and test_shell
  // paint first to an intermediate offscreen buffer and from there to the
  // window, and WebViewImpl::paint already correctly handles the case where the
  // compositor is active but the output needs to go to a WebCanvas.
  scoped_refptr<gfx::GLSurface> gl_surface =
      gfx::GLSurface::CreateOffscreenGLSurface(false, gfx::Size(1, 1));

  if (!gl_surface.get())
    return NULL;

  // TODO(kbr): This implementation doesn't yet support lost contexts
  // and therefore can't yet properly support GPU switching.
  gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;

  scoped_refptr<gfx::GLContext> gl_context = gfx::GLContext::CreateGLContext(
      share_group,
      gl_surface.get(),
      gpu_preference);

  if (!gl_context.get())
    return NULL;

  scoped_ptr<WebGraphicsContext3DInProcessImpl> context(
      new WebGraphicsContext3DInProcessImpl(
        gl_surface.get(), gl_context.get(), render_directly_to_web_view));
  if (!context->Initialize(attributes))
    return NULL;
  return context.release();
}

WebGraphicsContext3DInProcessImpl*
WebGraphicsContext3DInProcessImpl::CreateForWindow(
    WebGraphicsContext3D::Attributes attributes,
    gfx::AcceleratedWidget window,
    gfx::GLShareGroup* share_group) {
  if (!gfx::GLSurface::InitializeOneOff())
    return NULL;

  scoped_refptr<gfx::GLSurface> gl_surface =
      gfx::GLSurface::CreateViewGLSurface(false, window);
  if (!gl_surface.get())
    return NULL;

  gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu;

  scoped_refptr<gfx::GLContext> gl_context = gfx::GLContext::CreateGLContext(
      share_group,
      gl_surface.get(),
      gpu_preference);
  if (!gl_context.get())
    return NULL;
  scoped_ptr<WebGraphicsContext3DInProcessImpl> context(
      new WebGraphicsContext3DInProcessImpl(
          gl_surface.get(), gl_context.get(), true));
  if (!context->Initialize(attributes))
    return NULL;
  return context.release();
}

bool WebGraphicsContext3DInProcessImpl::Initialize(
    WebGraphicsContext3D::Attributes attributes) {
  is_gles2_ = gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2;


  attributes_ = attributes;

  // FIXME: for the moment we disable multisampling for the compositor.
  // It actually works in this implementation, but there are a few
  // considerations. First, we likely want to reduce the fuzziness in
  // these tests as much as possible because we want to run pixel tests.
  // Second, Mesa's multisampling doesn't seem to antialias straight
  // edges in some CSS 3D samples. Third, we don't have multisampling
  // support for the compositor in the normal case at the time of this
  // writing.
  if (render_directly_to_web_view_)
    attributes_.antialias = false;

  if (!gl_context_->MakeCurrent(gl_surface_.get())) {
    gl_context_ = NULL;
    return false;
  }

  const char* extensions =
      reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
  DCHECK(extensions);
  have_ext_framebuffer_object_ =
      strstr(extensions, "GL_EXT_framebuffer_object") != NULL;
  have_ext_framebuffer_multisample_ =
      strstr(extensions, "GL_EXT_framebuffer_multisample") != NULL;
  have_angle_framebuffer_multisample_ =
      strstr(extensions, "GL_ANGLE_framebuffer_multisample") != NULL;

  ValidateAttributes();

  if (!is_gles2_) {
    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
    glEnable(GL_POINT_SPRITE);
  }

  if (!AngleCreateCompilers()) {
    AngleDestroyCompilers();
    return false;
  }

  initialized_ = true;
  gl_context_->ReleaseCurrent(gl_surface_.get());

  if (attributes_.shareResources)
    g_all_shared_contexts.Pointer()->insert(this);

  return true;
}

void WebGraphicsContext3DInProcessImpl::ValidateAttributes() {
  const char* extensions =
      reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));

  if (attributes_.stencil) {
    if (strstr(extensions, "GL_OES_packed_depth_stencil") ||
        strstr(extensions, "GL_EXT_packed_depth_stencil")) {
      if (!attributes_.depth) {
        attributes_.depth = true;
      }
    } else {
      attributes_.stencil = false;
    }
  }
  if (attributes_.antialias) {
    bool isValidVendor = true;
#if defined(OS_MACOSX)
    // Currently in Mac we only turn on antialias if vendor is NVIDIA.
    const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
    if (!strstr(vendor, "NVIDIA"))
      isValidVendor = false;
#endif
    if (!(isValidVendor &&
          (have_ext_framebuffer_multisample_ ||
           (have_angle_framebuffer_multisample_ &&
            strstr(extensions, "GL_OES_rgb8_rgba8")))))
      attributes_.antialias = false;

    // Don't antialias when using Mesa to ensure more reliable testing and
    // because it doesn't appear to multisample straight lines correctly.
    const char* renderer =
        reinterpret_cast<const char*>(glGetString(GL_RENDERER));
    if (!strncmp(renderer, "Mesa", 4)) {
      attributes_.antialias = false;
    }
  }
}

void WebGraphicsContext3DInProcessImpl::ResolveMultisampledFramebuffer(
    WGC3Dint x, WGC3Dint y, WGC3Dsizei width, WGC3Dsizei height) {
  if (attributes_.antialias) {
    glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, multisample_fbo_);
    glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, fbo_);
    if (have_ext_framebuffer_multisample_) {
      glBlitFramebufferEXT(x, y,
                           x + width, y + height,
                           x, y,
                           x + width, y + height,
                           GL_COLOR_BUFFER_BIT, GL_NEAREST);
    } else {
      DCHECK(have_angle_framebuffer_multisample_);
      glBlitFramebufferANGLE(x, y,
                             x + width, y + height,
                             x, y,
                             x + width, y + height,
                             GL_COLOR_BUFFER_BIT, GL_NEAREST);
    }
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bound_fbo_);
  }
}

bool WebGraphicsContext3DInProcessImpl::makeContextCurrent() {
  return gl_context_->MakeCurrent(gl_surface_.get());
}

int WebGraphicsContext3DInProcessImpl::width() {
  return cached_width_;
}

int WebGraphicsContext3DInProcessImpl::height() {
  return cached_height_;
}

bool WebGraphicsContext3DInProcessImpl::isGLES2Compliant() {
  return is_gles2_;
}

bool WebGraphicsContext3DInProcessImpl::setParentContext(
    WebGraphicsContext3D* parent_context) {
  return false;
}

WebGLId WebGraphicsContext3DInProcessImpl::getPlatformTextureId() {
  return texture_;
}

void WebGraphicsContext3DInProcessImpl::prepareTexture() {
  if (!gl_surface_->IsOffscreen()) {
    gl_surface_->SwapBuffers();
  } else if (!render_directly_to_web_view_) {
    // We need to prepare our rendering results for the compositor.
    makeContextCurrent();
    ResolveMultisampledFramebuffer(0, 0, cached_width_, cached_height_);
  }
}

void WebGraphicsContext3DInProcessImpl::postSubBufferCHROMIUM(
    int x, int y, int width, int height) {
  DCHECK(gl_surface_->HasExtension("GL_CHROMIUM_post_sub_buffer"));
  gl_surface_->PostSubBuffer(x, y, width, height);
}

namespace {

int CreateTextureObject(GLenum target) {
  GLuint texture = 0;
  glGenTextures(1, &texture);
  glBindTexture(target, texture);
  glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  return texture;
}

}  // anonymous namespace

void WebGraphicsContext3DInProcessImpl::reshape(int width, int height) {
  cached_width_ = width;
  cached_height_ = height;
  makeContextCurrent();

  bool must_restore_fbo = false;
  if (gl_surface_->IsOffscreen())
    must_restore_fbo = AllocateOffscreenFrameBuffer(width, height);

  gl_surface_->Resize(gfx::Size(width, height));
  ClearRenderTarget();

  if (must_restore_fbo)
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bound_fbo_);

#ifdef FLIP_FRAMEBUFFER_VERTICALLY
  if (scanline_) {
    delete[] scanline_;
    scanline_ = 0;
  }
  scanline_ = new unsigned char[width * 4];
#endif  // FLIP_FRAMEBUFFER_VERTICALLY
}

bool WebGraphicsContext3DInProcessImpl::AllocateOffscreenFrameBuffer(
    int width, int height) {
  GLenum target = GL_TEXTURE_2D;

  if (!texture_) {
    // Generate the texture object
    texture_ = CreateTextureObject(target);
    // Generate the framebuffer object
    glGenFramebuffersEXT(1, &fbo_);
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_);
    bound_fbo_ = fbo_;
    if (attributes_.depth || attributes_.stencil)
      glGenRenderbuffersEXT(1, &depth_stencil_buffer_);
    // Generate the multisample framebuffer object
    if (attributes_.antialias) {
      glGenFramebuffersEXT(1, &multisample_fbo_);
      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, multisample_fbo_);
      bound_fbo_ = multisample_fbo_;
      glGenRenderbuffersEXT(1, &multisample_color_buffer_);
      if (attributes_.depth || attributes_.stencil)
        glGenRenderbuffersEXT(1, &multisample_depth_stencil_buffer_);
    }
  }

  GLint internal_multisampled_color_format = 0;
  GLint internal_color_format = 0;
  GLint color_format = 0;
  GLint internal_depth_stencil_format = 0;
  if (attributes_.alpha) {
    // GL_RGBA8_OES == GL_RGBA8
    internal_multisampled_color_format = GL_RGBA8;
    internal_color_format = is_gles2_ ? GL_RGBA : GL_RGBA8;
    color_format = GL_RGBA;
  } else {
    // GL_RGB8_OES == GL_RGB8
    internal_multisampled_color_format = GL_RGB8;
    internal_color_format = is_gles2_ ? GL_RGB : GL_RGB8;
    color_format = GL_RGB;
  }
  if (attributes_.stencil || attributes_.depth) {
    // We don't allow the logic where stencil is required and depth is not.
    // See GraphicsContext3DInternal constructor.
    if (attributes_.stencil && attributes_.depth) {
      internal_depth_stencil_format = GL_DEPTH24_STENCIL8_EXT;
    } else {
      if (is_gles2_)
        internal_depth_stencil_format = GL_DEPTH_COMPONENT16;
      else
        internal_depth_stencil_format = GL_DEPTH_COMPONENT;
    }
  }

  bool must_restore_fbo = false;

  // Resize multisampling FBO
  if (attributes_.antialias) {
    GLint max_sample_count;
    glGetIntegerv(GL_MAX_SAMPLES_EXT, &max_sample_count);
    GLint sample_count = std::min(8, max_sample_count);
    if (bound_fbo_ != multisample_fbo_) {
      must_restore_fbo = true;
      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, multisample_fbo_);
    }
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, multisample_color_buffer_);
    if (have_ext_framebuffer_multisample_) {
      glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT,
                                          sample_count,
                                          internal_multisampled_color_format,
                                          width,
                                          height);
    } else {
      DCHECK(have_angle_framebuffer_multisample_);
      glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER_EXT,
                                            sample_count,
                                            internal_multisampled_color_format,
                                            width,
                                            height);
    }
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
                                 GL_COLOR_ATTACHMENT0_EXT,
                                 GL_RENDERBUFFER_EXT,
                                 multisample_color_buffer_);
    if (attributes_.stencil || attributes_.depth) {
      glBindRenderbufferEXT(GL_RENDERBUFFER_EXT,
                            multisample_depth_stencil_buffer_);
      if (have_ext_framebuffer_multisample_) {
        glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT,
                                            sample_count,
                                            internal_depth_stencil_format,
                                            width,
                                            height);
      } else {
        DCHECK(have_angle_framebuffer_multisample_);
        glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER_EXT,
                                              sample_count,
                                              internal_depth_stencil_format,
                                              width,
                                              height);
      }
      if (attributes_.stencil)
        glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
                                     GL_STENCIL_ATTACHMENT_EXT,
                                     GL_RENDERBUFFER_EXT,
                                     multisample_depth_stencil_buffer_);
      if (attributes_.depth)
        glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
                                     GL_DEPTH_ATTACHMENT_EXT,
                                     GL_RENDERBUFFER_EXT,
                                     multisample_depth_stencil_buffer_);
    }
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
    GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
    if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
      LOG(ERROR) << "Multisampling framebuffer was incomplete";

      // FIXME: cleanup.
      NOTIMPLEMENTED();
    }
  }

  // Resize regular FBO
  if (bound_fbo_ != fbo_) {
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_);
    must_restore_fbo = true;
  }
  glBindTexture(target, texture_);
  glTexImage2D(target, 0, internal_color_format,
               width, height,
               0, color_format, GL_UNSIGNED_BYTE, 0);
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
                            GL_COLOR_ATTACHMENT0_EXT,
                            target,
                            texture_,
                            0);
  glBindTexture(target, 0);
  if (!attributes_.antialias && (attributes_.stencil || attributes_.depth)) {
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_stencil_buffer_);
    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
                             internal_depth_stencil_format,
                             width, height);
    if (attributes_.stencil)
      glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
                                   GL_STENCIL_ATTACHMENT_EXT,
                                   GL_RENDERBUFFER_EXT,
                                   depth_stencil_buffer_);
    if (attributes_.depth)
      glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
                                   GL_DEPTH_ATTACHMENT_EXT,
                                   GL_RENDERBUFFER_EXT,
                                   depth_stencil_buffer_);
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
  }
  GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
  if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
    LOG(ERROR) << "Framebuffer was incomplete";

    // FIXME: cleanup.
    NOTIMPLEMENTED();
  }

  if (attributes_.antialias) {
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, multisample_fbo_);
    if (bound_fbo_ == multisample_fbo_)
      must_restore_fbo = false;
  }
  return must_restore_fbo;
}

void WebGraphicsContext3DInProcessImpl::ClearRenderTarget() {
  // Initialize renderbuffers to 0.
  GLfloat clearColor[] = {0, 0, 0, 0}, clearDepth = 0;
  GLint clearStencil = 0;
  GLboolean colorMask[] = {GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE};
  GLboolean depthMask = GL_TRUE;
  GLuint stencilMask = 0xffffffff;
  GLboolean isScissorEnabled = GL_FALSE;
  GLboolean isDitherEnabled = GL_FALSE;
  GLbitfield clearMask = GL_COLOR_BUFFER_BIT;
  glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor);
  glClearColor(0, 0, 0, 0);
  glGetBooleanv(GL_COLOR_WRITEMASK, colorMask);
  glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  if (attributes_.depth) {
    glGetFloatv(GL_DEPTH_CLEAR_VALUE, &clearDepth);
    glClearDepth(1);
    glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMask);
    glDepthMask(GL_TRUE);
    clearMask |= GL_DEPTH_BUFFER_BIT;
  }
  if (attributes_.stencil) {
    glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &clearStencil);
    glClearStencil(0);
    glGetIntegerv(GL_STENCIL_WRITEMASK,
                  reinterpret_cast<GLint*>(&stencilMask));
    glStencilMaskSeparate(GL_FRONT, 0xffffffff);
    clearMask |= GL_STENCIL_BUFFER_BIT;
  }
  isScissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
  glDisable(GL_SCISSOR_TEST);
  isDitherEnabled = glIsEnabled(GL_DITHER);
  glDisable(GL_DITHER);

  glClear(clearMask);

  glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
  glColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]);
  if (attributes_.depth) {
    glClearDepth(clearDepth);
    glDepthMask(depthMask);
  }
  if (attributes_.stencil) {
    glClearStencil(clearStencil);
    glStencilMaskSeparate(GL_FRONT, stencilMask);
  }
  if (isScissorEnabled)
    glEnable(GL_SCISSOR_TEST);
  else
    glDisable(GL_SCISSOR_TEST);
  if (isDitherEnabled)
    glEnable(GL_DITHER);
  else
    glDisable(GL_DITHER);
}

#ifdef FLIP_FRAMEBUFFER_VERTICALLY
void WebGraphicsContext3DInProcessImpl::FlipVertically(
    unsigned char* framebuffer, unsigned int width, unsigned int height) {
  unsigned char* scanline = scanline_;
  if (!scanline)
    return;
  unsigned int row_bytes = width * 4;
  unsigned int count = height / 2;
  for (unsigned int i = 0; i < count; i++) {
    unsigned char* row_a = framebuffer + i * row_bytes;
    unsigned char* row_b = framebuffer + (height - i - 1) * row_bytes;
    // FIXME: this is where the multiplication of the alpha
    // channel into the color buffer will need to occur if the
    // user specifies the "premultiplyAlpha" flag in the context
    // creation attributes.
    memcpy(scanline, row_b, row_bytes);
    memcpy(row_b, row_a, row_bytes);
    memcpy(row_a, scanline, row_bytes);
  }
}
#endif

bool WebGraphicsContext3DInProcessImpl::readBackFramebuffer(
    unsigned char* pixels, size_t bufferSize, WebGLId framebuffer,
    int width, int height) {
  if (bufferSize != static_cast<size_t>(4 * width * height))
    return false;

  makeContextCurrent();

  // Earlier versions of this code used the GPU to flip the
  // framebuffer vertically before reading it back for compositing
  // via software. This code was quite complicated, used a lot of
  // GPU memory, and didn't provide an obvious speedup. Since this
  // vertical flip is only a temporary solution anyway until Chrome
  // is fully GPU composited, it wasn't worth the complexity.

  // In this implementation fbo_, not 0, is the drawing buffer, so
  // special-case that.
  if (framebuffer == 0)
    framebuffer = fbo_;

  if (framebuffer == fbo_)
    ResolveMultisampledFramebuffer(0, 0, width, height);
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);

  GLint pack_alignment = 4;
  bool must_restore_pack_alignment = false;
  glGetIntegerv(GL_PACK_ALIGNMENT, &pack_alignment);
  if (pack_alignment > 4) {
    glPixelStorei(GL_PACK_ALIGNMENT, 4);
    must_restore_pack_alignment = true;
  }

  if (is_gles2_) {
    // FIXME: consider testing for presence of GL_OES_read_format
    // and GL_EXT_read_format_bgra, and using GL_BGRA_EXT here
    // directly.
    glReadPixels(0, 0, width, height,
                 GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    for (size_t i = 0; i < bufferSize; i += 4) {
      std::swap(pixels[i], pixels[i + 2]);
    }
  } else {
    glReadPixels(0, 0, width, height,
                 GL_BGRA, GL_UNSIGNED_BYTE, pixels);
  }

  if (must_restore_pack_alignment)
    glPixelStorei(GL_PACK_ALIGNMENT, pack_alignment);

  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bound_fbo_);

#ifdef FLIP_FRAMEBUFFER_VERTICALLY
  if (pixels)
    FlipVertically(pixels, width, height);
#endif

  return true;
}

bool WebGraphicsContext3DInProcessImpl::readBackFramebuffer(
    unsigned char* pixels, size_t bufferSize) {
  return readBackFramebuffer(pixels, bufferSize, fbo_, width(), height());
}

void WebGraphicsContext3DInProcessImpl::synthesizeGLError(WGC3Denum error) {
  if (synthetic_errors_set_.find(error) == synthetic_errors_set_.end()) {
    synthetic_errors_set_.insert(error);
    synthetic_errors_list_.push_back(error);
  }
}

void* WebGraphicsContext3DInProcessImpl::mapBufferSubDataCHROMIUM(
    WGC3Denum target, WGC3Dintptr offset,
    WGC3Dsizeiptr size, WGC3Denum access) {
  return 0;
}

void WebGraphicsContext3DInProcessImpl::unmapBufferSubDataCHROMIUM(
    const void* mem) {
}

void* WebGraphicsContext3DInProcessImpl::mapTexSubImage2DCHROMIUM(
    WGC3Denum target, WGC3Dint level, WGC3Dint xoffset, WGC3Dint yoffset,
    WGC3Dsizei width, WGC3Dsizei height, WGC3Denum format, WGC3Denum type,
    WGC3Denum access) {
  return 0;
}

void WebGraphicsContext3DInProcessImpl::unmapTexSubImage2DCHROMIUM(
    const void* mem) {
}

void WebGraphicsContext3DInProcessImpl::setVisibilityCHROMIUM(bool visible) {
}

void WebGraphicsContext3DInProcessImpl::
    setMemoryAllocationChangedCallbackCHROMIUM(
        WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* callback) {
}

void WebGraphicsContext3DInProcessImpl::discardFramebufferEXT(
    WGC3Denum target, WGC3Dsizei numAttachments, const WGC3Denum* attachments) {
}

void WebGraphicsContext3DInProcessImpl::ensureFramebufferCHROMIUM() {
}

void WebGraphicsContext3DInProcessImpl::copyTextureToParentTextureCHROMIUM(
    WebGLId id, WebGLId id2) {
  NOTIMPLEMENTED();
}

void WebGraphicsContext3DInProcessImpl::bindUniformLocationCHROMIUM(
    WebGLId program, WGC3Dint location, const WGC3Dchar* uniform) {
  NOTIMPLEMENTED();
}

void WebGraphicsContext3DInProcessImpl::genMailboxCHROMIUM(
    WGC3Dbyte* mailbox) {
  NOTIMPLEMENTED();
}

void WebGraphicsContext3DInProcessImpl::produceTextureCHROMIUM(
    WGC3Denum target, const WGC3Dbyte* mailbox) {
  NOTIMPLEMENTED();
}

void WebGraphicsContext3DInProcessImpl::consumeTextureCHROMIUM(
    WGC3Denum target, const WGC3Dbyte* mailbox) {
  NOTIMPLEMENTED();
}

WebString WebGraphicsContext3DInProcessImpl::
    getRequestableExtensionsCHROMIUM() {
  return WebString();
}

void WebGraphicsContext3DInProcessImpl::requestExtensionCHROMIUM(const char*) {
}

void WebGraphicsContext3DInProcessImpl::blitFramebufferCHROMIUM(
    WGC3Dint srcX0, WGC3Dint srcY0, WGC3Dint srcX1, WGC3Dint srcY1,
    WGC3Dint dstX0, WGC3Dint dstY0, WGC3Dint dstX1, WGC3Dint dstY1,
    WGC3Dbitfield mask, WGC3Denum filter) {
}

void WebGraphicsContext3DInProcessImpl::renderbufferStorageMultisampleCHROMIUM(
    WGC3Denum target, WGC3Dsizei samples, WGC3Denum internalformat,
    WGC3Dsizei width, WGC3Dsizei height) {
}

// Helper macros to reduce the amount of code.

#define DELEGATE_TO_GL(name, glname)                                           \
void WebGraphicsContext3DInProcessImpl::name() {                               \
  makeContextCurrent();                                                        \
  gl##glname();                                                                \
}

#define DELEGATE_TO_GL_1(name, glname, t1)                                     \
void WebGraphicsContext3DInProcessImpl::name(t1 a1) {                          \
  makeContextCurrent();                                                        \
  gl##glname(a1);                                                              \
}

#define DELEGATE_TO_GL_1R(name, glname, t1, rt)                                \
rt WebGraphicsContext3DInProcessImpl::name(t1 a1) {                            \
  makeContextCurrent();                                                        \
  return gl##glname(a1);                                                       \
}

#define DELEGATE_TO_GL_1RB(name, glname, t1, rt)                               \
rt WebGraphicsContext3DInProcessImpl::name(t1 a1) {                            \
  makeContextCurrent();                                                        \
  return gl##glname(a1) ? true : false;                                        \
}

#define DELEGATE_TO_GL_2(name, glname, t1, t2)                                 \
void WebGraphicsContext3DInProcessImpl::name(t1 a1, t2 a2) {                   \
  makeContextCurrent();                                                        \
  gl##glname(a1, a2);                                                          \
}

#define DELEGATE_TO_GL_2R(name, glname, t1, t2, rt)                            \
rt WebGraphicsContext3DInProcessImpl::name(t1 a1, t2 a2) {                     \
  makeContextCurrent();                                                        \
  return gl##glname(a1, a2);                                                   \
}

#define DELEGATE_TO_GL_3(name, glname, t1, t2, t3)                             \
void WebGraphicsContext3DInProcessImpl::name(t1 a1, t2 a2, t3 a3) {            \
  makeContextCurrent();                                                        \
  gl##glname(a1, a2, a3);                                                      \
}

#define DELEGATE_TO_GL_4(name, glname, t1, t2, t3, t4)                         \
void WebGraphicsContext3DInProcessImpl::name(t1 a1, t2 a2, t3 a3, t4 a4) {     \
  makeContextCurrent();                                                        \
  gl##glname(a1, a2, a3, a4);                                                  \
}

#define DELEGATE_TO_GL_5(name, glname, t1, t2, t3, t4, t5)                     \
void WebGraphicsContext3DInProcessImpl::name(t1 a1, t2 a2, t3 a3, t4 a4,       \
                                             t5 a5) {                          \
  makeContextCurrent();                                                        \
  gl##glname(a1, a2, a3, a4, a5);                                              \
}

#define DELEGATE_TO_GL_6(name, glname, t1, t2, t3, t4, t5, t6)                 \
void WebGraphicsContext3DInProcessImpl::name(t1 a1, t2 a2, t3 a3, t4 a4,       \
                                             t5 a5, t6 a6) {                   \
  makeContextCurrent();                                                        \
  gl##glname(a1, a2, a3, a4, a5, a6);                                          \
}

#define DELEGATE_TO_GL_7(name, glname, t1, t2, t3, t4, t5, t6, t7)             \
void WebGraphicsContext3DInProcessImpl::name(t1 a1, t2 a2, t3 a3, t4 a4,       \
                                             t5 a5, t6 a6, t7 a7) {            \
  makeContextCurrent();                                                        \
  gl##glname(a1, a2, a3, a4, a5, a6, a7);                                      \
}

#define DELEGATE_TO_GL_8(name, glname, t1, t2, t3, t4, t5, t6, t7, t8)         \
void WebGraphicsContext3DInProcessImpl::name(t1 a1, t2 a2, t3 a3, t4 a4,       \
                                             t5 a5, t6 a6, t7 a7, t8 a8) {     \
  makeContextCurrent();                                                        \
  gl##glname(a1, a2, a3, a4, a5, a6, a7, a8);                                  \
}

#define DELEGATE_TO_GL_9(name, glname, t1, t2, t3, t4, t5, t6, t7, t8, t9)     \
void WebGraphicsContext3DInProcessImpl::name(t1 a1, t2 a2, t3 a3, t4 a4,       \
                                             t5 a5, t6 a6, t7 a7, t8 a8,       \
                                             t9 a9) {                          \
  makeContextCurrent();                                                        \
  gl##glname(a1, a2, a3, a4, a5, a6, a7, a8, a9);                              \
}

void WebGraphicsContext3DInProcessImpl::activeTexture(WGC3Denum texture) {
  // FIXME: query number of textures available.
  if (texture < GL_TEXTURE0 || texture > GL_TEXTURE0+32)
    // FIXME: raise exception.
    return;

  makeContextCurrent();
  glActiveTexture(texture);
}

DELEGATE_TO_GL_2(attachShader, AttachShader, WebGLId, WebGLId)

DELEGATE_TO_GL_3(bindAttribLocation, BindAttribLocation,
                 WebGLId, WGC3Duint, const WGC3Dchar*)

DELEGATE_TO_GL_2(bindBuffer, BindBuffer, WGC3Denum, WebGLId);

void WebGraphicsContext3DInProcessImpl::bindFramebuffer(
    WGC3Denum target, WebGLId framebuffer) {
  makeContextCurrent();
  if (!framebuffer)
    framebuffer = (attributes_.antialias ? multisample_fbo_ : fbo_);
  if (framebuffer != bound_fbo_) {
    glBindFramebufferEXT(target, framebuffer);
    bound_fbo_ = framebuffer;
  }
}

DELEGATE_TO_GL_2(bindRenderbuffer, BindRenderbufferEXT, WGC3Denum, WebGLId)

void WebGraphicsContext3DInProcessImpl::bindTexture(
    WGC3Denum target, WebGLId texture) {
  makeContextCurrent();
  glBindTexture(target, texture);
  bound_texture_ = texture;
}

DELEGATE_TO_GL_4(blendColor, BlendColor,
                 WGC3Dfloat, WGC3Dfloat, WGC3Dfloat, WGC3Dfloat)

DELEGATE_TO_GL_1(blendEquation, BlendEquation, WGC3Denum)

DELEGATE_TO_GL_2(blendEquationSeparate, BlendEquationSeparate,
                 WGC3Denum, WGC3Denum)

DELEGATE_TO_GL_2(blendFunc, BlendFunc, WGC3Denum, WGC3Denum)

DELEGATE_TO_GL_4(blendFuncSeparate, BlendFuncSeparate,
                 WGC3Denum, WGC3Denum, WGC3Denum, WGC3Denum)

DELEGATE_TO_GL_4(bufferData, BufferData,
                 WGC3Denum, WGC3Dsizeiptr, const void*, WGC3Denum)

DELEGATE_TO_GL_4(bufferSubData, BufferSubData,
                 WGC3Denum, WGC3Dintptr, WGC3Dsizeiptr, const void*)

DELEGATE_TO_GL_1R(checkFramebufferStatus, CheckFramebufferStatusEXT,
                  WGC3Denum, WGC3Denum)

DELEGATE_TO_GL_1(clear, Clear, WGC3Dbitfield)

DELEGATE_TO_GL_4(clearColor, ClearColor,
                 WGC3Dclampf, WGC3Dclampf, WGC3Dclampf, WGC3Dclampf)

DELEGATE_TO_GL_1(clearDepth, ClearDepth, WGC3Dclampf)

DELEGATE_TO_GL_1(clearStencil, ClearStencil, WGC3Dint)

DELEGATE_TO_GL_4(colorMask, ColorMask,
                 WGC3Dboolean, WGC3Dboolean, WGC3Dboolean, WGC3Dboolean)

void WebGraphicsContext3DInProcessImpl::compileShader(WebGLId shader) {
  makeContextCurrent();

  ShaderSourceMap::iterator result = shader_source_map_.find(shader);
  if (result == shader_source_map_.end()) {
    // Passing down to gl driver to generate the correct error; or the case
    // where the shader deletion is delayed when it's attached to a program.
    glCompileShader(shader);
    return;
  }
  ShaderSourceEntry* entry = result->second;
  DCHECK(entry);

  if (!AngleValidateShaderSource(entry)) {
    // Shader didn't validate; don't move forward with compiling
    // translated source.
    return;
  }

  const char* translated_source = entry->translated_source.get();
  int shader_length = translated_source ? strlen(translated_source) : 0;
  glShaderSource(
      shader, 1, const_cast<const char**>(&translated_source), &shader_length);
  glCompileShader(shader);

#ifndef NDEBUG
  int compileStatus;
  glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
  // DCHECK that ANGLE generated GLSL will be accepted by OpenGL
  DCHECK(compileStatus == GL_TRUE);
#endif
}

DELEGATE_TO_GL_8(compressedTexImage2D, CompressedTexImage2D,
                 WGC3Denum, WGC3Dint, WGC3Denum, WGC3Dint, WGC3Dint,
                 WGC3Dsizei, WGC3Dsizei, const void*)

DELEGATE_TO_GL_9(compressedTexSubImage2D, CompressedTexSubImage2D,
                 WGC3Denum, WGC3Dint, WGC3Dint, WGC3Dint, WGC3Dint, WGC3Dint,
                 WGC3Denum, WGC3Dsizei, const void*)

void WebGraphicsContext3DInProcessImpl::copyTexImage2D(
    WGC3Denum target, WGC3Dint level, WGC3Denum internalformat, WGC3Dint x,
    WGC3Dint y, WGC3Dsizei width, WGC3Dsizei height, WGC3Dint border) {
  makeContextCurrent();

  bool needsResolve = (attributes_.antialias && bound_fbo_ == multisample_fbo_);
  if (needsResolve) {
    ResolveMultisampledFramebuffer(x, y, width, height);
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_);
  }

  glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);

  if (needsResolve)
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bound_fbo_);
}

void WebGraphicsContext3DInProcessImpl::copyTexSubImage2D(
    WGC3Denum target, WGC3Dint level, WGC3Dint xoffset, WGC3Dint yoffset,
    WGC3Dint x, WGC3Dint y, WGC3Dsizei width, WGC3Dsizei height) {
  makeContextCurrent();

  bool needsResolve = (attributes_.antialias && bound_fbo_ == multisample_fbo_);
  if (needsResolve) {
    ResolveMultisampledFramebuffer(x, y, width, height);
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_);
  }

  glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);

  if (needsResolve)
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bound_fbo_);
}

DELEGATE_TO_GL_1(cullFace, CullFace, WGC3Denum)

DELEGATE_TO_GL_1(depthFunc, DepthFunc, WGC3Denum)

DELEGATE_TO_GL_1(depthMask, DepthMask, WGC3Dboolean)

DELEGATE_TO_GL_2(depthRange, DepthRange, WGC3Dclampf, WGC3Dclampf)

DELEGATE_TO_GL_2(detachShader, DetachShader, WebGLId, WebGLId)

DELEGATE_TO_GL_1(disable, Disable, WGC3Denum)

DELEGATE_TO_GL_1(disableVertexAttribArray, DisableVertexAttribArray, WGC3Duint)

DELEGATE_TO_GL_3(drawArrays, DrawArrays, WGC3Denum, WGC3Dint, WGC3Dsizei)

void WebGraphicsContext3DInProcessImpl::drawElements(
    WGC3Denum mode, WGC3Dsizei count, WGC3Denum type, WGC3Dintptr offset) {
  makeContextCurrent();
  glDrawElements(mode, count, type,
                 reinterpret_cast<void*>(static_cast<intptr_t>(offset)));
}

DELEGATE_TO_GL_1(enable, Enable, WGC3Denum)

DELEGATE_TO_GL_1(enableVertexAttribArray, EnableVertexAttribArray, WGC3Duint)

DELEGATE_TO_GL(finish, Finish)

DELEGATE_TO_GL(flush, Flush)

DELEGATE_TO_GL_4(framebufferRenderbuffer, FramebufferRenderbufferEXT,
                 WGC3Denum, WGC3Denum, WGC3Denum, WebGLId)

DELEGATE_TO_GL_5(framebufferTexture2D, FramebufferTexture2DEXT,
                 WGC3Denum, WGC3Denum, WGC3Denum, WebGLId, WGC3Dint)

DELEGATE_TO_GL_1(frontFace, FrontFace, WGC3Denum)

void WebGraphicsContext3DInProcessImpl::generateMipmap(WGC3Denum target) {
  makeContextCurrent();
  if (is_gles2_ || have_ext_framebuffer_object_)
    glGenerateMipmapEXT(target);
  // FIXME: provide alternative code path? This will be unpleasant
  // to implement if glGenerateMipmapEXT is not available -- it will
  // require a texture readback and re-upload.
}

bool WebGraphicsContext3DInProcessImpl::getActiveAttrib(
    WebGLId program, WGC3Duint index, ActiveInfo& info) {
  makeContextCurrent();
  if (!program) {
    synthesizeGLError(GL_INVALID_VALUE);
    return false;
  }
  GLint max_name_length = -1;
  glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_name_length);
  if (max_name_length < 0)
    return false;
  scoped_array<GLchar> name(new GLchar[max_name_length]);
  GLsizei length = 0;
  GLint size = -1;
  GLenum type = 0;
  glGetActiveAttrib(program, index, max_name_length,
                    &length, &size, &type, name.get());
  if (size < 0) {
    return false;
  }
  info.name = WebString::fromUTF8(name.get(), length);
  info.type = type;
  info.size = size;
  return true;
}

bool WebGraphicsContext3DInProcessImpl::getActiveUniform(
    WebGLId program, WGC3Duint index, ActiveInfo& info) {
  makeContextCurrent();
  GLint max_name_length = -1;
  glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_name_length);
  if (max_name_length < 0)
    return false;
  scoped_array<GLchar> name(new GLchar[max_name_length]);
  GLsizei length = 0;
  GLint size = -1;
  GLenum type = 0;
  glGetActiveUniform(program, index, max_name_length,
                     &length, &size, &type, name.get());
  if (size < 0) {
    return false;
  }
  info.name = WebString::fromUTF8(name.get(), length);
  info.type = type;
  info.size = size;
  return true;
}

DELEGATE_TO_GL_4(getAttachedShaders, GetAttachedShaders,
                 WebGLId, WGC3Dsizei, WGC3Dsizei*, WebGLId*)

DELEGATE_TO_GL_2R(getAttribLocation, GetAttribLocation,
                  WebGLId, const WGC3Dchar*, WGC3Dint)

DELEGATE_TO_GL_2(getBooleanv, GetBooleanv,
                 WGC3Denum, WGC3Dboolean*)

DELEGATE_TO_GL_3(getBufferParameteriv, GetBufferParameteriv,
                 WGC3Denum, WGC3Denum, WGC3Dint*)

WebGraphicsContext3D::Attributes WebGraphicsContext3DInProcessImpl::
    getContextAttributes() {
  return attributes_;
}

WGC3Denum WebGraphicsContext3DInProcessImpl::getError() {
  DCHECK(synthetic_errors_list_.size() == synthetic_errors_set_.size());
  if (!synthetic_errors_set_.empty()) {
    WGC3Denum error = synthetic_errors_list_.front();
    synthetic_errors_list_.pop_front();
    synthetic_errors_set_.erase(error);
    return error;
  }

  makeContextCurrent();
  return glGetError();
}

bool WebGraphicsContext3DInProcessImpl::isContextLost() {
  return false;
}

DELEGATE_TO_GL_2(getFloatv, GetFloatv, WGC3Denum, WGC3Dfloat*)

void WebGraphicsContext3DInProcessImpl::getFramebufferAttachmentParameteriv(
    WGC3Denum target, WGC3Denum attachment,
    WGC3Denum pname, WGC3Dint* value) {
  makeContextCurrent();
  if (attachment == GL_DEPTH_STENCIL_ATTACHMENT)
    attachment = GL_DEPTH_ATTACHMENT;  // Or GL_STENCIL_ATTACHMENT;
                                       // either works.
  glGetFramebufferAttachmentParameterivEXT(target, attachment, pname, value);
}

void WebGraphicsContext3DInProcessImpl::getIntegerv(
    WGC3Denum pname, WGC3Dint* value) {
  makeContextCurrent();
  if (is_gles2_) {
    glGetIntegerv(pname, value);
    return;
  }
  // Need to emulate MAX_FRAGMENT/VERTEX_UNIFORM_VECTORS and
  // MAX_VARYING_VECTORS because desktop GL's corresponding queries
  // return the number of components whereas GLES2 return the number
  // of vectors (each vector has 4 components).  Therefore, the value
  // returned by desktop GL needs to be divided by 4.
  switch (pname) {
  case MAX_FRAGMENT_UNIFORM_VECTORS:
    glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, value);
    *value /= 4;
    break;
  case MAX_VERTEX_UNIFORM_VECTORS:
    glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, value);
    *value /= 4;
    break;
  case MAX_VARYING_VECTORS:
    glGetIntegerv(GL_MAX_VARYING_FLOATS, value);
    *value /= 4;
    break;
  default:
    glGetIntegerv(pname, value);
  }
}

DELEGATE_TO_GL_3(getProgramiv, GetProgramiv, WebGLId, WGC3Denum, WGC3Dint*)

WebString WebGraphicsContext3DInProcessImpl::getProgramInfoLog(
    WebGLId program) {
  makeContextCurrent();
  GLint log_length;
  glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
  if (!log_length)
    return WebString();
  scoped_array<GLchar> log(new GLchar[log_length]);
  GLsizei returned_log_length;
  glGetProgramInfoLog(program, log_length, &returned_log_length, log.get());
  DCHECK(log_length == returned_log_length + 1);
  WebString res = WebString::fromUTF8(log.get(), returned_log_length);
  return res;
}

DELEGATE_TO_GL_3(getRenderbufferParameteriv, GetRenderbufferParameterivEXT,
                 WGC3Denum, WGC3Denum, WGC3Dint*)

void WebGraphicsContext3DInProcessImpl::getShaderiv(
    WebGLId shader, WGC3Denum pname, WGC3Dint* value) {
  makeContextCurrent();

  ShaderSourceMap::iterator result = shader_source_map_.find(shader);
  if (result != shader_source_map_.end()) {
    ShaderSourceEntry* entry = result->second;
    DCHECK(entry);
    switch (pname) {
      case GL_COMPILE_STATUS:
        if (!entry->is_valid) {
          *value = 0;
          return;
        }
        break;
      case GL_INFO_LOG_LENGTH:
        if (!entry->is_valid) {
          *value = entry->log.get() ? strlen(entry->log.get()) : 0;
          if (*value)
            (*value)++;
          return;
        }
        break;
      case GL_SHADER_SOURCE_LENGTH:
        *value = entry->source.get() ? strlen(entry->source.get()) : 0;
        if (*value)
          (*value)++;
        return;
    }
  }

  glGetShaderiv(shader, pname, value);
}

DELEGATE_TO_GL_4(getShaderPrecisionFormat, GetShaderPrecisionFormat,
                 WGC3Denum, WGC3Denum, WGC3Dint*, WGC3Dint*)

WebString WebGraphicsContext3DInProcessImpl::getShaderInfoLog(WebGLId shader) {
  makeContextCurrent();

  ShaderSourceMap::iterator result = shader_source_map_.find(shader);
  if (result != shader_source_map_.end()) {
    ShaderSourceEntry* entry = result->second;
    DCHECK(entry);
    if (!entry->is_valid) {
      if (!entry->log.get())
        return WebString();
      WebString res = WebString::fromUTF8(
          entry->log.get(), strlen(entry->log.get()));
      return res;
    }
  }

  GLint log_length = 0;
  glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
  if (log_length <= 1)
    return WebString();
  scoped_array<GLchar> log(new GLchar[log_length]);
  GLsizei returned_log_length;
  glGetShaderInfoLog(shader, log_length, &returned_log_length, log.get());
  DCHECK(log_length == returned_log_length + 1);
  WebString res = WebString::fromUTF8(log.get(), returned_log_length);
  return res;
}

WebString WebGraphicsContext3DInProcessImpl::getShaderSource(WebGLId shader) {
  makeContextCurrent();

  ShaderSourceMap::iterator result = shader_source_map_.find(shader);
  if (result != shader_source_map_.end()) {
    ShaderSourceEntry* entry = result->second;
    DCHECK(entry);
    if (!entry->source.get())
      return WebString();
    WebString res = WebString::fromUTF8(
        entry->source.get(), strlen(entry->source.get()));
    return res;
  }

  GLint log_length = 0;
  glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &log_length);
  if (log_length <= 1)
    return WebString();
  scoped_array<GLchar> log(new GLchar[log_length]);
  GLsizei returned_log_length;
  glGetShaderSource(shader, log_length, &returned_log_length, log.get());
  DCHECK(log_length == returned_log_length + 1);
  WebString res = WebString::fromUTF8(log.get(), returned_log_length);
  return res;
}

WebString WebGraphicsContext3DInProcessImpl::getString(WGC3Denum name) {
  makeContextCurrent();
  std::string result;
  if (name == GL_EXTENSIONS) {
    result = gl_context_->GetExtensions();
    if (!is_gles2_) {
      std::vector<std::string> split;
      base::SplitString(result, ' ', &split);
      if (std::find(split.begin(), split.end(), "GL_EXT_bgra") != split.end()) {
        // If we support GL_EXT_bgra, pretend we support a couple of GLES2
        // extension that are a subset of it.
        result += " GL_EXT_texture_format_BGRA8888 GL_EXT_read_format_bgra";
      }
    }
    std::string surface_extensions = gl_surface_->GetExtensions();
    if (!surface_extensions.empty())
      result += " " + surface_extensions;
  } else {
    result = reinterpret_cast<const char*>(glGetString(name));
  }
  return WebString::fromUTF8(result.c_str());
}

DELEGATE_TO_GL_3(getTexParameterfv, GetTexParameterfv,
                 WGC3Denum, WGC3Denum, WGC3Dfloat*)

DELEGATE_TO_GL_3(getTexParameteriv, GetTexParameteriv,
                 WGC3Denum, WGC3Denum, WGC3Dint*)

DELEGATE_TO_GL_3(getUniformfv, GetUniformfv, WebGLId, WGC3Dint, WGC3Dfloat*)

DELEGATE_TO_GL_3(getUniformiv, GetUniformiv, WebGLId, WGC3Dint, WGC3Dint*)

DELEGATE_TO_GL_2R(getUniformLocation, GetUniformLocation,
                  WebGLId, const WGC3Dchar*, WGC3Dint)

DELEGATE_TO_GL_3(getVertexAttribfv, GetVertexAttribfv,
                 WGC3Duint, WGC3Denum, WGC3Dfloat*)

DELEGATE_TO_GL_3(getVertexAttribiv, GetVertexAttribiv,
                 WGC3Duint, WGC3Denum, WGC3Dint*)

WGC3Dsizeiptr WebGraphicsContext3DInProcessImpl::getVertexAttribOffset(
    WGC3Duint index, WGC3Denum pname) {
  makeContextCurrent();
  void* pointer;
  glGetVertexAttribPointerv(index, pname, &pointer);
  return static_cast<WGC3Dsizeiptr>(reinterpret_cast<intptr_t>(pointer));
}

DELEGATE_TO_GL_2(hint, Hint, WGC3Denum, WGC3Denum)

DELEGATE_TO_GL_1RB(isBuffer, IsBuffer, WebGLId, WGC3Dboolean)

DELEGATE_TO_GL_1RB(isEnabled, IsEnabled, WGC3Denum, WGC3Dboolean)

DELEGATE_TO_GL_1RB(isFramebuffer, IsFramebufferEXT, WebGLId, WGC3Dboolean)

DELEGATE_TO_GL_1RB(isProgram, IsProgram, WebGLId, WGC3Dboolean)

DELEGATE_TO_GL_1RB(isRenderbuffer, IsRenderbufferEXT, WebGLId, WGC3Dboolean)

DELEGATE_TO_GL_1RB(isShader, IsShader, WebGLId, WGC3Dboolean)

DELEGATE_TO_GL_1RB(isTexture, IsTexture, WebGLId, WGC3Dboolean)

DELEGATE_TO_GL_1(lineWidth, LineWidth, WGC3Dfloat)

DELEGATE_TO_GL_1(linkProgram, LinkProgram, WebGLId)

DELEGATE_TO_GL_2(pixelStorei, PixelStorei, WGC3Denum, WGC3Dint)

DELEGATE_TO_GL_2(polygonOffset, PolygonOffset, WGC3Dfloat, WGC3Dfloat)

void WebGraphicsContext3DInProcessImpl::readPixels(
    WGC3Dint x, WGC3Dint y, WGC3Dsizei width, WGC3Dsizei height,
    WGC3Denum format, WGC3Denum type, void* pixels) {
  makeContextCurrent();
  // FIXME: remove the two glFlush calls when the driver bug is fixed, i.e.,
  // all previous rendering calls should be done before reading pixels.
  glFlush();
  bool needs_resolve =
      (attributes_.antialias && bound_fbo_ == multisample_fbo_);
  if (needs_resolve) {
    ResolveMultisampledFramebuffer(x, y, width, height);
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_);
    glFlush();
  }

  glReadPixels(x, y, width, height, format, type, pixels);

  if (needs_resolve)
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, bound_fbo_);
}

void WebGraphicsContext3DInProcessImpl::releaseShaderCompiler() {
}

void WebGraphicsContext3DInProcessImpl::renderbufferStorage(
    WGC3Denum target,
    WGC3Denum internalformat,
    WGC3Dsizei width,
    WGC3Dsizei height) {
  makeContextCurrent();
  if (!is_gles2_) {
    switch (internalformat) {
    case GL_DEPTH_STENCIL:
      internalformat = GL_DEPTH24_STENCIL8_EXT;
      break;
    case GL_DEPTH_COMPONENT16:
      internalformat = GL_DEPTH_COMPONENT;
      break;
    case GL_RGBA4:
    case GL_RGB5_A1:
      internalformat = GL_RGBA;
      break;
    case 0x8D62:  // GL_RGB565
      internalformat = GL_RGB;
      break;
    }
  }
  glRenderbufferStorageEXT(target, internalformat, width, height);
}

DELEGATE_TO_GL_2(sampleCoverage, SampleCoverage, WGC3Dclampf, WGC3Dboolean)

DELEGATE_TO_GL_4(scissor, Scissor, WGC3Dint, WGC3Dint, WGC3Dsizei, WGC3Dsizei)

void WebGraphicsContext3DInProcessImpl::texImage2D(
    WGC3Denum target, WGC3Dint level, WGC3Denum internalFormat,
    WGC3Dsizei width, WGC3Dsizei height, WGC3Dint border,
    WGC3Denum format, WGC3Denum type, const void* pixels) {
  makeContextCurrent();
  if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2) {
    if (format == GL_BGRA_EXT && internalFormat == GL_BGRA_EXT) {
      internalFormat = GL_RGBA;
    } else if (type == GL_FLOAT) {
      if (format == GL_RGBA) {
        internalFormat = GL_RGBA32F_ARB;
      } else if (format == GL_RGB) {
        internalFormat = GL_RGB32F_ARB;
      }
    }
  }
  glTexImage2D(target, level, internalFormat,
               width, height, border, format, type, pixels);
}

void WebGraphicsContext3DInProcessImpl::shaderSource(
    WebGLId shader, const WGC3Dchar* source) {
  makeContextCurrent();
  GLint length = source ? strlen(source) : 0;
  ShaderSourceMap::iterator result = shader_source_map_.find(shader);
  if (result != shader_source_map_.end()) {
    ShaderSourceEntry* entry = result->second;
    DCHECK(entry);
    entry->source.reset(new char[length + 1]);
    if (source)
      memcpy(entry->source.get(), source, (length + 1) * sizeof(char));
    else
      entry->source[0] = '\0';
  } else {
    glShaderSource(shader, 1, &source, &length);
  }
}

DELEGATE_TO_GL_3(stencilFunc, StencilFunc, WGC3Denum, WGC3Dint, WGC3Duint)

DELEGATE_TO_GL_4(stencilFuncSeparate, StencilFuncSeparate,
                 WGC3Denum, WGC3Denum, WGC3Dint, WGC3Duint)

DELEGATE_TO_GL_1(stencilMask, StencilMask, WGC3Duint)

DELEGATE_TO_GL_2(stencilMaskSeparate, StencilMaskSeparate,
                 WGC3Denum, WGC3Duint)

DELEGATE_TO_GL_3(stencilOp, StencilOp,
                 WGC3Denum, WGC3Denum, WGC3Denum)

DELEGATE_TO_GL_4(stencilOpSeparate, StencilOpSeparate,
                 WGC3Denum, WGC3Denum, WGC3Denum, WGC3Denum)

DELEGATE_TO_GL_3(texParameterf, TexParameterf, WGC3Denum, WGC3Denum, WGC3Dfloat)

DELEGATE_TO_GL_3(texParameteri, TexParameteri, WGC3Denum, WGC3Denum, WGC3Dint)

DELEGATE_TO_GL_9(texSubImage2D, TexSubImage2D,
                 WGC3Denum, WGC3Dint, WGC3Dint, WGC3Dint, WGC3Dsizei,
                 WGC3Dsizei, WGC3Denum, WGC3Denum, const void*)

DELEGATE_TO_GL_2(uniform1f, Uniform1f, WGC3Dint, WGC3Dfloat)

DELEGATE_TO_GL_3(uniform1fv, Uniform1fv,
                 WGC3Dint, WGC3Dsizei, const WGC3Dfloat*)

DELEGATE_TO_GL_2(uniform1i, Uniform1i, WGC3Dint, WGC3Dint)

DELEGATE_TO_GL_3(uniform1iv, Uniform1iv, WGC3Dint, WGC3Dsizei, const WGC3Dint*)

DELEGATE_TO_GL_3(uniform2f, Uniform2f, WGC3Dint, WGC3Dfloat, WGC3Dfloat)

DELEGATE_TO_GL_3(uniform2fv, Uniform2fv,
                 WGC3Dint, WGC3Dsizei, const WGC3Dfloat*)

DELEGATE_TO_GL_3(uniform2i, Uniform2i, WGC3Dint, WGC3Dint, WGC3Dint)

DELEGATE_TO_GL_3(uniform2iv, Uniform2iv, WGC3Dint, WGC3Dsizei, const WGC3Dint*)

DELEGATE_TO_GL_4(uniform3f, Uniform3f,
                 WGC3Dint, WGC3Dfloat, WGC3Dfloat, WGC3Dfloat)

DELEGATE_TO_GL_3(uniform3fv, Uniform3fv,
                 WGC3Dint, WGC3Dsizei, const WGC3Dfloat*)

DELEGATE_TO_GL_4(uniform3i, Uniform3i, WGC3Dint, WGC3Dint, WGC3Dint, WGC3Dint)

DELEGATE_TO_GL_3(uniform3iv, Uniform3iv, WGC3Dint, WGC3Dsizei, const WGC3Dint*)

DELEGATE_TO_GL_5(uniform4f, Uniform4f, WGC3Dint,
                 WGC3Dfloat, WGC3Dfloat, WGC3Dfloat, WGC3Dfloat)

DELEGATE_TO_GL_3(uniform4fv, Uniform4fv,
                 WGC3Dint, WGC3Dsizei, const WGC3Dfloat*)

DELEGATE_TO_GL_5(uniform4i, Uniform4i, WGC3Dint,
                 WGC3Dint, WGC3Dint, WGC3Dint, WGC3Dint)

DELEGATE_TO_GL_3(uniform4iv, Uniform4iv, WGC3Dint, WGC3Dsizei, const WGC3Dint*)

DELEGATE_TO_GL_4(uniformMatrix2fv, UniformMatrix2fv,
                 WGC3Dint, WGC3Dsizei, WGC3Dboolean, const WGC3Dfloat*)

DELEGATE_TO_GL_4(uniformMatrix3fv, UniformMatrix3fv,
                 WGC3Dint, WGC3Dsizei, WGC3Dboolean, const WGC3Dfloat*)

DELEGATE_TO_GL_4(uniformMatrix4fv, UniformMatrix4fv,
                 WGC3Dint, WGC3Dsizei, WGC3Dboolean, const WGC3Dfloat*)

DELEGATE_TO_GL_1(useProgram, UseProgram, WebGLId)

DELEGATE_TO_GL_1(validateProgram, ValidateProgram, WebGLId)

DELEGATE_TO_GL_2(vertexAttrib1f, VertexAttrib1f, WGC3Duint, WGC3Dfloat)

DELEGATE_TO_GL_2(vertexAttrib1fv, VertexAttrib1fv, WGC3Duint, const WGC3Dfloat*)

DELEGATE_TO_GL_3(vertexAttrib2f, VertexAttrib2f,
                 WGC3Duint, WGC3Dfloat, WGC3Dfloat)

DELEGATE_TO_GL_2(vertexAttrib2fv, VertexAttrib2fv, WGC3Duint, const WGC3Dfloat*)

DELEGATE_TO_GL_4(vertexAttrib3f, VertexAttrib3f,
                 WGC3Duint, WGC3Dfloat, WGC3Dfloat, WGC3Dfloat)

DELEGATE_TO_GL_2(vertexAttrib3fv, VertexAttrib3fv, WGC3Duint, const WGC3Dfloat*)

DELEGATE_TO_GL_5(vertexAttrib4f, VertexAttrib4f,
                 WGC3Duint, WGC3Dfloat, WGC3Dfloat, WGC3Dfloat, WGC3Dfloat)

DELEGATE_TO_GL_2(vertexAttrib4fv, VertexAttrib4fv, WGC3Duint, const WGC3Dfloat*)

void WebGraphicsContext3DInProcessImpl::vertexAttribPointer(
    WGC3Duint index, WGC3Dint size, WGC3Denum type, WGC3Dboolean normalized,
    WGC3Dsizei stride, WGC3Dintptr offset) {
  makeContextCurrent();
  glVertexAttribPointer(index, size, type, normalized, stride,
                        reinterpret_cast<void*>(static_cast<intptr_t>(offset)));
}

DELEGATE_TO_GL_4(viewport, Viewport, WGC3Dint, WGC3Dint, WGC3Dsizei, WGC3Dsizei)

WebGLId WebGraphicsContext3DInProcessImpl::createBuffer() {
  makeContextCurrent();
  GLuint o = 0;
  glGenBuffersARB(1, &o);
  return o;
}

WebGLId WebGraphicsContext3DInProcessImpl::createFramebuffer() {
  makeContextCurrent();
  GLuint o = 0;
  glGenFramebuffersEXT(1, &o);
  return o;
}

WebGLId WebGraphicsContext3DInProcessImpl::createProgram() {
  makeContextCurrent();
  return glCreateProgram();
}

WebGLId WebGraphicsContext3DInProcessImpl::createRenderbuffer() {
  makeContextCurrent();
  GLuint o = 0;
  glGenRenderbuffersEXT(1, &o);
  return o;
}

WebGLId WebGraphicsContext3DInProcessImpl::createShader(
    WGC3Denum shaderType) {
  makeContextCurrent();
  DCHECK(shaderType == GL_VERTEX_SHADER || shaderType == GL_FRAGMENT_SHADER);
  GLuint shader = glCreateShader(shaderType);
  if (shader) {
    ShaderSourceMap::iterator result = shader_source_map_.find(shader);
    if (result != shader_source_map_.end()) {
      delete result->second;
      shader_source_map_.erase(result);
    }
    shader_source_map_.insert(
        ShaderSourceMap::value_type(shader, new ShaderSourceEntry(shaderType)));
  }

  return shader;
}

WebGLId WebGraphicsContext3DInProcessImpl::createTexture() {
  makeContextCurrent();
  GLuint o = 0;
  glGenTextures(1, &o);
  return o;
}

void WebGraphicsContext3DInProcessImpl::deleteBuffer(WebGLId buffer) {
  makeContextCurrent();
  glDeleteBuffersARB(1, &buffer);
}

void WebGraphicsContext3DInProcessImpl::deleteFramebuffer(
    WebGLId framebuffer) {
  makeContextCurrent();
  glDeleteFramebuffersEXT(1, &framebuffer);
}

void WebGraphicsContext3DInProcessImpl::deleteProgram(WebGLId program) {
  makeContextCurrent();
  glDeleteProgram(program);
}

void WebGraphicsContext3DInProcessImpl::deleteRenderbuffer(
    WebGLId renderbuffer) {
  makeContextCurrent();
  glDeleteRenderbuffersEXT(1, &renderbuffer);
}

void WebGraphicsContext3DInProcessImpl::deleteShader(WebGLId shader) {
  makeContextCurrent();

  ShaderSourceMap::iterator result = shader_source_map_.find(shader);
  if (result != shader_source_map_.end()) {
    delete result->second;
    shader_source_map_.erase(result);
  }
  glDeleteShader(shader);
}

void WebGraphicsContext3DInProcessImpl::deleteTexture(WebGLId texture) {
  makeContextCurrent();
  glDeleteTextures(1, &texture);
}

WGC3Denum WebGraphicsContext3DInProcessImpl::getGraphicsResetStatusARB() {
  // TODO(kbr): this implementation doesn't support lost contexts yet.
  return GL_NO_ERROR;
}

void WebGraphicsContext3DInProcessImpl::texImageIOSurface2DCHROMIUM(
    WGC3Denum target, WGC3Dint width, WGC3Dint height,
    WGC3Duint ioSurfaceId, WGC3Duint plane) {
}

DELEGATE_TO_GL_5(texStorage2DEXT, TexStorage2DEXT,
                 WGC3Denum, WGC3Dint, WGC3Duint, WGC3Dint, WGC3Dint)

WebGLId WebGraphicsContext3DInProcessImpl::createQueryEXT() {
  makeContextCurrent();
  GLuint o = 0;
  glGenQueriesARB(1, &o);
  return o;
}

void WebGraphicsContext3DInProcessImpl::deleteQueryEXT(WebGLId query) {
  makeContextCurrent();
  glDeleteQueriesARB(1, &query);
}

DELEGATE_TO_GL_1R(isQueryEXT, IsQueryARB, WebGLId, WGC3Dboolean)
DELEGATE_TO_GL_2(beginQueryEXT, BeginQueryARB, WGC3Denum, WebGLId)
DELEGATE_TO_GL_1(endQueryEXT, EndQueryARB, WGC3Denum)
DELEGATE_TO_GL_3(getQueryivEXT, GetQueryivARB, WGC3Denum, WGC3Denum, WGC3Dint*)
DELEGATE_TO_GL_3(getQueryObjectuivEXT, GetQueryObjectuivARB,
                 WebGLId, WGC3Denum, WGC3Duint*)

void WebGraphicsContext3DInProcessImpl::copyTextureCHROMIUM(
    WGC3Denum, WGC3Duint, WGC3Duint, WGC3Dint, WGC3Denum) {
  NOTIMPLEMENTED();
}

GrGLInterface* WebGraphicsContext3DInProcessImpl::onCreateGrGLInterface() {
  return gfx::CreateInProcessSkiaGLBinding();
}

bool WebGraphicsContext3DInProcessImpl::AngleCreateCompilers() {
  if (!ShInitialize())
    return false;

  ShBuiltInResources resources;
  ShInitBuiltInResources(&resources);
  getIntegerv(GL_MAX_VERTEX_ATTRIBS, &resources.MaxVertexAttribs);
  getIntegerv(MAX_VERTEX_UNIFORM_VECTORS, &resources.MaxVertexUniformVectors);
  getIntegerv(MAX_VARYING_VECTORS, &resources.MaxVaryingVectors);
  getIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS,
              &resources.MaxVertexTextureImageUnits);
  getIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
              &resources.MaxCombinedTextureImageUnits);
  getIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &resources.MaxTextureImageUnits);
  getIntegerv(MAX_FRAGMENT_UNIFORM_VECTORS,
              &resources.MaxFragmentUniformVectors);
  // Always set to 1 for OpenGL ES.
  resources.MaxDrawBuffers = 1;

  fragment_compiler_ = ShConstructCompiler(
      SH_FRAGMENT_SHADER, SH_WEBGL_SPEC,
      is_gles2_ ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT, &resources);
  vertex_compiler_ = ShConstructCompiler(
      SH_VERTEX_SHADER, SH_WEBGL_SPEC,
      is_gles2_ ? SH_ESSL_OUTPUT : SH_GLSL_OUTPUT, &resources);
  return (fragment_compiler_ && vertex_compiler_);
}

void WebGraphicsContext3DInProcessImpl::AngleDestroyCompilers() {
  if (fragment_compiler_) {
    ShDestruct(fragment_compiler_);
    fragment_compiler_ = 0;
  }
  if (vertex_compiler_) {
    ShDestruct(vertex_compiler_);
    vertex_compiler_ = 0;
  }
}

bool WebGraphicsContext3DInProcessImpl::AngleValidateShaderSource(
    ShaderSourceEntry* entry) {
  entry->is_valid = false;
  entry->translated_source.reset();
  entry->log.reset();

  ShHandle compiler = 0;
  switch (entry->type) {
    case GL_FRAGMENT_SHADER:
      compiler = fragment_compiler_;
      break;
    case GL_VERTEX_SHADER:
      compiler = vertex_compiler_;
      break;
  }
  if (!compiler)
    return false;

  char* source = entry->source.get();
  if (!ShCompile(compiler, &source, 1, SH_OBJECT_CODE)) {
    int logSize = 0;
    ShGetInfo(compiler, SH_INFO_LOG_LENGTH, &logSize);
    if (logSize > 1) {
      entry->log.reset(new char[logSize]);
      ShGetInfoLog(compiler, entry->log.get());
    }
    return false;
  }

  int length = 0;
  ShGetInfo(compiler, SH_OBJECT_CODE_LENGTH, &length);
  if (length > 1) {
    entry->translated_source.reset(new char[length]);
    ShGetObjectCode(compiler, entry->translated_source.get());
  }
  entry->is_valid = true;
  return true;
}

}  // namespace gpu
}  // namespace webkit