root/cc/output/gl_renderer.cc

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

DEFINITIONS

This source file includes following definitions.
  1. HasPassed
  2. SetHasPassed
  3. contents_scale_
  4. RunOnWorkerThread
  5. NeedsIOSurfaceReadbackWorkaround
  6. UVTransform
  7. PremultipliedColor
  8. SamplerTypeFromTextureTarget
  9. Create
  10. on_demand_tile_raster_resource_id_
  11. DebugGLCall
  12. SetVisible
  13. SendManagedMemoryStats
  14. ReleaseRenderPassTextures
  15. DiscardPixels
  16. ClearFramebuffer
  17. BeginDrawingFrame
  18. DoNoOp
  19. DoDrawQuad
  20. DrawCheckerboardQuad
  21. DrawDebugBorderQuad
  22. ApplyImageFilter
  23. ApplyBlendModeWithBackdrop
  24. GetBackgroundWithFilters
  25. DrawRenderPassQuad
  26. SolidColorUniformLocation
  27. SetupQuadForAntialiasing
  28. DrawSolidColorQuad
  29. TileUniformLocation
  30. DrawTileQuad
  31. DrawContentQuad
  32. DrawYUVVideoQuad
  33. DrawStreamVideoQuad
  34. DrawPictureQuad
  35. FlushTextureQuadCache
  36. EnqueueTextureQuad
  37. DrawIOSurfaceQuad
  38. FinishDrawingFrame
  39. FinishDrawingQuadList
  40. FlippedFramebuffer
  41. EnsureScissorTestEnabled
  42. EnsureScissorTestDisabled
  43. CopyCurrentRenderPassToBitmap
  44. ToGLMatrix
  45. SetShaderQuadF
  46. SetShaderOpacity
  47. SetStencilEnabled
  48. SetBlendEnabled
  49. SetUseProgram
  50. DrawQuadGeometry
  51. CopyTextureToFramebuffer
  52. Finish
  53. SwapBuffers
  54. EnforceMemoryPolicy
  55. DiscardBackbuffer
  56. EnsureBackbuffer
  57. GetFramebufferPixels
  58. GetFramebufferPixelsAsync
  59. DoGetFramebufferPixels
  60. FinishedReadback
  61. PassOnSkBitmap
  62. GetFramebufferTexture
  63. UseScopedTexture
  64. BindFramebufferToOutputSurface
  65. BindFramebufferToTexture
  66. SetScissorTestRect
  67. SetDrawViewport
  68. InitializeSharedObjects
  69. GetTileCheckerboardProgram
  70. GetDebugBorderProgram
  71. GetSolidColorProgram
  72. GetSolidColorProgramAA
  73. GetRenderPassProgram
  74. GetRenderPassProgramAA
  75. GetRenderPassMaskProgram
  76. GetRenderPassMaskProgramAA
  77. GetRenderPassColorMatrixProgram
  78. GetRenderPassColorMatrixProgramAA
  79. GetRenderPassMaskColorMatrixProgram
  80. GetRenderPassMaskColorMatrixProgramAA
  81. GetTileProgram
  82. GetTileProgramOpaque
  83. GetTileProgramAA
  84. GetTileProgramSwizzle
  85. GetTileProgramSwizzleOpaque
  86. GetTileProgramSwizzleAA
  87. GetTextureProgram
  88. GetNonPremultipliedTextureProgram
  89. GetTextureBackgroundProgram
  90. GetNonPremultipliedTextureBackgroundProgram
  91. GetTextureIOSurfaceProgram
  92. GetVideoYUVProgram
  93. GetVideoYUVAProgram
  94. GetVideoStreamTextureProgram
  95. CleanupSharedObjects
  96. ReinitializeGLState
  97. IsContextLost
  98. ScheduleOverlays

// Copyright 2010 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 "cc/output/gl_renderer.h"

#include <algorithm>
#include <limits>
#include <set>
#include <string>
#include <vector>

#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "cc/base/math_util.h"
#include "cc/layers/video_layer_impl.h"
#include "cc/output/compositor_frame.h"
#include "cc/output/compositor_frame_metadata.h"
#include "cc/output/context_provider.h"
#include "cc/output/copy_output_request.h"
#include "cc/output/geometry_binding.h"
#include "cc/output/gl_frame_data.h"
#include "cc/output/output_surface.h"
#include "cc/output/render_surface_filters.h"
#include "cc/quads/picture_draw_quad.h"
#include "cc/quads/render_pass.h"
#include "cc/quads/stream_video_draw_quad.h"
#include "cc/quads/texture_draw_quad.h"
#include "cc/resources/layer_quad.h"
#include "cc/resources/raster_worker_pool.h"
#include "cc/resources/scoped_resource.h"
#include "cc/resources/texture_mailbox_deleter.h"
#include "cc/trees/damage_tracker.h"
#include "cc/trees/proxy.h"
#include "cc/trees/single_thread_proxy.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/context_support.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "gpu/command_buffer/common/gpu_memory_allocation.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkColorFilter.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrContext.h"
#include "third_party/skia/include/gpu/GrTexture.h"
#include "third_party/skia/include/gpu/SkGpuDevice.h"
#include "third_party/skia/include/gpu/SkGrTexturePixelRef.h"
#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
#include "ui/gfx/quad_f.h"
#include "ui/gfx/rect_conversions.h"

using gpu::gles2::GLES2Interface;

namespace cc {

namespace {

// TODO(epenner): This should probably be moved to output surface.
//
// This implements a simple fence based on client side swaps.
// This is to isolate the ResourceProvider from 'frames' which
// it shouldn't need to care about, while still allowing us to
// enforce good texture recycling behavior strictly throughout
// the compositor (don't recycle a texture while it's in use).
class SimpleSwapFence : public ResourceProvider::Fence {
 public:
  SimpleSwapFence() : has_passed_(false) {}
  virtual bool HasPassed() OVERRIDE { return has_passed_; }
  void SetHasPassed() { has_passed_ = true; }

 private:
  virtual ~SimpleSwapFence() {}
  bool has_passed_;
};

class OnDemandRasterTaskImpl : public internal::Task {
 public:
  OnDemandRasterTaskImpl(PicturePileImpl* picture_pile,
                         SkBitmap* bitmap,
                         gfx::Rect content_rect,
                         float contents_scale)
      : picture_pile_(picture_pile),
        bitmap_(bitmap),
        content_rect_(content_rect),
        contents_scale_(contents_scale) {
    DCHECK(picture_pile_);
    DCHECK(bitmap_);
  }

  // Overridden from internal::Task:
  virtual void RunOnWorkerThread() OVERRIDE {
    TRACE_EVENT0("cc", "OnDemandRasterTaskImpl::RunOnWorkerThread");
    SkCanvas canvas(*bitmap_);

    PicturePileImpl* picture_pile = picture_pile_->GetCloneForDrawingOnThread(
        RasterWorkerPool::GetPictureCloneIndexForCurrentThread());
    DCHECK(picture_pile);

    picture_pile->RasterToBitmap(&canvas, content_rect_, contents_scale_, NULL);
  }

 protected:
  virtual ~OnDemandRasterTaskImpl() {}

 private:
  PicturePileImpl* picture_pile_;
  SkBitmap* bitmap_;
  const gfx::Rect content_rect_;
  const float contents_scale_;

  DISALLOW_COPY_AND_ASSIGN(OnDemandRasterTaskImpl);
};

bool NeedsIOSurfaceReadbackWorkaround() {
#if defined(OS_MACOSX)
  // This isn't strictly required in DumpRenderTree-mode when Mesa is used,
  // but it doesn't seem to hurt.
  return true;
#else
  return false;
#endif
}

Float4 UVTransform(const TextureDrawQuad* quad) {
  gfx::PointF uv0 = quad->uv_top_left;
  gfx::PointF uv1 = quad->uv_bottom_right;
  Float4 xform = {{uv0.x(), uv0.y(), uv1.x() - uv0.x(), uv1.y() - uv0.y()}};
  if (quad->flipped) {
    xform.data[1] = 1.0f - xform.data[1];
    xform.data[3] = -xform.data[3];
  }
  return xform;
}

Float4 PremultipliedColor(SkColor color) {
  const float factor = 1.0f / 255.0f;
  const float alpha = SkColorGetA(color) * factor;

  Float4 result = {
      {SkColorGetR(color) * factor * alpha, SkColorGetG(color) * factor * alpha,
       SkColorGetB(color) * factor * alpha, alpha}};
  return result;
}

SamplerType SamplerTypeFromTextureTarget(GLenum target) {
  switch (target) {
    case GL_TEXTURE_2D:
      return SamplerType2D;
    case GL_TEXTURE_RECTANGLE_ARB:
      return SamplerType2DRect;
    case GL_TEXTURE_EXTERNAL_OES:
      return SamplerTypeExternalOES;
    default:
      NOTREACHED();
      return SamplerType2D;
  }
}

// Smallest unit that impact anti-aliasing output. We use this to
// determine when anti-aliasing is unnecessary.
const float kAntiAliasingEpsilon = 1.0f / 1024.0f;

}  // anonymous namespace

struct GLRenderer::PendingAsyncReadPixels {
  PendingAsyncReadPixels() : buffer(0) {}

  scoped_ptr<CopyOutputRequest> copy_request;
  base::CancelableClosure finished_read_pixels_callback;
  unsigned buffer;

 private:
  DISALLOW_COPY_AND_ASSIGN(PendingAsyncReadPixels);
};

scoped_ptr<GLRenderer> GLRenderer::Create(
    RendererClient* client,
    const LayerTreeSettings* settings,
    OutputSurface* output_surface,
    ResourceProvider* resource_provider,
    TextureMailboxDeleter* texture_mailbox_deleter,
    int highp_threshold_min) {
  return make_scoped_ptr(new GLRenderer(client,
                                        settings,
                                        output_surface,
                                        resource_provider,
                                        texture_mailbox_deleter,
                                        highp_threshold_min));
}

GLRenderer::GLRenderer(RendererClient* client,
                       const LayerTreeSettings* settings,
                       OutputSurface* output_surface,
                       ResourceProvider* resource_provider,
                       TextureMailboxDeleter* texture_mailbox_deleter,
                       int highp_threshold_min)
    : DirectRenderer(client, settings, output_surface, resource_provider),
      offscreen_framebuffer_id_(0),
      shared_geometry_quad_(gfx::RectF(-0.5f, -0.5f, 1.0f, 1.0f)),
      gl_(output_surface->context_provider()->ContextGL()),
      context_support_(output_surface->context_provider()->ContextSupport()),
      texture_mailbox_deleter_(texture_mailbox_deleter),
      is_backbuffer_discarded_(false),
      visible_(true),
      is_scissor_enabled_(false),
      scissor_rect_needs_reset_(true),
      stencil_shadow_(false),
      blend_shadow_(false),
      highp_threshold_min_(highp_threshold_min),
      highp_threshold_cache_(0),
      on_demand_tile_raster_resource_id_(0) {
  DCHECK(gl_);
  DCHECK(context_support_);

  ContextProvider::Capabilities context_caps =
      output_surface_->context_provider()->ContextCapabilities();

  capabilities_.using_partial_swap =
      settings_->partial_swap_enabled && context_caps.gpu.post_sub_buffer;

  DCHECK(!context_caps.gpu.iosurface || context_caps.gpu.texture_rectangle);

  capabilities_.using_egl_image = context_caps.gpu.egl_image_external;

  capabilities_.max_texture_size = resource_provider_->max_texture_size();
  capabilities_.best_texture_format = resource_provider_->best_texture_format();

  // The updater can access textures while the GLRenderer is using them.
  capabilities_.allow_partial_texture_updates = true;

  // Check for texture fast paths. Currently we always use MO8 textures,
  // so we only need to avoid POT textures if we have an NPOT fast-path.
  capabilities_.avoid_pow2_textures = context_caps.gpu.fast_npot_mo8_textures;

  capabilities_.using_offscreen_context3d = true;

  capabilities_.using_map_image =
      settings_->use_map_image && context_caps.gpu.map_image;

  capabilities_.using_discard_framebuffer =
      context_caps.gpu.discard_framebuffer;

  capabilities_.allow_rasterize_on_demand = true;

  InitializeSharedObjects();
}

GLRenderer::~GLRenderer() {
  while (!pending_async_read_pixels_.empty()) {
    PendingAsyncReadPixels* pending_read = pending_async_read_pixels_.back();
    pending_read->finished_read_pixels_callback.Cancel();
    pending_async_read_pixels_.pop_back();
  }

  in_use_overlay_resources_.clear();

  CleanupSharedObjects();
}

const RendererCapabilitiesImpl& GLRenderer::Capabilities() const {
  return capabilities_;
}

void GLRenderer::DebugGLCall(GLES2Interface* gl,
                             const char* command,
                             const char* file,
                             int line) {
  GLuint error = gl->GetError();
  if (error != GL_NO_ERROR)
    LOG(ERROR) << "GL command failed: File: " << file << "\n\tLine " << line
               << "\n\tcommand: " << command << ", error "
               << static_cast<int>(error) << "\n";
}

void GLRenderer::SetVisible(bool visible) {
  if (visible_ == visible)
    return;
  visible_ = visible;

  EnforceMemoryPolicy();

  context_support_->SetSurfaceVisible(visible);
}

void GLRenderer::SendManagedMemoryStats(size_t bytes_visible,
                                        size_t bytes_visible_and_nearby,
                                        size_t bytes_allocated) {
  gpu::ManagedMemoryStats stats;
  stats.bytes_required = bytes_visible;
  stats.bytes_nice_to_have = bytes_visible_and_nearby;
  stats.bytes_allocated = bytes_allocated;
  stats.backbuffer_requested = !is_backbuffer_discarded_;
  context_support_->SendManagedMemoryStats(stats);
}

void GLRenderer::ReleaseRenderPassTextures() { render_pass_textures_.clear(); }

void GLRenderer::DiscardPixels(bool has_external_stencil_test,
                               bool draw_rect_covers_full_surface) {
  if (has_external_stencil_test || !draw_rect_covers_full_surface ||
      !capabilities_.using_discard_framebuffer)
    return;
  bool using_default_framebuffer =
      !current_framebuffer_lock_ &&
      output_surface_->capabilities().uses_default_gl_framebuffer;
  GLenum attachments[] = {static_cast<GLenum>(
      using_default_framebuffer ? GL_COLOR_EXT : GL_COLOR_ATTACHMENT0_EXT)};
  gl_->DiscardFramebufferEXT(
      GL_FRAMEBUFFER, arraysize(attachments), attachments);
}

void GLRenderer::ClearFramebuffer(DrawingFrame* frame,
                                  bool has_external_stencil_test) {
  // It's unsafe to clear when we have a stencil test because glClear ignores
  // stencil.
  if (has_external_stencil_test) {
    DCHECK(!frame->current_render_pass->has_transparent_background);
    return;
  }

  // On DEBUG builds, opaque render passes are cleared to blue to easily see
  // regions that were not drawn on the screen.
  if (frame->current_render_pass->has_transparent_background)
    GLC(gl_, gl_->ClearColor(0, 0, 0, 0));
  else
    GLC(gl_, gl_->ClearColor(0, 0, 1, 1));

  bool always_clear = false;
#ifndef NDEBUG
  always_clear = true;
#endif
  if (always_clear || frame->current_render_pass->has_transparent_background) {
    GLbitfield clear_bits = GL_COLOR_BUFFER_BIT;
    if (always_clear)
      clear_bits |= GL_STENCIL_BUFFER_BIT;
    gl_->Clear(clear_bits);
  }
}

void GLRenderer::BeginDrawingFrame(DrawingFrame* frame) {
  if (frame->device_viewport_rect.IsEmpty())
    return;

  TRACE_EVENT0("cc", "GLRenderer::BeginDrawingFrame");

  // TODO(enne): Do we need to reinitialize all of this state per frame?
  ReinitializeGLState();
}

void GLRenderer::DoNoOp() {
  GLC(gl_, gl_->BindFramebuffer(GL_FRAMEBUFFER, 0));
  GLC(gl_, gl_->Flush());
}

void GLRenderer::DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) {
  DCHECK(quad->rect.Contains(quad->visible_rect));
  if (quad->material != DrawQuad::TEXTURE_CONTENT) {
    FlushTextureQuadCache();
  }

  switch (quad->material) {
    case DrawQuad::INVALID:
      NOTREACHED();
      break;
    case DrawQuad::CHECKERBOARD:
      DrawCheckerboardQuad(frame, CheckerboardDrawQuad::MaterialCast(quad));
      break;
    case DrawQuad::DEBUG_BORDER:
      DrawDebugBorderQuad(frame, DebugBorderDrawQuad::MaterialCast(quad));
      break;
    case DrawQuad::IO_SURFACE_CONTENT:
      DrawIOSurfaceQuad(frame, IOSurfaceDrawQuad::MaterialCast(quad));
      break;
    case DrawQuad::PICTURE_CONTENT:
      DrawPictureQuad(frame, PictureDrawQuad::MaterialCast(quad));
      break;
    case DrawQuad::RENDER_PASS:
      DrawRenderPassQuad(frame, RenderPassDrawQuad::MaterialCast(quad));
      break;
    case DrawQuad::SOLID_COLOR:
      DrawSolidColorQuad(frame, SolidColorDrawQuad::MaterialCast(quad));
      break;
    case DrawQuad::STREAM_VIDEO_CONTENT:
      DrawStreamVideoQuad(frame, StreamVideoDrawQuad::MaterialCast(quad));
      break;
    case DrawQuad::SURFACE_CONTENT:
      // Surface content should be fully resolved to other quad types before
      // reaching a direct renderer.
      NOTREACHED();
      break;
    case DrawQuad::TEXTURE_CONTENT:
      EnqueueTextureQuad(frame, TextureDrawQuad::MaterialCast(quad));
      break;
    case DrawQuad::TILED_CONTENT:
      DrawTileQuad(frame, TileDrawQuad::MaterialCast(quad));
      break;
    case DrawQuad::YUV_VIDEO_CONTENT:
      DrawYUVVideoQuad(frame, YUVVideoDrawQuad::MaterialCast(quad));
      break;
  }
}

