/*
 * Copyright (C) 2010 Apple Inc. All rights reserved.
 * Copyright (C) 2011 Google Inc. All rights reserved.
 * Copyright (C) 2012 ChangSeok Oh <shivamidow@gmail.com>
 * Copyright (C) 2012 Research In Motion Limited. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#include "config.h"

#if USE(3D_GRAPHICS)

#include "GraphicsContext3D.h"
#include "GraphicsContext3DPrivate.h"

#include "CanvasRenderingContext.h"
#if USE(OPENGL_ES_2)
#include "Extensions3DOpenGLES.h"
#else
#include "Extensions3DOpenGL.h"
#endif
#include "GraphicsContext.h"
#include "ImageBuffer.h"
#include "ImageData.h"
#include "IntRect.h"
#include "IntSize.h"
#include "NotImplemented.h"
#include "WebGLObject.h"
#include <cstring>
#include <runtime/ArrayBuffer.h>
#include <runtime/ArrayBufferView.h>
#include <runtime/Float32Array.h>
#include <runtime/Int32Array.h>
#include <runtime/Uint8Array.h>
#include <wtf/MainThread.h>
#include <wtf/OwnArrayPtr.h>
#include <wtf/UnusedParam.h>
#include <wtf/text/CString.h>

#if USE(OPENGL_ES_2)
#include "OpenGLESShims.h"
#elif PLATFORM(MAC)
#include <OpenGL/gl.h>
#elif PLATFORM(GTK) || PLATFORM(EFL) || PLATFORM(QT)
#include "OpenGLShims.h"
#endif

#if PLATFORM(BLACKBERRY)
#include <BlackBerryPlatformLog.h>
#endif

#include "GLUtils.h"


namespace WebCore {

void GraphicsContext3D::validateDepthStencil(const char* packedDepthStencilExtension)
{
    Extensions3D* extensions = getExtensions();
    if (m_attrs.stencil) {
        if (extensions->supports(packedDepthStencilExtension)) {
            extensions->ensureEnabled(packedDepthStencilExtension);
            // Force depth if stencil is true.
            m_attrs.depth = true;
        } else
            m_attrs.stencil = false;
    }
    if (m_attrs.antialias) {
        bool isValidVendor = true;
        // Currently in Mac we only turn on antialias if vendor is NVIDIA,
        // or if ATI and on 10.7.2 and above.
        const char* vendor = reinterpret_cast<const char*>(::glGetString(GL_VENDOR));
        if (!vendor || (!std::strstr(vendor, "NVIDIA") && !(std::strstr(vendor, "ATI") && systemAllowsMultisamplingOnATICards())))
            isValidVendor = false;
        if (!isValidVendor || !extensions->supports("GL_ANGLE_framebuffer_multisample") || isGLES2Compliant())
            m_attrs.antialias = false;
        else
            extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample");
    }
}

bool GraphicsContext3D::isResourceSafe()
{
    return false;
}

void GraphicsContext3D::paintRenderingResultsToCanvas(ImageBuffer* imageBuffer, DrawingBuffer*)
{
    int rowBytes = m_currentWidth * 4;
    int totalBytes = rowBytes * m_currentHeight;

    OwnArrayPtr<unsigned char> pixels = adoptArrayPtr(new unsigned char[totalBytes]);
    if (!pixels)
        return;

    readRenderingResults(pixels.get(), totalBytes);

    if (!m_attrs.premultipliedAlpha) {
        for (int i = 0; i < totalBytes; i += 4) {
            // Premultiply alpha.
            pixels[i + 0] = std::min(255, pixels[i + 0] * pixels[i + 3] / 255);
            pixels[i + 1] = std::min(255, pixels[i + 1] * pixels[i + 3] / 255);
            pixels[i + 2] = std::min(255, pixels[i + 2] * pixels[i + 3] / 255);
        }
    }

#if PLATFORM(BLACKBERRY)
    paintToCanvas(pixels.get(), m_currentWidth, m_currentHeight,
                  imageBuffer->internalSize().width(), imageBuffer->internalSize().height(), imageBuffer->context(), true);
#elif USE(OPENVG)
    paintToCanvas(pixels.get(), m_currentWidth, m_currentHeight,
                  imageBuffer->internalSize().width(), imageBuffer->internalSize().height(), imageBuffer->context());
#else
    paintToCanvas(pixels.get(), m_currentWidth, m_currentHeight,
                  imageBuffer->internalSize().width(), imageBuffer->internalSize().height(), imageBuffer->context()->platformContext());
#endif
}

bool GraphicsContext3D::paintCompositedResultsToCanvas(ImageBuffer*)
{
    // Not needed at the moment, so return that nothing was done.
    return false;
}

PassRefPtr<ImageData> GraphicsContext3D::paintRenderingResultsToImageData(DrawingBuffer*)
{
    // Reading premultiplied alpha would involve unpremultiplying, which is
    // lossy.
    if (m_attrs.premultipliedAlpha)
        return nullptr;

    RefPtr<ImageData> imageData = ImageData::create(IntSize(m_currentWidth, m_currentHeight));
    unsigned char* pixels = imageData->data()->data();
    int totalBytes = 4 * m_currentWidth * m_currentHeight;

    readRenderingResults(pixels, totalBytes);

    // Convert to RGBA.
    for (int i = 0; i < totalBytes; i += 4)
        std::swap(pixels[i], pixels[i + 2]);

    return imageData.release();
}

void GraphicsContext3D::prepareTexture()
{
    if (m_layerComposited)
        return;

    makeContextCurrent();
    if (m_attrs.antialias)
        resolveMultisamplingIfNecessary();

    ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_fbo);
    CHECK_GL_ERROR();
    ::glActiveTexture(GL_TEXTURE0);
    CHECK_GL_ERROR();

#if PLATFORM(BLACKBERRY)
    if (!platformTexture()) {
        GLuint tex = 0;
        ::glGenTextures(1, &tex);
        ::glBindTexture(GL_TEXTURE_2D, tex);
        CHECK_GL_ERROR();
        ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_currentWidth, m_currentHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
        CHECK_GL_ERROR();
        m_compositingLayer->setTextureID(tex);
    }
    ::glBindTexture(GL_TEXTURE_2D, platformTexture());
#else
    if(m_compositorTexture)
    	::glBindTexture(GL_TEXTURE_2D, m_compositorTexture);
    else
    	return;
#endif
    CHECK_GL_ERROR();

    ::glCopyTexImage2D(GL_TEXTURE_2D, 0, m_internalColorFormat, 0, 0, m_currentWidth, m_currentHeight, 0);
    CHECK_GL_ERROR();
    ::glBindTexture(GL_TEXTURE_2D, m_boundTexture0);
    ::glActiveTexture(m_activeTexture);
    CHECK_GL_ERROR();
    if (m_boundFBO != m_fbo)
        ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_boundFBO);
    CHECK_GL_ERROR();
    ::glFinish(); // TODO: this shouldn't be necessary
    CHECK_GL_ERROR();
    m_layerComposited = true;
}

void GraphicsContext3D::readRenderingResults(unsigned char *pixels, int pixelsSize)
{
    if (pixelsSize < m_currentWidth * m_currentHeight * 4)
        return;

    makeContextCurrent();

    bool mustRestoreFBO = false;
    if (m_attrs.antialias) {
        resolveMultisamplingIfNecessary();
        ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_fbo);
        CHECK_GL_ERROR();
        mustRestoreFBO = true;
    } else {
        if (m_boundFBO != m_fbo) {
            mustRestoreFBO = true;
            ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_fbo);
            CHECK_GL_ERROR();
        }
    }

    GLint packAlignment = 4;
    bool mustRestorePackAlignment = false;
    ::glGetIntegerv(GL_PACK_ALIGNMENT, &packAlignment);
    CHECK_GL_ERROR();
    if (packAlignment > 4) {
        ::glPixelStorei(GL_PACK_ALIGNMENT, 4);
        CHECK_GL_ERROR();
        mustRestorePackAlignment = true;
    }

    readPixelsAndConvertToBGRAIfNecessary(0, 0, m_currentWidth, m_currentHeight, pixels);

    if (mustRestorePackAlignment)
        ::glPixelStorei(GL_PACK_ALIGNMENT, packAlignment);
    CHECK_GL_ERROR();

    if (mustRestoreFBO)
        ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_boundFBO);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::reshape(int width, int height)
{
    if (!platformGraphicsContext3D())
        return;

    if (width == m_currentWidth && height == m_currentHeight)
        return;

#if PLATFORM(QT) && USE(GRAPHICS_SURFACE)
    ::glFlush(); // Make sure all GL calls have been committed before resizing.
    createGraphicsSurfaces(IntSize(width, height));
#endif

    m_currentWidth = width;
    m_currentHeight = height;

    makeContextCurrent();
    validateAttributes();

    bool mustRestoreFBO = reshapeFBOs(IntSize(width, height));

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

    ::glClear(clearMask);
    CHECK_GL_ERROR();

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

    if (mustRestoreFBO)
        ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_boundFBO);
    CHECK_GL_ERROR();

    ::glFlush();
    CHECK_GL_ERROR();
}

IntSize GraphicsContext3D::getInternalFramebufferSize() const
{
    return IntSize(m_currentWidth, m_currentHeight);
}

void GraphicsContext3D::activeTexture(GC3Denum texture)
{
    makeContextCurrent();
    m_activeTexture = texture;
    ::glActiveTexture(texture);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::attachShader(Platform3DObject program, Platform3DObject shader)
{
    ASSERT(program);
    ASSERT(shader);
    makeContextCurrent();
    ::glAttachShader(program, shader);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::bindAttribLocation(Platform3DObject program, GC3Duint index, const String& name)
{
    ASSERT(program);
    makeContextCurrent();
    ::glBindAttribLocation(program, index, name.utf8().data());
    CHECK_GL_ERROR();
}

void GraphicsContext3D::bindBuffer(GC3Denum target, Platform3DObject buffer)
{
    makeContextCurrent();
    ::glBindBuffer(target, buffer);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::bindFramebuffer(GC3Denum target, Platform3DObject buffer)
{
    makeContextCurrent();
    GLuint fbo;
    if (buffer)
        fbo = buffer;
    else
#if PLATFORM(BLACKBERRY)
        fbo = m_fbo;
#else
        fbo = (m_attrs.antialias ? m_multisampleFBO : m_fbo);
#endif

    if(isDirectGLMode()){
    	if(fbo == 0)
            ::glBindFramebuffer(GL_FRAMEBUFFER, 0);
    	else
            ::glBindFramebufferEXT(target, fbo);
    }
    else if (fbo != m_boundFBO) {
        ::glBindFramebufferEXT(target, fbo);
    }

    m_boundFBO = fbo;

    CHECK_GL_ERROR();
}

void GraphicsContext3D::bindRenderbuffer(GC3Denum target, Platform3DObject renderbuffer)
{
    makeContextCurrent();
    ::glBindRenderbufferEXT(target, renderbuffer);
    CHECK_GL_ERROR();
}


void GraphicsContext3D::bindTexture(GC3Denum target, Platform3DObject texture)
{
    makeContextCurrent();
    if (m_activeTexture == GL_TEXTURE0 && target == GL_TEXTURE_2D)
        m_boundTexture0 = texture;
    ::glBindTexture(target, texture);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::blendColor(GC3Dclampf red, GC3Dclampf green, GC3Dclampf blue, GC3Dclampf alpha)
{
    makeContextCurrent();
    ::glBlendColor(red, green, blue, alpha);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::blendEquation(GC3Denum mode)
{
    makeContextCurrent();
    ::glBlendEquation(mode);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::blendEquationSeparate(GC3Denum modeRGB, GC3Denum modeAlpha)
{
    makeContextCurrent();
    ::glBlendEquationSeparate(modeRGB, modeAlpha);
    CHECK_GL_ERROR();
}


void GraphicsContext3D::blendFunc(GC3Denum sfactor, GC3Denum dfactor)
{
    makeContextCurrent();
    ::glBlendFunc(sfactor, dfactor);
    CHECK_GL_ERROR();
}       

void GraphicsContext3D::blendFuncSeparate(GC3Denum srcRGB, GC3Denum dstRGB, GC3Denum srcAlpha, GC3Denum dstAlpha)
{
    makeContextCurrent();
    ::glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::bufferData(GC3Denum target, GC3Dsizeiptr size, GC3Denum usage)
{
    makeContextCurrent();
    ::glBufferData(target, size, 0, usage);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::bufferData(GC3Denum target, GC3Dsizeiptr size, const void* data, GC3Denum usage)
{
    makeContextCurrent();
    if(size >= m_optimizedDataBuffer.size())
    	::glBufferData(target, size, data, usage);
    else{
    	if(data)
    		memcpy(m_optimizedDataBuffer.data(), data, size);
    	::glBufferData(target, m_optimizedDataBuffer.size(), m_optimizedDataBuffer.data(), usage);
    }
    CHECK_GL_ERROR();
}

void GraphicsContext3D::bufferSubData(GC3Denum target, GC3Dintptr offset, GC3Dsizeiptr size, const void* data)
{
    makeContextCurrent();
    ::glBufferSubData(target, offset, size, data);
    CHECK_GL_ERROR();
}

GC3Denum GraphicsContext3D::checkFramebufferStatus(GC3Denum target)
{
    makeContextCurrent();
    GC3Denum ret = ::glCheckFramebufferStatusEXT(target);
    CHECK_GL_ERROR();
    return ret;
}

void GraphicsContext3D::clearColor(GC3Dclampf r, GC3Dclampf g, GC3Dclampf b, GC3Dclampf a)
{
    makeContextCurrent();
    ::glClearColor(r, g, b, a);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::clear(GC3Dbitfield mask)
{
    makeContextCurrent();
    ::glClear(mask);
}

void GraphicsContext3D::clearStencil(GC3Dint s)
{
    makeContextCurrent();
    ::glClearStencil(s);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::colorMask(GC3Dboolean red, GC3Dboolean green, GC3Dboolean blue, GC3Dboolean alpha)
{
    makeContextCurrent();
    ::glColorMask(red, green, blue, alpha);
    CHECK_GL_ERROR();
}

static void doCompileShader(Platform3DObject shader, const String& source, int& GLCompileSuccess, GLint& logLength)
{
	CString cString = source.utf8();
	const char* translatedShaderPtr = cString.data();
	GLint translatedShaderLength = cString.length();

	::glShaderSource(shader, 1, &translatedShaderPtr, &translatedShaderLength);
	CHECK_GL_ERROR();

	::glCompileShader(shader);
	CHECK_GL_ERROR();

	::glGetShaderiv(shader, GraphicsContext3D::COMPILE_STATUS, &GLCompileSuccess);
	CHECK_GL_ERROR();

	// Populate the shader log
	::glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
	CHECK_GL_ERROR();
}

void GraphicsContext3D::compileShader(Platform3DObject shader)
{
    ASSERT(shader);
    makeContextCurrent();

    String translatedShaderSource = m_extensions->getTranslatedShaderSourceANGLE(shader);

    if (!translatedShaderSource.length())
        return;

	int GLCompileSuccess;
	GLint logLength;

    doCompileShader(shader, translatedShaderSource, GLCompileSuccess, logLength);

	if (GLCompileSuccess == GL_TRUE || !logLength)
		return;

    if(translatedShaderSource.find("precision") == notFound) {
    	// work around bug '0:1: S0032: no default precision defined for variable'
    	translatedShaderSource = String("precision highp float;\n") + translatedShaderSource;

        doCompileShader(shader, translatedShaderSource, GLCompileSuccess, logLength);

    	if (GLCompileSuccess == GL_TRUE || !logLength)
    		return;
    }
    
	HashMap<Platform3DObject, GraphicsContext3D::ShaderSourceEntry>::iterator result = m_shaderSourceMap.find(shader);
	GraphicsContext3D::ShaderSourceEntry& entry = result->second;

	GLsizei size = 0;
	OwnArrayPtr<GLchar> info = adoptArrayPtr(new GLchar[logLength]);
	::glGetShaderInfoLog(shader, logLength, &size, info.get());
	CHECK_GL_ERROR();

	entry.log = info.get();

	if (GLCompileSuccess != GL_TRUE || logLength)
		WTF::logMessage(String("compileShader ") + entry.log);
    
    // ASSERT that ANGLE generated GLSL will be accepted by OpenGL.
    ASSERT(GLCompileSuccess == GL_TRUE);
}

void GraphicsContext3D::copyTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Dint border)
{
    makeContextCurrent();
#if !PLATFORM(BLACKBERRY)
    if (m_attrs.antialias && m_boundFBO == m_multisampleFBO) {
        resolveMultisamplingIfNecessary(IntRect(x, y, width, height));
        ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_fbo);
        CHECK_GL_ERROR();
    }
#endif
    ::glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
    CHECK_GL_ERROR();
#if !PLATFORM(BLACKBERRY)
    if (m_attrs.antialias && m_boundFBO == m_multisampleFBO)
        ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
#endif
    CHECK_GL_ERROR();
}

void GraphicsContext3D::copyTexSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height)
{
    makeContextCurrent();
#if !PLATFORM(BLACKBERRY)
    if (m_attrs.antialias && m_boundFBO == m_multisampleFBO) {
        resolveMultisamplingIfNecessary(IntRect(x, y, width, height));
        ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_fbo);
        CHECK_GL_ERROR();
    }
#endif
    ::glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
    CHECK_GL_ERROR();
#if !PLATFORM(BLACKBERRY)
    if (m_attrs.antialias && m_boundFBO == m_multisampleFBO)
        ::glBindFramebufferEXT(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO);
#endif
    CHECK_GL_ERROR();
}

void GraphicsContext3D::cullFace(GC3Denum mode)
{
    makeContextCurrent();
    ::glCullFace(mode);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::depthFunc(GC3Denum func)
{
    makeContextCurrent();
    ::glDepthFunc(func);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::depthMask(GC3Dboolean flag)
{
    makeContextCurrent();
    ::glDepthMask(flag);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::detachShader(Platform3DObject program, Platform3DObject shader)
{
    ASSERT(program);
    ASSERT(shader);
    makeContextCurrent();
    ::glDetachShader(program, shader);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::disable(GC3Denum cap)
{
    makeContextCurrent();
    ::glDisable(cap);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::disableVertexAttribArray(GC3Duint index)
{
    makeContextCurrent();
    ::glDisableVertexAttribArray(index);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::drawArrays(GC3Denum mode, GC3Dint first, GC3Dsizei count)
{
    makeContextCurrent();
    ::glDrawArrays(mode, first, count);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::drawElements(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset)
{
    makeContextCurrent();
    ::glDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(static_cast<intptr_t>(offset)));
    CHECK_GL_ERROR();
}

void GraphicsContext3D::enable(GC3Denum cap)
{
    makeContextCurrent();
    ::glEnable(cap);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::enableVertexAttribArray(GC3Duint index)
{
    makeContextCurrent();
    ::glEnableVertexAttribArray(index);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::finish()
{
    makeContextCurrent();
    ::glFinish();
    CHECK_GL_ERROR();
}

void GraphicsContext3D::flush()
{
    makeContextCurrent();
    ::glFlush();
    CHECK_GL_ERROR();
}

void GraphicsContext3D::framebufferRenderbuffer(GC3Denum target, GC3Denum attachment, GC3Denum renderbuffertarget, Platform3DObject buffer)
{
    makeContextCurrent();
    ::glFramebufferRenderbufferEXT(target, attachment, renderbuffertarget, buffer);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::framebufferTexture2D(GC3Denum target, GC3Denum attachment, GC3Denum textarget, Platform3DObject texture, GC3Dint level)
{
    makeContextCurrent();
    ::glFramebufferTexture2DEXT(target, attachment, textarget, texture, level);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::frontFace(GC3Denum mode)
{
    makeContextCurrent();
    ::glFrontFace(mode);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::generateMipmap(GC3Denum target)
{
    makeContextCurrent();
    ::glGenerateMipmapEXT(target);
    CHECK_GL_ERROR();
}

bool GraphicsContext3D::getActiveAttrib(Platform3DObject program, GC3Duint index, ActiveInfo& info)
{
    if (!program) {
        synthesizeGLErrorWrapper(INVALID_VALUE);
        return false;
    }
    makeContextCurrent();
    GLint maxAttributeSize = 0;
    ::glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxAttributeSize);
    CHECK_GL_ERROR();
    GLchar name[maxAttributeSize]; // GL_ACTIVE_ATTRIBUTE_MAX_LENGTH includes null termination.
    GLsizei nameLength = 0;
    GLint size = 0;
    GLenum type = 0;
    ::glGetActiveAttrib(program, index, maxAttributeSize, &nameLength, &size, &type, name);
    CHECK_GL_ERROR();
    if (!nameLength)
        return false;
    info.name = String(name, nameLength);
    info.type = type;
    info.size = size;
    return true;
}

bool GraphicsContext3D::getActiveUniform(Platform3DObject program, GC3Duint index, ActiveInfo& info)
{
    if (!program) {
        synthesizeGLErrorWrapper(INVALID_VALUE);
        return false;
    }

    makeContextCurrent();
    GLint maxUniformSize = 0;
    ::glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformSize);
    CHECK_GL_ERROR();

    OwnArrayPtr<GLchar> name = adoptArrayPtr(new GLchar[maxUniformSize]); // GL_ACTIVE_UNIFORM_MAX_LENGTH includes null termination.
    GLsizei nameLength = 0;
    GLint size = 0;
    GLenum type = 0;
    ::glGetActiveUniform(program, index, maxUniformSize, &nameLength, &size, &type, name.get());
    CHECK_GL_ERROR();
    if (!nameLength)
        return false;

    info.name = String(name.get(), nameLength);
    info.type = type;
    info.size = size;

    return true;
}
    
void GraphicsContext3D::getAttachedShaders(Platform3DObject program, GC3Dsizei maxCount, GC3Dsizei* count, Platform3DObject* shaders)
{
    if (!program) {
        synthesizeGLErrorWrapper(INVALID_VALUE);
        return;
    }
    makeContextCurrent();
    ::glGetAttachedShaders(program, maxCount, count, shaders);
    CHECK_GL_ERROR();
}

int GraphicsContext3D::getAttribLocation(Platform3DObject program, const String& name)
{
    if (!program)
        return -1;

    makeContextCurrent();
    int ret = ::glGetAttribLocation(program, name.utf8().data());
    CHECK_GL_ERROR();
    return ret;
}

GraphicsContext3D::Attributes GraphicsContext3D::getContextAttributes()
{
    return m_attrs;
}

GC3Denum GraphicsContext3D::getError()
{
    if (m_syntheticErrors.size() > 0) {
        ListHashSet<GC3Denum>::iterator iter = m_syntheticErrors.begin();
        GC3Denum err = *iter;
        m_syntheticErrors.remove(iter);
        return err;
    }

    makeContextCurrent();
    return ::glGetError();
}

String GraphicsContext3D::getString(GC3Denum name)
{
    makeContextCurrent();
    const GLubyte* s = ::glGetString(name);
    CHECK_GL_ERROR();
    return String(reinterpret_cast<const char*>(s));
}

void GraphicsContext3D::hint(GC3Denum target, GC3Denum mode)
{
    makeContextCurrent();
    ::glHint(target, mode);
    CHECK_GL_ERROR();
}

GC3Dboolean GraphicsContext3D::isBuffer(Platform3DObject buffer)
{
    if (!buffer)
        return GL_FALSE;

    makeContextCurrent();
    GC3Dboolean ret = ::glIsBuffer(buffer);
    CHECK_GL_ERROR();
    return ret;
}

GC3Dboolean GraphicsContext3D::isEnabled(GC3Denum cap)
{
    makeContextCurrent();
    GC3Dboolean ret = ::glIsEnabled(cap);
    CHECK_GL_ERROR();
    return ret;
}

GC3Dboolean GraphicsContext3D::isFramebuffer(Platform3DObject framebuffer)
{
    if (!framebuffer)
        return GL_FALSE;

    makeContextCurrent();
    GC3Dboolean ret = ::glIsFramebufferEXT(framebuffer);
    CHECK_GL_ERROR();
    return ret;
}

GC3Dboolean GraphicsContext3D::isProgram(Platform3DObject program)
{
    if (!program)
        return GL_FALSE;

    makeContextCurrent();
    GC3Dboolean ret = ::glIsProgram(program);
    CHECK_GL_ERROR();
    return ret;
}

GC3Dboolean GraphicsContext3D::isRenderbuffer(Platform3DObject renderbuffer)
{
    if (!renderbuffer)
        return GL_FALSE;

    makeContextCurrent();
    GC3Dboolean ret = ::glIsRenderbufferEXT(renderbuffer);
    CHECK_GL_ERROR();
    return ret;
}

GC3Dboolean GraphicsContext3D::isShader(Platform3DObject shader)
{
    if (!shader)
        return GL_FALSE;

    makeContextCurrent();
    GC3Dboolean ret = ::glIsShader(shader);
    CHECK_GL_ERROR();
    return ret;
}

GC3Dboolean GraphicsContext3D::isTexture(Platform3DObject texture)
{
    if (!texture)
        return GL_FALSE;

    makeContextCurrent();
    GC3Dboolean ret = ::glIsTexture(texture);
    CHECK_GL_ERROR();
    return ret;
}

void GraphicsContext3D::lineWidth(GC3Dfloat width)
{
    makeContextCurrent();
    ::glLineWidth(width);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::linkProgram(Platform3DObject program)
{
    ASSERT(program);
    makeContextCurrent();
    ::glLinkProgram(program);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::pixelStorei(GC3Denum pname, GC3Dint param)
{
    makeContextCurrent();
    ::glPixelStorei(pname, param);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::polygonOffset(GC3Dfloat factor, GC3Dfloat units)
{
    makeContextCurrent();
    ::glPolygonOffset(factor, units);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::sampleCoverage(GC3Dclampf value, GC3Dboolean invert)
{
    makeContextCurrent();
    ::glSampleCoverage(value, invert);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::scissor(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height)
{
    makeContextCurrent();
    ::glScissor(x, y, width, height);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::shaderSource(Platform3DObject shader, const String& string)
{
    ASSERT(shader);

    makeContextCurrent();

    ShaderSourceEntry entry;

    entry.source = string;

    m_shaderSourceMap.set(shader, entry);
}

void GraphicsContext3D::stencilFunc(GC3Denum func, GC3Dint ref, GC3Duint mask)
{
    makeContextCurrent();
    ::glStencilFunc(func, ref, mask);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::stencilFuncSeparate(GC3Denum face, GC3Denum func, GC3Dint ref, GC3Duint mask)
{
    makeContextCurrent();
    ::glStencilFuncSeparate(face, func, ref, mask);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::stencilMask(GC3Duint mask)
{
    makeContextCurrent();
    ::glStencilMask(mask);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::stencilMaskSeparate(GC3Denum face, GC3Duint mask)
{
    makeContextCurrent();
    ::glStencilMaskSeparate(face, mask);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::stencilOp(GC3Denum fail, GC3Denum zfail, GC3Denum zpass)
{
    makeContextCurrent();
    ::glStencilOp(fail, zfail, zpass);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::stencilOpSeparate(GC3Denum face, GC3Denum fail, GC3Denum zfail, GC3Denum zpass)
{
    makeContextCurrent();
    ::glStencilOpSeparate(face, fail, zfail, zpass);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::texParameterf(GC3Denum target, GC3Denum pname, GC3Dfloat value)
{
    makeContextCurrent();
    ::glTexParameterf(target, pname, value);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::texParameteri(GC3Denum target, GC3Denum pname, GC3Dint value)
{
    makeContextCurrent();
    ::glTexParameteri(target, pname, value);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform1f(GC3Dint location, GC3Dfloat v0)
{
    makeContextCurrent();
    ::glUniform1f(location, v0);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform1fv(GC3Dint location, GC3Dsizei size, GC3Dfloat* array)
{
    makeContextCurrent();
    ::glUniform1fv(location, size, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform2f(GC3Dint location, GC3Dfloat v0, GC3Dfloat v1)
{
    makeContextCurrent();
    ::glUniform2f(location, v0, v1);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform2fv(GC3Dint location, GC3Dsizei size, GC3Dfloat* array)
{
    // FIXME: length needs to be a multiple of 2.
    makeContextCurrent();
    ::glUniform2fv(location, size, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform3f(GC3Dint location, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2)
{
    makeContextCurrent();
    ::glUniform3f(location, v0, v1, v2);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform3fv(GC3Dint location, GC3Dsizei size, GC3Dfloat* array)
{
    // FIXME: length needs to be a multiple of 3.
    makeContextCurrent();
    ::glUniform3fv(location, size, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform4f(GC3Dint location, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2, GC3Dfloat v3)
{
    makeContextCurrent();
    ::glUniform4f(location, v0, v1, v2, v3);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform4fv(GC3Dint location, GC3Dsizei size, GC3Dfloat* array)
{
    // FIXME: length needs to be a multiple of 4.
    makeContextCurrent();
    ::glUniform4fv(location, size, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform1i(GC3Dint location, GC3Dint v0)
{
    makeContextCurrent();
    ::glUniform1i(location, v0);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform1iv(GC3Dint location, GC3Dsizei size, GC3Dint* array)
{
    makeContextCurrent();
    ::glUniform1iv(location, size, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform2i(GC3Dint location, GC3Dint v0, GC3Dint v1)
{
    makeContextCurrent();
    ::glUniform2i(location, v0, v1);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform2iv(GC3Dint location, GC3Dsizei size, GC3Dint* array)
{
    // FIXME: length needs to be a multiple of 2.
    makeContextCurrent();
    ::glUniform2iv(location, size, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform3i(GC3Dint location, GC3Dint v0, GC3Dint v1, GC3Dint v2)
{
    makeContextCurrent();
    ::glUniform3i(location, v0, v1, v2);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform3iv(GC3Dint location, GC3Dsizei size, GC3Dint* array)
{
    // FIXME: length needs to be a multiple of 3.
    makeContextCurrent();
    ::glUniform3iv(location, size, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform4i(GC3Dint location, GC3Dint v0, GC3Dint v1, GC3Dint v2, GC3Dint v3)
{
    makeContextCurrent();
    ::glUniform4i(location, v0, v1, v2, v3);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniform4iv(GC3Dint location, GC3Dsizei size, GC3Dint* array)
{
    // FIXME: length needs to be a multiple of 4.
    makeContextCurrent();
    ::glUniform4iv(location, size, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniformMatrix2fv(GC3Dint location, GC3Dsizei size, GC3Dboolean transpose, GC3Dfloat* array)
{
    // FIXME: length needs to be a multiple of 4.
    makeContextCurrent();
    ::glUniformMatrix2fv(location, size, transpose, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniformMatrix3fv(GC3Dint location, GC3Dsizei size, GC3Dboolean transpose, GC3Dfloat* array)
{
    // FIXME: length needs to be a multiple of 9.
    makeContextCurrent();
    ::glUniformMatrix3fv(location, size, transpose, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::uniformMatrix4fv(GC3Dint location, GC3Dsizei size, GC3Dboolean transpose, GC3Dfloat* array)
{
    // FIXME: length needs to be a multiple of 16.
    makeContextCurrent();
    ::glUniformMatrix4fv(location, size, transpose, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::useProgram(Platform3DObject program)
{
    makeContextCurrent();
    ::glUseProgram(program);
    CHECK_GL_ERROR();

    m_private->didCallUseProgram();
}

void GraphicsContext3D::validateProgram(Platform3DObject program)
{
    ASSERT(program);

    makeContextCurrent();
    ::glValidateProgram(program);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::vertexAttrib1f(GC3Duint index, GC3Dfloat v0)
{
    makeContextCurrent();
    ::glVertexAttrib1f(index, v0);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::vertexAttrib1fv(GC3Duint index, GC3Dfloat* array)
{
    makeContextCurrent();
    ::glVertexAttrib1fv(index, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::vertexAttrib2f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1)
{
    makeContextCurrent();
    ::glVertexAttrib2f(index, v0, v1);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::vertexAttrib2fv(GC3Duint index, GC3Dfloat* array)
{
    makeContextCurrent();
    ::glVertexAttrib2fv(index, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::vertexAttrib3f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2)
{
    makeContextCurrent();
    ::glVertexAttrib3f(index, v0, v1, v2);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::vertexAttrib3fv(GC3Duint index, GC3Dfloat* array)
{
    makeContextCurrent();
    ::glVertexAttrib3fv(index, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::vertexAttrib4f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2, GC3Dfloat v3)
{
    makeContextCurrent();
    ::glVertexAttrib4f(index, v0, v1, v2, v3);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::vertexAttrib4fv(GC3Duint index, GC3Dfloat* array)
{
    makeContextCurrent();
    ::glVertexAttrib4fv(index, array);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::vertexAttribPointer(GC3Duint index, GC3Dint size, GC3Denum type, GC3Dboolean normalized, GC3Dsizei stride, GC3Dintptr offset)
{
    makeContextCurrent();
    ::glVertexAttribPointer(index, size, type, normalized, stride, reinterpret_cast<GLvoid*>(static_cast<intptr_t>(offset)));
    CHECK_GL_ERROR();
}

void GraphicsContext3D::viewport(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height)
{
    makeContextCurrent();

    if(isDirectGLMode()){
    	IntSize size = m_private->defaultFrameBufferSize();
    	x = 0;
    	y = 0;
    	width = size.width();
    	height = size.height();
    }

    ::glViewport(x, y, width, height);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::getBooleanv(GC3Denum pname, GC3Dboolean* value)
{
    makeContextCurrent();
    ::glGetBooleanv(pname, value);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::getBufferParameteriv(GC3Denum target, GC3Denum pname, GC3Dint* value)
{
    makeContextCurrent();
    ::glGetBufferParameteriv(target, pname, value);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::getFloatv(GC3Denum pname, GC3Dfloat* value)
{
    makeContextCurrent();
    ::glGetFloatv(pname, value);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::getFramebufferAttachmentParameteriv(GC3Denum target, GC3Denum attachment, GC3Denum pname, GC3Dint* value)
{
    makeContextCurrent();
    if (attachment == DEPTH_STENCIL_ATTACHMENT)
        attachment = DEPTH_ATTACHMENT; // Or STENCIL_ATTACHMENT, either works.
    ::glGetFramebufferAttachmentParameterivEXT(target, attachment, pname, value);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::getProgramiv(Platform3DObject program, GC3Denum pname, GC3Dint* value)
{
    makeContextCurrent();
    ::glGetProgramiv(program, pname, value);
    CHECK_GL_ERROR();
}

String GraphicsContext3D::getProgramInfoLog(Platform3DObject program)
{
    ASSERT(program);

    makeContextCurrent();
    GLint length = 0;
    ::glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
    CHECK_GL_ERROR();
    if (!length)
        return String(); 

    GLsizei size = 0;
    OwnArrayPtr<GLchar> info = adoptArrayPtr(new GLchar[length]);
    ::glGetProgramInfoLog(program, length, &size, info.get());
    CHECK_GL_ERROR();

    return String(info.get());
}

void GraphicsContext3D::getRenderbufferParameteriv(GC3Denum target, GC3Denum pname, GC3Dint* value)
{
    makeContextCurrent();
    ::glGetRenderbufferParameterivEXT(target, pname, value);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::getShaderiv(Platform3DObject shader, GC3Denum pname, GC3Dint* value)
{
    ASSERT(shader);

    makeContextCurrent();

    HashMap<Platform3DObject, ShaderSourceEntry>::iterator result = m_shaderSourceMap.find(shader);
    
    switch (pname) {
    case DELETE_STATUS:
    case SHADER_TYPE:
        ::glGetShaderiv(shader, pname, value);
        CHECK_GL_ERROR();
        break;
    case COMPILE_STATUS:
        if (result == m_shaderSourceMap.end()) {
            *value = static_cast<int>(false);
            return;
        }
        *value = static_cast<int>(result->second.isValid);
        break;
    case INFO_LOG_LENGTH:
        if (result == m_shaderSourceMap.end()) {
            *value = 0;
            return;
        }
        *value = getShaderInfoLog(shader).length();
        break;
    case SHADER_SOURCE_LENGTH:
        *value = getShaderSource(shader).length();
        break;
    default:
        synthesizeGLErrorWrapper(INVALID_ENUM);
    }
}

String GraphicsContext3D::getShaderInfoLog(Platform3DObject shader)
{
    ASSERT(shader);

    makeContextCurrent();

    HashMap<Platform3DObject, ShaderSourceEntry>::iterator result = m_shaderSourceMap.find(shader);
    if (result == m_shaderSourceMap.end())
        return String(); 

    ShaderSourceEntry entry = result->second;
    if (!entry.isValid)
        return entry.log;

    GLint length = 0;
    ::glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
    CHECK_GL_ERROR();
    if (!length)
        return String(); 

    GLsizei size = 0;
    OwnArrayPtr<GLchar> info = adoptArrayPtr(new GLchar[length]);
    ::glGetShaderInfoLog(shader, length, &size, info.get());
    CHECK_GL_ERROR();

    return String(info.get());
}

String GraphicsContext3D::getShaderSource(Platform3DObject shader)
{
    ASSERT(shader);

    makeContextCurrent();

    HashMap<Platform3DObject, ShaderSourceEntry>::iterator result = m_shaderSourceMap.find(shader);
    if (result == m_shaderSourceMap.end())
        return String(); 

    return result->second.source;
}


void GraphicsContext3D::getTexParameterfv(GC3Denum target, GC3Denum pname, GC3Dfloat* value)
{
    makeContextCurrent();
    ::glGetTexParameterfv(target, pname, value);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::getTexParameteriv(GC3Denum target, GC3Denum pname, GC3Dint* value)
{
    makeContextCurrent();
    ::glGetTexParameteriv(target, pname, value);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::getUniformfv(Platform3DObject program, GC3Dint location, GC3Dfloat* value)
{
    makeContextCurrent();
    ::glGetUniformfv(program, location, value);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::getUniformiv(Platform3DObject program, GC3Dint location, GC3Dint* value)
{
    makeContextCurrent();
    ::glGetUniformiv(program, location, value);
    CHECK_GL_ERROR();
}

GC3Dint GraphicsContext3D::getUniformLocation(Platform3DObject program, const String& name)
{
    ASSERT(program);

    makeContextCurrent();
    GC3Dint ret = ::glGetUniformLocation(program, name.utf8().data());
    CHECK_GL_ERROR();
    return ret;
}

void GraphicsContext3D::getVertexAttribfv(GC3Duint index, GC3Denum pname, GC3Dfloat* value)
{
    makeContextCurrent();
    ::glGetVertexAttribfv(index, pname, value);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::getVertexAttribiv(GC3Duint index, GC3Denum pname, GC3Dint* value)
{
    makeContextCurrent();
    ::glGetVertexAttribiv(index, pname, value);
    CHECK_GL_ERROR();
}

GC3Dsizeiptr GraphicsContext3D::getVertexAttribOffset(GC3Duint index, GC3Denum pname)
{
    makeContextCurrent();

    GLvoid* pointer = 0;
    ::glGetVertexAttribPointerv(index, pname, &pointer);
    CHECK_GL_ERROR();
    return static_cast<GC3Dsizeiptr>(reinterpret_cast<intptr_t>(pointer));
}

void GraphicsContext3D::texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoff, GC3Dint yoff, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, const void* pixels)
{
    makeContextCurrent();

    // FIXME: we will need to deal with PixelStore params when dealing with image buffers that differ from the subimage size.
    ::glTexSubImage2D(target, level, xoff, yoff, width, height, format, type, pixels);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::compressedTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Dsizei imageSize, const void* data)
{
    makeContextCurrent();
    ::glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data);
    CHECK_GL_ERROR();
}

void GraphicsContext3D::compressedTexSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Dsizei imageSize, const void* data)
{
    makeContextCurrent();
    ::glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data);
    CHECK_GL_ERROR();
}

Platform3DObject GraphicsContext3D::createBuffer()
{
    makeContextCurrent();

    if(m_cachedGLBuffers.size())
	    return m_cachedGLBuffers.takeLast();

    GLuint o = 0;
    glGenBuffers(1, &o);
    return o;
}

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

Platform3DObject GraphicsContext3D::createProgram()
{
    makeContextCurrent();
    return glCreateProgram();
}

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

Platform3DObject GraphicsContext3D::createShader(GC3Denum type)
{
    makeContextCurrent();
    return glCreateShader((type == FRAGMENT_SHADER) ? GL_FRAGMENT_SHADER : GL_VERTEX_SHADER);
}

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

void GraphicsContext3D::deleteBuffer(Platform3DObject buffer)
{
    makeContextCurrent();

    if(m_cachedGLBuffers.size() < m_private->cachedBufferNumber())
    	m_cachedGLBuffers.append(buffer);
    else
    	glDeleteBuffers(1, &buffer);
}

void GraphicsContext3D::deleteFramebuffer(Platform3DObject framebuffer)
{
    makeContextCurrent();
    glDeleteFramebuffersEXT(1, &framebuffer);
}

void GraphicsContext3D::deleteProgram(Platform3DObject program)
{
    makeContextCurrent();
    glDeleteProgram(program);
}

void GraphicsContext3D::deleteRenderbuffer(Platform3DObject renderbuffer)
{
    makeContextCurrent();
    glDeleteRenderbuffersEXT(1, &renderbuffer);
}

void GraphicsContext3D::deleteShader(Platform3DObject shader)
{
    makeContextCurrent();
    glDeleteShader(shader);
}

void GraphicsContext3D::deleteTexture(Platform3DObject texture)
{
    makeContextCurrent();
    glDeleteTextures(1, &texture);
}

void GraphicsContext3D::synthesizeGLError(GC3Denum error)
{
    m_syntheticErrors.add(error);
}

void GraphicsContext3D::markContextChanged()
{
    m_layerComposited = false;

    m_private->markContextChanged();
}

/*
 * The below code is merged from GraphicsContext3DCairo to share with OpenVG
 */

PassRefPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3D::Attributes attributes, HostWindow* hostWindow, bool isDirectGLMode, GraphicsContext3D::RenderStyle renderStyle)
{
    // This implementation doesn't currently support rendering directly to the HostWindow.
    if (renderStyle == RenderDirectlyToHostWindow)
        return nullptr;

#if !USE(OPENGL_ES_2)
    static bool initialized = false;
    static bool success = true;
    if (!initialized) {
        success = initializeOpenGLShims();
        initialized = true;
    }
    if (!success)
        return nullptr;
#endif

    RefPtr<GraphicsContext3D> context = adoptRef(new GraphicsContext3D(attributes, hostWindow, isDirectGLMode, false));
    return context.release();
}

GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attributes, HostWindow*, bool isDirectGLMode, bool)
: m_currentWidth(0)
, m_currentHeight(0)
, m_attrs(attributes)
, m_texture(0)
#if !PLATFORM(BLACKBERRY)
, m_compositorTexture(0)
#endif
, m_fbo(0)
#if !PLATFORM(BLACKBERRY)
, m_depthBuffer(0)
, m_stencilBuffer(0)
#endif
, m_depthStencilBuffer(0)
, m_isGLES2Compliant(false)
, m_isDirectGLMode(isDirectGLMode)
, m_boundFBO(0)
, m_activeTexture(GL_TEXTURE0)
, m_boundTexture0(0)
, m_multisampleFBO(0)
, m_multisampleDepthStencilBuffer(0)
, m_multisampleColorBuffer(0)
, m_private(GraphicsContext3DPrivate::create(this, isDirectGLMode))
{
    makeContextCurrent();

    validateAttributes();

    m_isGLES2Compliant = m_private->isGLES2Compliant();

    if(!m_isDirectGLMode){
    	// Create a texture to render into.
		::glGenTextures(1, &m_texture);
		::glBindTexture(GL_TEXTURE_2D, m_texture);
	    CHECK_GL_ERROR();
		::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
		::glBindTexture(GL_TEXTURE_2D, 0);
		CHECK_GL_ERROR();

	    // Create an FBO.
    	::glGenFramebuffersEXT(1, &m_fbo);
    }

    ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
    CHECK_GL_ERROR();

    m_boundFBO = m_fbo;
    if (!m_attrs.antialias && (m_attrs.stencil || m_attrs.depth))
        ::glGenRenderbuffersEXT(1, &m_depthStencilBuffer);
    CHECK_GL_ERROR();
    
    // Create a multisample FBO.
    if (m_attrs.antialias) {
        ::glGenFramebuffersEXT(1, &m_multisampleFBO);
        ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_multisampleFBO);
        m_boundFBO = m_multisampleFBO;
        ::glGenRenderbuffersEXT(1, &m_multisampleColorBuffer);
        if (m_attrs.stencil || m_attrs.depth)
            ::glGenRenderbuffersEXT(1, &m_multisampleDepthStencilBuffer);
        CHECK_GL_ERROR();
    }

    // ANGLE initialization.
    ShBuiltInResources ANGLEResources;
    ShInitBuiltInResources(&ANGLEResources);

    getIntegerv(GraphicsContext3D::MAX_VERTEX_ATTRIBS, &ANGLEResources.MaxVertexAttribs);
    getIntegerv(GraphicsContext3D::MAX_VERTEX_UNIFORM_VECTORS, &ANGLEResources.MaxVertexUniformVectors);
    getIntegerv(GraphicsContext3D::MAX_VARYING_VECTORS, &ANGLEResources.MaxVaryingVectors);
    getIntegerv(GraphicsContext3D::MAX_VERTEX_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxVertexTextureImageUnits);
    getIntegerv(GraphicsContext3D::MAX_COMBINED_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxCombinedTextureImageUnits);
    getIntegerv(GraphicsContext3D::MAX_TEXTURE_IMAGE_UNITS, &ANGLEResources.MaxTextureImageUnits);
    getIntegerv(GraphicsContext3D::MAX_FRAGMENT_UNIFORM_VECTORS, &ANGLEResources.MaxFragmentUniformVectors);

    // Always set to 1 for OpenGL ES.
    ANGLEResources.MaxDrawBuffers = 1;
    m_compiler.setResources(ANGLEResources);

