// 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/gl_renderer.h"

#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "build/build_config.h"
#include "cc/damage_tracker.h"
#include "cc/geometry_binding.h"
#include "cc/layer_quad.h"
#include "cc/math_util.h"
#include "cc/platform_color.h"
#include "cc/priority_calculator.h"
#include "cc/proxy.h"
#include "cc/render_pass.h"
#include "cc/render_surface_filters.h"
#include "cc/scoped_resource.h"
#include "cc/settings.h"
#include "cc/single_thread_proxy.h"
#include "cc/stream_video_draw_quad.h"
#include "cc/texture_draw_quad.h"
#include "cc/video_layer_impl.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/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 "ui/gfx/quad_f.h"
#include "ui/gfx/rect_conversions.h"
#include <public/WebGraphicsContext3D.h>
#include <public/WebSharedGraphicsContext3D.h>
#include <set>
#include <string>
#include <vector>

using namespace std;
using WebKit::WebGraphicsContext3D;
using WebKit::WebGraphicsMemoryAllocation;
using WebKit::WebSharedGraphicsContext3D;
using WebKit::WebTransformationMatrix;

namespace cc {

namespace {

bool needsIOSurfaceReadbackWorkaround()
{
#if defined(OS_MACOSX)
    return true;
#else
    return false;
#endif
}

} // anonymous namespace

scoped_ptr<GLRenderer> GLRenderer::create(RendererClient* client, ResourceProvider* resourceProvider)
{
    scoped_ptr<GLRenderer> renderer(make_scoped_ptr(new GLRenderer(client, resourceProvider)));
    if (!renderer->initialize())
        return scoped_ptr<GLRenderer>();

    return renderer.Pass();
}

GLRenderer::GLRenderer(RendererClient* client,
                           ResourceProvider* resourceProvider)
    : DirectRenderer(client, resourceProvider)
    , m_offscreenFramebufferId(0)
    , m_sharedGeometryQuad(gfx::RectF(-0.5f, -0.5f, 1.0f, 1.0f))
    , m_context(resourceProvider->graphicsContext3D())
    , m_isViewportChanged(false)
    , m_isFramebufferDiscarded(false)
    , m_discardFramebufferWhenNotVisible(false)
    , m_isUsingBindUniform(false)
    , m_visible(true)
{
    DCHECK(m_context);
}

bool GLRenderer::initialize()
{
    if (!m_context->makeContextCurrent())
        return false;

    m_context->setContextLostCallback(this);
    m_context->pushGroupMarkerEXT("CompositorContext");

    std::string extensionsString = UTF16ToASCII(m_context->getString(GL_EXTENSIONS));
    std::vector<std::string> extensionsList;
    base::SplitString(extensionsString, ' ', &extensionsList);
    std::set<string> extensions(extensionsList.begin(), extensionsList.end());

    if (settings().acceleratePainting && extensions.count("GL_EXT_texture_format_BGRA8888")
                                      && extensions.count("GL_EXT_read_format_bgra"))
        m_capabilities.usingAcceleratedPainting = true;
    else
        m_capabilities.usingAcceleratedPainting = false;


    m_capabilities.contextHasCachedFrontBuffer = extensions.count("GL_CHROMIUM_front_buffer_cached");

    m_capabilities.usingPartialSwap = Settings::partialSwapEnabled() && extensions.count("GL_CHROMIUM_post_sub_buffer");

    // Use the swapBuffers callback only with the threaded proxy.
    if (m_client->hasImplThread())
        m_capabilities.usingSwapCompleteCallback = extensions.count("GL_CHROMIUM_swapbuffers_complete_callback");
    if (m_capabilities.usingSwapCompleteCallback)
        m_context->setSwapBuffersCompleteCallbackCHROMIUM(this);

    m_capabilities.usingSetVisibility = extensions.count("GL_CHROMIUM_set_visibility");

    if (extensions.count("GL_CHROMIUM_iosurface"))
        DCHECK(extensions.count("GL_ARB_texture_rectangle"));

    m_capabilities.usingGpuMemoryManager = extensions.count("GL_CHROMIUM_gpu_memory_manager");
    if (m_capabilities.usingGpuMemoryManager)
        m_context->setMemoryAllocationChangedCallbackCHROMIUM(this);

    m_capabilities.usingDiscardFramebuffer = extensions.count("GL_CHROMIUM_discard_framebuffer");

    m_capabilities.usingEglImage = extensions.count("GL_OES_EGL_image_external");

    GLC(m_context, m_context->getIntegerv(GL_MAX_TEXTURE_SIZE, &m_capabilities.maxTextureSize));
    m_capabilities.bestTextureFormat = PlatformColor::bestTextureFormat(m_context, extensions.count("GL_EXT_texture_format_BGRA8888"));

    m_isUsingBindUniform = extensions.count("GL_CHROMIUM_bind_uniform_location");

    if (!initializeSharedObjects())
        return false;

    // Make sure the viewport and context gets initialized, even if it is to zero.
    viewportChanged();
    return true;
}

GLRenderer::~GLRenderer()
{
    m_context->setSwapBuffersCompleteCallbackCHROMIUM(0);
    m_context->setMemoryAllocationChangedCallbackCHROMIUM(0);
    m_context->setContextLostCallback(0);
    cleanupSharedObjects();
}

const RendererCapabilities& GLRenderer::capabilities() const
{
    return m_capabilities;
}

WebGraphicsContext3D* GLRenderer::context()
{
    return m_context;
}

void GLRenderer::debugGLCall(WebGraphicsContext3D* context, const char* command, const char* file, int line)
{
    unsigned long error = context->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 (m_visible == visible)
        return;
    m_visible = visible;

    enforceMemoryPolicy();

    // TODO: Replace setVisibilityCHROMIUM with an extension to explicitly manage front/backbuffers
    // crbug.com/116049
    if (m_capabilities.usingSetVisibility)
        m_context->setVisibilityCHROMIUM(visible);
}

void GLRenderer::sendManagedMemoryStats(size_t bytesVisible, size_t bytesVisibleAndNearby, size_t bytesAllocated)
{
    WebKit::WebGraphicsManagedMemoryStats stats;
    stats.bytesVisible = bytesVisible;
    stats.bytesVisibleAndNearby = bytesVisibleAndNearby;
    stats.bytesAllocated = bytesAllocated;
    stats.backbufferRequested = !m_isFramebufferDiscarded;
    m_context->sendManagedMemoryStatsCHROMIUM(&stats);
}

void GLRenderer::releaseRenderPassTextures()
{
    m_renderPassTextures.clear();
}

void GLRenderer::viewportChanged()
{
    m_isViewportChanged = true;
}

void GLRenderer::clearFramebuffer(DrawingFrame& frame)
{
    // On DEBUG builds, opaque render passes are cleared to blue to easily see regions that were not drawn on the screen.
    if (frame.currentRenderPass->hasTransparentBackground())
        GLC(m_context, m_context->clearColor(0, 0, 0, 0));
    else
        GLC(m_context, m_context->clearColor(0, 0, 1, 1));

#ifdef NDEBUG
    if (frame.currentRenderPass->hasTransparentBackground())
#endif
        m_context->clear(GL_COLOR_BUFFER_BIT);
}

void GLRenderer::beginDrawingFrame(DrawingFrame& frame)
{
    // FIXME: Remove this once framebuffer is automatically recreated on first use
    ensureFramebuffer();

    if (viewportSize().IsEmpty())
        return;

    TRACE_EVENT0("cc", "GLRenderer::drawLayers");
    if (m_isViewportChanged) {
        // Only reshape when we know we are going to draw. Otherwise, the reshape
        // can leave the window at the wrong size if we never draw and the proper
        // viewport size is never set.
        m_isViewportChanged = false;
        m_context->reshape(viewportWidth(), viewportHeight());
    }

    makeContextCurrent();
    // Bind the common vertex attributes used for drawing all the layers.
    m_sharedGeometry->prepareForDraw();

    GLC(m_context, m_context->disable(GL_DEPTH_TEST));
    GLC(m_context, m_context->disable(GL_CULL_FACE));
    GLC(m_context, m_context->enable(GL_SCISSOR_TEST));
    GLC(m_context, m_context->colorMask(true, true, true, true));
    GLC(m_context, m_context->enable(GL_BLEND));
    GLC(m_context, m_context->blendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
    GLC(context(), context()->activeTexture(GL_TEXTURE0));
}

void GLRenderer::doNoOp()
{
    GLC(m_context, m_context->bindFramebuffer(GL_FRAMEBUFFER, 0));
    GLC(m_context, m_context->flush());
}

void GLRenderer::drawQuad(DrawingFrame& frame, const DrawQuad* quad)
{
    if (quad->needsBlending())
        GLC(m_context, m_context->enable(GL_BLEND));
    else
        GLC(m_context, m_context->disable(GL_BLEND));

    switch (quad->material()) {
    case DrawQuad::Invalid:
        NOTREACHED();
        break;
    case DrawQuad::Checkerboard:
        drawCheckerboardQuad(frame, CheckerboardDrawQuad::materialCast(quad));
        break;
    case DrawQuad::DebugBorder:
        drawDebugBorderQuad(frame, DebugBorderDrawQuad::materialCast(quad));
        break;
    case DrawQuad::IOSurfaceContent:
        drawIOSurfaceQuad(frame, IOSurfaceDrawQuad::materialCast(quad));
        break;
    case DrawQuad::RenderPass:
        drawRenderPassQuad(frame, RenderPassDrawQuad::materialCast(quad));
        break;
    case DrawQuad::SolidColor:
        drawSolidColorQuad(frame, SolidColorDrawQuad::materialCast(quad));
        break;
    case DrawQuad::StreamVideoContent:
        drawStreamVideoQuad(frame, StreamVideoDrawQuad::materialCast(quad));
        break;
    case DrawQuad::TextureContent:
        drawTextureQuad(frame, TextureDrawQuad::materialCast(quad));
        break;
    case DrawQuad::TiledContent:
        drawTileQuad(frame, TileDrawQuad::materialCast(quad));
        break;
    case DrawQuad::YUVVideoContent:
        drawYUVVideoQuad(frame, YUVVideoDrawQuad::materialCast(quad));
        break;
    }
}

void GLRenderer::drawCheckerboardQuad(const DrawingFrame& frame, const CheckerboardDrawQuad* quad)
{
    const TileCheckerboardProgram* program = tileCheckerboardProgram();
    DCHECK(program && program->initialized());
    GLC(context(), context()->useProgram(program->program()));

    SkColor color = quad->color();
    GLC(context(), context()->uniform4f(program->fragmentShader().colorLocation(), SkColorGetR(color) / 255.0, SkColorGetG(color) / 255.0, SkColorGetB(color) / 255.0, 1));

    const int checkerboardWidth = 16;
    float frequency = 1.0 / checkerboardWidth;

    gfx::Rect tileRect = quad->quadRect();
    float texOffsetX = tileRect.x() % checkerboardWidth;
    float texOffsetY = tileRect.y() % checkerboardWidth;
    float texScaleX = tileRect.width();
    float texScaleY = tileRect.height();
    GLC(context(), context()->uniform4f(program->fragmentShader().texTransformLocation(), texOffsetX, texOffsetY, texScaleX, texScaleY));

    GLC(context(), context()->uniform1f(program->fragmentShader().frequencyLocation(), frequency));

    setShaderOpacity(quad->opacity(), program->fragmentShader().alphaLocation());
    drawQuadGeometry(frame, quad->quadTransform(), quad->quadRect(), program->vertexShader().matrixLocation());
}

void GLRenderer::drawDebugBorderQuad(const DrawingFrame& frame, const DebugBorderDrawQuad* quad)
{
    static float glMatrix[16];
    const SolidColorProgram* program = solidColorProgram();
    DCHECK(program && program->initialized());
    GLC(context(), context()->useProgram(program->program()));

    // Use the full quadRect for debug quads to not move the edges based on partial swaps.
    const gfx::Rect& layerRect = quad->quadRect();
    WebTransformationMatrix renderMatrix = quad->quadTransform();
    renderMatrix.translate(0.5 * layerRect.width() + layerRect.x(), 0.5 * layerRect.height() + layerRect.y());
    renderMatrix.scaleNonUniform(layerRect.width(), layerRect.height());
    GLRenderer::toGLMatrix(&glMatrix[0], frame.projectionMatrix * renderMatrix);
    GLC(context(), context()->uniformMatrix4fv(program->vertexShader().matrixLocation(), 1, false, &glMatrix[0]));

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

    GLC(context(), context()->uniform4f(program->fragmentShader().colorLocation(), (SkColorGetR(color) / 255.0) * alpha, (SkColorGetG(color) / 255.0) * alpha, (SkColorGetB(color) / 255.0) * alpha, alpha));

    GLC(context(), context()->lineWidth(quad->width()));

    // The indices for the line are stored in the same array as the triangle indices.
    GLC(context(), context()->drawElements(GL_LINE_LOOP, 4, GL_UNSIGNED_SHORT, 6 * sizeof(unsigned short)));
}

static WebGraphicsContext3D* getFilterContext(bool hasImplThread)
{
    if (hasImplThread)
        return WebSharedGraphicsContext3D::compositorThreadContext();
    else
        return WebSharedGraphicsContext3D::mainThreadContext();
}

static GrContext* getFilterGrContext(bool hasImplThread)
{
    if (hasImplThread)
        return WebSharedGraphicsContext3D::compositorThreadGrContext();
    else
        return WebSharedGraphicsContext3D::mainThreadGrContext();
}

static inline SkBitmap applyFilters(GLRenderer* renderer, const WebKit::WebFilterOperations& filters, ScopedResource* sourceTexture, bool hasImplThread)
{
    if (filters.isEmpty())
        return SkBitmap();

    WebGraphicsContext3D* filterContext = getFilterContext(hasImplThread);
    GrContext* filterGrContext = getFilterGrContext(hasImplThread);

    if (!filterContext || !filterGrContext)
        return SkBitmap();

    renderer->context()->flush();

    ResourceProvider::ScopedWriteLockGL lock(renderer->resourceProvider(), sourceTexture->id());
    SkBitmap source = RenderSurfaceFilters::apply(filters, lock.textureId(), sourceTexture->size(), filterContext, filterGrContext);
    return source;
}

static SkBitmap applyImageFilter(GLRenderer* renderer, SkImageFilter* filter, ScopedResource* sourceTexture, bool hasImplThread)
{
    if (!filter)
        return SkBitmap();

    WebGraphicsContext3D* context3d = getFilterContext(hasImplThread);
    GrContext* grContext = getFilterGrContext(hasImplThread);

    if (!context3d || !grContext)
        return SkBitmap();

    renderer->context()->flush();

    ResourceProvider::ScopedWriteLockGL lock(renderer->resourceProvider(), sourceTexture->id());

    // Wrap the source texture in a Ganesh platform texture.
    GrPlatformTextureDesc platformTextureDescription;
    platformTextureDescription.fWidth = sourceTexture->size().width();
    platformTextureDescription.fHeight = sourceTexture->size().height();
    platformTextureDescription.fConfig = kSkia8888_GrPixelConfig;
    platformTextureDescription.fTextureHandle = lock.textureId();
    SkAutoTUnref<GrTexture> texture(grContext->createPlatformTexture(platformTextureDescription));

    // Place the platform texture inside an SkBitmap.
    SkBitmap source;
    source.setConfig(SkBitmap::kARGB_8888_Config, sourceTexture->size().width(), sourceTexture->size().height());
    source.setPixelRef(new SkGrPixelRef(texture.get()))->unref();

    // 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;
    GrAutoScratchTexture scratchTexture(grContext, desc, GrContext::kExact_ScratchTexMatch);
    SkAutoTUnref<GrTexture> backingStore(scratchTexture.detach());

    // Create a device and canvas using that backing store.
    SkGpuDevice device(grContext, backingStore.get());
    SkCanvas canvas(&device);

    // Draw the source bitmap through the filter to the canvas.
    SkPaint paint;
    paint.setImageFilter(filter);
    canvas.clear(0x0);
    canvas.drawSprite(source, 0, 0, &paint);
    canvas.flush();
    context3d->flush();
    return device.accessBitmap(false);
}

scoped_ptr<ScopedResource> GLRenderer::drawBackgroundFilters(
    DrawingFrame& frame, const RenderPassDrawQuad* quad,
    const WebKit::WebFilterOperations& filters,
    const WebTransformationMatrix& contentsDeviceTransform,
    const WebTransformationMatrix& contentsDeviceTransformInverse)
{
    // 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.

    // FIXME: When this algorithm changes, update LayerTreeHost::prioritizeTextures() accordingly.

    if (filters.isEmpty())
        return scoped_ptr<ScopedResource>();

    // FIXME: 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.
    if (frame.currentRenderPass->hasTransparentBackground())
        return scoped_ptr<ScopedResource>();
    DCHECK(!frame.currentTexture);

    // FIXME: Do a single readback for both the surface and replica and cache the filtered results (once filter textures are not reused).
    gfx::Rect deviceRect = gfx::ToEnclosingRect(MathUtil::mapClippedRect(contentsDeviceTransform, sharedGeometryQuad().BoundingBox()));

    int top, right, bottom, left;
    filters.getOutsets(top, right, bottom, left);
    deviceRect.Inset(-left, -top, -right, -bottom);

    deviceRect.Intersect(frame.currentRenderPass->outputRect());

    scoped_ptr<ScopedResource> deviceBackgroundTexture = ScopedResource::create(m_resourceProvider);
    if (!getFramebufferTexture(deviceBackgroundTexture.get(), deviceRect))
        return scoped_ptr<ScopedResource>();

    SkBitmap filteredDeviceBackground = applyFilters(this, filters, deviceBackgroundTexture.get(), m_client->hasImplThread());
    if (!filteredDeviceBackground.getTexture())
        return scoped_ptr<ScopedResource>();

    GrTexture* texture = reinterpret_cast<GrTexture*>(filteredDeviceBackground.getTexture());
    int filteredDeviceBackgroundTextureId = texture->getTextureHandle();

    scoped_ptr<ScopedResource> backgroundTexture = ScopedResource::create(m_resourceProvider);
    if (!backgroundTexture->allocate(Renderer::ImplPool, quad->quadRect().size(), GL_RGBA, ResourceProvider::TextureUsageFramebuffer))
        return scoped_ptr<ScopedResource>();

    const RenderPass* targetRenderPass = frame.currentRenderPass;
    bool usingBackgroundTexture = useScopedTexture(frame, backgroundTexture.get(), quad->quadRect());

    if (usingBackgroundTexture) {
        // Copy the readback pixels from device to the background texture for the surface.
        WebTransformationMatrix deviceToFramebufferTransform;
        deviceToFramebufferTransform.translate(quad->quadRect().width() / 2.0, quad->quadRect().height() / 2.0);
        deviceToFramebufferTransform.scale3d(quad->quadRect().width(), quad->quadRect().height(), 1);
        deviceToFramebufferTransform.multiply(contentsDeviceTransformInverse);
        copyTextureToFramebuffer(frame, filteredDeviceBackgroundTextureId, deviceRect, deviceToFramebufferTransform);
    }

    useRenderPass(frame, targetRenderPass);

    if (!usingBackgroundTexture)
        return scoped_ptr<ScopedResource>();
    return backgroundTexture.Pass();
}

void GLRenderer::drawRenderPassQuad(DrawingFrame& frame, const RenderPassDrawQuad* quad)
{
    CachedResource* contentsTexture = m_renderPassTextures.get(quad->renderPassId());
    if (!contentsTexture || !contentsTexture->id())
        return;

    const RenderPass* renderPass = frame.renderPassesById->get(quad->renderPassId());
    DCHECK(renderPass);
    if (!renderPass)
        return;

    WebTransformationMatrix quadRectMatrix;
    quadRectTransform(&quadRectMatrix, quad->quadTransform(), quad->quadRect());
    WebTransformationMatrix contentsDeviceTransform = (frame.windowMatrix * frame.projectionMatrix * quadRectMatrix).to2dTransform();

    // Can only draw surface if device matrix is invertible.
    if (!contentsDeviceTransform.isInvertible())
        return;

    WebTransformationMatrix contentsDeviceTransformInverse = contentsDeviceTransform.inverse();
    scoped_ptr<ScopedResource> backgroundTexture = drawBackgroundFilters(
        frame, quad, renderPass->backgroundFilters(),
        contentsDeviceTransform, contentsDeviceTransformInverse);

    // FIXME: 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 filterBitmap;
    if (renderPass->filter()) {
        filterBitmap = applyImageFilter(this, renderPass->filter(), contentsTexture, m_client->hasImplThread());
    } else {
        filterBitmap = applyFilters(this, renderPass->filters(), contentsTexture, m_client->hasImplThread());
    }
    scoped_ptr<ResourceProvider::ScopedReadLockGL> contentsResourceLock;
    unsigned contentsTextureId = 0;
    if (filterBitmap.getTexture()) {
        GrTexture* texture = reinterpret_cast<GrTexture*>(filterBitmap.getTexture());
        contentsTextureId = texture->getTextureHandle();
    } else {
        contentsResourceLock = make_scoped_ptr(new ResourceProvider::ScopedReadLockGL(m_resourceProvider, contentsTexture->id()));
        contentsTextureId = contentsResourceLock->textureId();
    }

    // Draw the background texture if there is one.
    if (backgroundTexture) {
        DCHECK(backgroundTexture->size() == quad->quadRect().size());
        ResourceProvider::ScopedReadLockGL lock(m_resourceProvider, backgroundTexture->id());
        copyTextureToFramebuffer(frame, lock.textureId(), quad->quadRect(), quad->quadTransform());
    }

    bool clipped = false;
    gfx::QuadF deviceQuad = MathUtil::mapQuad(contentsDeviceTransform, sharedGeometryQuad(), clipped);
    DCHECK(!clipped);
    LayerQuad deviceLayerBounds = LayerQuad(gfx::QuadF(deviceQuad.BoundingBox()));
    LayerQuad deviceLayerEdges = LayerQuad(deviceQuad);

    // Use anti-aliasing programs only when necessary.
    bool useAA = (!deviceQuad.IsRectilinear() || !deviceQuad.BoundingBox().IsExpressibleAsRect());
    if (useAA) {
        deviceLayerBounds.inflateAntiAliasingDistance();
        deviceLayerEdges.inflateAntiAliasingDistance();
    }

    scoped_ptr<ResourceProvider::ScopedReadLockGL> maskResourceLock;
    unsigned maskTextureId = 0;
    if (quad->maskResourceId()) {
        maskResourceLock.reset(new ResourceProvider::ScopedReadLockGL(m_resourceProvider, quad->maskResourceId()));
        maskTextureId = maskResourceLock->textureId();
    }

    // FIXME: use the backgroundTexture and blend the background in with this draw instead of having a separate copy of the background texture.

    context()->bindTexture(GL_TEXTURE_2D, contentsTextureId);

    int shaderQuadLocation = -1;
    int shaderEdgeLocation = -1;
    int shaderMaskSamplerLocation = -1;
    int shaderMaskTexCoordScaleLocation = -1;
    int shaderMaskTexCoordOffsetLocation = -1;
    int shaderMatrixLocation = -1;
    int shaderAlphaLocation = -1;
    if (useAA && maskTextureId) {
        const RenderPassMaskProgramAA* program = renderPassMaskProgramAA();
        GLC(context(), context()->useProgram(program->program()));
        GLC(context(), context()->uniform1i(program->fragmentShader().samplerLocation(), 0));

        shaderQuadLocation = program->vertexShader().pointLocation();
        shaderEdgeLocation = program->fragmentShader().edgeLocation();
        shaderMaskSamplerLocation = program->fragmentShader().maskSamplerLocation();
        shaderMaskTexCoordScaleLocation = program->fragmentShader().maskTexCoordScaleLocation();
        shaderMaskTexCoordOffsetLocation = program->fragmentShader().maskTexCoordOffsetLocation();
        shaderMatrixLocation = program->vertexShader().matrixLocation();
        shaderAlphaLocation = program->fragmentShader().alphaLocation();
    } else if (!useAA && maskTextureId) {
        const RenderPassMaskProgram* program = renderPassMaskProgram();
        GLC(context(), context()->useProgram(program->program()));
        GLC(context(), context()->uniform1i(program->fragmentShader().samplerLocation(), 0));

        shaderMaskSamplerLocation = program->fragmentShader().maskSamplerLocation();
        shaderMaskTexCoordScaleLocation = program->fragmentShader().maskTexCoordScaleLocation();
        shaderMaskTexCoordOffsetLocation = program->fragmentShader().maskTexCoordOffsetLocation();
        shaderMatrixLocation = program->vertexShader().matrixLocation();
        shaderAlphaLocation = program->fragmentShader().alphaLocation();
    } else if (useAA && !maskTextureId) {
        const RenderPassProgramAA* program = renderPassProgramAA();
        GLC(context(), context()->useProgram(program->program()));
        GLC(context(), context()->uniform1i(program->fragmentShader().samplerLocation(), 0));

        shaderQuadLocation = program->vertexShader().pointLocation();
        shaderEdgeLocation = program->fragmentShader().edgeLocation();
        shaderMatrixLocation = program->vertexShader().matrixLocation();
        shaderAlphaLocation = program->fragmentShader().alphaLocation();
    } else {
        const RenderPassProgram* program = renderPassProgram();
        GLC(context(), context()->useProgram(program->program()));
        GLC(context(), context()->uniform1i(program->fragmentShader().samplerLocation(), 0));

        shaderMatrixLocation = program->vertexShader().matrixLocation();
        shaderAlphaLocation = program->fragmentShader().alphaLocation();
    }

    if (shaderMaskSamplerLocation != -1) {
        DCHECK(shaderMaskTexCoordScaleLocation != 1);
        DCHECK(shaderMaskTexCoordOffsetLocation != 1);
        GLC(context(), context()->activeTexture(GL_TEXTURE1));
        GLC(context(), context()->uniform1i(shaderMaskSamplerLocation, 1));
        GLC(context(), context()->uniform2f(shaderMaskTexCoordScaleLocation, quad->maskTexCoordScaleX(), quad->maskTexCoordScaleY()));
        GLC(context(), context()->uniform2f(shaderMaskTexCoordOffsetLocation, quad->maskTexCoordOffsetX(), quad->maskTexCoordOffsetY()));
        context()->bindTexture(GL_TEXTURE_2D, maskTextureId);
        GLC(context(), context()->activeTexture(GL_TEXTURE0));
    }

    if (shaderEdgeLocation != -1) {
        float edge[24];
        deviceLayerEdges.toFloatArray(edge);
        deviceLayerBounds.toFloatArray(&edge[12]);
        GLC(context(), context()->uniform3fv(shaderEdgeLocation, 8, edge));
    }

    // Map device space quad to surface space. contentsDeviceTransform has no 3d component since it was generated with to2dTransform() so we don't need to project.
    gfx::QuadF surfaceQuad = MathUtil::mapQuad(contentsDeviceTransformInverse, deviceLayerEdges.ToQuadF(), clipped);
    DCHECK(!clipped);

    setShaderOpacity(quad->opacity(), shaderAlphaLocation);
    setShaderQuadF(surfaceQuad, shaderQuadLocation);
    drawQuadGeometry(frame, quad->quadTransform(), quad->quadRect(), shaderMatrixLocation);

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

void GLRenderer::drawSolidColorQuad(const DrawingFrame& frame, const SolidColorDrawQuad* quad)
{
    const SolidColorProgram* program = solidColorProgram();
    GLC(context(), context()->useProgram(program->program()));

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

    GLC(context(), context()->uniform4f(program->fragmentShader().colorLocation(), (SkColorGetR(color) / 255.0) * alpha, (SkColorGetG(color) / 255.0) * alpha, (SkColorGetB(color) / 255.0) * alpha, alpha));

    drawQuadGeometry(frame, quad->quadTransform(), quad->quadRect(), program->vertexShader().matrixLocation());
}

struct TileProgramUniforms {
    unsigned program;
    unsigned samplerLocation;
    unsigned vertexTexTransformLocation;
    unsigned fragmentTexTransformLocation;
    unsigned edgeLocation;
    unsigned matrixLocation;
    unsigned alphaLocation;
    unsigned pointLocation;
};

template<class T>
static void tileUniformLocation(T program, TileProgramUniforms& uniforms)
{
    uniforms.program = program->program();
    uniforms.vertexTexTransformLocation = program->vertexShader().vertexTexTransformLocation();
    uniforms.matrixLocation = program->vertexShader().matrixLocation();
    uniforms.pointLocation = program->vertexShader().pointLocation();

    uniforms.samplerLocation = program->fragmentShader().samplerLocation();
    uniforms.alphaLocation = program->fragmentShader().alphaLocation();
    uniforms.fragmentTexTransformLocation = program->fragmentShader().fragmentTexTransformLocation();
    uniforms.edgeLocation = program->fragmentShader().edgeLocation();
}

void GLRenderer::drawTileQuad(const DrawingFrame& frame, const TileDrawQuad* quad)
{
    gfx::Rect tileRect = quad->quadVisibleRect();

    gfx::RectF clampRect(tileRect);
    // 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.
    const float epsilon = 1 / 1024.0f;
    float clampX = min(0.5, clampRect.width() / 2.0 - epsilon);
    float clampY = min(0.5, clampRect.height() / 2.0 - epsilon);
    clampRect.Inset(clampX, clampY, clampX, clampY);

    gfx::Vector2dF textureOffset = quad->textureOffset() + clampRect.OffsetFromOrigin() - quad->quadRect().OffsetFromOrigin();

    // Map clamping rectangle to unit square.
    float vertexTexTranslateX = -clampRect.x() / clampRect.width();
    float vertexTexTranslateY = -clampRect.y() / clampRect.height();
    float vertexTexScaleX = tileRect.width() / clampRect.width();
    float vertexTexScaleY = tileRect.height() / clampRect.height();

    // Map to normalized texture coordinates.
    const gfx::Size& textureSize = quad->textureSize();
    float fragmentTexTranslateX = textureOffset.x() / textureSize.width();
    float fragmentTexTranslateY = textureOffset.y() / textureSize.height();
    float fragmentTexScaleX = clampRect.width() / textureSize.width();
    float fragmentTexScaleY = clampRect.height() / textureSize.height();


    gfx::QuadF localQuad;
    WebTransformationMatrix deviceTransform = WebTransformationMatrix(frame.windowMatrix * frame.projectionMatrix * quad->quadTransform()).to2dTransform();
    if (!deviceTransform.isInvertible())
        return;

    bool clipped = false;
    gfx::QuadF deviceLayerQuad = MathUtil::mapQuad(deviceTransform, gfx::QuadF(quad->visibleContentRect()), clipped);
    DCHECK(!clipped);

    TileProgramUniforms uniforms;
    // For now, we simply skip anti-aliasing with the quad is clipped. This only happens
    // on perspective transformed layers that go partially behind the camera.
    if (quad->isAntialiased() && !clipped) {
        if (quad->swizzleContents())
            tileUniformLocation(tileProgramSwizzleAA(), uniforms);
        else
            tileUniformLocation(tileProgramAA(), uniforms);
    } else {
        if (quad->needsBlending()) {
            if (quad->swizzleContents())
                tileUniformLocation(tileProgramSwizzle(), uniforms);
            else
                tileUniformLocation(tileProgram(), uniforms);
        } else {
            if (quad->swizzleContents())
                tileUniformLocation(tileProgramSwizzleOpaque(), uniforms);
            else
                tileUniformLocation(tileProgramOpaque(), uniforms);
        }
    }

    GLC(context(), context()->useProgram(uniforms.program));
    GLC(context(), context()->uniform1i(uniforms.samplerLocation, 0));
    ResourceProvider::ScopedReadLockGL quadResourceLock(m_resourceProvider, quad->resourceId());
    GLC(context(), context()->bindTexture(GL_TEXTURE_2D, quadResourceLock.textureId()));
    GLC(context(), context()->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, quad->textureFilter()));
    GLC(context(), context()->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, quad->textureFilter()));

    bool useAA = !clipped && quad->isAntialiased();
    if (useAA) {
        LayerQuad deviceLayerBounds = LayerQuad(gfx::QuadF(deviceLayerQuad.BoundingBox()));
        deviceLayerBounds.inflateAntiAliasingDistance();

        LayerQuad deviceLayerEdges = LayerQuad(deviceLayerQuad);
        deviceLayerEdges.inflateAntiAliasingDistance();

        float edge[24];
        deviceLayerEdges.toFloatArray(edge);
        deviceLayerBounds.toFloatArray(&edge[12]);
        GLC(context(), context()->uniform3fv(uniforms.edgeLocation, 8, edge));

        GLC(context(), context()->uniform4f(uniforms.vertexTexTransformLocation, vertexTexTranslateX, vertexTexTranslateY, vertexTexScaleX, vertexTexScaleY));
        GLC(context(), context()->uniform4f(uniforms.fragmentTexTransformLocation, fragmentTexTranslateX, fragmentTexTranslateY, fragmentTexScaleX, fragmentTexScaleY));

        gfx::PointF bottomRight(tileRect.right(), tileRect.bottom());
        gfx::PointF bottomLeft(tileRect.x(), tileRect.bottom());
        gfx::PointF topLeft(tileRect.x(), tileRect.y());
        gfx::PointF topRight(tileRect.right(), tileRect.y());

        // Map points to device space.
        bottomRight = MathUtil::mapPoint(deviceTransform, bottomRight, clipped);
        DCHECK(!clipped);
        bottomLeft = MathUtil::mapPoint(deviceTransform, bottomLeft, clipped);
        DCHECK(!clipped);
        topLeft = MathUtil::mapPoint(deviceTransform, topLeft, clipped);
        DCHECK(!clipped);
        topRight = MathUtil::mapPoint(deviceTransform, topRight, clipped);
        DCHECK(!clipped);

        LayerQuad::Edge bottomEdge(bottomRight, bottomLeft);
        LayerQuad::Edge leftEdge(bottomLeft, topLeft);
        LayerQuad::Edge topEdge(topLeft, topRight);
        LayerQuad::Edge rightEdge(topRight, bottomRight);

        // Only apply anti-aliasing to edges not clipped by culling or scissoring.
        if (quad->topEdgeAA() && tileRect.y() == quad->quadRect().y())
            topEdge = deviceLayerEdges.top();
        if (quad->leftEdgeAA() && tileRect.x() == quad->quadRect().x())
            leftEdge = deviceLayerEdges.left();
        if (quad->rightEdgeAA() && tileRect.right() == quad->quadRect().right())
            rightEdge = deviceLayerEdges.right();
        if (quad->bottomEdgeAA() && tileRect.bottom() == quad->quadRect().bottom())
            bottomEdge = deviceLayerEdges.bottom();

        float sign = gfx::QuadF(tileRect).IsCounterClockwise() ? -1 : 1;
        bottomEdge.scale(sign);
        leftEdge.scale(sign);
        topEdge.scale(sign);
        rightEdge.scale(sign);

        // Create device space quad.
        LayerQuad deviceQuad(leftEdge, topEdge, rightEdge, bottomEdge);

        // Map device space quad to local space. deviceTransform has no 3d component since it was generated with to2dTransform() so we don't need to project.
        WebTransformationMatrix deviceTransformInverse = deviceTransform.inverse();
        localQuad = MathUtil::mapQuad(deviceTransformInverse, deviceQuad.ToQuadF(), clipped);

        // We should not DCHECK(!clipped) here, because anti-aliasing inflation may cause deviceQuad to become
        // clipped. To our knowledge this scenario does not need to be handled differently than the unclipped case.
    } else {
        // Move fragment shader transform to vertex shader. We can do this while
        // still producing correct results as fragmentTexTransformLocation
        // should always be non-negative when tiles are transformed in a way
        // that could result in sampling outside the layer.
        vertexTexScaleX *= fragmentTexScaleX;
        vertexTexScaleY *= fragmentTexScaleY;
        vertexTexTranslateX *= fragmentTexScaleX;
        vertexTexTranslateY *= fragmentTexScaleY;
        vertexTexTranslateX += fragmentTexTranslateX;
        vertexTexTranslateY += fragmentTexTranslateY;

        GLC(context(), context()->uniform4f(uniforms.vertexTexTransformLocation, vertexTexTranslateX, vertexTexTranslateY, vertexTexScaleX, vertexTexScaleY));

        localQuad = gfx::RectF(tileRect);
    }

    // Normalize to tileRect.
    localQuad.Scale(1.0f / tileRect.width(), 1.0f / tileRect.height());

    setShaderOpacity(quad->opacity(), uniforms.alphaLocation);
    setShaderQuadF(localQuad, uniforms.pointLocation);

    // The tile quad shader behaves differently compared to all other shaders.
    // 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 quadRect.
    gfx::RectF centeredRect(gfx::PointF(-0.5 * tileRect.width(), -0.5 * tileRect.height()), tileRect.size());
    drawQuadGeometry(frame, quad->quadTransform(), centeredRect, uniforms.matrixLocation);
}

void GLRenderer::drawYUVVideoQuad(const DrawingFrame& frame, const YUVVideoDrawQuad* quad)
{
    const VideoYUVProgram* program = videoYUVProgram();
    DCHECK(program && program->initialized());

    const VideoLayerImpl::FramePlane& yPlane = quad->yPlane();
    const VideoLayerImpl::FramePlane& uPlane = quad->uPlane();
    const VideoLayerImpl::FramePlane& vPlane = quad->vPlane();

    ResourceProvider::ScopedReadLockGL yPlaneLock(m_resourceProvider, yPlane.resourceId);
    ResourceProvider::ScopedReadLockGL uPlaneLock(m_resourceProvider, uPlane.resourceId);
    ResourceProvider::ScopedReadLockGL vPlaneLock(m_resourceProvider, vPlane.resourceId);
    GLC(context(), context()->activeTexture(GL_TEXTURE1));
    GLC(context(), context()->bindTexture(GL_TEXTURE_2D, yPlaneLock.textureId()));
    GLC(context(), context()->activeTexture(GL_TEXTURE2));
    GLC(context(), context()->bindTexture(GL_TEXTURE_2D, uPlaneLock.textureId()));
    GLC(context(), context()->activeTexture(GL_TEXTURE3));
    GLC(context(), context()->bindTexture(GL_TEXTURE_2D, vPlaneLock.textureId()));

    GLC(context(), context()->useProgram(program->program()));

    GLC(context(), context()->uniform2f(program->vertexShader().texScaleLocation(), quad->texScale().width(), quad->texScale().height()));
    GLC(context(), context()->uniform1i(program->fragmentShader().yTextureLocation(), 1));
    GLC(context(), context()->uniform1i(program->fragmentShader().uTextureLocation(), 2));
    GLC(context(), context()->uniform1i(program->fragmentShader().vTextureLocation(), 3));

    // 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 yuv2RGB[9] = {
        1.164f, 1.164f, 1.164f,
        0.f, -.391f, 2.018f,
        1.596f, -.813f, 0.f,
    };
    GLC(context(), context()->uniformMatrix3fv(program->fragmentShader().yuvMatrixLocation(), 1, 0, yuv2RGB));