void GLRenderer::DrawCheckerboardQuad(const DrawingFrame* frame,
                                      const CheckerboardDrawQuad* quad) {
  SetBlendEnabled(quad->ShouldDrawWithBlending());

  const TileCheckerboardProgram* program = GetTileCheckerboardProgram();
  DCHECK(program && (program->initialized() || IsContextLost()));
  SetUseProgram(program->program());

  SkColor color = quad->color;
  GLC(gl_,
      gl_->Uniform4f(program->fragment_shader().color_location(),
                     SkColorGetR(color) * (1.0f / 255.0f),
                     SkColorGetG(color) * (1.0f / 255.0f),
                     SkColorGetB(color) * (1.0f / 255.0f),
                     1));

  const int checkerboard_width = 16;
  float frequency = 1.0f / checkerboard_width;

  gfx::Rect tile_rect = quad->rect;
  float tex_offset_x = tile_rect.x() % checkerboard_width;
  float tex_offset_y = tile_rect.y() % checkerboard_width;
  float tex_scale_x = tile_rect.width();
  float tex_scale_y = tile_rect.height();
  GLC(gl_,
      gl_->Uniform4f(program->fragment_shader().tex_transform_location(),
                     tex_offset_x,
                     tex_offset_y,
                     tex_scale_x,
                     tex_scale_y));

  GLC(gl_,
      gl_->Uniform1f(program->fragment_shader().frequency_location(),
                     frequency));

  SetShaderOpacity(quad->opacity(),
                   program->fragment_shader().alpha_location());
  DrawQuadGeometry(frame,
                   quad->quadTransform(),
                   quad->rect,
                   program->vertex_shader().matrix_location());
}

void GLRenderer::DrawDebugBorderQuad(const DrawingFrame* frame,
                                     const DebugBorderDrawQuad* quad) {
  SetBlendEnabled(quad->ShouldDrawWithBlending());

  static float gl_matrix[16];
  const DebugBorderProgram* program = GetDebugBorderProgram();
  DCHECK(program && (program->initialized() || IsContextLost()));
  SetUseProgram(program->program());

  // Use the full quad_rect for debug quads to not move the edges based on
  // partial swaps.
  gfx::Rect layer_rect = quad->rect;
  gfx::Transform render_matrix = quad->quadTransform();
  render_matrix.Translate(0.5f * layer_rect.width() + layer_rect.x(),
                          0.5f * layer_rect.height() + layer_rect.y());
  render_matrix.Scale(layer_rect.width(), layer_rect.height());
  GLRenderer::ToGLMatrix(&gl_matrix[0],
                         frame->projection_matrix * render_matrix);
  GLC(gl_,
      gl_->UniformMatrix4fv(
          program->vertex_shader().matrix_location(), 1, false, &gl_matrix[0]));

  SkColor color = quad->color;
  float alpha = SkColorGetA(color) * (1.0f / 255.0f);

  GLC(gl_,
      gl_->Uniform4f(program->fragment_shader().color_location(),
                     (SkColorGetR(color) * (1.0f / 255.0f)) * alpha,
                     (SkColorGetG(color) * (1.0f / 255.0f)) * alpha,
                     (SkColorGetB(color) * (1.0f / 255.0f)) * alpha,
                     alpha));

  GLC(gl_, gl_->LineWidth(quad->width));

  // The indices for the line are stored in the same array as the triangle
  // indices.
  GLC(gl_, gl_->DrawElements(GL_LINE_LOOP, 4, GL_UNSIGNED_SHORT, 0));
}

static SkBitmap ApplyImageFilter(GLRenderer* renderer,
                                 ContextProvider* offscreen_contexts,
                                 const gfx::Point& origin,
                                 SkImageFilter* filter,
                                 ScopedResource* source_texture_resource) {
  if (!filter)
    return SkBitmap();

  if (!offscreen_contexts || !offscreen_contexts->GrContext())
    return SkBitmap();

  ResourceProvider::ScopedWriteLockGL lock(renderer->resource_provider(),
                                           source_texture_resource->id());

  // Flush the compositor context to ensure that textures there are available
  // in the shared context.  Do this after locking/creating the compositor
  // texture.
  renderer->resource_provider()->Flush();

  // Wrap the source texture in a Ganesh platform texture.
  GrBackendTextureDesc backend_texture_description;
  backend_texture_description.fWidth = source_texture_resource->size().width();
  backend_texture_description.fHeight =
      source_texture_resource->size().height();
  backend_texture_description.fConfig = kSkia8888_GrPixelConfig;
  backend_texture_description.fTextureHandle = lock.texture_id();
  backend_texture_description.fOrigin = kBottomLeft_GrSurfaceOrigin;
  skia::RefPtr<GrTexture> texture =
      skia::AdoptRef(offscreen_contexts->GrContext()->wrapBackendTexture(
          backend_texture_description));

  SkImageInfo info = {
    source_texture_resource->size().width(),
    source_texture_resource->size().height(),
    kPMColor_SkColorType,
    kPremul_SkAlphaType
  };
  // Place the platform texture inside an SkBitmap.
  SkBitmap source;
  source.setConfig(info);
  skia::RefPtr<SkGrPixelRef> pixel_ref =
      skia::AdoptRef(new SkGrPixelRef(info, texture.get()));
  source.setPixelRef(pixel_ref.get());

  // Create a scratch texture for backing store.
  GrTextureDesc desc;
  desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
  desc.fSampleCnt = 0;
  desc.fWidth = source.width();
  desc.fHeight = source.height();
  desc.fConfig = kSkia8888_GrPixelConfig;
  desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
  GrAutoScratchTexture scratch_texture(
      offscreen_contexts->GrContext(), desc, GrContext::kExact_ScratchTexMatch);
  skia::RefPtr<GrTexture> backing_store =
      skia::AdoptRef(scratch_texture.detach());

  // Create a device and canvas using that backing store.
  SkGpuDevice device(offscreen_contexts->GrContext(), backing_store.get());
  SkCanvas canvas(&device);

  // Draw the source bitmap through the filter to the canvas.
  SkPaint paint;
  paint.setImageFilter(filter);
  canvas.clear(SK_ColorTRANSPARENT);

  // TODO(senorblanco): in addition to the origin translation here, the canvas
  // should also be scaled to accomodate device pixel ratio and pinch zoom. See
  // crbug.com/281516 and crbug.com/281518.
  canvas.translate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y()));
  canvas.drawSprite(source, 0, 0, &paint);

  // Flush skia context so that all the rendered stuff appears on the
  // texture.
  offscreen_contexts->GrContext()->flush();

  // Flush the GL context so rendering results from this context are
  // visible in the compositor's context.
  offscreen_contexts->ContextGL()->Flush();

  return device.accessBitmap(false);
}

static SkBitmap ApplyBlendModeWithBackdrop(
    GLRenderer* renderer,
    ContextProvider* offscreen_contexts,
    SkBitmap source_bitmap_with_filters,
    ScopedResource* source_texture_resource,
    ScopedResource* background_texture_resource,
    SkXfermode::Mode blend_mode) {
  if (!offscreen_contexts || !offscreen_contexts->GrContext())
    return source_bitmap_with_filters;

  DCHECK(background_texture_resource);
  DCHECK(source_texture_resource);

  gfx::Size source_size = source_texture_resource->size();
  gfx::Size background_size = background_texture_resource->size();

  DCHECK_LE(background_size.width(), source_size.width());
  DCHECK_LE(background_size.height(), source_size.height());

  int source_texture_with_filters_id;
  scoped_ptr<ResourceProvider::ScopedReadLockGL> lock;
  if (source_bitmap_with_filters.getTexture()) {
    DCHECK_EQ(source_size.width(), source_bitmap_with_filters.width());
    DCHECK_EQ(source_size.height(), source_bitmap_with_filters.height());
    GrTexture* texture =
        reinterpret_cast<GrTexture*>(source_bitmap_with_filters.getTexture());
    source_texture_with_filters_id = texture->getTextureHandle();
  } else {
    lock.reset(new ResourceProvider::ScopedReadLockGL(
        renderer->resource_provider(), source_texture_resource->id()));
    source_texture_with_filters_id = lock->texture_id();
  }

  ResourceProvider::ScopedReadLockGL lock_background(
      renderer->resource_provider(), background_texture_resource->id());

  // Flush the compositor context to ensure that textures there are available
  // in the shared context.  Do this after locking/creating the compositor
  // texture.
  renderer->resource_provider()->Flush();

  // Wrap the source texture in a Ganesh platform texture.
  GrBackendTextureDesc backend_texture_description;
  backend_texture_description.fConfig = kSkia8888_GrPixelConfig;
  backend_texture_description.fOrigin = kBottomLeft_GrSurfaceOrigin;

  backend_texture_description.fWidth = source_size.width();
  backend_texture_description.fHeight = source_size.height();
  backend_texture_description.fTextureHandle = source_texture_with_filters_id;
  skia::RefPtr<GrTexture> source_texture =
      skia::AdoptRef(offscreen_contexts->GrContext()->wrapBackendTexture(
          backend_texture_description));

  backend_texture_description.fWidth = background_size.width();
  backend_texture_description.fHeight = background_size.height();
  backend_texture_description.fTextureHandle = lock_background.texture_id();
  skia::RefPtr<GrTexture> background_texture =
      skia::AdoptRef(offscreen_contexts->GrContext()->wrapBackendTexture(
          backend_texture_description));

  SkImageInfo source_info = {
    source_size.width(),
    source_size.height(),
    kPMColor_SkColorType,
    kPremul_SkAlphaType
  };
  // Place the platform texture inside an SkBitmap.
  SkBitmap source;
  source.setConfig(source_info);
  skia::RefPtr<SkGrPixelRef> source_pixel_ref =
      skia::AdoptRef(new SkGrPixelRef(source_info, source_texture.get()));
  source.setPixelRef(source_pixel_ref.get());

  SkImageInfo background_info = {
    background_size.width(),
    background_size.height(),
    kPMColor_SkColorType,
    kPremul_SkAlphaType
  };

  SkBitmap background;
  background.setConfig(background_info);
  skia::RefPtr<SkGrPixelRef> background_pixel_ref =
      skia::AdoptRef(new SkGrPixelRef(
          background_info, background_texture.get()));
  background.setPixelRef(background_pixel_ref.get());

  // Create a scratch texture for backing store.
  GrTextureDesc desc;
  desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
  desc.fSampleCnt = 0;
  desc.fWidth = source.width();
  desc.fHeight = source.height();
  desc.fConfig = kSkia8888_GrPixelConfig;
  desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
  GrAutoScratchTexture scratch_texture(
      offscreen_contexts->GrContext(), desc, GrContext::kExact_ScratchTexMatch);
  skia::RefPtr<GrTexture> backing_store =
      skia::AdoptRef(scratch_texture.detach());

  // Create a device and canvas using that backing store.
  SkGpuDevice device(offscreen_contexts->GrContext(), backing_store.get());
  SkCanvas canvas(&device);

  // Draw the source bitmap through the filter to the canvas.
  canvas.clear(SK_ColorTRANSPARENT);
  canvas.drawSprite(background, 0, 0);
  SkPaint paint;
  paint.setXfermodeMode(blend_mode);
  canvas.drawSprite(source, 0, 0, &paint);

  // Flush skia context so that all the rendered stuff appears on the
  // texture.
  offscreen_contexts->GrContext()->flush();

  // Flush the GL context so rendering results from this context are
  // visible in the compositor's context.
  offscreen_contexts->ContextGL()->Flush();

  return device.accessBitmap(false);
}