#if !USE(OPENGL_ES_2)
    ::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
    ::glEnable(GL_POINT_SPRITE);
#endif
    ::glClearColor(0, 0, 0, 0);
    CHECK_GL_ERROR();

    if(m_private->bufferDataOptimizedSize() > 0)
    	m_optimizedDataBuffer.resize(m_private->bufferDataOptimizedSize());
}

GraphicsContext3D::~GraphicsContext3D()
{
    makeContextCurrent();

    while(m_cachedGLBuffers.size()){
	    Platform3DObject buffer = m_cachedGLBuffers.takeLast();
	    glDeleteBuffers(1, &buffer);
    }

    if(m_texture)
    	::glDeleteTextures(1, &m_texture);

    if(m_compositorTexture)
        ::glDeleteTextures(1, &m_compositorTexture);

    if (m_attrs.antialias) {
        ::glDeleteRenderbuffersEXT(1, &m_multisampleColorBuffer);
        if (m_attrs.stencil || m_attrs.depth)
            ::glDeleteRenderbuffersEXT(1, &m_multisampleDepthStencilBuffer);
        ::glDeleteFramebuffersEXT(1, &m_multisampleFBO);
    } else {
        if (m_attrs.stencil || m_attrs.depth)
            ::glDeleteRenderbuffersEXT(1, &m_depthStencilBuffer);
    }

    if(isDirectGLMode()){
        if(m_fbo != 0)
        	::glDeleteFramebuffersEXT(1, &m_fbo);
    }
    else
    	::glDeleteFramebuffersEXT(1, &m_fbo);
}

void GraphicsContext3D::setContextLostCallback(PassOwnPtr<ContextLostCallback>)
{
}

void GraphicsContext3D::setErrorMessageCallback(PassOwnPtr<ErrorMessageCallback>)
{
}

PlatformGraphicsContext3D GraphicsContext3D::platformGraphicsContext3D()
{
    return m_private->platformContext();
}

#if USE(ACCELERATED_COMPOSITING)
PlatformLayer* GraphicsContext3D::platformLayer() const
{
    return m_private.get();
}
#endif


}

#endif // USE(3D_GRAPHICS)