    // 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 yuvAdjust[3] = {
        -0.0625f,
        -0.5f,
        -0.5f,
    };
    GLC(context(), context()->uniform3fv(program->fragmentShader().yuvAdjLocation(), 1, yuvAdjust));

    setShaderOpacity(quad->opacity(), program->fragmentShader().alphaLocation());
    drawQuadGeometry(frame, quad->quadTransform(), quad->quadRect(), program->vertexShader().matrixLocation());

    // Reset active texture back to texture 0.
    GLC(context(), context()->activeTexture(GL_TEXTURE0));
}

void GLRenderer::drawStreamVideoQuad(const DrawingFrame& frame, const StreamVideoDrawQuad* quad)
{
    static float glMatrix[16];

    DCHECK(m_capabilities.usingEglImage);

    const VideoStreamTextureProgram* program = videoStreamTextureProgram();
    GLC(context(), context()->useProgram(program->program()));

    toGLMatrix(&glMatrix[0], quad->matrix());
    GLC(context(), context()->uniformMatrix4fv(program->vertexShader().texMatrixLocation(), 1, false, glMatrix));

    GLC(context(), context()->bindTexture(GL_TEXTURE_EXTERNAL_OES, quad->textureId()));

    GLC(context(), context()->uniform1i(program->fragmentShader().samplerLocation(), 0));

    setShaderOpacity(quad->opacity(), program->fragmentShader().alphaLocation());
    drawQuadGeometry(frame, quad->quadTransform(), quad->quadRect(), program->vertexShader().matrixLocation());
}