scoped_ptr<ScopedResource> GLRenderer::GetBackgroundWithFilters(
    DrawingFrame* frame,
    const RenderPassDrawQuad* quad,
    const gfx::Transform& contents_device_transform,
    const gfx::Transform& contents_device_transform_inverse,
    bool* background_changed) {
  // This method draws a background filter, which applies a filter to any pixels
  // behind the quad and seen through its background.  The algorithm works as
  // follows:
  // 1. Compute a bounding box around the pixels that will be visible through
  // the quad.
  // 2. Read the pixels in the bounding box into a buffer R.
  // 3. Apply the background filter to R, so that it is applied in the pixels'
  // coordinate space.
  // 4. Apply the quad's inverse transform to map the pixels in R into the
  // quad's content space. This implicitly clips R by the content bounds of the
  // quad since the destination texture has bounds matching the quad's content.
  // 5. Draw the background texture for the contents using the same transform as
  // used to draw the contents itself. This is done without blending to replace
  // the current background pixels with the new filtered background.
  // 6. Draw the contents of the quad over drop of the new background with
  // blending, as per usual. The filtered background pixels will show through
  // any non-opaque pixels in this draws.
  //
  // Pixel copies in this algorithm occur at steps 2, 3, 4, and 5.

  // TODO(danakj): When this algorithm changes, update
  // LayerTreeHost::PrioritizeTextures() accordingly.

  // TODO(danakj): We only allow background filters on an opaque render surface
  // because other surfaces may contain translucent pixels, and the contents
  // behind those translucent pixels wouldn't have the filter applied.
  bool apply_background_filters =
      !frame->current_render_pass->has_transparent_background;
  DCHECK(!frame->current_texture);

  // TODO(ajuma): Add support for reference filters once
  // FilterOperations::GetOutsets supports reference filters.
  if (apply_background_filters && quad->background_filters.HasReferenceFilter())
    apply_background_filters = false;

  // TODO(danakj): Do a single readback for both the surface and replica and
  // cache the filtered results (once filter textures are not reused).
  gfx::Rect window_rect = gfx::ToEnclosingRect(MathUtil::MapClippedRect(
      contents_device_transform, SharedGeometryQuad().BoundingBox()));

  int top, right, bottom, left;
  quad->background_filters.GetOutsets(&top, &right, &bottom, &left);
  window_rect.Inset(-left, -top, -right, -bottom);

  window_rect.Intersect(
      MoveFromDrawToWindowSpace(frame->current_render_pass->output_rect));

  scoped_ptr<ScopedResource> device_background_texture =
      ScopedResource::Create(resource_provider_);
  // The TextureUsageFramebuffer hint makes ResourceProvider avoid immutable
  // storage allocation (texStorage2DEXT) for this texture. copyTexImage2D fails
  // when called on a texture having immutable storage.
  device_background_texture->Allocate(
      window_rect.size(), ResourceProvider::TextureUsageFramebuffer, RGBA_8888);
  {
    ResourceProvider::ScopedWriteLockGL lock(resource_provider_,
                                             device_background_texture->id());
    GetFramebufferTexture(
        lock.texture_id(), device_background_texture->format(), window_rect);
  }

  skia::RefPtr<SkImageFilter> filter = RenderSurfaceFilters::BuildImageFilter(
      quad->background_filters, device_background_texture->size());

  SkBitmap filtered_device_background;
  if (apply_background_filters) {
    filtered_device_background =
        ApplyImageFilter(this,
                         frame->offscreen_context_provider,
                         quad->rect.origin(),
                         filter.get(),
                         device_background_texture.get());
  }
  *background_changed = (filtered_device_background.getTexture() != NULL);

  int filtered_device_background_texture_id = 0;
  scoped_ptr<ResourceProvider::ScopedReadLockGL> lock;
  if (filtered_device_background.getTexture()) {
    GrTexture* texture =
        reinterpret_cast<GrTexture*>(filtered_device_background.getTexture());
    filtered_device_background_texture_id = texture->getTextureHandle();
  } else {
    lock.reset(new ResourceProvider::ScopedReadLockGL(
        resource_provider_, device_background_texture->id()));
    filtered_device_background_texture_id = lock->texture_id();
  }

  scoped_ptr<ScopedResource> background_texture =
      ScopedResource::Create(resource_provider_);
  background_texture->Allocate(
      quad->rect.size(), ResourceProvider::TextureUsageFramebuffer, RGBA_8888);

  const RenderPass* target_render_pass = frame->current_render_pass;
  bool using_background_texture =
      UseScopedTexture(frame, background_texture.get(), quad->rect);

  if (using_background_texture) {
    // Copy the readback pixels from device to the background texture for the
    // surface.
    gfx::Transform device_to_framebuffer_transform;
    device_to_framebuffer_transform.Translate(
        quad->rect.width() * 0.5f + quad->rect.x(),
        quad->rect.height() * 0.5f + quad->rect.y());
    device_to_framebuffer_transform.Scale(quad->rect.width(),
                                          quad->rect.height());
    device_to_framebuffer_transform.PreconcatTransform(
        contents_device_transform_inverse);

#ifndef NDEBUG
    GLC(gl_, gl_->ClearColor(0, 0, 1, 1));
    gl_->Clear(GL_COLOR_BUFFER_BIT);
#endif

    // The filtered_deveice_background_texture is oriented the same as the frame
    // buffer. The transform we are copying with has a vertical flip, as well as
    // the |device_to_framebuffer_transform|, which cancel each other out. So do
    // not flip the contents in the shader to maintain orientation.
    bool flip_vertically = false;

    CopyTextureToFramebuffer(frame,
                             filtered_device_background_texture_id,
                             window_rect,
                             device_to_framebuffer_transform,
                             flip_vertically);
  }

  UseRenderPass(frame, target_render_pass);

  if (!using_background_texture)
    return scoped_ptr<ScopedResource>();
  return background_texture.Pass();
}

void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame,
                                    const RenderPassDrawQuad* quad) {
  SetBlendEnabled(quad->ShouldDrawWithBlending());

  ScopedResource* contents_texture =
      render_pass_textures_.get(quad->render_pass_id);
  if (!contents_texture || !contents_texture->id())
    return;

  gfx::Transform quad_rect_matrix;
  QuadRectTransform(&quad_rect_matrix, quad->quadTransform(), quad->rect);
  gfx::Transform contents_device_transform =
      frame->window_matrix * frame->projection_matrix * quad_rect_matrix;
  contents_device_transform.FlattenTo2d();

  // Can only draw surface if device matrix is invertible.
  gfx::Transform contents_device_transform_inverse(
      gfx::Transform::kSkipInitialization);
  if (!contents_device_transform.GetInverse(&contents_device_transform_inverse))
    return;

  bool need_background_texture =
      quad->shared_quad_state->blend_mode != SkXfermode::kSrcOver_Mode ||
      !quad->background_filters.IsEmpty();
  bool background_changed = false;
  scoped_ptr<ScopedResource> background_texture;
  if (need_background_texture) {
    // The pixels from the filtered background should completely replace the
    // current pixel values.
    bool disable_blending = blend_enabled();
    if (disable_blending)
      SetBlendEnabled(false);

    background_texture =
        GetBackgroundWithFilters(frame,
                                 quad,
                                 contents_device_transform,
                                 contents_device_transform_inverse,
                                 &background_changed);

    if (disable_blending)
      SetBlendEnabled(true);
  }

  // TODO(senorblanco): Cache this value so that we don't have to do it for both
  // the surface and its replica.  Apply filters to the contents texture.
  SkBitmap filter_bitmap;
  SkScalar color_matrix[20];
  bool use_color_matrix = false;
  // TODO(ajuma): Always use RenderSurfaceFilters::BuildImageFilter, not just
  // when we have a reference filter.
  if (!quad->filters.IsEmpty()) {
    skia::RefPtr<SkImageFilter> filter = RenderSurfaceFilters::BuildImageFilter(
        quad->filters, contents_texture->size());
    if (filter) {
      skia::RefPtr<SkColorFilter> cf;

      {
        SkColorFilter* colorfilter_rawptr = NULL;
        filter->asColorFilter(&colorfilter_rawptr);
        cf = skia::AdoptRef(colorfilter_rawptr);
      }

      if (cf && cf->asColorMatrix(color_matrix) && !filter->getInput(0)) {
        // We have a single color matrix as a filter; apply it locally
        // in the compositor.
        use_color_matrix = true;
      } else {
        filter_bitmap = ApplyImageFilter(this,
                                         frame->offscreen_context_provider,
                                         quad->rect.origin(),
                                         filter.get(),
                                         contents_texture);
      }
    }
  }

  if (quad->shared_quad_state->blend_mode != SkXfermode::kSrcOver_Mode &&
      background_texture) {
    filter_bitmap =
        ApplyBlendModeWithBackdrop(this,
                                   frame->offscreen_context_provider,
                                   filter_bitmap,
                                   contents_texture,
                                   background_texture.get(),
                                   quad->shared_quad_state->blend_mode);
  }

  // Draw the background texture if it has some filters applied.
  if (background_texture && background_changed) {
    DCHECK(background_texture->size() == quad->rect.size());
    ResourceProvider::ScopedReadLockGL lock(resource_provider_,
                                            background_texture->id());

    // The background_texture is oriented the same as the frame buffer. The
    // transform we are copying with has a vertical flip, so flip the contents
    // in the shader to maintain orientation
    bool flip_vertically = true;

    CopyTextureToFramebuffer(frame,
                             lock.texture_id(),
                             quad->rect,
                             quad->quadTransform(),
                             flip_vertically);
  }

  bool clipped = false;
  gfx::QuadF device_quad = MathUtil::MapQuad(
      contents_device_transform, SharedGeometryQuad(), &clipped);
  LayerQuad device_layer_bounds(gfx::QuadF(device_quad.BoundingBox()));
  LayerQuad device_layer_edges(device_quad);

  // Use anti-aliasing programs only when necessary.
  bool use_aa =
      !clipped && (!device_quad.IsRectilinear() ||
                   !gfx::IsNearestRectWithinDistance(device_quad.BoundingBox(),
                                                     kAntiAliasingEpsilon));
  if (use_aa) {
    device_layer_bounds.InflateAntiAliasingDistance();
    device_layer_edges.InflateAntiAliasingDistance();
  }

  scoped_ptr<ResourceProvider::ScopedReadLockGL> mask_resource_lock;
  unsigned mask_texture_id = 0;
  if (quad->mask_resource_id) {
    mask_resource_lock.reset(new ResourceProvider::ScopedReadLockGL(
        resource_provider_, quad->mask_resource_id));
    mask_texture_id = mask_resource_lock->texture_id();
  }

  // TODO(danakj): use the background_texture and blend the background in with
  // this draw instead of having a separate copy of the background texture.

  scoped_ptr<ResourceProvider::ScopedSamplerGL> contents_resource_lock;
  if (filter_bitmap.getTexture()) {
    GrTexture* texture =
        reinterpret_cast<GrTexture*>(filter_bitmap.getTexture());
    DCHECK_EQ(GL_TEXTURE0, ResourceProvider::GetActiveTextureUnit(gl_));
    gl_->BindTexture(GL_TEXTURE_2D, texture->getTextureHandle());
  } else {
    contents_resource_lock =
        make_scoped_ptr(new ResourceProvider::ScopedSamplerGL(
            resource_provider_, contents_texture->id(), GL_LINEAR));
    DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
              contents_resource_lock->target());
  }

  TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
      gl_,
      &highp_threshold_cache_,
      highp_threshold_min_,
      quad->shared_quad_state->visible_content_rect.bottom_right());

  int shader_quad_location = -1;
  int shader_edge_location = -1;
  int shader_viewport_location = -1;
  int shader_mask_sampler_location = -1;
  int shader_mask_tex_coord_scale_location = -1;
  int shader_mask_tex_coord_offset_location = -1;
  int shader_matrix_location = -1;
  int shader_alpha_location = -1;
  int shader_color_matrix_location = -1;
  int shader_color_offset_location = -1;
  int shader_tex_transform_location = -1;

  if (use_aa && mask_texture_id && !use_color_matrix) {
    const RenderPassMaskProgramAA* program =
        GetRenderPassMaskProgramAA(tex_coord_precision);
    SetUseProgram(program->program());
    GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0));

    shader_quad_location = program->vertex_shader().quad_location();
    shader_edge_location = program->vertex_shader().edge_location();
    shader_viewport_location = program->vertex_shader().viewport_location();
    shader_mask_sampler_location =
        program->fragment_shader().mask_sampler_location();
    shader_mask_tex_coord_scale_location =
        program->fragment_shader().mask_tex_coord_scale_location();
    shader_mask_tex_coord_offset_location =
        program->fragment_shader().mask_tex_coord_offset_location();
    shader_matrix_location = program->vertex_shader().matrix_location();
    shader_alpha_location = program->fragment_shader().alpha_location();
    shader_tex_transform_location =
        program->vertex_shader().tex_transform_location();
  } else if (!use_aa && mask_texture_id && !use_color_matrix) {
    const RenderPassMaskProgram* program =
        GetRenderPassMaskProgram(tex_coord_precision);
    SetUseProgram(program->program());
    GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0));

    shader_mask_sampler_location =
        program->fragment_shader().mask_sampler_location();
    shader_mask_tex_coord_scale_location =
        program->fragment_shader().mask_tex_coord_scale_location();
    shader_mask_tex_coord_offset_location =
        program->fragment_shader().mask_tex_coord_offset_location();
    shader_matrix_location = program->vertex_shader().matrix_location();
    shader_alpha_location = program->fragment_shader().alpha_location();
    shader_tex_transform_location =
        program->vertex_shader().tex_transform_location();
  } else if (use_aa && !mask_texture_id && !use_color_matrix) {
    const RenderPassProgramAA* program =
        GetRenderPassProgramAA(tex_coord_precision);
    SetUseProgram(program->program());
    GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0));

    shader_quad_location = program->vertex_shader().quad_location();
    shader_edge_location = program->vertex_shader().edge_location();
    shader_viewport_location = program->vertex_shader().viewport_location();
    shader_matrix_location = program->vertex_shader().matrix_location();
    shader_alpha_location = program->fragment_shader().alpha_location();
    shader_tex_transform_location =
        program->vertex_shader().tex_transform_location();
  } else if (use_aa && mask_texture_id && use_color_matrix) {
    const RenderPassMaskColorMatrixProgramAA* program =
        GetRenderPassMaskColorMatrixProgramAA(tex_coord_precision);
    SetUseProgram(program->program());
    GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0));

    shader_matrix_location = program->vertex_shader().matrix_location();
    shader_quad_location = program->vertex_shader().quad_location();
    shader_tex_transform_location =
        program->vertex_shader().tex_transform_location();
    shader_edge_location = program->vertex_shader().edge_location();
    shader_viewport_location = program->vertex_shader().viewport_location();
    shader_alpha_location = program->fragment_shader().alpha_location();
    shader_mask_sampler_location =
        program->fragment_shader().mask_sampler_location();
    shader_mask_tex_coord_scale_location =
        program->fragment_shader().mask_tex_coord_scale_location();
    shader_mask_tex_coord_offset_location =
        program->fragment_shader().mask_tex_coord_offset_location();
    shader_color_matrix_location =
        program->fragment_shader().color_matrix_location();
    shader_color_offset_location =
        program->fragment_shader().color_offset_location();
  } else if (use_aa && !mask_texture_id && use_color_matrix) {
    const RenderPassColorMatrixProgramAA* program =
        GetRenderPassColorMatrixProgramAA(tex_coord_precision);
    SetUseProgram(program->program());
    GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0));

    shader_matrix_location = program->vertex_shader().matrix_location();
    shader_quad_location = program->vertex_shader().quad_location();
    shader_tex_transform_location =
        program->vertex_shader().tex_transform_location();
    shader_edge_location = program->vertex_shader().edge_location();
    shader_viewport_location = program->vertex_shader().viewport_location();
    shader_alpha_location = program->fragment_shader().alpha_location();
    shader_color_matrix_location =
        program->fragment_shader().color_matrix_location();
    shader_color_offset_location =
        program->fragment_shader().color_offset_location();
  } else if (!use_aa && mask_texture_id && use_color_matrix) {
    const RenderPassMaskColorMatrixProgram* program =
        GetRenderPassMaskColorMatrixProgram(tex_coord_precision);
    SetUseProgram(program->program());
    GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0));

    shader_matrix_location = program->vertex_shader().matrix_location();
    shader_tex_transform_location =
        program->vertex_shader().tex_transform_location();
    shader_mask_sampler_location =
        program->fragment_shader().mask_sampler_location();
    shader_mask_tex_coord_scale_location =
        program->fragment_shader().mask_tex_coord_scale_location();
    shader_mask_tex_coord_offset_location =
        program->fragment_shader().mask_tex_coord_offset_location();
    shader_alpha_location = program->fragment_shader().alpha_location();
    shader_color_matrix_location =
        program->fragment_shader().color_matrix_location();
    shader_color_offset_location =
        program->fragment_shader().color_offset_location();
  } else if (!use_aa && !mask_texture_id && use_color_matrix) {
    const RenderPassColorMatrixProgram* program =
        GetRenderPassColorMatrixProgram(tex_coord_precision);
    SetUseProgram(program->program());
    GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0));

    shader_matrix_location = program->vertex_shader().matrix_location();
    shader_tex_transform_location =
        program->vertex_shader().tex_transform_location();
    shader_alpha_location = program->fragment_shader().alpha_location();
    shader_color_matrix_location =
        program->fragment_shader().color_matrix_location();
    shader_color_offset_location =
        program->fragment_shader().color_offset_location();
  } else {
    const RenderPassProgram* program =
        GetRenderPassProgram(tex_coord_precision);
    SetUseProgram(program->program());
    GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0));

    shader_matrix_location = program->vertex_shader().matrix_location();
    shader_alpha_location = program->fragment_shader().alpha_location();
    shader_tex_transform_location =
        program->vertex_shader().tex_transform_location();
  }
  float tex_scale_x =
      quad->rect.width() / static_cast<float>(contents_texture->size().width());
  float tex_scale_y = quad->rect.height() /
                      static_cast<float>(contents_texture->size().height());
  DCHECK_LE(tex_scale_x, 1.0f);
  DCHECK_LE(tex_scale_y, 1.0f);

  DCHECK(shader_tex_transform_location != -1 || IsContextLost());
  // Flip the content vertically in the shader, as the RenderPass input
  // texture is already oriented the same way as the framebuffer, but the
  // projection transform does a flip.
  GLC(gl_,
      gl_->Uniform4f(shader_tex_transform_location,
                     0.0f,
                     tex_scale_y,
                     tex_scale_x,
                     -tex_scale_y));

  scoped_ptr<ResourceProvider::ScopedSamplerGL> shader_mask_sampler_lock;
  if (shader_mask_sampler_location != -1) {
    DCHECK_NE(shader_mask_tex_coord_scale_location, 1);
    DCHECK_NE(shader_mask_tex_coord_offset_location, 1);
    GLC(gl_, gl_->Uniform1i(shader_mask_sampler_location, 1));

    float mask_tex_scale_x = quad->mask_uv_rect.width() / tex_scale_x;
    float mask_tex_scale_y = quad->mask_uv_rect.height() / tex_scale_y;

    // Mask textures are oriented vertically flipped relative to the framebuffer
    // and the RenderPass contents texture, so we flip the tex coords from the
    // RenderPass texture to find the mask texture coords.
    GLC(gl_,
        gl_->Uniform2f(shader_mask_tex_coord_offset_location,
                       quad->mask_uv_rect.x(),
                       quad->mask_uv_rect.y() + quad->mask_uv_rect.height()));
    GLC(gl_,
        gl_->Uniform2f(shader_mask_tex_coord_scale_location,
                       mask_tex_scale_x,
                       -mask_tex_scale_y));
    shader_mask_sampler_lock = make_scoped_ptr(
        new ResourceProvider::ScopedSamplerGL(resource_provider_,
                                              quad->mask_resource_id,
                                              GL_TEXTURE1,
                                              GL_LINEAR));
    DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
              shader_mask_sampler_lock->target());
  }

  if (shader_edge_location != -1) {
    float edge[24];
    device_layer_edges.ToFloatArray(edge);
    device_layer_bounds.ToFloatArray(&edge[12]);
    GLC(gl_, gl_->Uniform3fv(shader_edge_location, 8, edge));
  }

  if (shader_viewport_location != -1) {
    float viewport[4] = {static_cast<float>(viewport_.x()),
                         static_cast<float>(viewport_.y()),
                         static_cast<float>(viewport_.width()),
                         static_cast<float>(viewport_.height()), };
    GLC(gl_, gl_->Uniform4fv(shader_viewport_location, 1, viewport));
  }

  if (shader_color_matrix_location != -1) {
    float matrix[16];
    for (int i = 0; i < 4; ++i) {
      for (int j = 0; j < 4; ++j)
        matrix[i * 4 + j] = SkScalarToFloat(color_matrix[j * 5 + i]);
    }
    GLC(gl_,
        gl_->UniformMatrix4fv(shader_color_matrix_location, 1, false, matrix));
  }
  static const float kScale = 1.0f / 255.0f;
  if (shader_color_offset_location != -1) {
    float offset[4];
    for (int i = 0; i < 4; ++i)
      offset[i] = SkScalarToFloat(color_matrix[i * 5 + 4]) * kScale;

    GLC(gl_, gl_->Uniform4fv(shader_color_offset_location, 1, offset));
  }

  // Map device space quad to surface space. contents_device_transform has no 3d
  // component since it was flattened, so we don't need to project.
  gfx::QuadF surface_quad = MathUtil::MapQuad(contents_device_transform_inverse,
                                              device_layer_edges.ToQuadF(),
                                              &clipped);

  SetShaderOpacity(quad->opacity(), shader_alpha_location);
  SetShaderQuadF(surface_quad, shader_quad_location);
  DrawQuadGeometry(
      frame, quad->quadTransform(), quad->rect, shader_matrix_location);

  // Flush the compositor context before the filter bitmap goes out of
  // scope, so the draw gets processed before the filter texture gets deleted.
  if (filter_bitmap.getTexture())
    GLC(gl_, gl_->Flush());
}

struct SolidColorProgramUniforms {
  unsigned program;
  unsigned matrix_location;
  unsigned viewport_location;
  unsigned quad_location;
  unsigned edge_location;
  unsigned color_location;
};

template <class T>
static void SolidColorUniformLocation(T program,
                                      SolidColorProgramUniforms* uniforms) {
  uniforms->program = program->program();
  uniforms->matrix_location = program->vertex_shader().matrix_location();
  uniforms->viewport_location = program->vertex_shader().viewport_location();
  uniforms->quad_location = program->vertex_shader().quad_location();
  uniforms->edge_location = program->vertex_shader().edge_location();
  uniforms->color_location = program->fragment_shader().color_location();
}

// static
bool GLRenderer::SetupQuadForAntialiasing(
    const gfx::Transform& device_transform,
    const DrawQuad* quad,
    gfx::QuadF* local_quad,
    float edge[24]) {
  gfx::Rect tile_rect = quad->visible_rect;

  bool clipped = false;
  gfx::QuadF device_layer_quad = MathUtil::MapQuad(
      device_transform, gfx::QuadF(quad->visibleContentRect()), &clipped);

  bool is_axis_aligned_in_target = device_layer_quad.IsRectilinear();
  bool is_nearest_rect_within_epsilon =
      is_axis_aligned_in_target &&
      gfx::IsNearestRectWithinDistance(device_layer_quad.BoundingBox(),
                                       kAntiAliasingEpsilon);
  // AAing clipped quads is not supported by the code yet.
  bool use_aa = !clipped && !is_nearest_rect_within_epsilon && quad->IsEdge();
  if (!use_aa)
    return false;

  LayerQuad device_layer_bounds(gfx::QuadF(device_layer_quad.BoundingBox()));
  device_layer_bounds.InflateAntiAliasingDistance();

  LayerQuad device_layer_edges(device_layer_quad);
  device_layer_edges.InflateAntiAliasingDistance();

  device_layer_edges.ToFloatArray(edge);
  device_layer_bounds.ToFloatArray(&edge[12]);

  gfx::PointF bottom_right = tile_rect.bottom_right();
  gfx::PointF bottom_left = tile_rect.bottom_left();
  gfx::PointF top_left = tile_rect.origin();
  gfx::PointF top_right = tile_rect.top_right();

  // Map points to device space.
  bottom_right = MathUtil::MapPoint(device_transform, bottom_right, &clipped);
  DCHECK(!clipped);
  bottom_left = MathUtil::MapPoint(device_transform, bottom_left, &clipped);
  DCHECK(!clipped);
  top_left = MathUtil::MapPoint(device_transform, top_left, &clipped);
  DCHECK(!clipped);
  top_right = MathUtil::MapPoint(device_transform, top_right, &clipped);
  DCHECK(!clipped);

  LayerQuad::Edge bottom_edge(bottom_right, bottom_left);
  LayerQuad::Edge left_edge(bottom_left, top_left);
  LayerQuad::Edge top_edge(top_left, top_right);
  LayerQuad::Edge right_edge(top_right, bottom_right);

  // Only apply anti-aliasing to edges not clipped by culling or scissoring.
  if (quad->IsTopEdge() && tile_rect.y() == quad->rect.y())
    top_edge = device_layer_edges.top();
  if (quad->IsLeftEdge() && tile_rect.x() == quad->rect.x())
    left_edge = device_layer_edges.left();
  if (quad->IsRightEdge() && tile_rect.right() == quad->rect.right())
    right_edge = device_layer_edges.right();
  if (quad->IsBottomEdge() && tile_rect.bottom() == quad->rect.bottom())
    bottom_edge = device_layer_edges.bottom();

  float sign = gfx::QuadF(tile_rect).IsCounterClockwise() ? -1 : 1;
  bottom_edge.scale(sign);
  left_edge.scale(sign);
  top_edge.scale(sign);
  right_edge.scale(sign);

  // Create device space quad.
  LayerQuad device_quad(left_edge, top_edge, right_edge, bottom_edge);

  // Map device space quad to local space. device_transform has no 3d
  // component since it was flattened, so we don't need to project.  We should
  // have already checked that the transform was uninvertible above.
  gfx::Transform inverse_device_transform(gfx::Transform::kSkipInitialization);
  bool did_invert = device_transform.GetInverse(&inverse_device_transform);
  DCHECK(did_invert);
  *local_quad = MathUtil::MapQuad(
      inverse_device_transform, device_quad.ToQuadF(), &clipped);
  // We should not DCHECK(!clipped) here, because anti-aliasing inflation may
  // cause device_quad to become clipped. To our knowledge this scenario does
  // not need to be handled differently than the unclipped case.

  return true;
}

void GLRenderer::DrawSolidColorQuad(const DrawingFrame* frame,
                                    const SolidColorDrawQuad* quad) {
  gfx::Rect tile_rect = quad->visible_rect;

  SkColor color = quad->color;
  float opacity = quad->opacity();
  float alpha = (SkColorGetA(color) * (1.0f / 255.0f)) * opacity;

  // Early out if alpha is small enough that quad doesn't contribute to output.
  if (alpha < std::numeric_limits<float>::epsilon() &&
      quad->ShouldDrawWithBlending())
    return;

  gfx::Transform device_transform =
      frame->window_matrix * frame->projection_matrix * quad->quadTransform();
  device_transform.FlattenTo2d();
  if (!device_transform.IsInvertible())
    return;

  gfx::QuadF local_quad = gfx::QuadF(gfx::RectF(tile_rect));
  float edge[24];
  bool use_aa =
      settings_->allow_antialiasing && !quad->force_anti_aliasing_off &&
      SetupQuadForAntialiasing(device_transform, quad, &local_quad, edge);

  SolidColorProgramUniforms uniforms;
  if (use_aa)
    SolidColorUniformLocation(GetSolidColorProgramAA(), &uniforms);
  else
    SolidColorUniformLocation(GetSolidColorProgram(), &uniforms);
  SetUseProgram(uniforms.program);

  GLC(gl_,
      gl_->Uniform4f(uniforms.color_location,
                     (SkColorGetR(color) * (1.0f / 255.0f)) * alpha,
                     (SkColorGetG(color) * (1.0f / 255.0f)) * alpha,
                     (SkColorGetB(color) * (1.0f / 255.0f)) * alpha,
                     alpha));
  if (use_aa) {
    float viewport[4] = {static_cast<float>(viewport_.x()),
                         static_cast<float>(viewport_.y()),
                         static_cast<float>(viewport_.width()),
                         static_cast<float>(viewport_.height()), };
    GLC(gl_, gl_->Uniform4fv(uniforms.viewport_location, 1, viewport));
    GLC(gl_, gl_->Uniform3fv(uniforms.edge_location, 8, edge));
  }

  // Enable blending when the quad properties require it or if we decided
  // to use antialiasing.
  SetBlendEnabled(quad->ShouldDrawWithBlending() || use_aa);

  // Normalize to tile_rect.
  local_quad.Scale(1.0f / tile_rect.width(), 1.0f / tile_rect.height());

  SetShaderQuadF(local_quad, uniforms.quad_location);

  // The transform and vertex data are used to figure out the extents that the
  // un-antialiased quad should have and which vertex this is and the float
  // quad passed in via uniform is the actual geometry that gets used to draw
  // it. This is why this centered rect is used and not the original quad_rect.
  gfx::RectF centered_rect(
      gfx::PointF(-0.5f * tile_rect.width(), -0.5f * tile_rect.height()),
      tile_rect.size());
  DrawQuadGeometry(
      frame, quad->quadTransform(), centered_rect, uniforms.matrix_location);
}

struct TileProgramUniforms {
  unsigned program;
  unsigned matrix_location;
  unsigned viewport_location;
  unsigned quad_location;
  unsigned edge_location;
  unsigned vertex_tex_transform_location;
  unsigned sampler_location;
  unsigned fragment_tex_transform_location;
  unsigned alpha_location;
};

template <class T>
static void TileUniformLocation(T program, TileProgramUniforms* uniforms) {
  uniforms->program = program->program();
  uniforms->matrix_location = program->vertex_shader().matrix_location();
  uniforms->viewport_location = program->vertex_shader().viewport_location();
  uniforms->quad_location = program->vertex_shader().quad_location();
  uniforms->edge_location = program->vertex_shader().edge_location();
  uniforms->vertex_tex_transform_location =
      program->vertex_shader().vertex_tex_transform_location();

  uniforms->sampler_location = program->fragment_shader().sampler_location();
  uniforms->alpha_location = program->fragment_shader().alpha_location();
  uniforms->fragment_tex_transform_location =
      program->fragment_shader().fragment_tex_transform_location();
}

void GLRenderer::DrawTileQuad(const DrawingFrame* frame,
                              const TileDrawQuad* quad) {
  DrawContentQuad(frame, quad, quad->resource_id);
}