struct TextureProgramBinding {
    template<class Program> void set(Program* program)
    {
        DCHECK(program && program->initialized());
        programId = program->program();
        samplerLocation = program->fragmentShader().samplerLocation();
        matrixLocation = program->vertexShader().matrixLocation();
        alphaLocation = program->fragmentShader().alphaLocation();
    }
    int programId;
    int samplerLocation;
    int matrixLocation;
    int alphaLocation;
};

struct TexTransformTextureProgramBinding : TextureProgramBinding {
    template<class Program> void set(Program* program)
    {
        TextureProgramBinding::set(program);
        texTransformLocation = program->vertexShader().texTransformLocation();
    }
    int texTransformLocation;
};

void GLRenderer::drawTextureQuad(const DrawingFrame& frame, const TextureDrawQuad* quad)
{
    TexTransformTextureProgramBinding binding;
    if (quad->flipped())
        binding.set(textureProgramFlip());
    else
        binding.set(textureProgram());
    GLC(context(), context()->useProgram(binding.programId));
    GLC(context(), context()->uniform1i(binding.samplerLocation, 0));
    const gfx::RectF& uvRect = quad->uvRect();
    GLC(context(), context()->uniform4f(binding.texTransformLocation, uvRect.x(), uvRect.y(), uvRect.width(), uvRect.height()));

    ResourceProvider::ScopedReadLockGL quadResourceLock(m_resourceProvider, quad->resourceId());
    GLC(context(), context()->bindTexture(GL_TEXTURE_2D, quadResourceLock.textureId()));

    // FIXME: setting the texture parameters every time is redundant. Move this code somewhere
    // where it will only happen once per texture.
    GLC(context(), context()->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
    GLC(context(), context()->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
    GLC(context(), context()->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
    GLC(context(), context()->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));

    if (!quad->premultipliedAlpha()) {
        // As it turns out, the premultiplied alpha blending function (ONE, ONE_MINUS_SRC_ALPHA)
        // will never cause the alpha channel to be set to anything less than 1.0 if it is
        // initialized to that value! Therefore, premultipliedAlpha being false is the first
        // situation we can generally see an alpha channel less than 1.0 coming out of the
        // compositor. This is causing platform differences in some layout tests (see
        // https://bugs.webkit.org/show_bug.cgi?id=82412), so in this situation, use a separate
        // blend function for the alpha channel to avoid modifying it. Don't use colorMask for this
        // as it has performance implications on some platforms.
        GLC(context(), context()->blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE));
    }

    setShaderOpacity(quad->opacity(), binding.alphaLocation);
    drawQuadGeometry(frame, quad->quadTransform(), quad->quadRect(), binding.matrixLocation);

    if (!quad->premultipliedAlpha())
        GLC(m_context, m_context->blendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
}

void GLRenderer::drawIOSurfaceQuad(const DrawingFrame& frame, const IOSurfaceDrawQuad* quad)
{
    TexTransformTextureProgramBinding binding;
    binding.set(textureIOSurfaceProgram());

    GLC(context(), context()->useProgram(binding.programId));
    GLC(context(), context()->uniform1i(binding.samplerLocation, 0));
    if (quad->orientation() == IOSurfaceDrawQuad::Flipped)
        GLC(context(), context()->uniform4f(binding.texTransformLocation, 0, quad->ioSurfaceSize().height(), quad->ioSurfaceSize().width(), quad->ioSurfaceSize().height() * -1.0));
    else
        GLC(context(), context()->uniform4f(binding.texTransformLocation, 0, 0, quad->ioSurfaceSize().width(), quad->ioSurfaceSize().height()));

    GLC(context(), context()->bindTexture(GL_TEXTURE_RECTANGLE_ARB, quad->ioSurfaceTextureId()));

    setShaderOpacity(quad->opacity(), binding.alphaLocation);
    drawQuadGeometry(frame, quad->quadTransform(), quad->quadRect(), binding.matrixLocation);

    GLC(context(), context()->bindTexture(GL_TEXTURE_RECTANGLE_ARB, 0));
}

void GLRenderer::finishDrawingFrame(DrawingFrame& frame)
{
    m_currentFramebufferLock.reset();
    m_swapBufferRect.Union(gfx::ToEnclosingRect(frame.rootDamageRect));

    GLC(m_context, m_context->disable(GL_BLEND));
}

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

void GLRenderer::toGLMatrix(float* flattened, const WebTransformationMatrix& m)
{
    flattened[0] = m.m11();
    flattened[1] = m.m12();
    flattened[2] = m.m13();
    flattened[3] = m.m14();
    flattened[4] = m.m21();
    flattened[5] = m.m22();
    flattened[6] = m.m23();
    flattened[7] = m.m24();
    flattened[8] = m.m31();
    flattened[9] = m.m32();
    flattened[10] = m.m33();
    flattened[11] = m.m34();
    flattened[12] = m.m41();
    flattened[13] = m.m42();
    flattened[14] = m.m43();
    flattened[15] = m.m44();
}

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

    float point[8];
    point[0] = quad.p1().x();
    point[1] = quad.p1().y();
    point[2] = quad.p2().x();
    point[3] = quad.p2().y();
    point[4] = quad.p3().x();
    point[5] = quad.p3().y();
    point[6] = quad.p4().x();
    point[7] = quad.p4().y();
    GLC(m_context, m_context->uniform2fv(quadLocation, 4, point));
}

void GLRenderer::setShaderOpacity(float opacity, int alphaLocation)
{
    if (alphaLocation != -1)
        GLC(m_context, m_context->uniform1f(alphaLocation, opacity));
}

void GLRenderer::drawQuadGeometry(const DrawingFrame& frame, const WebKit::WebTransformationMatrix& drawTransform, const gfx::RectF& quadRect, int matrixLocation)
{
    WebTransformationMatrix quadRectMatrix;
    quadRectTransform(&quadRectMatrix, drawTransform, quadRect);
    static float glMatrix[16];
    toGLMatrix(&glMatrix[0], frame.projectionMatrix * quadRectMatrix);
    GLC(m_context, m_context->uniformMatrix4fv(matrixLocation, 1, false, &glMatrix[0]));

    GLC(m_context, m_context->drawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0));
}