void GLRenderer::DrawContentQuad(const DrawingFrame* frame,
                                 const ContentDrawQuadBase* quad,
                                 ResourceProvider::ResourceId resource_id) {
  gfx::Rect tile_rect = quad->visible_rect;

  gfx::RectF tex_coord_rect = MathUtil::ScaleRectProportional(
      quad->tex_coord_rect, quad->rect, tile_rect);
  float tex_to_geom_scale_x = quad->rect.width() / quad->tex_coord_rect.width();
  float tex_to_geom_scale_y =
      quad->rect.height() / quad->tex_coord_rect.height();

  gfx::RectF clamp_geom_rect(tile_rect);
  gfx::RectF clamp_tex_rect(tex_coord_rect);
  // Clamp texture coordinates to avoid sampling outside the layer
  // by deflating the tile region half a texel or half a texel
  // minus epsilon for one pixel layers. The resulting clamp region
  // is mapped to the unit square by the vertex shader and mapped
  // back to normalized texture coordinates by the fragment shader
  // after being clamped to 0-1 range.
  float tex_clamp_x =
      std::min(0.5f, 0.5f * clamp_tex_rect.width() - kAntiAliasingEpsilon);
  float tex_clamp_y =
      std::min(0.5f, 0.5f * clamp_tex_rect.height() - kAntiAliasingEpsilon);
  float geom_clamp_x =
      std::min(tex_clamp_x * tex_to_geom_scale_x,
               0.5f * clamp_geom_rect.width() - kAntiAliasingEpsilon);
  float geom_clamp_y =
      std::min(tex_clamp_y * tex_to_geom_scale_y,
               0.5f * clamp_geom_rect.height() - kAntiAliasingEpsilon);
  clamp_geom_rect.Inset(geom_clamp_x, geom_clamp_y, geom_clamp_x, geom_clamp_y);
  clamp_tex_rect.Inset(tex_clamp_x, tex_clamp_y, tex_clamp_x, tex_clamp_y);

  // Map clamping rectangle to unit square.
  float vertex_tex_translate_x = -clamp_geom_rect.x() / clamp_geom_rect.width();
  float vertex_tex_translate_y =
      -clamp_geom_rect.y() / clamp_geom_rect.height();
  float vertex_tex_scale_x = tile_rect.width() / clamp_geom_rect.width();
  float vertex_tex_scale_y = tile_rect.height() / clamp_geom_rect.height();

  TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
      gl_, &highp_threshold_cache_, highp_threshold_min_, quad->texture_size);

  gfx::Transform device_transform =
      frame->window_matrix * frame->projection_matrix * quad->quadTransform();
  device_transform.FlattenTo2d();
  if (!device_transform.IsInvertible())
    return;

  gfx::QuadF local_quad = gfx::QuadF(gfx::RectF(tile_rect));
  float edge[24];
  bool use_aa =
      settings_->allow_antialiasing &&
      SetupQuadForAntialiasing(device_transform, quad, &local_quad, edge);

  bool scaled = (tex_to_geom_scale_x != 1.f || tex_to_geom_scale_y != 1.f);
  GLenum filter = (use_aa || scaled ||
                   !quad->quadTransform().IsIdentityOrIntegerTranslation())
                      ? GL_LINEAR
                      : GL_NEAREST;
  ResourceProvider::ScopedSamplerGL quad_resource_lock(
      resource_provider_, resource_id, filter);
  SamplerType sampler =
      SamplerTypeFromTextureTarget(quad_resource_lock.target());

  float fragment_tex_translate_x = clamp_tex_rect.x();
  float fragment_tex_translate_y = clamp_tex_rect.y();
  float fragment_tex_scale_x = clamp_tex_rect.width();
  float fragment_tex_scale_y = clamp_tex_rect.height();

  // Map to normalized texture coordinates.
  if (sampler != SamplerType2DRect) {
    gfx::Size texture_size = quad->texture_size;
    DCHECK(!texture_size.IsEmpty());
    fragment_tex_translate_x /= texture_size.width();
    fragment_tex_translate_y /= texture_size.height();
    fragment_tex_scale_x /= texture_size.width();
    fragment_tex_scale_y /= texture_size.height();
  }

  TileProgramUniforms uniforms;
  if (use_aa) {
    if (quad->swizzle_contents) {
      TileUniformLocation(GetTileProgramSwizzleAA(tex_coord_precision, sampler),
                          &uniforms);
    } else {
      TileUniformLocation(GetTileProgramAA(tex_coord_precision, sampler),
                          &uniforms);
    }
  } else {
    if (quad->ShouldDrawWithBlending()) {
      if (quad->swizzle_contents) {
        TileUniformLocation(GetTileProgramSwizzle(tex_coord_precision, sampler),
                            &uniforms);
      } else {
        TileUniformLocation(GetTileProgram(tex_coord_precision, sampler),
                            &uniforms);
      }
    } else {
      if (quad->swizzle_contents) {
        TileUniformLocation(
            GetTileProgramSwizzleOpaque(tex_coord_precision, sampler),
            &uniforms);
      } else {
        TileUniformLocation(GetTileProgramOpaque(tex_coord_precision, sampler),
                            &uniforms);
      }
    }
  }

  SetUseProgram(uniforms.program);
  GLC(gl_, gl_->Uniform1i(uniforms.sampler_location, 0));

  if (use_aa) {
    float viewport[4] = {static_cast<float>(viewport_.x()),
                         static_cast<float>(viewport_.y()),
                         static_cast<float>(viewport_.width()),
                         static_cast<float>(viewport_.height()), };
    GLC(gl_, gl_->Uniform4fv(uniforms.viewport_location, 1, viewport));
    GLC(gl_, gl_->Uniform3fv(uniforms.edge_location, 8, edge));

    GLC(gl_,
        gl_->Uniform4f(uniforms.vertex_tex_transform_location,
                       vertex_tex_translate_x,
                       vertex_tex_translate_y,
                       vertex_tex_scale_x,
                       vertex_tex_scale_y));
    GLC(gl_,
        gl_->Uniform4f(uniforms.fragment_tex_transform_location,
                       fragment_tex_translate_x,
                       fragment_tex_translate_y,
                       fragment_tex_scale_x,
                       fragment_tex_scale_y));
  } else {
    // Move fragment shader transform to vertex shader. We can do this while
    // still producing correct results as fragment_tex_transform_location
    // should always be non-negative when tiles are transformed in a way
    // that could result in sampling outside the layer.
    vertex_tex_scale_x *= fragment_tex_scale_x;
    vertex_tex_scale_y *= fragment_tex_scale_y;
    vertex_tex_translate_x *= fragment_tex_scale_x;
    vertex_tex_translate_y *= fragment_tex_scale_y;
    vertex_tex_translate_x += fragment_tex_translate_x;
    vertex_tex_translate_y += fragment_tex_translate_y;

    GLC(gl_,
        gl_->Uniform4f(uniforms.vertex_tex_transform_location,
                       vertex_tex_translate_x,
                       vertex_tex_translate_y,
                       vertex_tex_scale_x,
                       vertex_tex_scale_y));
  }

  // Enable blending when the quad properties require it or if we decided
  // to use antialiasing.
  SetBlendEnabled(quad->ShouldDrawWithBlending() || use_aa);

  // Normalize to tile_rect.
  local_quad.Scale(1.0f / tile_rect.width(), 1.0f / tile_rect.height());

  SetShaderOpacity(quad->opacity(), uniforms.alpha_location);
  SetShaderQuadF(local_quad, uniforms.quad_location);

  // The transform and vertex data are used to figure out the extents that the
  // un-antialiased quad should have and which vertex this is and the float
  // quad passed in via uniform is the actual geometry that gets used to draw
  // it. This is why this centered rect is used and not the original quad_rect.
  gfx::RectF centered_rect(
      gfx::PointF(-0.5f * tile_rect.width(), -0.5f * tile_rect.height()),
      tile_rect.size());
  DrawQuadGeometry(
      frame, quad->quadTransform(), centered_rect, uniforms.matrix_location);
}

void GLRenderer::DrawYUVVideoQuad(const DrawingFrame* frame,
                                  const YUVVideoDrawQuad* quad) {
  SetBlendEnabled(quad->ShouldDrawWithBlending());

  TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
      gl_,
      &highp_threshold_cache_,
      highp_threshold_min_,
      quad->shared_quad_state->visible_content_rect.bottom_right());

  bool use_alpha_plane = quad->a_plane_resource_id != 0;

  ResourceProvider::ScopedSamplerGL y_plane_lock(
      resource_provider_, quad->y_plane_resource_id, GL_TEXTURE1, GL_LINEAR);
  DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), y_plane_lock.target());
  ResourceProvider::ScopedSamplerGL u_plane_lock(
      resource_provider_, quad->u_plane_resource_id, GL_TEXTURE2, GL_LINEAR);
  DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), u_plane_lock.target());
  ResourceProvider::ScopedSamplerGL v_plane_lock(
      resource_provider_, quad->v_plane_resource_id, GL_TEXTURE3, GL_LINEAR);
  DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), v_plane_lock.target());
  scoped_ptr<ResourceProvider::ScopedSamplerGL> a_plane_lock;
  if (use_alpha_plane) {
    a_plane_lock.reset(new ResourceProvider::ScopedSamplerGL(
        resource_provider_, quad->a_plane_resource_id, GL_TEXTURE4, GL_LINEAR));
    DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), a_plane_lock->target());
  }

  int matrix_location = -1;
  int tex_scale_location = -1;
  int tex_offset_location = -1;
  int y_texture_location = -1;
  int u_texture_location = -1;
  int v_texture_location = -1;
  int a_texture_location = -1;
  int yuv_matrix_location = -1;
  int yuv_adj_location = -1;
  int alpha_location = -1;
  if (use_alpha_plane) {
    const VideoYUVAProgram* program = GetVideoYUVAProgram(tex_coord_precision);
    DCHECK(program && (program->initialized() || IsContextLost()));
    SetUseProgram(program->program());
    matrix_location = program->vertex_shader().matrix_location();
    tex_scale_location = program->vertex_shader().tex_scale_location();
    tex_offset_location = program->vertex_shader().tex_offset_location();
    y_texture_location = program->fragment_shader().y_texture_location();
    u_texture_location = program->fragment_shader().u_texture_location();
    v_texture_location = program->fragment_shader().v_texture_location();
    a_texture_location = program->fragment_shader().a_texture_location();
    yuv_matrix_location = program->fragment_shader().yuv_matrix_location();
    yuv_adj_location = program->fragment_shader().yuv_adj_location();
    alpha_location = program->fragment_shader().alpha_location();
  } else {
    const VideoYUVProgram* program = GetVideoYUVProgram(tex_coord_precision);
    DCHECK(program && (program->initialized() || IsContextLost()));
    SetUseProgram(program->program());
    matrix_location = program->vertex_shader().matrix_location();
    tex_scale_location = program->vertex_shader().tex_scale_location();
    tex_offset_location = program->vertex_shader().tex_offset_location();
    y_texture_location = program->fragment_shader().y_texture_location();
    u_texture_location = program->fragment_shader().u_texture_location();
    v_texture_location = program->fragment_shader().v_texture_location();
    yuv_matrix_location = program->fragment_shader().yuv_matrix_location();
    yuv_adj_location = program->fragment_shader().yuv_adj_location();
    alpha_location = program->fragment_shader().alpha_location();
  }

  GLC(gl_,
      gl_->Uniform2f(tex_scale_location,
                     quad->tex_coord_rect.width(),
                     quad->tex_coord_rect.height()));
  GLC(gl_,
      gl_->Uniform2f(tex_offset_location,
                     quad->tex_coord_rect.x(),
                     quad->tex_coord_rect.y()));
  GLC(gl_, gl_->Uniform1i(y_texture_location, 1));
  GLC(gl_, gl_->Uniform1i(u_texture_location, 2));
  GLC(gl_, gl_->Uniform1i(v_texture_location, 3));
  if (use_alpha_plane)
    GLC(gl_, gl_->Uniform1i(a_texture_location, 4));

  // These values are magic numbers that are used in the transformation from YUV
  // to RGB color values.  They are taken from the following webpage:
  // http://www.fourcc.org/fccyvrgb.php
  float yuv_to_rgb[9] = {1.164f, 1.164f, 1.164f, 0.0f, -.391f,
                         2.018f, 1.596f, -.813f, 0.0f, };
  GLC(gl_, gl_->UniformMatrix3fv(yuv_matrix_location, 1, 0, yuv_to_rgb));

  // These values map to 16, 128, and 128 respectively, and are computed
  // as a fraction over 256 (e.g. 16 / 256 = 0.0625).
  // They are used in the YUV to RGBA conversion formula:
  //   Y - 16   : Gives 16 values of head and footroom for overshooting
  //   U - 128  : Turns unsigned U into signed U [-128,127]
  //   V - 128  : Turns unsigned V into signed V [-128,127]
  float yuv_adjust[3] = {-0.0625f, -0.5f, -0.5f, };
  GLC(gl_, gl_->Uniform3fv(yuv_adj_location, 1, yuv_adjust));

  SetShaderOpacity(quad->opacity(), alpha_location);
  DrawQuadGeometry(frame, quad->quadTransform(), quad->rect, matrix_location);
}

void GLRenderer::DrawStreamVideoQuad(const DrawingFrame* frame,
                                     const StreamVideoDrawQuad* quad) {
  SetBlendEnabled(quad->ShouldDrawWithBlending());

  static float gl_matrix[16];

  DCHECK(capabilities_.using_egl_image);

  TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
      gl_,
      &highp_threshold_cache_,
      highp_threshold_min_,
      quad->shared_quad_state->visible_content_rect.bottom_right());

  const VideoStreamTextureProgram* program =
      GetVideoStreamTextureProgram(tex_coord_precision);
  SetUseProgram(program->program());

  ToGLMatrix(&gl_matrix[0], quad->matrix);
  GLC(gl_,
      gl_->UniformMatrix4fv(
          program->vertex_shader().tex_matrix_location(), 1, false, gl_matrix));

  ResourceProvider::ScopedReadLockGL lock(resource_provider_,
                                          quad->resource_id);
  DCHECK_EQ(GL_TEXTURE0, ResourceProvider::GetActiveTextureUnit(gl_));
  GLC(gl_, gl_->BindTexture(GL_TEXTURE_EXTERNAL_OES, lock.texture_id()));

  GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0));

  SetShaderOpacity(quad->opacity(),
                   program->fragment_shader().alpha_location());
  DrawQuadGeometry(frame,
                   quad->quadTransform(),
                   quad->rect,
                   program->vertex_shader().matrix_location());
}

void GLRenderer::DrawPictureQuad(const DrawingFrame* frame,
                                 const PictureDrawQuad* quad) {
  if (on_demand_tile_raster_bitmap_.width() != quad->texture_size.width() ||
      on_demand_tile_raster_bitmap_.height() != quad->texture_size.height()) {
    on_demand_tile_raster_bitmap_.allocN32Pixels(quad->texture_size.width(),
                                                 quad->texture_size.height());

    if (on_demand_tile_raster_resource_id_)
      resource_provider_->DeleteResource(on_demand_tile_raster_resource_id_);

    on_demand_tile_raster_resource_id_ =
        resource_provider_->CreateGLTexture(quad->texture_size,
                                            GL_TEXTURE_2D,
                                            GL_TEXTURE_POOL_UNMANAGED_CHROMIUM,
                                            GL_CLAMP_TO_EDGE,
                                            ResourceProvider::TextureUsageAny,
                                            quad->texture_format);
  }

  // Create and run on-demand raster task for tile.
  scoped_refptr<internal::Task> on_demand_raster_task(
      new OnDemandRasterTaskImpl(quad->picture_pile,
                                 &on_demand_tile_raster_bitmap_,
                                 quad->content_rect,
                                 quad->contents_scale));
  RunOnDemandRasterTask(on_demand_raster_task.get());

  uint8_t* bitmap_pixels = NULL;
  SkBitmap on_demand_tile_raster_bitmap_dest;
  SkColorType colorType = ResourceFormatToSkColorType(quad->texture_format);
  if (on_demand_tile_raster_bitmap_.colorType() != colorType) {
    on_demand_tile_raster_bitmap_.copyTo(&on_demand_tile_raster_bitmap_dest,
                                         colorType);
    // TODO(kaanb): The GL pipeline assumes a 4-byte alignment for the
    // bitmap data. This check will be removed once crbug.com/293728 is fixed.
    CHECK_EQ(0u, on_demand_tile_raster_bitmap_dest.rowBytes() % 4);
    bitmap_pixels = reinterpret_cast<uint8_t*>(
        on_demand_tile_raster_bitmap_dest.getPixels());
  } else {
    bitmap_pixels =
        reinterpret_cast<uint8_t*>(on_demand_tile_raster_bitmap_.getPixels());
  }

  resource_provider_->SetPixels(on_demand_tile_raster_resource_id_,
                                bitmap_pixels,
                                gfx::Rect(quad->texture_size),
                                gfx::Rect(quad->texture_size),
                                gfx::Vector2d());

  DrawContentQuad(frame, quad, on_demand_tile_raster_resource_id_);
}

struct TextureProgramBinding {
  template <class Program>
  void Set(Program* program) {
    DCHECK(program);
    program_id = program->program();
    sampler_location = program->fragment_shader().sampler_location();
    matrix_location = program->vertex_shader().matrix_location();
    background_color_location =
        program->fragment_shader().background_color_location();
  }
  int program_id;
  int sampler_location;
  int matrix_location;
  int background_color_location;
};

struct TexTransformTextureProgramBinding : TextureProgramBinding {
  template <class Program>
  void Set(Program* program) {
    TextureProgramBinding::Set(program);
    tex_transform_location = program->vertex_shader().tex_transform_location();
    vertex_opacity_location =
        program->vertex_shader().vertex_opacity_location();
  }
  int tex_transform_location;
  int vertex_opacity_location;
};

void GLRenderer::FlushTextureQuadCache() {
  // Check to see if we have anything to draw.
  if (draw_cache_.program_id == 0)
    return;

  // Set the correct blending mode.
  SetBlendEnabled(draw_cache_.needs_blending);

  // Bind the program to the GL state.
  SetUseProgram(draw_cache_.program_id);

  // Bind the correct texture sampler location.
  GLC(gl_, gl_->Uniform1i(draw_cache_.sampler_location, 0));

  // Assume the current active textures is 0.
  ResourceProvider::ScopedReadLockGL locked_quad(resource_provider_,
                                                 draw_cache_.resource_id);
  DCHECK_EQ(GL_TEXTURE0, ResourceProvider::GetActiveTextureUnit(gl_));
  GLC(gl_, gl_->BindTexture(GL_TEXTURE_2D, locked_quad.texture_id()));

  COMPILE_ASSERT(sizeof(Float4) == 4 * sizeof(float),  // NOLINT(runtime/sizeof)
                 struct_is_densely_packed);
  COMPILE_ASSERT(
      sizeof(Float16) == 16 * sizeof(float),  // NOLINT(runtime/sizeof)
      struct_is_densely_packed);

  // Upload the tranforms for both points and uvs.
  GLC(gl_,
      gl_->UniformMatrix4fv(
          static_cast<int>(draw_cache_.matrix_location),
          static_cast<int>(draw_cache_.matrix_data.size()),
          false,
          reinterpret_cast<float*>(&draw_cache_.matrix_data.front())));
  GLC(gl_,
      gl_->Uniform4fv(
          static_cast<int>(draw_cache_.uv_xform_location),
          static_cast<int>(draw_cache_.uv_xform_data.size()),
          reinterpret_cast<float*>(&draw_cache_.uv_xform_data.front())));

  if (draw_cache_.background_color != SK_ColorTRANSPARENT) {
    Float4 background_color = PremultipliedColor(draw_cache_.background_color);
    GLC(gl_,
        gl_->Uniform4fv(
            draw_cache_.background_color_location, 1, background_color.data));
  }

  GLC(gl_,
      gl_->Uniform1fv(
          static_cast<int>(draw_cache_.vertex_opacity_location),
          static_cast<int>(draw_cache_.vertex_opacity_data.size()),
          static_cast<float*>(&draw_cache_.vertex_opacity_data.front())));

  // Draw the quads!
  GLC(gl_,
      gl_->DrawElements(GL_TRIANGLES,
                        6 * draw_cache_.matrix_data.size(),
                        GL_UNSIGNED_SHORT,
                        0));

  // Clear the cache.
  draw_cache_.program_id = 0;
  draw_cache_.uv_xform_data.resize(0);
  draw_cache_.vertex_opacity_data.resize(0);
  draw_cache_.matrix_data.resize(0);
}

void GLRenderer::EnqueueTextureQuad(const DrawingFrame* frame,
                                    const TextureDrawQuad* quad) {
  TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
      gl_,
      &highp_threshold_cache_,
      highp_threshold_min_,
      quad->shared_quad_state->visible_content_rect.bottom_right());

  // Choose the correct texture program binding
  TexTransformTextureProgramBinding binding;
  if (quad->premultiplied_alpha) {
    if (quad->background_color == SK_ColorTRANSPARENT) {
      binding.Set(GetTextureProgram(tex_coord_precision));
    } else {
      binding.Set(GetTextureBackgroundProgram(tex_coord_precision));
    }
  } else {
    if (quad->background_color == SK_ColorTRANSPARENT) {
      binding.Set(GetNonPremultipliedTextureProgram(tex_coord_precision));
    } else {
      binding.Set(
          GetNonPremultipliedTextureBackgroundProgram(tex_coord_precision));
    }
  }

  int resource_id = quad->resource_id;

  if (draw_cache_.program_id != binding.program_id ||
      draw_cache_.resource_id != resource_id ||
      draw_cache_.needs_blending != quad->ShouldDrawWithBlending() ||
      draw_cache_.background_color != quad->background_color ||
      draw_cache_.matrix_data.size() >= 8) {
    FlushTextureQuadCache();
    draw_cache_.program_id = binding.program_id;
    draw_cache_.resource_id = resource_id;
    draw_cache_.needs_blending = quad->ShouldDrawWithBlending();
    draw_cache_.background_color = quad->background_color;

    draw_cache_.uv_xform_location = binding.tex_transform_location;
    draw_cache_.background_color_location = binding.background_color_location;
    draw_cache_.vertex_opacity_location = binding.vertex_opacity_location;
    draw_cache_.matrix_location = binding.matrix_location;
    draw_cache_.sampler_location = binding.sampler_location;
  }

  // Generate the uv-transform
  draw_cache_.uv_xform_data.push_back(UVTransform(quad));

  // Generate the vertex opacity
  const float opacity = quad->opacity();
  draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[0] * opacity);
  draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[1] * opacity);
  draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[2] * opacity);
  draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[3] * opacity);

  // Generate the transform matrix
  gfx::Transform quad_rect_matrix;
  QuadRectTransform(&quad_rect_matrix, quad->quadTransform(), quad->rect);
  quad_rect_matrix = frame->projection_matrix * quad_rect_matrix;

  Float16 m;
  quad_rect_matrix.matrix().asColMajorf(m.data);
  draw_cache_.matrix_data.push_back(m);
}

void GLRenderer::DrawIOSurfaceQuad(const DrawingFrame* frame,
                                   const IOSurfaceDrawQuad* quad) {
  SetBlendEnabled(quad->ShouldDrawWithBlending());

  TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
      gl_,
      &highp_threshold_cache_,
      highp_threshold_min_,
      quad->shared_quad_state->visible_content_rect.bottom_right());

  TexTransformTextureProgramBinding binding;
  binding.Set(GetTextureIOSurfaceProgram(tex_coord_precision));

  SetUseProgram(binding.program_id);
  GLC(gl_, gl_->Uniform1i(binding.sampler_location, 0));
  if (quad->orientation == IOSurfaceDrawQuad::FLIPPED) {
    GLC(gl_,
        gl_->Uniform4f(binding.tex_transform_location,
                       0,
                       quad->io_surface_size.height(),
                       quad->io_surface_size.width(),
                       quad->io_surface_size.height() * -1.0f));
  } else {
    GLC(gl_,
        gl_->Uniform4f(binding.tex_transform_location,
                       0,
                       0,
                       quad->io_surface_size.width(),
                       quad->io_surface_size.height()));
  }

  const float vertex_opacity[] = {quad->opacity(), quad->opacity(),
                                  quad->opacity(), quad->opacity()};
  GLC(gl_, gl_->Uniform1fv(binding.vertex_opacity_location, 4, vertex_opacity));

  ResourceProvider::ScopedReadLockGL lock(resource_provider_,
                                          quad->io_surface_resource_id);
  DCHECK_EQ(GL_TEXTURE0, ResourceProvider::GetActiveTextureUnit(gl_));
  GLC(gl_, gl_->BindTexture(GL_TEXTURE_RECTANGLE_ARB, lock.texture_id()));

  DrawQuadGeometry(
      frame, quad->quadTransform(), quad->rect, binding.matrix_location);

  GLC(gl_, gl_->BindTexture(GL_TEXTURE_RECTANGLE_ARB, 0));
}

void GLRenderer::FinishDrawingFrame(DrawingFrame* frame) {
  current_framebuffer_lock_.reset();
  swap_buffer_rect_.Union(gfx::ToEnclosingRect(frame->root_damage_rect));

  GLC(gl_, gl_->Disable(GL_BLEND));
  blend_shadow_ = false;

  ScheduleOverlays(frame);
}

void GLRenderer::FinishDrawingQuadList() { FlushTextureQuadCache(); }

bool GLRenderer::FlippedFramebuffer() const { return true; }

void GLRenderer::EnsureScissorTestEnabled() {
  if (is_scissor_enabled_)
    return;

  FlushTextureQuadCache();
  GLC(gl_, gl_->Enable(GL_SCISSOR_TEST));
  is_scissor_enabled_ = true;
}

void GLRenderer::EnsureScissorTestDisabled() {
  if (!is_scissor_enabled_)
    return;

  FlushTextureQuadCache();
  GLC(gl_, gl_->Disable(GL_SCISSOR_TEST));
  is_scissor_enabled_ = false;
}

void GLRenderer::CopyCurrentRenderPassToBitmap(
    DrawingFrame* frame,
    scoped_ptr<CopyOutputRequest> request) {
  gfx::Rect copy_rect = frame->current_render_pass->output_rect;
  if (request->has_area())
    copy_rect.Intersect(request->area());
  GetFramebufferPixelsAsync(copy_rect, request.Pass());
}

void GLRenderer::ToGLMatrix(float* gl_matrix, const gfx::Transform& transform) {
  transform.matrix().asColMajorf(gl_matrix);
}

void GLRenderer::SetShaderQuadF(const gfx::QuadF& quad, int quad_location) {
  if (quad_location == -1)
    return;

  float gl_quad[8];
  gl_quad[0] = quad.p1().x();
  gl_quad[1] = quad.p1().y();
  gl_quad[2] = quad.p2().x();
  gl_quad[3] = quad.p2().y();
  gl_quad[4] = quad.p3().x();
  gl_quad[5] = quad.p3().y();
  gl_quad[6] = quad.p4().x();
  gl_quad[7] = quad.p4().y();
  GLC(gl_, gl_->Uniform2fv(quad_location, 4, gl_quad));
}

void GLRenderer::SetShaderOpacity(float opacity, int alpha_location) {
  if (alpha_location != -1)
    GLC(gl_, gl_->Uniform1f(alpha_location, opacity));
}

void GLRenderer::SetStencilEnabled(bool enabled) {
  if (enabled == stencil_shadow_)
    return;

  if (enabled)
    GLC(gl_, gl_->Enable(GL_STENCIL_TEST));
  else
    GLC(gl_, gl_->Disable(GL_STENCIL_TEST));
  stencil_shadow_ = enabled;
}

void GLRenderer::SetBlendEnabled(bool enabled) {
  if (enabled == blend_shadow_)
    return;

  if (enabled)
    GLC(gl_, gl_->Enable(GL_BLEND));
  else
    GLC(gl_, gl_->Disable(GL_BLEND));
  blend_shadow_ = enabled;
}

void GLRenderer::SetUseProgram(unsigned program) {
  if (program == program_shadow_)
    return;
  gl_->UseProgram(program);
  program_shadow_ = program;
}

void GLRenderer::DrawQuadGeometry(const DrawingFrame* frame,
                                  const gfx::Transform& draw_transform,
                                  const gfx::RectF& quad_rect,
                                  int matrix_location) {
  gfx::Transform quad_rect_matrix;
  QuadRectTransform(&quad_rect_matrix, draw_transform, quad_rect);
  static float gl_matrix[16];
  ToGLMatrix(&gl_matrix[0], frame->projection_matrix * quad_rect_matrix);
  GLC(gl_, gl_->UniformMatrix4fv(matrix_location, 1, false, &gl_matrix[0]));

  GLC(gl_, gl_->DrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0));
}

void GLRenderer::CopyTextureToFramebuffer(const DrawingFrame* frame,
                                          int texture_id,
                                          const gfx::Rect& rect,
                                          const gfx::Transform& draw_matrix,
                                          bool flip_vertically) {
  TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired(
      gl_, &highp_threshold_cache_, highp_threshold_min_, rect.bottom_right());

  const RenderPassProgram* program = GetRenderPassProgram(tex_coord_precision);
  SetUseProgram(program->program());

  GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0));

  if (flip_vertically) {
    GLC(gl_,
        gl_->Uniform4f(program->vertex_shader().tex_transform_location(),
                       0.f,
                       1.f,
                       1.f,
                       -1.f));
  } else {
    GLC(gl_,
        gl_->Uniform4f(program->vertex_shader().tex_transform_location(),
                       0.f,
                       0.f,
                       1.f,
                       1.f));
  }

  SetShaderOpacity(1.f, program->fragment_shader().alpha_location());
  DCHECK_EQ(GL_TEXTURE0, ResourceProvider::GetActiveTextureUnit(gl_));
  GLC(gl_, gl_->BindTexture(GL_TEXTURE_2D, texture_id));
  DrawQuadGeometry(
      frame, draw_matrix, rect, program->vertex_shader().matrix_location());
}

void GLRenderer::Finish() {
  TRACE_EVENT0("cc", "GLRenderer::Finish");
  GLC(gl_, gl_->Finish());
}