void GLRenderer::copyTextureToFramebuffer(const DrawingFrame& frame, int textureId, const gfx::Rect& rect, const WebTransformationMatrix& drawMatrix)
{
    const RenderPassProgram* program = renderPassProgram();

    GLC(context(), context()->bindTexture(GL_TEXTURE_2D, textureId));
    GLC(context(), context()->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
    GLC(context(), context()->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
    GLC(context(), context()->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
    GLC(context(), context()->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));

    GLC(context(), context()->useProgram(program->program()));
    GLC(context(), context()->uniform1i(program->fragmentShader().samplerLocation(), 0));
    setShaderOpacity(1, program->fragmentShader().alphaLocation());
    drawQuadGeometry(frame, drawMatrix, rect, program->vertexShader().matrixLocation());
}

void GLRenderer::finish()
{
    TRACE_EVENT0("cc", "GLRenderer::finish");
    m_context->finish();
}

bool GLRenderer::swapBuffers()
{
    DCHECK(m_visible);
    DCHECK(!m_isFramebufferDiscarded);

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

    if (m_capabilities.usingPartialSwap) {
        // If supported, we can save significant bandwidth by only swapping the damaged/scissored region (clamped to the viewport)
        m_swapBufferRect.Intersect(gfx::Rect(gfx::Point(), viewportSize()));
        int flippedYPosOfRectBottom = viewportHeight() - m_swapBufferRect.y() - m_swapBufferRect.height();
        m_context->postSubBufferCHROMIUM(m_swapBufferRect.x(), flippedYPosOfRectBottom, m_swapBufferRect.width(), m_swapBufferRect.height());
    } else {
        // Note that currently this has the same effect as swapBuffers; we should
        // consider exposing a different entry point on WebGraphicsContext3D.
        m_context->prepareTexture();
    }

    m_swapBufferRect = gfx::Rect();

    return true;
}

void GLRenderer::onSwapBuffersComplete()
{
    m_client->onSwapBuffersComplete();
}

void GLRenderer::onMemoryAllocationChanged(WebGraphicsMemoryAllocation allocation)
{
    // Just ignore the memory manager when it says to set the limit to zero
    // bytes. This will happen when the memory manager thinks that the renderer
    // is not visible (which the renderer knows better).
    if (allocation.bytesLimitWhenVisible) {
        ManagedMemoryPolicy policy(
            allocation.bytesLimitWhenVisible,
            priorityCutoffValue(allocation.priorityCutoffWhenVisible),
            allocation.bytesLimitWhenNotVisible,
            priorityCutoffValue(allocation.priorityCutoffWhenNotVisible));

        if (allocation.enforceButDoNotKeepAsPolicy)
            m_client->enforceManagedMemoryPolicy(policy);
        else
            m_client->setManagedMemoryPolicy(policy);
    }

    bool oldDiscardFramebufferWhenNotVisible = m_discardFramebufferWhenNotVisible;
    m_discardFramebufferWhenNotVisible = !allocation.suggestHaveBackbuffer;
    enforceMemoryPolicy();
    if (allocation.enforceButDoNotKeepAsPolicy)
        m_discardFramebufferWhenNotVisible = oldDiscardFramebufferWhenNotVisible;
}

int GLRenderer::priorityCutoffValue(WebKit::WebGraphicsMemoryAllocation::PriorityCutoff priorityCutoff)
{
    switch (priorityCutoff) {
    case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowNothing:
        return PriorityCalculator::allowNothingCutoff();
    case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowVisibleOnly:
        return PriorityCalculator::allowVisibleOnlyCutoff();
    case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowVisibleAndNearby:
        return PriorityCalculator::allowVisibleAndNearbyCutoff();
    case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowEverything:
        return PriorityCalculator::allowEverythingCutoff();
    }
    NOTREACHED();
    return 0;
}

void GLRenderer::enforceMemoryPolicy()
{
    if (!m_visible) {
        TRACE_EVENT0("cc", "GLRenderer::enforceMemoryPolicy dropping resources");
        releaseRenderPassTextures();
        if (m_discardFramebufferWhenNotVisible)
            discardFramebuffer();
        GLC(m_context, m_context->flush());
    }
}

void GLRenderer::discardFramebuffer()
{
    if (m_isFramebufferDiscarded)
        return;

    if (!m_capabilities.usingDiscardFramebuffer)
        return;

    // FIXME: Update attachments argument to appropriate values once they are no longer ignored.
    m_context->discardFramebufferEXT(GL_TEXTURE_2D, 0, 0);
    m_isFramebufferDiscarded = true;

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

void GLRenderer::ensureFramebuffer()
{
    if (!m_isFramebufferDiscarded)
        return;

    if (!m_capabilities.usingDiscardFramebuffer)
        return;

    m_context->ensureFramebufferCHROMIUM();
    m_isFramebufferDiscarded = false;
}

void GLRenderer::onContextLost()
{
    m_client->didLoseContext();
}


void GLRenderer::getFramebufferPixels(void *pixels, const gfx::Rect& rect)
{
    DCHECK(rect.right() <= viewportWidth());
    DCHECK(rect.bottom() <= viewportHeight());

    if (!pixels)
        return;

    makeContextCurrent();

    bool doWorkaround = needsIOSurfaceReadbackWorkaround();

    GLuint temporaryTexture = 0;
    GLuint temporaryFBO = 0;

    if (doWorkaround) {
        // 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>

        temporaryTexture = m_context->createTexture();
        GLC(m_context, m_context->bindTexture(GL_TEXTURE_2D, temporaryTexture));
        GLC(m_context, m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
        GLC(m_context, m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
        GLC(m_context, m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
        GLC(m_context, m_context->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.
        GLC(m_context, m_context->copyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, viewportSize().width(), viewportSize().height(), 0));
        temporaryFBO = m_context->createFramebuffer();
        // Attach this texture to an FBO, and perform the readback from that FBO.
        GLC(m_context, m_context->bindFramebuffer(GL_FRAMEBUFFER, temporaryFBO));
        GLC(m_context, m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, temporaryTexture, 0));

        DCHECK(m_context->checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
    }

    scoped_array<uint8_t> srcPixels(new uint8_t[rect.width() * rect.height() * 4]);
    GLC(m_context, m_context->readPixels(rect.x(), viewportSize().height() - rect.bottom(), rect.width(), rect.height(),
                                         GL_RGBA, GL_UNSIGNED_BYTE, srcPixels.get()));

    uint8_t* destPixels = static_cast<uint8_t*>(pixels);
    size_t rowBytes = rect.width() * 4;
    int numRows = rect.height();
    size_t totalBytes = numRows * rowBytes;
    for (size_t destY = 0; destY < totalBytes; destY += rowBytes) {
        // Flip Y axis.
        size_t srcY = totalBytes - destY - rowBytes;
        // Swizzle BGRA -> RGBA.
        for (size_t x = 0; x < rowBytes; x += 4) {
            destPixels[destY + (x+0)] = srcPixels.get()[srcY + (x+2)];
            destPixels[destY + (x+1)] = srcPixels.get()[srcY + (x+1)];
            destPixels[destY + (x+2)] = srcPixels.get()[srcY + (x+0)];
            destPixels[destY + (x+3)] = srcPixels.get()[srcY + (x+3)];
        }
    }

    if (doWorkaround) {
        // Clean up.
        GLC(m_context, m_context->bindFramebuffer(GL_FRAMEBUFFER, 0));
        GLC(m_context, m_context->bindTexture(GL_TEXTURE_2D, 0));
        GLC(m_context, m_context->deleteFramebuffer(temporaryFBO));
        GLC(m_context, m_context->deleteTexture(temporaryTexture));
    }

    enforceMemoryPolicy();
}

bool GLRenderer::getFramebufferTexture(ScopedResource* texture, const gfx::Rect& deviceRect)
{
    DCHECK(!texture->id() || (texture->size() == deviceRect.size() && texture->format() == GL_RGB));

    if (!texture->id() && !texture->allocate(Renderer::ImplPool, deviceRect.size(), GL_RGB, ResourceProvider::TextureUsageAny))
        return false;

    ResourceProvider::ScopedWriteLockGL lock(m_resourceProvider, texture->id());
    GLC(m_context, m_context->bindTexture(GL_TEXTURE_2D, lock.textureId()));
    GLC(m_context, m_context->copyTexImage2D(GL_TEXTURE_2D, 0, texture->format(),
                                             deviceRect.x(), deviceRect.y(), deviceRect.width(), deviceRect.height(), 0));
    return true;
}

bool GLRenderer::useScopedTexture(DrawingFrame& frame, const ScopedResource* texture, const gfx::Rect& viewportRect)
{
    DCHECK(texture->id());
    frame.currentRenderPass = 0;
    frame.currentTexture = texture;

    return bindFramebufferToTexture(frame, texture, viewportRect);
}

void GLRenderer::bindFramebufferToOutputSurface(DrawingFrame& frame)
{
    m_currentFramebufferLock.reset();
    GLC(m_context, m_context->bindFramebuffer(GL_FRAMEBUFFER, 0));
}

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

    GLC(m_context, m_context->bindFramebuffer(GL_FRAMEBUFFER, m_offscreenFramebufferId));
    m_currentFramebufferLock = make_scoped_ptr(new ResourceProvider::ScopedWriteLockGL(m_resourceProvider, texture->id()));
    unsigned textureId = m_currentFramebufferLock->textureId();
    GLC(m_context, m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0));

    DCHECK(m_context->checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);

    initializeMatrices(frame, framebufferRect, false);
    setDrawViewportSize(framebufferRect.size());

    return true;
}

void GLRenderer::setScissorTestRect(const gfx::Rect& scissorRect)
{
    GLC(m_context, m_context->scissor(scissorRect.x(), scissorRect.y(), scissorRect.width(), scissorRect.height()));
}

void GLRenderer::setDrawViewportSize(const gfx::Size& viewportSize)
{
    GLC(m_context, m_context->viewport(0, 0, viewportSize.width(), viewportSize.height()));
}

bool GLRenderer::makeContextCurrent()
{
    return m_context->makeContextCurrent();
}

bool GLRenderer::initializeSharedObjects()
{
    TRACE_EVENT0("cc", "GLRenderer::initializeSharedObjects");
    makeContextCurrent();

    // Create an FBO for doing offscreen rendering.
    GLC(m_context, m_offscreenFramebufferId = m_context->createFramebuffer());

    // We will always need these programs to render, so create the programs eagerly so that the shader compilation can
    // start while we do other work. Other programs are created lazily on first access.
    m_sharedGeometry = make_scoped_ptr(new GeometryBinding(m_context, quadVertexRect()));
    m_renderPassProgram = make_scoped_ptr(new RenderPassProgram(m_context));
    m_tileProgram = make_scoped_ptr(new TileProgram(m_context));
    m_tileProgramOpaque = make_scoped_ptr(new TileProgramOpaque(m_context));

    GLC(m_context, m_context->flush());

    return true;
}

const GLRenderer::TileCheckerboardProgram* GLRenderer::tileCheckerboardProgram()
{
    if (!m_tileCheckerboardProgram)
        m_tileCheckerboardProgram = make_scoped_ptr(new TileCheckerboardProgram(m_context));
    if (!m_tileCheckerboardProgram->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::checkerboardProgram::initalize");
        m_tileCheckerboardProgram->initialize(m_context, m_isUsingBindUniform);
    }
    return m_tileCheckerboardProgram.get();
}

const GLRenderer::SolidColorProgram* GLRenderer::solidColorProgram()
{
    if (!m_solidColorProgram)
        m_solidColorProgram = make_scoped_ptr(new SolidColorProgram(m_context));
    if (!m_solidColorProgram->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::solidColorProgram::initialize");
        m_solidColorProgram->initialize(m_context, m_isUsingBindUniform);
    }
    return m_solidColorProgram.get();
}

const GLRenderer::RenderPassProgram* GLRenderer::renderPassProgram()
{
    DCHECK(m_renderPassProgram);
    if (!m_renderPassProgram->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::renderPassProgram::initialize");
        m_renderPassProgram->initialize(m_context, m_isUsingBindUniform);
    }
    return m_renderPassProgram.get();
}

const GLRenderer::RenderPassProgramAA* GLRenderer::renderPassProgramAA()
{
    if (!m_renderPassProgramAA)
        m_renderPassProgramAA = make_scoped_ptr(new RenderPassProgramAA(m_context));
    if (!m_renderPassProgramAA->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::renderPassProgramAA::initialize");
        m_renderPassProgramAA->initialize(m_context, m_isUsingBindUniform);
    }
    return m_renderPassProgramAA.get();
}

const GLRenderer::RenderPassMaskProgram* GLRenderer::renderPassMaskProgram()
{
    if (!m_renderPassMaskProgram)
        m_renderPassMaskProgram = make_scoped_ptr(new RenderPassMaskProgram(m_context));
    if (!m_renderPassMaskProgram->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::renderPassMaskProgram::initialize");
        m_renderPassMaskProgram->initialize(m_context, m_isUsingBindUniform);
    }
    return m_renderPassMaskProgram.get();
}

const GLRenderer::RenderPassMaskProgramAA* GLRenderer::renderPassMaskProgramAA()
{
    if (!m_renderPassMaskProgramAA)
        m_renderPassMaskProgramAA = make_scoped_ptr(new RenderPassMaskProgramAA(m_context));
    if (!m_renderPassMaskProgramAA->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::renderPassMaskProgramAA::initialize");
        m_renderPassMaskProgramAA->initialize(m_context, m_isUsingBindUniform);
    }
    return m_renderPassMaskProgramAA.get();
}

const GLRenderer::TileProgram* GLRenderer::tileProgram()
{
    DCHECK(m_tileProgram);
    if (!m_tileProgram->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::tileProgram::initialize");
        m_tileProgram->initialize(m_context, m_isUsingBindUniform);
    }
    return m_tileProgram.get();
}

const GLRenderer::TileProgramOpaque* GLRenderer::tileProgramOpaque()
{
    DCHECK(m_tileProgramOpaque);
    if (!m_tileProgramOpaque->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::tileProgramOpaque::initialize");
        m_tileProgramOpaque->initialize(m_context, m_isUsingBindUniform);
    }
    return m_tileProgramOpaque.get();
}

const GLRenderer::TileProgramAA* GLRenderer::tileProgramAA()
{
    if (!m_tileProgramAA)
        m_tileProgramAA = make_scoped_ptr(new TileProgramAA(m_context));
    if (!m_tileProgramAA->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::tileProgramAA::initialize");
        m_tileProgramAA->initialize(m_context, m_isUsingBindUniform);
    }
    return m_tileProgramAA.get();
}

const GLRenderer::TileProgramSwizzle* GLRenderer::tileProgramSwizzle()
{
    if (!m_tileProgramSwizzle)
        m_tileProgramSwizzle = make_scoped_ptr(new TileProgramSwizzle(m_context));
    if (!m_tileProgramSwizzle->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::tileProgramSwizzle::initialize");
        m_tileProgramSwizzle->initialize(m_context, m_isUsingBindUniform);
    }
    return m_tileProgramSwizzle.get();
}

const GLRenderer::TileProgramSwizzleOpaque* GLRenderer::tileProgramSwizzleOpaque()
{
    if (!m_tileProgramSwizzleOpaque)
        m_tileProgramSwizzleOpaque = make_scoped_ptr(new TileProgramSwizzleOpaque(m_context));
    if (!m_tileProgramSwizzleOpaque->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::tileProgramSwizzleOpaque::initialize");
        m_tileProgramSwizzleOpaque->initialize(m_context, m_isUsingBindUniform);
    }
    return m_tileProgramSwizzleOpaque.get();
}

const GLRenderer::TileProgramSwizzleAA* GLRenderer::tileProgramSwizzleAA()
{
    if (!m_tileProgramSwizzleAA)
        m_tileProgramSwizzleAA = make_scoped_ptr(new TileProgramSwizzleAA(m_context));
    if (!m_tileProgramSwizzleAA->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::tileProgramSwizzleAA::initialize");
        m_tileProgramSwizzleAA->initialize(m_context, m_isUsingBindUniform);
    }
    return m_tileProgramSwizzleAA.get();
}

const GLRenderer::TextureProgram* GLRenderer::textureProgram()
{
    if (!m_textureProgram)
        m_textureProgram = make_scoped_ptr(new TextureProgram(m_context));
    if (!m_textureProgram->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::textureProgram::initialize");
        m_textureProgram->initialize(m_context, m_isUsingBindUniform);
    }
    return m_textureProgram.get();
}

const GLRenderer::TextureProgramFlip* GLRenderer::textureProgramFlip()
{
    if (!m_textureProgramFlip)
        m_textureProgramFlip = make_scoped_ptr(new TextureProgramFlip(m_context));
    if (!m_textureProgramFlip->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::textureProgramFlip::initialize");
        m_textureProgramFlip->initialize(m_context, m_isUsingBindUniform);
    }
    return m_textureProgramFlip.get();
}

const GLRenderer::TextureIOSurfaceProgram* GLRenderer::textureIOSurfaceProgram()
{
    if (!m_textureIOSurfaceProgram)
        m_textureIOSurfaceProgram = make_scoped_ptr(new TextureIOSurfaceProgram(m_context));
    if (!m_textureIOSurfaceProgram->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::textureIOSurfaceProgram::initialize");
        m_textureIOSurfaceProgram->initialize(m_context, m_isUsingBindUniform);
    }
    return m_textureIOSurfaceProgram.get();
}

const GLRenderer::VideoYUVProgram* GLRenderer::videoYUVProgram()
{
    if (!m_videoYUVProgram)
        m_videoYUVProgram = make_scoped_ptr(new VideoYUVProgram(m_context));
    if (!m_videoYUVProgram->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::videoYUVProgram::initialize");
        m_videoYUVProgram->initialize(m_context, m_isUsingBindUniform);
    }
    return m_videoYUVProgram.get();
}

const GLRenderer::VideoStreamTextureProgram* GLRenderer::videoStreamTextureProgram()
{
    if (!m_videoStreamTextureProgram)
        m_videoStreamTextureProgram = make_scoped_ptr(new VideoStreamTextureProgram(m_context));
    if (!m_videoStreamTextureProgram->initialized()) {
        TRACE_EVENT0("cc", "GLRenderer::streamTextureProgram::initialize");
        m_videoStreamTextureProgram->initialize(m_context, m_isUsingBindUniform);
    }
    return m_videoStreamTextureProgram.get();
}

void GLRenderer::cleanupSharedObjects()
{
    makeContextCurrent();

    m_sharedGeometry.reset();

    if (m_tileProgram)
        m_tileProgram->cleanup(m_context);
    if (m_tileProgramOpaque)
        m_tileProgramOpaque->cleanup(m_context);
    if (m_tileProgramSwizzle)
        m_tileProgramSwizzle->cleanup(m_context);
    if (m_tileProgramSwizzleOpaque)
        m_tileProgramSwizzleOpaque->cleanup(m_context);
    if (m_tileProgramAA)
        m_tileProgramAA->cleanup(m_context);
    if (m_tileProgramSwizzleAA)
        m_tileProgramSwizzleAA->cleanup(m_context);
    if (m_tileCheckerboardProgram)
        m_tileCheckerboardProgram->cleanup(m_context);

    if (m_renderPassMaskProgram)
        m_renderPassMaskProgram->cleanup(m_context);
    if (m_renderPassProgram)
        m_renderPassProgram->cleanup(m_context);
    if (m_renderPassMaskProgramAA)
        m_renderPassMaskProgramAA->cleanup(m_context);
    if (m_renderPassProgramAA)
        m_renderPassProgramAA->cleanup(m_context);

    if (m_textureProgram)
        m_textureProgram->cleanup(m_context);
    if (m_textureProgramFlip)
        m_textureProgramFlip->cleanup(m_context);
    if (m_textureIOSurfaceProgram)
        m_textureIOSurfaceProgram->cleanup(m_context);

    if (m_videoYUVProgram)
        m_videoYUVProgram->cleanup(m_context);
    if (m_videoStreamTextureProgram)
        m_videoStreamTextureProgram->cleanup(m_context);

    if (m_solidColorProgram)
        m_solidColorProgram->cleanup(m_context);

    if (m_offscreenFramebufferId)
        GLC(m_context, m_context->deleteFramebuffer(m_offscreenFramebufferId));

    releaseRenderPassTextures();
}

bool GLRenderer::isContextLost()
{
    return (m_context->getGraphicsResetStatusARB() != GL_NO_ERROR);
}

}  // namespace cc