void GLRenderer::SwapBuffers(const CompositorFrameMetadata& metadata) {
  DCHECK(!is_backbuffer_discarded_);

  TRACE_EVENT0("cc,benchmark", "GLRenderer::SwapBuffers");
  // We're done! Time to swapbuffers!

  gfx::Size surface_size = output_surface_->SurfaceSize();

  CompositorFrame compositor_frame;
  compositor_frame.metadata = metadata;
  compositor_frame.gl_frame_data = make_scoped_ptr(new GLFrameData);
  compositor_frame.gl_frame_data->size = surface_size;
  if (capabilities_.using_partial_swap) {
    // If supported, we can save significant bandwidth by only swapping the
    // damaged/scissored region (clamped to the viewport).
    swap_buffer_rect_.Intersect(gfx::Rect(surface_size));
    int flipped_y_pos_of_rect_bottom = surface_size.height() -
                                       swap_buffer_rect_.y() -
                                       swap_buffer_rect_.height();
    compositor_frame.gl_frame_data->sub_buffer_rect =
        gfx::Rect(swap_buffer_rect_.x(),
                  flipped_y_pos_of_rect_bottom,
                  swap_buffer_rect_.width(),
                  swap_buffer_rect_.height());
  } else {
    compositor_frame.gl_frame_data->sub_buffer_rect =
        gfx::Rect(output_surface_->SurfaceSize());
  }
  output_surface_->SwapBuffers(&compositor_frame);

  // Release previously used overlay resources and hold onto the pending ones
  // until the next swap buffers.
  in_use_overlay_resources_.clear();
  in_use_overlay_resources_.swap(pending_overlay_resources_);

  swap_buffer_rect_ = gfx::Rect();

  // We don't have real fences, so we mark read fences as passed
  // assuming a double-buffered GPU pipeline. A texture can be
  // written to after one full frame has past since it was last read.
  if (last_swap_fence_.get())
    static_cast<SimpleSwapFence*>(last_swap_fence_.get())->SetHasPassed();
  last_swap_fence_ = resource_provider_->GetReadLockFence();
  resource_provider_->SetReadLockFence(new SimpleSwapFence());
}

void GLRenderer::EnforceMemoryPolicy() {
  if (!visible_) {
    TRACE_EVENT0("cc", "GLRenderer::EnforceMemoryPolicy dropping resources");
    ReleaseRenderPassTextures();
    DiscardBackbuffer();
    resource_provider_->ReleaseCachedData();
    GLC(gl_, gl_->Flush());
  }
}

void GLRenderer::DiscardBackbuffer() {
  if (is_backbuffer_discarded_)
    return;

  output_surface_->DiscardBackbuffer();

  is_backbuffer_discarded_ = true;

  // Damage tracker needs a full reset every time framebuffer is discarded.
  client_->SetFullRootLayerDamage();
}

void GLRenderer::EnsureBackbuffer() {
  if (!is_backbuffer_discarded_)
    return;

  output_surface_->EnsureBackbuffer();
  is_backbuffer_discarded_ = false;
}

void GLRenderer::GetFramebufferPixels(void* pixels, const gfx::Rect& rect) {
  if (!pixels || rect.IsEmpty())
    return;

  // This function assumes that it is reading the root frame buffer.
  DCHECK(!current_framebuffer_lock_);

  scoped_ptr<PendingAsyncReadPixels> pending_read(new PendingAsyncReadPixels);
  pending_async_read_pixels_.insert(pending_async_read_pixels_.begin(),
                                    pending_read.Pass());

  // This is a syncronous call since the callback is null.
  gfx::Rect window_rect = MoveFromDrawToWindowSpace(rect);
  DoGetFramebufferPixels(static_cast<uint8*>(pixels),
                         window_rect,
                         AsyncGetFramebufferPixelsCleanupCallback());
}

void GLRenderer::GetFramebufferPixelsAsync(
    const gfx::Rect& rect,
    scoped_ptr<CopyOutputRequest> request) {
  DCHECK(!request->IsEmpty());
  if (request->IsEmpty())
    return;
  if (rect.IsEmpty())
    return;

  gfx::Rect window_rect = MoveFromDrawToWindowSpace(rect);

  if (!request->force_bitmap_result()) {
    bool own_mailbox = !request->has_texture_mailbox();

    GLuint texture_id = 0;
    gl_->GenTextures(1, &texture_id);

    gpu::Mailbox mailbox;
    if (own_mailbox) {
      GLC(gl_, gl_->GenMailboxCHROMIUM(mailbox.name));
    } else {
      mailbox = request->texture_mailbox().mailbox();
      DCHECK_EQ(static_cast<unsigned>(GL_TEXTURE_2D),
                request->texture_mailbox().target());
      DCHECK(!mailbox.IsZero());
      unsigned incoming_sync_point = request->texture_mailbox().sync_point();
      if (incoming_sync_point)
        GLC(gl_, gl_->WaitSyncPointCHROMIUM(incoming_sync_point));
    }

    GLC(gl_, gl_->BindTexture(GL_TEXTURE_2D, texture_id));
    if (own_mailbox) {
      GLC(gl_,
          gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
      GLC(gl_,
          gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
      GLC(gl_,
          gl_->TexParameteri(
              GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
      GLC(gl_,
          gl_->TexParameteri(
              GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
      GLC(gl_, gl_->ProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name));
    } else {
      GLC(gl_, gl_->ConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name));
    }
    GetFramebufferTexture(texture_id, RGBA_8888, window_rect);
    GLC(gl_, gl_->BindTexture(GL_TEXTURE_2D, 0));

    unsigned sync_point = gl_->InsertSyncPointCHROMIUM();
    TextureMailbox texture_mailbox(mailbox, GL_TEXTURE_2D, sync_point);

    scoped_ptr<SingleReleaseCallback> release_callback;
    if (own_mailbox) {
      release_callback = texture_mailbox_deleter_->GetReleaseCallback(
          output_surface_->context_provider(), texture_id);
    } else {
      gl_->DeleteTextures(1, &texture_id);
    }

    request->SendTextureResult(
        window_rect.size(), texture_mailbox, release_callback.Pass());
    return;
  }

  DCHECK(request->force_bitmap_result());

  scoped_ptr<SkBitmap> bitmap(new SkBitmap);
  bitmap->allocN32Pixels(window_rect.width(), window_rect.height());

  scoped_ptr<SkAutoLockPixels> lock(new SkAutoLockPixels(*bitmap));

  // Save a pointer to the pixels, the bitmap is owned by the cleanup_callback.
  uint8* pixels = static_cast<uint8*>(bitmap->getPixels());

  AsyncGetFramebufferPixelsCleanupCallback cleanup_callback =
      base::Bind(&GLRenderer::PassOnSkBitmap,
                 base::Unretained(this),
                 base::Passed(&bitmap),
                 base::Passed(&lock));

  scoped_ptr<PendingAsyncReadPixels> pending_read(new PendingAsyncReadPixels);
  pending_read->copy_request = request.Pass();
  pending_async_read_pixels_.insert(pending_async_read_pixels_.begin(),
                                    pending_read.Pass());

  // This is an asyncronous call since the callback is not null.
  DoGetFramebufferPixels(pixels, window_rect, cleanup_callback);
}

void GLRenderer::DoGetFramebufferPixels(
    uint8* dest_pixels,
    const gfx::Rect& window_rect,
    const AsyncGetFramebufferPixelsCleanupCallback& cleanup_callback) {
  DCHECK_GE(window_rect.x(), 0);
  DCHECK_GE(window_rect.y(), 0);
  DCHECK_LE(window_rect.right(), current_surface_size_.width());
  DCHECK_LE(window_rect.bottom(), current_surface_size_.height());

  bool is_async = !cleanup_callback.is_null();

  bool do_workaround = NeedsIOSurfaceReadbackWorkaround();

  unsigned temporary_texture = 0;
  unsigned temporary_fbo = 0;

  if (do_workaround) {
    // On Mac OS X, calling glReadPixels() against an FBO whose color attachment
    // is an IOSurface-backed texture causes corruption of future glReadPixels()
    // calls, even those on different OpenGL contexts. It is believed that this
    // is the root cause of top crasher
    // http://crbug.com/99393. <rdar://problem/10949687>

    gl_->GenTextures(1, &temporary_texture);
    GLC(gl_, gl_->BindTexture(GL_TEXTURE_2D, temporary_texture));
    GLC(gl_,
        gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
    GLC(gl_,
        gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
    GLC(gl_,
        gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
    GLC(gl_,
        gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
    // Copy the contents of the current (IOSurface-backed) framebuffer into a
    // temporary texture.
    GetFramebufferTexture(
        temporary_texture, RGBA_8888, gfx::Rect(current_surface_size_));
    gl_->GenFramebuffers(1, &temporary_fbo);
    // Attach this texture to an FBO, and perform the readback from that FBO.
    GLC(gl_, gl_->BindFramebuffer(GL_FRAMEBUFFER, temporary_fbo));
    GLC(gl_,
        gl_->FramebufferTexture2D(GL_FRAMEBUFFER,
                                  GL_COLOR_ATTACHMENT0,
                                  GL_TEXTURE_2D,
                                  temporary_texture,
                                  0));

    DCHECK_EQ(static_cast<unsigned>(GL_FRAMEBUFFER_COMPLETE),
              gl_->CheckFramebufferStatus(GL_FRAMEBUFFER));
  }

  GLuint buffer = 0;
  gl_->GenBuffers(1, &buffer);
  GLC(gl_, gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, buffer));
  GLC(gl_,
      gl_->BufferData(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM,
                      4 * window_rect.size().GetArea(),
                      NULL,
                      GL_STREAM_READ));

  GLuint query = 0;
  if (is_async) {
    gl_->GenQueriesEXT(1, &query);
    GLC(gl_, gl_->BeginQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM, query));
  }

  GLC(gl_,
      gl_->ReadPixels(window_rect.x(),
                      window_rect.y(),
                      window_rect.width(),
                      window_rect.height(),
                      GL_RGBA,
                      GL_UNSIGNED_BYTE,
                      NULL));

  GLC(gl_, gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0));

  if (do_workaround) {
    // Clean up.
    GLC(gl_, gl_->BindFramebuffer(GL_FRAMEBUFFER, 0));
    GLC(gl_, gl_->BindTexture(GL_TEXTURE_2D, 0));
    GLC(gl_, gl_->DeleteFramebuffers(1, &temporary_fbo));
    GLC(gl_, gl_->DeleteTextures(1, &temporary_texture));
  }

  base::Closure finished_callback = base::Bind(&GLRenderer::FinishedReadback,
                                               base::Unretained(this),
                                               cleanup_callback,
                                               buffer,
                                               query,
                                               dest_pixels,
                                               window_rect.size());
  // Save the finished_callback so it can be cancelled.
  pending_async_read_pixels_.front()->finished_read_pixels_callback.Reset(
      finished_callback);
  base::Closure cancelable_callback =
      pending_async_read_pixels_.front()->
          finished_read_pixels_callback.callback();

  // Save the buffer to verify the callbacks happen in the expected order.
  pending_async_read_pixels_.front()->buffer = buffer;

  if (is_async) {
    GLC(gl_, gl_->EndQueryEXT(GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM));
    context_support_->SignalQuery(query, cancelable_callback);
  } else {
    resource_provider_->Finish();
    finished_callback.Run();
  }

  EnforceMemoryPolicy();
}

void GLRenderer::FinishedReadback(
    const AsyncGetFramebufferPixelsCleanupCallback& cleanup_callback,
    unsigned source_buffer,
    unsigned query,
    uint8* dest_pixels,
    const gfx::Size& size) {
  DCHECK(!pending_async_read_pixels_.empty());

  if (query != 0) {
    GLC(gl_, gl_->DeleteQueriesEXT(1, &query));
  }

  PendingAsyncReadPixels* current_read = pending_async_read_pixels_.back();
  // Make sure we service the readbacks in order.
  DCHECK_EQ(source_buffer, current_read->buffer);

  uint8* src_pixels = NULL;

  if (source_buffer != 0) {
    GLC(gl_,
        gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, source_buffer));
    src_pixels = static_cast<uint8*>(gl_->MapBufferCHROMIUM(
        GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, GL_READ_ONLY));

    if (src_pixels) {
      size_t row_bytes = size.width() * 4;
      int num_rows = size.height();
      size_t total_bytes = num_rows * row_bytes;
      for (size_t dest_y = 0; dest_y < total_bytes; dest_y += row_bytes) {
        // Flip Y axis.
        size_t src_y = total_bytes - dest_y - row_bytes;
        // Swizzle OpenGL -> Skia byte order.
        for (size_t x = 0; x < row_bytes; x += 4) {
          dest_pixels[dest_y + x + SK_R32_SHIFT / 8] =
              src_pixels[src_y + x + 0];
          dest_pixels[dest_y + x + SK_G32_SHIFT / 8] =
              src_pixels[src_y + x + 1];
          dest_pixels[dest_y + x + SK_B32_SHIFT / 8] =
              src_pixels[src_y + x + 2];
          dest_pixels[dest_y + x + SK_A32_SHIFT / 8] =
              src_pixels[src_y + x + 3];
        }
      }

      GLC(gl_,
          gl_->UnmapBufferCHROMIUM(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM));
    }
    GLC(gl_, gl_->BindBuffer(GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM, 0));
    GLC(gl_, gl_->DeleteBuffers(1, &source_buffer));
  }

  // TODO(danakj): This can go away when synchronous readback is no more and its
  // contents can just move here.
  if (!cleanup_callback.is_null())
    cleanup_callback.Run(current_read->copy_request.Pass(), src_pixels != NULL);

  pending_async_read_pixels_.pop_back();
}

void GLRenderer::PassOnSkBitmap(scoped_ptr<SkBitmap> bitmap,
                                scoped_ptr<SkAutoLockPixels> lock,
                                scoped_ptr<CopyOutputRequest> request,
                                bool success) {
  DCHECK(request->force_bitmap_result());

  lock.reset();
  if (success)
    request->SendBitmapResult(bitmap.Pass());
}

void GLRenderer::GetFramebufferTexture(unsigned texture_id,
                                       ResourceFormat texture_format,
                                       const gfx::Rect& window_rect) {
  DCHECK(texture_id);
  DCHECK_GE(window_rect.x(), 0);
  DCHECK_GE(window_rect.y(), 0);
  DCHECK_LE(window_rect.right(), current_surface_size_.width());
  DCHECK_LE(window_rect.bottom(), current_surface_size_.height());

  GLC(gl_, gl_->BindTexture(GL_TEXTURE_2D, texture_id));
  GLC(gl_,
      gl_->CopyTexImage2D(GL_TEXTURE_2D,
                          0,
                          GLDataFormat(texture_format),
                          window_rect.x(),
                          window_rect.y(),
                          window_rect.width(),
                          window_rect.height(),
                          0));
  GLC(gl_, gl_->BindTexture(GL_TEXTURE_2D, 0));
}

bool GLRenderer::UseScopedTexture(DrawingFrame* frame,
                                  const ScopedResource* texture,
                                  const gfx::Rect& viewport_rect) {
  DCHECK(texture->id());
  frame->current_render_pass = NULL;
  frame->current_texture = texture;

  return BindFramebufferToTexture(frame, texture, viewport_rect);
}

void GLRenderer::BindFramebufferToOutputSurface(DrawingFrame* frame) {
  current_framebuffer_lock_.reset();
  output_surface_->BindFramebuffer();

  if (output_surface_->HasExternalStencilTest()) {
    SetStencilEnabled(true);
    GLC(gl_, gl_->StencilFunc(GL_EQUAL, 1, 1));
  } else {
    SetStencilEnabled(false);
  }
}

bool GLRenderer::BindFramebufferToTexture(DrawingFrame* frame,
                                          const ScopedResource* texture,
                                          const gfx::Rect& target_rect) {
  DCHECK(texture->id());

  current_framebuffer_lock_.reset();

  SetStencilEnabled(false);
  GLC(gl_, gl_->BindFramebuffer(GL_FRAMEBUFFER, offscreen_framebuffer_id_));
  current_framebuffer_lock_ =
      make_scoped_ptr(new ResourceProvider::ScopedWriteLockGL(
          resource_provider_, texture->id()));
  unsigned texture_id = current_framebuffer_lock_->texture_id();
  GLC(gl_,
      gl_->FramebufferTexture2D(
          GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0));

  DCHECK(gl_->CheckFramebufferStatus(GL_FRAMEBUFFER) ==
             GL_FRAMEBUFFER_COMPLETE ||
         IsContextLost());

  InitializeViewport(
      frame, target_rect, gfx::Rect(target_rect.size()), target_rect.size());
  return true;
}

void GLRenderer::SetScissorTestRect(const gfx::Rect& scissor_rect) {
  EnsureScissorTestEnabled();

  // Don't unnecessarily ask the context to change the scissor, because it
  // may cause undesired GPU pipeline flushes.
  if (scissor_rect == scissor_rect_ && !scissor_rect_needs_reset_)
    return;

  scissor_rect_ = scissor_rect;
  FlushTextureQuadCache();
  GLC(gl_,
      gl_->Scissor(scissor_rect.x(),
                   scissor_rect.y(),
                   scissor_rect.width(),
                   scissor_rect.height()));

  scissor_rect_needs_reset_ = false;
}

void GLRenderer::SetDrawViewport(const gfx::Rect& window_space_viewport) {
  viewport_ = window_space_viewport;
  GLC(gl_,
      gl_->Viewport(window_space_viewport.x(),
                    window_space_viewport.y(),
                    window_space_viewport.width(),
                    window_space_viewport.height()));
}

void GLRenderer::InitializeSharedObjects() {
  TRACE_EVENT0("cc", "GLRenderer::InitializeSharedObjects");

  // Create an FBO for doing offscreen rendering.
  GLC(gl_, gl_->GenFramebuffers(1, &offscreen_framebuffer_id_));

  shared_geometry_ = make_scoped_ptr(
      new GeometryBinding(gl_, QuadVertexRect()));
}

const GLRenderer::TileCheckerboardProgram*
GLRenderer::GetTileCheckerboardProgram() {
  if (!tile_checkerboard_program_.initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::checkerboardProgram::initalize");
    tile_checkerboard_program_.Initialize(output_surface_->context_provider(),
                                          TexCoordPrecisionNA,
                                          SamplerTypeNA);
  }
  return &tile_checkerboard_program_;
}

const GLRenderer::DebugBorderProgram* GLRenderer::GetDebugBorderProgram() {
  if (!debug_border_program_.initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::debugBorderProgram::initialize");
    debug_border_program_.Initialize(output_surface_->context_provider(),
                                     TexCoordPrecisionNA,
                                     SamplerTypeNA);
  }
  return &debug_border_program_;
}

const GLRenderer::SolidColorProgram* GLRenderer::GetSolidColorProgram() {
  if (!solid_color_program_.initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::solidColorProgram::initialize");
    solid_color_program_.Initialize(output_surface_->context_provider(),
                                    TexCoordPrecisionNA,
                                    SamplerTypeNA);
  }
  return &solid_color_program_;
}

const GLRenderer::SolidColorProgramAA* GLRenderer::GetSolidColorProgramAA() {
  if (!solid_color_program_aa_.initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::solidColorProgramAA::initialize");
    solid_color_program_aa_.Initialize(output_surface_->context_provider(),
                                       TexCoordPrecisionNA,
                                       SamplerTypeNA);
  }
  return &solid_color_program_aa_;
}

const GLRenderer::RenderPassProgram* GLRenderer::GetRenderPassProgram(
    TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  RenderPassProgram* program = &render_pass_program_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::renderPassProgram::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2D);
  }
  return program;
}

const GLRenderer::RenderPassProgramAA* GLRenderer::GetRenderPassProgramAA(
    TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  RenderPassProgramAA* program = &render_pass_program_aa_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::renderPassProgramAA::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2D);
  }
  return program;
}

const GLRenderer::RenderPassMaskProgram* GLRenderer::GetRenderPassMaskProgram(
    TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  RenderPassMaskProgram* program = &render_pass_mask_program_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::renderPassMaskProgram::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2D);
  }
  return program;
}

const GLRenderer::RenderPassMaskProgramAA*
GLRenderer::GetRenderPassMaskProgramAA(TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  RenderPassMaskProgramAA* program = &render_pass_mask_program_aa_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::renderPassMaskProgramAA::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2D);
  }
  return program;
}

const GLRenderer::RenderPassColorMatrixProgram*
GLRenderer::GetRenderPassColorMatrixProgram(TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  RenderPassColorMatrixProgram* program =
      &render_pass_color_matrix_program_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::renderPassColorMatrixProgram::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2D);
  }
  return program;
}

const GLRenderer::RenderPassColorMatrixProgramAA*
GLRenderer::GetRenderPassColorMatrixProgramAA(TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  RenderPassColorMatrixProgramAA* program =
      &render_pass_color_matrix_program_aa_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc",
                 "GLRenderer::renderPassColorMatrixProgramAA::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2D);
  }
  return program;
}

const GLRenderer::RenderPassMaskColorMatrixProgram*
GLRenderer::GetRenderPassMaskColorMatrixProgram(TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  RenderPassMaskColorMatrixProgram* program =
      &render_pass_mask_color_matrix_program_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc",
                 "GLRenderer::renderPassMaskColorMatrixProgram::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2D);
  }
  return program;
}

const GLRenderer::RenderPassMaskColorMatrixProgramAA*
GLRenderer::GetRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  RenderPassMaskColorMatrixProgramAA* program =
      &render_pass_mask_color_matrix_program_aa_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc",
                 "GLRenderer::renderPassMaskColorMatrixProgramAA::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2D);
  }
  return program;
}

const GLRenderer::TileProgram* GLRenderer::GetTileProgram(
    TexCoordPrecision precision,
    SamplerType sampler) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  DCHECK_GE(sampler, 0);
  DCHECK_LT(sampler, NumSamplerTypes);
  TileProgram* program = &tile_program_[precision][sampler];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::tileProgram::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, sampler);
  }
  return program;
}

const GLRenderer::TileProgramOpaque* GLRenderer::GetTileProgramOpaque(
    TexCoordPrecision precision,
    SamplerType sampler) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  DCHECK_GE(sampler, 0);
  DCHECK_LT(sampler, NumSamplerTypes);
  TileProgramOpaque* program = &tile_program_opaque_[precision][sampler];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::tileProgramOpaque::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, sampler);
  }
  return program;
}

const GLRenderer::TileProgramAA* GLRenderer::GetTileProgramAA(
    TexCoordPrecision precision,
    SamplerType sampler) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  DCHECK_GE(sampler, 0);
  DCHECK_LT(sampler, NumSamplerTypes);
  TileProgramAA* program = &tile_program_aa_[precision][sampler];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::tileProgramAA::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, sampler);
  }
  return program;
}

const GLRenderer::TileProgramSwizzle* GLRenderer::GetTileProgramSwizzle(
    TexCoordPrecision precision,
    SamplerType sampler) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  DCHECK_GE(sampler, 0);
  DCHECK_LT(sampler, NumSamplerTypes);
  TileProgramSwizzle* program = &tile_program_swizzle_[precision][sampler];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::tileProgramSwizzle::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, sampler);
  }
  return program;
}

const GLRenderer::TileProgramSwizzleOpaque*
GLRenderer::GetTileProgramSwizzleOpaque(TexCoordPrecision precision,
                                        SamplerType sampler) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  DCHECK_GE(sampler, 0);
  DCHECK_LT(sampler, NumSamplerTypes);
  TileProgramSwizzleOpaque* program =
      &tile_program_swizzle_opaque_[precision][sampler];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::tileProgramSwizzleOpaque::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, sampler);
  }
  return program;
}

const GLRenderer::TileProgramSwizzleAA* GLRenderer::GetTileProgramSwizzleAA(
    TexCoordPrecision precision,
    SamplerType sampler) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  DCHECK_GE(sampler, 0);
  DCHECK_LT(sampler, NumSamplerTypes);
  TileProgramSwizzleAA* program = &tile_program_swizzle_aa_[precision][sampler];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::tileProgramSwizzleAA::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, sampler);
  }
  return program;
}

const GLRenderer::TextureProgram* GLRenderer::GetTextureProgram(
    TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  TextureProgram* program = &texture_program_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::textureProgram::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2D);
  }
  return program;
}

const GLRenderer::NonPremultipliedTextureProgram*
GLRenderer::GetNonPremultipliedTextureProgram(TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  NonPremultipliedTextureProgram* program =
      &nonpremultiplied_texture_program_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc",
                 "GLRenderer::NonPremultipliedTextureProgram::Initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2D);
  }
  return program;
}

const GLRenderer::TextureBackgroundProgram*
GLRenderer::GetTextureBackgroundProgram(TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  TextureBackgroundProgram* program = &texture_background_program_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::textureProgram::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2D);
  }
  return program;
}

const GLRenderer::NonPremultipliedTextureBackgroundProgram*
GLRenderer::GetNonPremultipliedTextureBackgroundProgram(
    TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  NonPremultipliedTextureBackgroundProgram* program =
      &nonpremultiplied_texture_background_program_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc",
                 "GLRenderer::NonPremultipliedTextureProgram::Initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2D);
  }
  return program;
}

const GLRenderer::TextureProgram* GLRenderer::GetTextureIOSurfaceProgram(
    TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  TextureProgram* program = &texture_io_surface_program_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::textureIOSurfaceProgram::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2DRect);
  }
  return program;
}

const GLRenderer::VideoYUVProgram* GLRenderer::GetVideoYUVProgram(
    TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  VideoYUVProgram* program = &video_yuv_program_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::videoYUVProgram::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2D);
  }
  return program;
}

const GLRenderer::VideoYUVAProgram* GLRenderer::GetVideoYUVAProgram(
    TexCoordPrecision precision) {
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  VideoYUVAProgram* program = &video_yuva_program_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::videoYUVAProgram::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerType2D);
  }
  return program;
}

const GLRenderer::VideoStreamTextureProgram*
GLRenderer::GetVideoStreamTextureProgram(TexCoordPrecision precision) {
  if (!Capabilities().using_egl_image)
    return NULL;
  DCHECK_GE(precision, 0);
  DCHECK_LT(precision, NumTexCoordPrecisions);
  VideoStreamTextureProgram* program =
      &video_stream_texture_program_[precision];
  if (!program->initialized()) {
    TRACE_EVENT0("cc", "GLRenderer::streamTextureProgram::initialize");
    program->Initialize(
        output_surface_->context_provider(), precision, SamplerTypeExternalOES);
  }
  return program;
}

void GLRenderer::CleanupSharedObjects() {
  shared_geometry_.reset();

  for (int i = 0; i < NumTexCoordPrecisions; ++i) {
    for (int j = 0; j < NumSamplerTypes; ++j) {
      tile_program_[i][j].Cleanup(gl_);
      tile_program_opaque_[i][j].Cleanup(gl_);
      tile_program_swizzle_[i][j].Cleanup(gl_);
      tile_program_swizzle_opaque_[i][j].Cleanup(gl_);
      tile_program_aa_[i][j].Cleanup(gl_);
      tile_program_swizzle_aa_[i][j].Cleanup(gl_);
    }

    render_pass_mask_program_[i].Cleanup(gl_);
    render_pass_program_[i].Cleanup(gl_);
    render_pass_mask_program_aa_[i].Cleanup(gl_);
    render_pass_program_aa_[i].Cleanup(gl_);
    render_pass_color_matrix_program_[i].Cleanup(gl_);
    render_pass_mask_color_matrix_program_aa_[i].Cleanup(gl_);
    render_pass_color_matrix_program_aa_[i].Cleanup(gl_);
    render_pass_mask_color_matrix_program_[i].Cleanup(gl_);

    texture_program_[i].Cleanup(gl_);
    nonpremultiplied_texture_program_[i].Cleanup(gl_);
    texture_background_program_[i].Cleanup(gl_);
    nonpremultiplied_texture_background_program_[i].Cleanup(gl_);
    texture_io_surface_program_[i].Cleanup(gl_);

    video_yuv_program_[i].Cleanup(gl_);
    video_yuva_program_[i].Cleanup(gl_);
    video_stream_texture_program_[i].Cleanup(gl_);
  }

  tile_checkerboard_program_.Cleanup(gl_);

  debug_border_program_.Cleanup(gl_);
  solid_color_program_.Cleanup(gl_);
  solid_color_program_aa_.Cleanup(gl_);

  if (offscreen_framebuffer_id_)
    GLC(gl_, gl_->DeleteFramebuffers(1, &offscreen_framebuffer_id_));

  if (on_demand_tile_raster_resource_id_)
    resource_provider_->DeleteResource(on_demand_tile_raster_resource_id_);

  ReleaseRenderPassTextures();
}

void GLRenderer::ReinitializeGLState() {
  // Bind the common vertex attributes used for drawing all the layers.
  shared_geometry_->PrepareForDraw();

  GLC(gl_, gl_->Disable(GL_DEPTH_TEST));
  GLC(gl_, gl_->Disable(GL_CULL_FACE));
  GLC(gl_, gl_->ColorMask(true, true, true, true));
  GLC(gl_, gl_->Disable(GL_STENCIL_TEST));
  stencil_shadow_ = false;
  GLC(gl_, gl_->Enable(GL_BLEND));
  blend_shadow_ = true;
  GLC(gl_, gl_->BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
  GLC(gl_, gl_->ActiveTexture(GL_TEXTURE0));
  program_shadow_ = 0;

  // Make sure scissoring starts as disabled.
  is_scissor_enabled_ = false;
  GLC(gl_, gl_->Disable(GL_SCISSOR_TEST));
  scissor_rect_needs_reset_ = true;
}

bool GLRenderer::IsContextLost() {
  return output_surface_->context_provider()->IsContextLost();
}

void GLRenderer::ScheduleOverlays(DrawingFrame* frame) {
  if (!frame->overlay_list.size())
    return;

  ResourceProvider::ResourceIdArray resources;
  OverlayCandidateList& overlays = frame->overlay_list;
  OverlayCandidateList::iterator it;
  for (it = overlays.begin(); it != overlays.end(); ++it) {
    const OverlayCandidate& overlay = *it;
    // Skip primary plane.
    if (overlay.plane_z_order == 0)
      continue;

    pending_overlay_resources_.push_back(
        make_scoped_ptr(new ResourceProvider::ScopedReadLockGL(
            resource_provider(), overlay.resource_id)));

    context_support_->ScheduleOverlayPlane(
        overlay.plane_z_order,
        overlay.transform,
        pending_overlay_resources_.back()->texture_id(),
        overlay.display_rect,
        overlay.uv_rect);
  }
}

}  // namespace cc

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