C++程序  |  2219行  |  71.75 KB

/*
 * Copyright (C) 2009 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "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 THE COPYRIGHT
 * OWNER 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 ENABLE(3D_CANVAS)

#include "GraphicsContext3D.h"

#include "CachedImage.h"
#include "CString.h"
#include "HTMLCanvasElement.h"
#include "HTMLImageElement.h"
#include "ImageBuffer.h"
#include "ImageData.h"
#include "NotImplemented.h"
#include "WebGLBuffer.h"
#include "WebGLByteArray.h"
#include "WebGLFloatArray.h"
#include "WebGLFramebuffer.h"
#include "WebGLIntArray.h"
#include "WebGLProgram.h"
#include "WebGLRenderbuffer.h"
#include "WebGLRenderingContext.h"
#include "WebGLShader.h"
#include "WebGLTexture.h"
#include "WebGLUnsignedByteArray.h"

#include <stdio.h>
#include <wtf/FastMalloc.h>

#if OS(WINDOWS)
#include <windows.h>
#endif

#include "GL/glew.h"

#if PLATFORM(CG)
#include "GraphicsContext.h"
#include <CoreGraphics/CGContext.h>
#include <CoreGraphics/CGBitmapContext.h>
#include <CoreGraphics/CGImage.h>
#include <OpenGL/OpenGL.h>
#else
#define FLIP_FRAMEBUFFER_VERTICALLY
#endif

#if PLATFORM(SKIA)
#include "NativeImageSkia.h"
#endif

#if OS(DARWIN)
#define USE_TEXTURE_RECTANGLE_FOR_FRAMEBUFFER
#endif

#if OS(LINUX)
#include <dlfcn.h>
#include "GL/glxew.h"
#endif

using namespace std;

namespace WebCore {

// GraphicsContext3DInternal -----------------------------------------------------

// Uncomment this to render to a separate window for debugging
// #define RENDER_TO_DEBUGGING_WINDOW

#define EXTRACT(val) (!val ? 0 : val->object())

class GraphicsContext3DInternal {
public:
    GraphicsContext3DInternal(GraphicsContext3D::Attributes attrs);
    ~GraphicsContext3DInternal();

    bool makeContextCurrent();

    PlatformGraphicsContext3D platformGraphicsContext3D() const;
    Platform3DObject platformTexture() const;

    void reshape(int width, int height);

    void beginPaint(WebGLRenderingContext* context);

    bool validateTextureTarget(int target);
    bool validateTextureParameter(int param);

    void activeTexture(unsigned long texture);
    void bindBuffer(unsigned long target,
                    WebGLBuffer* buffer);
    void bindFramebuffer(unsigned long target,
                         WebGLFramebuffer* framebuffer);
    void bindTexture(unsigned long target,
                     WebGLTexture* texture);
    void bufferDataImpl(unsigned long target, int size, const void* data, unsigned long usage);
    void disableVertexAttribArray(unsigned long index);
    void enableVertexAttribArray(unsigned long index);
    unsigned long getError();
    GraphicsContext3D::Attributes getContextAttributes();
    void vertexAttribPointer(unsigned long indx, int size, int type, bool normalized,
                             unsigned long stride, unsigned long offset);
    void viewportImpl(long x, long y, unsigned long width, unsigned long height);

    void synthesizeGLError(unsigned long error);

private:
    GraphicsContext3D::Attributes m_attrs;

    unsigned int m_texture;
    unsigned int m_fbo;
    unsigned int m_depthBuffer;
    unsigned int m_cachedWidth, m_cachedHeight;

    // For tracking which FBO is bound
    unsigned int m_boundFBO;

#ifdef FLIP_FRAMEBUFFER_VERTICALLY
    unsigned char* m_scanline;
    void flipVertically(unsigned char* framebuffer,
                        unsigned int width,
                        unsigned int height);
#endif

    // Note: we aren't currently using this information, but we will
    // need to in order to verify that all enabled vertex arrays have
    // a valid buffer bound -- to avoid crashes on certain cards.
    unsigned int m_boundArrayBuffer;
    class VertexAttribPointerState {
    public:
        VertexAttribPointerState();

        bool enabled;
        unsigned long buffer;
        unsigned long indx;
        int size;
        int type;
        bool normalized;
        unsigned long stride;
        unsigned long offset;
    };

    enum {
        NumTrackedPointerStates = 2
    };
    VertexAttribPointerState m_vertexAttribPointerState[NumTrackedPointerStates];

    // Errors raised by synthesizeGLError().
    ListHashSet<unsigned long> m_syntheticErrors;

#if PLATFORM(SKIA)
    // If the width and height of the Canvas's backing store don't
    // match those that we were given in the most recent call to
    // reshape(), then we need an intermediate bitmap to read back the
    // frame buffer into. This seems to happen when CSS styles are
    // used to resize the Canvas.
    SkBitmap* m_resizingBitmap;
#endif

    static bool s_initializedGLEW;
#if OS(WINDOWS)
    HWND  m_canvasWindow;
    HDC   m_canvasDC;
    HGLRC m_contextObj;
#elif PLATFORM(CG)
    CGLPBufferObj m_pbuffer;
    CGLContextObj m_contextObj;
    unsigned char* m_renderOutput;
#elif OS(LINUX)
    GLXContext m_contextObj;
    GLXPbuffer m_pbuffer;

    // In order to avoid problems caused by linking against libGL, we
    // dynamically look up all the symbols we need.
    // http://code.google.com/p/chromium/issues/detail?id=16800
    class GLConnection {
      public:
        ~GLConnection();

        static GLConnection* create();

        GLXFBConfig* chooseFBConfig(int screen, const int *attrib_list, int *nelements)
        {
            return m_glXChooseFBConfig(m_display, screen, attrib_list, nelements);
        }

        GLXContext createNewContext(GLXFBConfig config, int renderType, GLXContext shareList, Bool direct)
        {
            return m_glXCreateNewContext(m_display, config, renderType, shareList, direct);
        }

        GLXPbuffer createPbuffer(GLXFBConfig config, const int *attribList)
        {
            return m_glXCreatePbuffer(m_display, config, attribList);
        }

        void destroyPbuffer(GLXPbuffer pbuf)
        {
            m_glXDestroyPbuffer(m_display, pbuf);
        }

        Bool makeCurrent(GLXDrawable drawable, GLXContext ctx)
        {
            return m_glXMakeCurrent(m_display, drawable, ctx);
        }

        void destroyContext(GLXContext ctx)
        {
            m_glXDestroyContext(m_display, ctx);
        }

        GLXContext getCurrentContext()
        {
            return m_glXGetCurrentContext();
        }

      private:
        Display* m_display;
        void* m_libGL;
        PFNGLXCHOOSEFBCONFIGPROC m_glXChooseFBConfig;
        PFNGLXCREATENEWCONTEXTPROC m_glXCreateNewContext;
        PFNGLXCREATEPBUFFERPROC m_glXCreatePbuffer;
        PFNGLXDESTROYPBUFFERPROC m_glXDestroyPbuffer;
        typedef Bool (* PFNGLXMAKECURRENTPROC)(Display* dpy, GLXDrawable drawable, GLXContext ctx);
        PFNGLXMAKECURRENTPROC m_glXMakeCurrent;
        typedef void (* PFNGLXDESTROYCONTEXTPROC)(Display* dpy, GLXContext ctx);
        PFNGLXDESTROYCONTEXTPROC m_glXDestroyContext;
        typedef GLXContext (* PFNGLXGETCURRENTCONTEXTPROC)(void);
        PFNGLXGETCURRENTCONTEXTPROC m_glXGetCurrentContext;

        GLConnection(Display* display,
                     void* libGL,
                     PFNGLXCHOOSEFBCONFIGPROC chooseFBConfig,
                     PFNGLXCREATENEWCONTEXTPROC createNewContext,
                     PFNGLXCREATEPBUFFERPROC createPbuffer,
                     PFNGLXDESTROYPBUFFERPROC destroyPbuffer,
                     PFNGLXMAKECURRENTPROC makeCurrent,
                     PFNGLXDESTROYCONTEXTPROC destroyContext,
                     PFNGLXGETCURRENTCONTEXTPROC getCurrentContext)
            : m_libGL(libGL)
            , m_display(display)
            , m_glXChooseFBConfig(chooseFBConfig)
            , m_glXCreateNewContext(createNewContext)
            , m_glXCreatePbuffer(createPbuffer)
            , m_glXDestroyPbuffer(destroyPbuffer)
            , m_glXMakeCurrent(makeCurrent)
            , m_glXDestroyContext(destroyContext)
            , m_glXGetCurrentContext(getCurrentContext)
        {
        }
    };

    static GLConnection* s_gl;
#else
    #error Must port GraphicsContext3D to your platform
#endif
};

bool GraphicsContext3DInternal::s_initializedGLEW = false;

#if OS(LINUX)
GraphicsContext3DInternal::GLConnection* GraphicsContext3DInternal::s_gl = 0;

GraphicsContext3DInternal::GLConnection* GraphicsContext3DInternal::GLConnection::create()
{
    Display* dpy = XOpenDisplay(0);
    if (!dpy) {
        printf("GraphicsContext3D: error opening X display\n");
        return 0;
    }

    // We use RTLD_GLOBAL semantics so that GLEW initialization works;
    // GLEW expects to be able to open the current process's handle
    // and do dlsym's of GL entry points from there.
    void* libGL = dlopen("libGL.so.1", RTLD_LAZY | RTLD_GLOBAL);
    if (!libGL) {
        XCloseDisplay(dpy);
        printf("GraphicsContext3D: error opening libGL.so.1: %s\n", dlerror());
        return 0;
    }

    PFNGLXCHOOSEFBCONFIGPROC chooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC) dlsym(libGL, "glXChooseFBConfig");
    PFNGLXCREATENEWCONTEXTPROC createNewContext = (PFNGLXCREATENEWCONTEXTPROC) dlsym(libGL, "glXCreateNewContext");
    PFNGLXCREATEPBUFFERPROC createPbuffer = (PFNGLXCREATEPBUFFERPROC) dlsym(libGL, "glXCreatePbuffer");
    PFNGLXDESTROYPBUFFERPROC destroyPbuffer = (PFNGLXDESTROYPBUFFERPROC) dlsym(libGL, "glXDestroyPbuffer");
    PFNGLXMAKECURRENTPROC makeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(libGL, "glXMakeCurrent");
    PFNGLXDESTROYCONTEXTPROC destroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(libGL, "glXDestroyContext");
    PFNGLXGETCURRENTCONTEXTPROC getCurrentContext = (PFNGLXGETCURRENTCONTEXTPROC) dlsym(libGL, "glXGetCurrentContext");
    if (!chooseFBConfig || !createNewContext || !createPbuffer
        || !destroyPbuffer || !makeCurrent || !destroyContext
        || !getCurrentContext) {
        XCloseDisplay(dpy);
        dlclose(libGL);
        printf("GraphicsContext3D: error looking up bootstrapping entry points\n");
        return 0;
    }
    return new GLConnection(dpy,
                            libGL,
                            chooseFBConfig,
                            createNewContext,
                            createPbuffer,
                            destroyPbuffer,
                            makeCurrent,
                            destroyContext,
                            getCurrentContext);
}

GraphicsContext3DInternal::GLConnection::~GLConnection()
{
    XCloseDisplay(m_display);
    dlclose(m_libGL);
}

#endif // OS(LINUX)

GraphicsContext3DInternal::VertexAttribPointerState::VertexAttribPointerState()
    : enabled(false)
    , buffer(0)
    , indx(0)
    , size(0)
    , type(0)
    , normalized(false)
    , stride(0)
    , offset(0)
{
}

GraphicsContext3DInternal::GraphicsContext3DInternal(GraphicsContext3D::Attributes attrs)
    : m_attrs(attrs)
    , m_texture(0)
    , m_fbo(0)
    , m_depthBuffer(0)
    , m_boundFBO(0)
#ifdef FLIP_FRAMEBUFFER_VERTICALLY
    , m_scanline(0)
#endif
    , m_boundArrayBuffer(0)
#if PLATFORM(SKIA)
    , m_resizingBitmap(0)
#endif
#if OS(WINDOWS)
    , m_canvasWindow(0)
    , m_canvasDC(0)
    , m_contextObj(0)
#elif PLATFORM(CG)
    , m_pbuffer(0)
    , m_contextObj(0)
    , m_renderOutput(0)
#elif OS(LINUX)
    , m_contextObj(0)
    , m_pbuffer(0)
#else
#error Must port to your platform
#endif
{
    // FIXME: we need to take into account the user's requested
    // context creation attributes, in particular stencil and
    // antialias, and determine which could and could not be honored
    // based on the capabilities of the OpenGL implementation.
    m_attrs.alpha = true;
    m_attrs.depth = true;
    m_attrs.stencil = false;
    m_attrs.antialias = false;
    m_attrs.premultipliedAlpha = true;

#if OS(WINDOWS)
    WNDCLASS wc;
    if (!GetClassInfo(GetModuleHandle(0), L"CANVASGL", &wc)) {
        ZeroMemory(&wc, sizeof(WNDCLASS));
        wc.style = CS_OWNDC;
        wc.hInstance = GetModuleHandle(0);
        wc.lpfnWndProc = DefWindowProc;
        wc.lpszClassName = L"CANVASGL";

        if (!RegisterClass(&wc)) {
            printf("GraphicsContext3D: RegisterClass failed\n");
            return;
        }
    }

    m_canvasWindow = CreateWindow(L"CANVASGL", L"CANVASGL",
                                  WS_CAPTION,
                                  CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
                                  CW_USEDEFAULT, 0, 0, GetModuleHandle(0), 0);
    if (!m_canvasWindow) {
        printf("GraphicsContext3DInternal: CreateWindow failed\n");
        return;
    }

    // get the device context
    m_canvasDC = GetDC(m_canvasWindow);
    if (!m_canvasDC) {
        printf("GraphicsContext3DInternal: GetDC failed\n");
        return;
    }

    // find default pixel format
    PIXELFORMATDESCRIPTOR pfd;
    ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR));
    pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL
#ifdef RENDER_TO_DEBUGGING_WINDOW
        | PFD_DOUBLEBUFFER
#endif // RENDER_TO_DEBUGGING_WINDOW
        ;
    int pixelformat = ChoosePixelFormat(m_canvasDC, &pfd);

    // set the pixel format for the dc
    if (!SetPixelFormat(m_canvasDC, pixelformat, &pfd)) {
        printf("GraphicsContext3D: SetPixelFormat failed\n");
        return;
    }

    // create rendering context
    m_contextObj = wglCreateContext(m_canvasDC);
    if (!m_contextObj) {
        printf("GraphicsContext3D: wglCreateContext failed\n");
        return;
    }

    if (!wglMakeCurrent(m_canvasDC, m_contextObj)) {
        printf("GraphicsContext3D: wglMakeCurrent failed\n");
        return;
    }

#ifdef RENDER_TO_DEBUGGING_WINDOW
    typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval);
    PFNWGLSWAPINTERVALEXTPROC setSwapInterval = 0;
    setSwapInterval = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT");
    if (setSwapInterval)
        setSwapInterval(1);
#endif // RENDER_TO_DEBUGGING_WINDOW

#elif PLATFORM(CG)
    // Create a 1x1 pbuffer and associated context to bootstrap things
    CGLPixelFormatAttribute attribs[] = {
        (CGLPixelFormatAttribute) kCGLPFAPBuffer,
        (CGLPixelFormatAttribute) 0
    };
    CGLPixelFormatObj pixelFormat;
    GLint numPixelFormats;
    if (CGLChoosePixelFormat(attribs, &pixelFormat, &numPixelFormats) != kCGLNoError) {
        printf("GraphicsContext3D: error choosing pixel format\n");
        return;
    }
    if (!pixelFormat) {
        printf("GraphicsContext3D: no pixel format selected\n");
        return;
    }
    CGLContextObj context;
    CGLError res = CGLCreateContext(pixelFormat, 0, &context);
    CGLDestroyPixelFormat(pixelFormat);
    if (res != kCGLNoError) {
        printf("GraphicsContext3D: error creating context\n");
        return;
    }
    CGLPBufferObj pbuffer;
    if (CGLCreatePBuffer(1, 1, GL_TEXTURE_2D, GL_RGBA, 0, &pbuffer) != kCGLNoError) {
        CGLDestroyContext(context);
        printf("GraphicsContext3D: error creating pbuffer\n");
        return;
    }
    if (CGLSetPBuffer(context, pbuffer, 0, 0, 0) != kCGLNoError) {
        CGLDestroyContext(context);
        CGLDestroyPBuffer(pbuffer);
        printf("GraphicsContext3D: error attaching pbuffer to context\n");
        return;
    }
    if (CGLSetCurrentContext(context) != kCGLNoError) {
        CGLDestroyContext(context);
        CGLDestroyPBuffer(pbuffer);
        printf("GraphicsContext3D: error making context current\n");
        return;
    }
    m_pbuffer = pbuffer;
    m_contextObj = context;
#elif OS(LINUX)
    if (!s_gl) {
        s_gl = GLConnection::create();
        if (!s_gl)
            return;
    }

    int configAttrs[] = {
        GLX_DRAWABLE_TYPE,
        GLX_PBUFFER_BIT,
        GLX_RENDER_TYPE,
        GLX_RGBA_BIT,
        GLX_DOUBLEBUFFER,
        0,
        0
    };
    int nelements = 0;
    GLXFBConfig* config = s_gl->chooseFBConfig(0, configAttrs, &nelements);
    if (!config) {
        printf("GraphicsContext3D: glXChooseFBConfig failed\n");
        return;
    }
    if (!nelements) {
        printf("GraphicsContext3D: glXChooseFBConfig returned 0 elements\n");
        XFree(config);
        return;
    }
    GLXContext context = s_gl->createNewContext(config[0], GLX_RGBA_TYPE, 0, True);
    if (!context) {
        printf("GraphicsContext3D: glXCreateNewContext failed\n");
        XFree(config);
        return;
    }
    int pbufferAttrs[] = {
        GLX_PBUFFER_WIDTH,
        1,
        GLX_PBUFFER_HEIGHT,
        1,
        0
    };
    GLXPbuffer pbuffer = s_gl->createPbuffer(config[0], pbufferAttrs);
    XFree(config);
    if (!pbuffer) {
        printf("GraphicsContext3D: glxCreatePbuffer failed\n");
        return;
    }
    if (!s_gl->makeCurrent(pbuffer, context)) {
        printf("GraphicsContext3D: glXMakeCurrent failed\n");
        return;
    }
    m_contextObj = context;
    m_pbuffer = pbuffer;
#else
#error Must port to your platform
#endif

    if (!s_initializedGLEW) {
        // Initialize GLEW and check for GL 2.0 support by the drivers.
        GLenum glewInitResult = glewInit();
        if (glewInitResult != GLEW_OK) {
            printf("GraphicsContext3D: GLEW initialization failed\n");
            return;
        }
        if (!glewIsSupported("GL_VERSION_2_0")) {
            printf("GraphicsContext3D: OpenGL 2.0 not supported\n");
            return;
        }
        s_initializedGLEW = true;
    }
}

GraphicsContext3DInternal::~GraphicsContext3DInternal()
{
    makeContextCurrent();
#ifndef RENDER_TO_DEBUGGING_WINDOW
    glDeleteRenderbuffersEXT(1, &m_depthBuffer);
    glDeleteTextures(1, &m_texture);
#ifdef FLIP_FRAMEBUFFER_VERTICALLY
    if (m_scanline)
        delete[] m_scanline;
#endif
    glDeleteFramebuffersEXT(1, &m_fbo);
#endif // !RENDER_TO_DEBUGGING_WINDOW
#if PLATFORM(SKIA)
    if (m_resizingBitmap)
        delete m_resizingBitmap;
#endif
#if OS(WINDOWS)
    wglMakeCurrent(0, 0);
    wglDeleteContext(m_contextObj);
    ReleaseDC(m_canvasWindow, m_canvasDC);
    DestroyWindow(m_canvasWindow);
#elif PLATFORM(CG)
    CGLSetCurrentContext(0);
    CGLDestroyContext(m_contextObj);
    CGLDestroyPBuffer(m_pbuffer);
    if (m_renderOutput)
        delete[] m_renderOutput;
#elif OS(LINUX)
    s_gl->makeCurrent(0, 0);
    s_gl->destroyContext(m_contextObj);
    s_gl->destroyPbuffer(m_pbuffer);
#else
#error Must port to your platform
#endif
    m_contextObj = 0;
}

bool GraphicsContext3DInternal::makeContextCurrent()
{
#if OS(WINDOWS)
    if (wglGetCurrentContext() != m_contextObj)
        if (wglMakeCurrent(m_canvasDC, m_contextObj))
            return true;
#elif PLATFORM(CG)
    if (CGLGetCurrentContext() != m_contextObj)
        if (CGLSetCurrentContext(m_contextObj) == kCGLNoError)
            return true;
#elif OS(LINUX)
    if (s_gl->getCurrentContext() != m_contextObj)
        if (s_gl->makeCurrent(m_pbuffer, m_contextObj))
            return true;
#else
#error Must port to your platform
#endif
    return false;
}

PlatformGraphicsContext3D GraphicsContext3DInternal::platformGraphicsContext3D() const
{
    return m_contextObj;
}

Platform3DObject GraphicsContext3DInternal::platformTexture() const
{
    return m_texture;
}

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

void GraphicsContext3DInternal::reshape(int width, int height)
{
#ifdef RENDER_TO_DEBUGGING_WINDOW
    SetWindowPos(m_canvasWindow, HWND_TOP, 0, 0, width, height,
                 SWP_NOMOVE);
    ShowWindow(m_canvasWindow, SW_SHOW);
#endif

    m_cachedWidth = width;
    m_cachedHeight = height;
    makeContextCurrent();

#ifndef RENDER_TO_DEBUGGING_WINDOW
#ifdef USE_TEXTURE_RECTANGLE_FOR_FRAMEBUFFER
    // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on Mac OS X
    GLenum target = GL_TEXTURE_RECTANGLE_ARB;
#else
    GLenum target = GL_TEXTURE_2D;
#endif
    if (!m_texture) {
        // Generate the texture object
        m_texture = createTextureObject(target);
        // Generate the framebuffer object
        glGenFramebuffersEXT(1, &m_fbo);
        // Generate the depth buffer
        glGenRenderbuffersEXT(1, &m_depthBuffer);
    }

    // Reallocate the color and depth buffers
    glBindTexture(target, m_texture);
    glTexImage2D(target, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    glBindTexture(target, 0);

    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
    m_boundFBO = m_fbo;
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_depthBuffer);
    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height);
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);

    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, target, m_texture, 0);
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_depthBuffer);
    GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
    if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
        printf("GraphicsContext3D: framebuffer was incomplete\n");

        // FIXME: cleanup.
        notImplemented();
    }
#endif  // RENDER_TO_DEBUGGING_WINDOW

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

    glClear(GL_COLOR_BUFFER_BIT);

#if PLATFORM(CG)
    // Need to reallocate the client-side backing store.
    // FIXME: make this more efficient.
    if (m_renderOutput) {
        delete[] m_renderOutput;
        m_renderOutput = 0;
    }
    int rowBytes = width * 4;
    m_renderOutput = new unsigned char[height * rowBytes];
#endif  // PLATFORM(CG)
}

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

void GraphicsContext3DInternal::beginPaint(WebGLRenderingContext* context)
{
    makeContextCurrent();

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

    HTMLCanvasElement* canvas = context->canvas();
    ImageBuffer* imageBuffer = canvas->buffer();
    unsigned char* pixels = 0;
    bool mustRestoreFBO = (m_boundFBO != m_fbo);
    if (mustRestoreFBO)
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
#if PLATFORM(SKIA)
    const SkBitmap* canvasBitmap = imageBuffer->context()->platformContext()->bitmap();
    const SkBitmap* readbackBitmap = 0;
    ASSERT(canvasBitmap->config() == SkBitmap::kARGB_8888_Config);
    if (canvasBitmap->width() == m_cachedWidth && canvasBitmap->height() == m_cachedHeight) {
        // This is the fastest and most common case. We read back
        // directly into the canvas's backing store.
        readbackBitmap = canvasBitmap;
        if (m_resizingBitmap) {
            delete m_resizingBitmap;
            m_resizingBitmap = 0;
        }
    } else {
        // We need to allocate a temporary bitmap for reading back the
        // pixel data. We will then use Skia to rescale this bitmap to
        // the size of the canvas's backing store.
        if (m_resizingBitmap && (m_resizingBitmap->width() != m_cachedWidth || m_resizingBitmap->height() != m_cachedHeight)) {
            delete m_resizingBitmap;
            m_resizingBitmap = 0;
        }
        if (!m_resizingBitmap) {
            m_resizingBitmap = new SkBitmap();
            m_resizingBitmap->setConfig(SkBitmap::kARGB_8888_Config,
                                        m_cachedWidth,
                                        m_cachedHeight);
            if (!m_resizingBitmap->allocPixels()) {
                delete m_resizingBitmap;
                m_resizingBitmap = 0;
                return;
            }
        }
        readbackBitmap = m_resizingBitmap;
    }

    // Read back the frame buffer.
    SkAutoLockPixels bitmapLock(*readbackBitmap);
    pixels = static_cast<unsigned char*>(readbackBitmap->getPixels());
    glReadPixels(0, 0, m_cachedWidth, m_cachedHeight, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
#elif PLATFORM(CG)
    if (m_renderOutput) {
        pixels = m_renderOutput;
        glReadPixels(0, 0, m_cachedWidth, m_cachedHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
    }
#else
#error Must port to your platform
#endif

    if (mustRestoreFBO)
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_boundFBO);

#ifdef FLIP_FRAMEBUFFER_VERTICALLY
    if (pixels)
        flipVertically(pixels, m_cachedWidth, m_cachedHeight);
#endif

#if PLATFORM(SKIA)
    if (m_resizingBitmap) {
        // We need to draw the resizing bitmap into the canvas's backing store.
        SkCanvas canvas(*canvasBitmap);
        SkRect dst;
        dst.set(0, 0, canvasBitmap->width(), canvasBitmap->height());
        canvas.drawBitmapRect(*m_resizingBitmap, 0, dst);
    }
#elif PLATFORM(CG)
    if (m_renderOutput) {
        int rowBytes = m_cachedWidth * 4;
        CGDataProviderRef dataProvider = CGDataProviderCreateWithData(0, m_renderOutput, rowBytes * m_cachedHeight, 0);
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGImageRef cgImage = CGImageCreate(m_cachedWidth,
                                           m_cachedHeight,
                                           8,
                                           32,
                                           rowBytes,
                                           colorSpace,
                                           kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
                                           dataProvider,
                                           0,
                                           false,
                                           kCGRenderingIntentDefault);
        // CSS styling may cause the canvas's content to be resized on
        // the page. Go back to the Canvas to figure out the correct
        // width and height to draw.
        CGRect rect = CGRectMake(0, 0,
                                 context->canvas()->width(),
                                 context->canvas()->height());
        // We want to completely overwrite the previous frame's
        // rendering results.
        CGContextSetBlendMode(imageBuffer->context()->platformContext(),
                              kCGBlendModeCopy);
        CGContextSetInterpolationQuality(imageBuffer->context()->platformContext(),
                                         kCGInterpolationNone);
        CGContextDrawImage(imageBuffer->context()->platformContext(),
                           rect, cgImage);
        CGImageRelease(cgImage);
        CGColorSpaceRelease(colorSpace);
        CGDataProviderRelease(dataProvider);
    }
#else
#error Must port to your platform
#endif

#endif  // RENDER_TO_DEBUGGING_WINDOW
}

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

    makeContextCurrent();
    glActiveTexture(texture);
}

void GraphicsContext3DInternal::bindBuffer(unsigned long target,
                                           WebGLBuffer* buffer)
{
    makeContextCurrent();
    GLuint bufID = EXTRACT(buffer);
    if (target == GL_ARRAY_BUFFER)
        m_boundArrayBuffer = bufID;
    glBindBuffer(target, bufID);
}

void GraphicsContext3DInternal::bindFramebuffer(unsigned long target,
                                                WebGLFramebuffer* framebuffer)
{
    makeContextCurrent();
    GLuint id = EXTRACT(framebuffer);
    if (!id)
        id = m_fbo;
    glBindFramebufferEXT(target, id);
    m_boundFBO = id;
}

// If we didn't have to hack GL_TEXTURE_WRAP_R for cube maps,
// we could just use:
// GL_SAME_METHOD_2_X2(BindTexture, bindTexture, unsigned long, WebGLTexture*)
void GraphicsContext3DInternal::bindTexture(unsigned long target,
                                            WebGLTexture* texture)
{
    makeContextCurrent();
    unsigned int textureObject = EXTRACT(texture);

    glBindTexture(target, textureObject);

    // FIXME: GL_TEXTURE_WRAP_R isn't exposed in the OpenGL ES 2.0
    // API. On desktop OpenGL implementations it seems necessary to
    // set this wrap mode to GL_CLAMP_TO_EDGE to get correct behavior
    // of cube maps.
    if (texture) {
        if (target == GL_TEXTURE_CUBE_MAP) {
            if (!texture->isCubeMapRWrapModeInitialized()) {
                glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
                texture->setCubeMapRWrapModeInitialized(true);
            }
        } else
            texture->setCubeMapRWrapModeInitialized(false);
    }
}

void GraphicsContext3DInternal::bufferDataImpl(unsigned long target, int size, const void* data, unsigned long usage)
{
    makeContextCurrent();
    // FIXME: make this verification more efficient.
    GLint binding = 0;
    GLenum binding_target = GL_ARRAY_BUFFER_BINDING;
    if (target == GL_ELEMENT_ARRAY_BUFFER)
        binding_target = GL_ELEMENT_ARRAY_BUFFER_BINDING;
    glGetIntegerv(binding_target, &binding);
    if (binding <= 0) {
        // FIXME: raise exception.
        // LogMessagef(("bufferData: no buffer bound"));
        return;
    }

    glBufferData(target,
                   size,
                   data,
                   usage);
}

void GraphicsContext3DInternal::disableVertexAttribArray(unsigned long index)
{
    makeContextCurrent();
    if (index < NumTrackedPointerStates)
        m_vertexAttribPointerState[index].enabled = false;
    glDisableVertexAttribArray(index);
}

void GraphicsContext3DInternal::enableVertexAttribArray(unsigned long index)
{
    makeContextCurrent();
    if (index < NumTrackedPointerStates)
        m_vertexAttribPointerState[index].enabled = true;
    glEnableVertexAttribArray(index);
}

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

    makeContextCurrent();
    return glGetError();
}

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

void GraphicsContext3DInternal::vertexAttribPointer(unsigned long indx, int size, int type, bool normalized,
                                                    unsigned long stride, unsigned long offset)
{
    makeContextCurrent();

    if (m_boundArrayBuffer <= 0) {
        // FIXME: raise exception.
        // LogMessagef(("bufferData: no buffer bound"));
        return;
    }

    if (indx < NumTrackedPointerStates) {
        VertexAttribPointerState& state = m_vertexAttribPointerState[indx];
        state.buffer = m_boundArrayBuffer;
        state.indx = indx;
        state.size = size;
        state.type = type;
        state.normalized = normalized;
        state.stride = stride;
        state.offset = offset;
    }

    glVertexAttribPointer(indx, size, type, normalized, stride,
                          reinterpret_cast<void*>(static_cast<intptr_t>(offset)));
}

void GraphicsContext3DInternal::viewportImpl(long x, long y, unsigned long width, unsigned long height)
{
    glViewport(x, y, width, height);
}

void GraphicsContext3DInternal::synthesizeGLError(unsigned long error)
{
    m_syntheticErrors.add(error);
}

// GraphicsContext3D -----------------------------------------------------

/* Helper macros for when we're just wrapping a gl method, so that
 * we can avoid having to type this 500 times.  Note that these MUST
 * NOT BE USED if we need to check any of the parameters.
 */

#define GL_SAME_METHOD_0(glname, name)                                         \
void GraphicsContext3D::name()                                                 \
{                                                                              \
    makeContextCurrent();                                                      \
    gl##glname();                                                              \
}

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

#define GL_SAME_METHOD_1_X(glname, name, t1)                                   \
void GraphicsContext3D::name(t1 a1)                                            \
{                                                                              \
    makeContextCurrent();                                                      \
    gl##glname(EXTRACT(a1));                                                   \
}

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

#define GL_SAME_METHOD_2_X12(glname, name, t1, t2)                             \
void GraphicsContext3D::name(t1 a1, t2 a2)                                     \
{                                                                              \
    makeContextCurrent();                                                      \
    gl##glname(EXTRACT(a1), EXTRACT(a2));                                      \
}

#define GL_SAME_METHOD_2_X2(glname, name, t1, t2)                              \
void GraphicsContext3D::name(t1 a1, t2 a2)                                     \
{                                                                              \
    makeContextCurrent();                                                      \
    gl##glname(a1, EXTRACT(a2));                                               \
}

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

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

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

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

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

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

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

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

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

PassOwnPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3D::Attributes attrs)
{
    PassOwnPtr<GraphicsContext3D> context = new GraphicsContext3D(attrs);
    // FIXME: add error checking
    return context;
}

GraphicsContext3D::GraphicsContext3D(GraphicsContext3D::Attributes attrs)
    : m_currentWidth(0)
    , m_currentHeight(0)
    , m_internal(new GraphicsContext3DInternal(attrs))
{
}

GraphicsContext3D::~GraphicsContext3D()
{
}

PlatformGraphicsContext3D GraphicsContext3D::platformGraphicsContext3D() const
{
    return m_internal->platformGraphicsContext3D();
}

Platform3DObject GraphicsContext3D::platformTexture() const
{
    return m_internal->platformTexture();
}

void GraphicsContext3D::makeContextCurrent()
{
    m_internal->makeContextCurrent();
}

void GraphicsContext3D::reshape(int width, int height)
{
    if (width == m_currentWidth && height == m_currentHeight)
        return;

    m_currentWidth = width;
    m_currentHeight = height;

    m_internal->reshape(width, height);
}

void GraphicsContext3D::beginPaint(WebGLRenderingContext* context)
{
    m_internal->beginPaint(context);
}

void GraphicsContext3D::endPaint()
{
}

int GraphicsContext3D::sizeInBytes(int type)
{
    switch (type) {
    case GL_BYTE:
        return sizeof(GLbyte);
    case GL_UNSIGNED_BYTE:
        return sizeof(GLubyte);
    case GL_SHORT:
        return sizeof(GLshort);
    case GL_UNSIGNED_SHORT:
        return sizeof(GLushort);
    case GL_INT:
        return sizeof(GLint);
    case GL_UNSIGNED_INT:
        return sizeof(GLuint);
    case GL_FLOAT:
        return sizeof(GLfloat);
    default: // FIXME: default cases are discouraged in WebKit.
        return 0;
    }
}

unsigned GraphicsContext3D::createBuffer()
{
    makeContextCurrent();
    GLuint o;
    glGenBuffers(1, &o);
    return o;
}

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

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

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

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

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

void GraphicsContext3D::deleteBuffer(unsigned buffer)
{
    makeContextCurrent();
    glDeleteBuffers(1, &buffer);
}

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

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

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

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

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

void GraphicsContext3D::activeTexture(unsigned long texture)
{
    m_internal->activeTexture(texture);
}

GL_SAME_METHOD_2_X12(AttachShader, attachShader, WebGLProgram*, WebGLShader*)

void GraphicsContext3D::bindAttribLocation(WebGLProgram* program,
                                           unsigned long index,
                                           const String& name)
{
    if (!program)
        return;
    makeContextCurrent();
    glBindAttribLocation(EXTRACT(program), index, name.utf8().data());
}

void GraphicsContext3D::bindBuffer(unsigned long target,
                                   WebGLBuffer* buffer)
{
    m_internal->bindBuffer(target, buffer);
}

void GraphicsContext3D::bindFramebuffer(unsigned long target, WebGLFramebuffer* framebuffer)
{
    m_internal->bindFramebuffer(target, framebuffer);
}

GL_SAME_METHOD_2_X2(BindRenderbufferEXT, bindRenderbuffer, unsigned long, WebGLRenderbuffer*)

// If we didn't have to hack GL_TEXTURE_WRAP_R for cube maps,
// we could just use:
// GL_SAME_METHOD_2_X2(BindTexture, bindTexture, unsigned long, WebGLTexture*)
void GraphicsContext3D::bindTexture(unsigned long target,
                                    WebGLTexture* texture)
{
    m_internal->bindTexture(target, texture);
}

GL_SAME_METHOD_4(BlendColor, blendColor, double, double, double, double)

GL_SAME_METHOD_1(BlendEquation, blendEquation, unsigned long)

GL_SAME_METHOD_2(BlendEquationSeparate, blendEquationSeparate, unsigned long, unsigned long)

GL_SAME_METHOD_2(BlendFunc, blendFunc, unsigned long, unsigned long)

GL_SAME_METHOD_4(BlendFuncSeparate, blendFuncSeparate, unsigned long, unsigned long, unsigned long, unsigned long)

void GraphicsContext3D::bufferData(unsigned long target, int size, unsigned long usage)
{
    m_internal->bufferDataImpl(target, size, 0, usage);
}

void GraphicsContext3D::bufferData(unsigned long target, WebGLArray* array, unsigned long usage)
{
    m_internal->bufferDataImpl(target, array->byteLength(), array->baseAddress(), usage);
}

void GraphicsContext3D::bufferSubData(unsigned long target, long offset, WebGLArray* array)
{
    if (!array || !array->length())
        return;

    makeContextCurrent();
    // FIXME: make this verification more efficient.
    GLint binding = 0;
    GLenum binding_target = GL_ARRAY_BUFFER_BINDING;
    if (target == GL_ELEMENT_ARRAY_BUFFER)
        binding_target = GL_ELEMENT_ARRAY_BUFFER_BINDING;
    glGetIntegerv(binding_target, &binding);
    if (binding <= 0) {
        // FIXME: raise exception.
        // LogMessagef(("bufferSubData: no buffer bound"));
        return;
    }
    glBufferSubData(target, offset, array->byteLength(), array->baseAddress());
}

unsigned long GraphicsContext3D::checkFramebufferStatus(unsigned long target)
{
    makeContextCurrent();
    return glCheckFramebufferStatusEXT(target);
}

GL_SAME_METHOD_1(Clear, clear, unsigned long)

GL_SAME_METHOD_4(ClearColor, clearColor, double, double, double, double)

GL_SAME_METHOD_1(ClearDepth, clearDepth, double)

GL_SAME_METHOD_1(ClearStencil, clearStencil, long)

GL_SAME_METHOD_4(ColorMask, colorMask, bool, bool, bool, bool)

GL_SAME_METHOD_1_X(CompileShader, compileShader, WebGLShader*)

GL_SAME_METHOD_8(CopyTexImage2D, copyTexImage2D, unsigned long, long, unsigned long, long, long, unsigned long, unsigned long, long)

GL_SAME_METHOD_8(CopyTexSubImage2D, copyTexSubImage2D, unsigned long, long, long, long, long, long, unsigned long, unsigned long)

GL_SAME_METHOD_1(CullFace, cullFace, unsigned long)

GL_SAME_METHOD_1(DepthFunc, depthFunc, unsigned long)

GL_SAME_METHOD_1(DepthMask, depthMask, bool)

GL_SAME_METHOD_2(DepthRange, depthRange, double, double)

void GraphicsContext3D::detachShader(WebGLProgram* program, WebGLShader* shader)
{
    if (!program || !shader)
        return;

    makeContextCurrent();
    glDetachShader(EXTRACT(program), EXTRACT(shader));
}

GL_SAME_METHOD_1(Disable, disable, unsigned long)

void GraphicsContext3D::disableVertexAttribArray(unsigned long index)
{
    m_internal->disableVertexAttribArray(index);
}

void GraphicsContext3D::drawArrays(unsigned long mode, long first, long count)
{
    switch (mode) {
    case GL_TRIANGLES:
    case GL_TRIANGLE_STRIP:
    case GL_TRIANGLE_FAN:
    case GL_POINTS:
    case GL_LINE_STRIP:
    case GL_LINE_LOOP:
    case GL_LINES:
        break;
    default: // FIXME: default cases are discouraged in WebKit.
        // FIXME: output log message, raise exception.
        // LogMessage(NS_LITERAL_CSTRING("drawArrays: invalid mode"));
        // return NS_ERROR_DOM_SYNTAX_ERR;
        return;
    }

    if (first+count < first || first+count < count) {
        // FIXME: output log message, raise exception.
        // LogMessage(NS_LITERAL_CSTRING("drawArrays: overflow in first+count"));
        // return NS_ERROR_INVALID_ARG;
        return;
    }

    // FIXME: validate against currently bound buffer.
    // if (!ValidateBuffers(first+count))
    //     return NS_ERROR_INVALID_ARG;

    makeContextCurrent();
    glDrawArrays(mode, first, count);
}

void GraphicsContext3D::drawElements(unsigned long mode, unsigned long count, unsigned long type, long offset)
{
    makeContextCurrent();
    // FIXME: make this verification more efficient.
    GLint binding = 0;
    GLenum binding_target = GL_ELEMENT_ARRAY_BUFFER_BINDING;
    glGetIntegerv(binding_target, &binding);
    if (binding <= 0) {
        // FIXME: raise exception.
        // LogMessagef(("bufferData: no buffer bound"));
        return;
    }
    glDrawElements(mode, count, type,
                   reinterpret_cast<void*>(static_cast<intptr_t>(offset)));
}

GL_SAME_METHOD_1(Enable, enable, unsigned long)

void GraphicsContext3D::enableVertexAttribArray(unsigned long index)
{
    m_internal->enableVertexAttribArray(index);
}

GL_SAME_METHOD_0(Finish, finish)

GL_SAME_METHOD_0(Flush, flush)

GL_SAME_METHOD_4_X4(FramebufferRenderbufferEXT, framebufferRenderbuffer, unsigned long, unsigned long, unsigned long, WebGLRenderbuffer*)

GL_SAME_METHOD_5_X4(FramebufferTexture2DEXT, framebufferTexture2D, unsigned long, unsigned long, unsigned long, WebGLTexture*, long)

GL_SAME_METHOD_1(FrontFace, frontFace, unsigned long)

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

bool GraphicsContext3D::getActiveAttrib(WebGLProgram* program, unsigned long index, ActiveInfo& info)
{
    if (!program) {
        synthesizeGLError(INVALID_VALUE);
        return false;
    }
    GLint maxNameLength = -1;
    glGetProgramiv(EXTRACT(program), GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxNameLength);
    if (maxNameLength < 0)
        return false;
    GLchar* name = 0;
    if (!tryFastMalloc(maxNameLength * sizeof(GLchar)).getValue(name)) {
        synthesizeGLError(OUT_OF_MEMORY);
        return false;
    }
    GLsizei length = 0;
    GLint size = -1;
    GLenum type = 0;
    glGetActiveAttrib(EXTRACT(program), index, maxNameLength,
                      &length, &size, &type, name);
    if (size < 0) {
        fastFree(name);
        return false;
    }
    info.name = String(name, length);
    info.type = type;
    info.size = size;
    fastFree(name);
    return true;
}

bool GraphicsContext3D::getActiveUniform(WebGLProgram* program, unsigned long index, ActiveInfo& info)
{
    if (!program) {
        synthesizeGLError(INVALID_VALUE);
        return false;
    }
    GLint maxNameLength = -1;
    glGetProgramiv(EXTRACT(program), GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxNameLength);
    if (maxNameLength < 0)
        return false;
    GLchar* name = 0;
    if (!tryFastMalloc(maxNameLength * sizeof(GLchar)).getValue(name)) {
        synthesizeGLError(OUT_OF_MEMORY);
        return false;
    }
    GLsizei length = 0;
    GLint size = -1;
    GLenum type = 0;
    glGetActiveUniform(EXTRACT(program), index, maxNameLength,
                       &length, &size, &type, name);
    if (size < 0) {
        fastFree(name);
        return false;
    }
    info.name = String(name, length);
    info.type = type;
    info.size = size;
    fastFree(name);
    return true;
}

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

    makeContextCurrent();
    return glGetAttribLocation(EXTRACT(program), name.utf8().data());
}

void GraphicsContext3D::getBooleanv(unsigned long pname, unsigned char* value)
{
    makeContextCurrent();
    glGetBooleanv(pname, value);
}

void GraphicsContext3D::getBufferParameteriv(unsigned long target, unsigned long pname, int* value)
{
    makeContextCurrent();
    glGetBufferParameteriv(target, pname, value);
}

GraphicsContext3D::Attributes GraphicsContext3D::getContextAttributes()
{
    return m_internal->getContextAttributes();
}

unsigned long GraphicsContext3D::getError()
{
    return m_internal->getError();
}

void GraphicsContext3D::getFloatv(unsigned long pname, float* value)
{
    makeContextCurrent();
    glGetFloatv(pname, value);
}

void GraphicsContext3D::getFramebufferAttachmentParameteriv(unsigned long target,
                                                            unsigned long attachment,
                                                            unsigned long pname,
                                                            int* value)
{
    makeContextCurrent();
    glGetFramebufferAttachmentParameterivEXT(target, attachment, pname, value);
}

void GraphicsContext3D::getIntegerv(unsigned long pname, int* value)
{
    makeContextCurrent();
    glGetIntegerv(pname, value);
}

void GraphicsContext3D::getProgramiv(WebGLProgram* program,
                                     unsigned long pname,
                                     int* value)
{
    makeContextCurrent();
    glGetProgramiv(EXTRACT(program), pname, value);
}

String GraphicsContext3D::getProgramInfoLog(WebGLProgram* program)
{
    makeContextCurrent();
    GLuint programID = EXTRACT(program);
    GLint logLength;
    glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &logLength);
    if (!logLength)
        return String();
    GLchar* log = 0;
    if (!tryFastMalloc(logLength * sizeof(GLchar)).getValue(log))
        return String();
    GLsizei returnedLogLength;
    glGetProgramInfoLog(programID, logLength, &returnedLogLength, log);
    ASSERT(logLength == returnedLogLength + 1);
    String res = String(log, returnedLogLength);
    fastFree(log);
    return res;
}

void GraphicsContext3D::getRenderbufferParameteriv(unsigned long target,
                                                   unsigned long pname,
                                                   int* value)
{
    makeContextCurrent();
    glGetRenderbufferParameterivEXT(target, pname, value);
}

void GraphicsContext3D::getShaderiv(WebGLShader* shader,
                                    unsigned long pname,
                                    int* value)
{
    makeContextCurrent();
    glGetShaderiv(EXTRACT(shader), pname, value);
}

String GraphicsContext3D::getShaderInfoLog(WebGLShader* shader)
{
    makeContextCurrent();
    GLuint shaderID = EXTRACT(shader);
    GLint logLength;
    glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &logLength);
    if (!logLength)
        return String();
    GLchar* log = 0;
    if (!tryFastMalloc(logLength * sizeof(GLchar)).getValue(log))
        return String();
    GLsizei returnedLogLength;
    glGetShaderInfoLog(shaderID, logLength, &returnedLogLength, log);
    ASSERT(logLength == returnedLogLength + 1);
    String res = String(log, returnedLogLength);
    fastFree(log);
    return res;
}

String GraphicsContext3D::getShaderSource(WebGLShader* shader)
{
    makeContextCurrent();
    GLuint shaderID = EXTRACT(shader);
    GLint logLength;
    glGetShaderiv(shaderID, GL_SHADER_SOURCE_LENGTH, &logLength);
    if (!logLength)
        return String();
    GLchar* log = 0;
    if (!tryFastMalloc(logLength * sizeof(GLchar)).getValue(log))
        return String();
    GLsizei returnedLogLength;
    glGetShaderSource(shaderID, logLength, &returnedLogLength, log);
    ASSERT(logLength == returnedLogLength + 1);
    String res = String(log, returnedLogLength);
    fastFree(log);
    return res;
}

String GraphicsContext3D::getString(unsigned long name)
{
    makeContextCurrent();
    return String(reinterpret_cast<const char*>(glGetString(name)));
}

void GraphicsContext3D::getTexParameterfv(unsigned long target, unsigned long pname, float* value)
{
    makeContextCurrent();
    glGetTexParameterfv(target, pname, value);
}

void GraphicsContext3D::getTexParameteriv(unsigned long target, unsigned long pname, int* value)
{
    makeContextCurrent();
    glGetTexParameteriv(target, pname, value);
}

void GraphicsContext3D::getUniformfv(WebGLProgram* program, long location, float* value)
{
    makeContextCurrent();
    glGetUniformfv(EXTRACT(program), location, value);
}

void GraphicsContext3D::getUniformiv(WebGLProgram* program, long location, int* value)
{
    makeContextCurrent();
    glGetUniformiv(EXTRACT(program), location, value);
}

long GraphicsContext3D::getUniformLocation(WebGLProgram* program, const String& name)
{
    if (!program)
        return -1;

    makeContextCurrent();
    return glGetUniformLocation(EXTRACT(program), name.utf8().data());
}

void GraphicsContext3D::getVertexAttribfv(unsigned long index,
                                          unsigned long pname,
                                          float* value)
{
    makeContextCurrent();
    glGetVertexAttribfv(index, pname, value);
}

void GraphicsContext3D::getVertexAttribiv(unsigned long index,
                                          unsigned long pname,
                                          int* value)
{
    makeContextCurrent();
    glGetVertexAttribiv(index, pname, value);
}

long GraphicsContext3D::getVertexAttribOffset(unsigned long index, unsigned long pname)
{
    // FIXME: implement.
    notImplemented();
    return 0;
}

GL_SAME_METHOD_2(Hint, hint, unsigned long, unsigned long);

bool GraphicsContext3D::isBuffer(WebGLBuffer* buffer)
{
    makeContextCurrent();
    return glIsBuffer(EXTRACT(buffer));
}

bool GraphicsContext3D::isEnabled(unsigned long cap)
{
    makeContextCurrent();
    return glIsEnabled(cap);
}

bool GraphicsContext3D::isFramebuffer(WebGLFramebuffer* framebuffer)
{
    makeContextCurrent();
    return glIsFramebufferEXT(EXTRACT(framebuffer));
}

bool GraphicsContext3D::isProgram(WebGLProgram* program)
{
    makeContextCurrent();
    return glIsProgram(EXTRACT(program));
}

bool GraphicsContext3D::isRenderbuffer(WebGLRenderbuffer* renderbuffer)
{
    makeContextCurrent();
    return glIsRenderbufferEXT(EXTRACT(renderbuffer));
}

bool GraphicsContext3D::isShader(WebGLShader* shader)
{
    makeContextCurrent();
    return glIsShader(EXTRACT(shader));
}

bool GraphicsContext3D::isTexture(WebGLTexture* texture)
{
    makeContextCurrent();
    return glIsTexture(EXTRACT(texture));
}

GL_SAME_METHOD_1(LineWidth, lineWidth, double)

GL_SAME_METHOD_1_X(LinkProgram, linkProgram, WebGLProgram*)

void GraphicsContext3D::pixelStorei(unsigned long pname, long param)
{
    if (pname != GL_PACK_ALIGNMENT && pname != GL_UNPACK_ALIGNMENT) {
        // FIXME: Create a fake GL error and throw an exception.
        return;
    }

    makeContextCurrent();
    glPixelStorei(pname, param);
}

GL_SAME_METHOD_2(PolygonOffset, polygonOffset, double, double)

PassRefPtr<WebGLArray> GraphicsContext3D::readPixels(long x, long y,
                                                      unsigned long width, unsigned long height,
                                                      unsigned long format, unsigned long type) {
    // FIXME: support more pixel formats and types.
    if (!((format == GL_RGBA) && (type == GL_UNSIGNED_BYTE)))
        return 0;

    // FIXME: take into account pack alignment.
    RefPtr<WebGLUnsignedByteArray> array = WebGLUnsignedByteArray::create(width * height * 4);
    glReadPixels(x, y, width, height, format, type, array->baseAddress());
    return array;
}

void GraphicsContext3D::releaseShaderCompiler()
{
}

GL_SAME_METHOD_4(RenderbufferStorageEXT, renderbufferStorage, unsigned long, unsigned long, unsigned long, unsigned long)

GL_SAME_METHOD_2(SampleCoverage, sampleCoverage, double, bool)

GL_SAME_METHOD_4(Scissor, scissor, long, long, unsigned long, unsigned long)

void GraphicsContext3D::shaderSource(WebGLShader* shader, const String& source)
{
    makeContextCurrent();
    CString str = source.utf8();
    const char* data = str.data();
    GLint length = str.length();
    glShaderSource(EXTRACT(shader), 1, &data, &length);
}

GL_SAME_METHOD_3(StencilFunc, stencilFunc, unsigned long, long, unsigned long)

GL_SAME_METHOD_4(StencilFuncSeparate, stencilFuncSeparate, unsigned long, unsigned long, long, unsigned long)

GL_SAME_METHOD_1(StencilMask, stencilMask, unsigned long)

GL_SAME_METHOD_2(StencilMaskSeparate, stencilMaskSeparate, unsigned long, unsigned long)

GL_SAME_METHOD_3(StencilOp, stencilOp, unsigned long, unsigned long, unsigned long)

GL_SAME_METHOD_4(StencilOpSeparate, stencilOpSeparate, unsigned long, unsigned long, unsigned long, unsigned long)

void GraphicsContext3D::synthesizeGLError(unsigned long error)
{
    m_internal->synthesizeGLError(error);
}

int GraphicsContext3D::texImage2D(unsigned target,
                                  unsigned level,
                                  unsigned internalformat,
                                  unsigned width,
                                  unsigned height,
                                  unsigned border,
                                  unsigned format,
                                  unsigned type,
                                  void* pixels)
{
    // FIXME: must do validation similar to JOGL's to ensure that
    // the incoming array is of the appropriate length.
    glTexImage2D(target,
                 level,
                 internalformat,
                 width,
                 height,
                 border,
                 format,
                 type,
                 pixels);
    return 0;
}

// Remove premultiplied alpha from color channels.
// FIXME: this is lossy. Must retrieve original values from HTMLImageElement.
static void unmultiplyAlpha(unsigned char* rgbaData, int numPixels)
{
    for (int j = 0; j < numPixels; j++) {
        float b = rgbaData[4*j+0] / 255.0f;
        float g = rgbaData[4*j+1] / 255.0f;
        float r = rgbaData[4*j+2] / 255.0f;
        float a = rgbaData[4*j+3] / 255.0f;
        if (a > 0.0f) {
            b /= a;
            g /= a;
            r /= a;
            b = (b > 1.0f) ? 1.0f : b;
            g = (g > 1.0f) ? 1.0f : g;
            r = (r > 1.0f) ? 1.0f : r;
            rgbaData[4*j+0] = (unsigned char) (b * 255.0f);
            rgbaData[4*j+1] = (unsigned char) (g * 255.0f);
            rgbaData[4*j+2] = (unsigned char) (r * 255.0f);
        }
    }
}

// FIXME: this must be changed to refer to the original image data
// rather than unmultiplying the alpha channel.
static int texImage2DHelper(unsigned target, unsigned level,
                            int width, int height,
                            int rowBytes,
                            bool flipY,
                            bool premultiplyAlpha,
                            GLenum format,
                            bool skipAlpha,
                            unsigned char* pixels)
{
    ASSERT(format == GL_RGBA || format == GL_BGRA);
    GLint internalFormat = GL_RGBA8;
    if (skipAlpha) {
        internalFormat = GL_RGB8;
        // Ignore the alpha channel
        premultiplyAlpha = true;
    }
    if (flipY) {
        // Need to flip images vertically. To avoid making a copy of
        // the entire image, we perform a ton of glTexSubImage2D
        // calls. FIXME: should rethink this strategy for efficiency.
        glTexImage2D(target, level, internalFormat,
                     width,
                     height,
                     0,
                     format,
                     GL_UNSIGNED_BYTE,
                     0);
        unsigned char* row = 0;
        bool allocatedRow = false;
        if (!premultiplyAlpha) {
            row = new unsigned char[rowBytes];
            allocatedRow = true;
        }
        for (int i = 0; i < height; i++) {
            if (premultiplyAlpha)
                row = pixels + (rowBytes * i);
            else {
                memcpy(row, pixels + (rowBytes * i), rowBytes);
                unmultiplyAlpha(row, width);
            }
            glTexSubImage2D(target, level, 0, height - i - 1,
                            width, 1,
                            format,
                            GL_UNSIGNED_BYTE,
                            row);
        }
        if (allocatedRow)
            delete[] row;
    } else {
        // The pixels of cube maps' faces are defined with a top-down
        // scanline ordering, unlike GL_TEXTURE_2D, so when uploading
        // these, the above vertical flip is the wrong thing to do.
        if (premultiplyAlpha)
            glTexImage2D(target, level, internalFormat,
                         width,
                         height,
                         0,
                         format,
                         GL_UNSIGNED_BYTE,
                         pixels);
        else {
            glTexImage2D(target, level, internalFormat,
                         width,
                         height,
                         0,
                         format,
                         GL_UNSIGNED_BYTE,
                         0);
            unsigned char* row = new unsigned char[rowBytes];
            for (int i = 0; i < height; i++) {
                memcpy(row, pixels + (rowBytes * i), rowBytes);
                unmultiplyAlpha(row, width);
                glTexSubImage2D(target, level, 0, i,
                                width, 1,
                                format,
                                GL_UNSIGNED_BYTE,
                                row);
            }
            delete[] row;
        }
    }
    return 0;
}

int GraphicsContext3D::texImage2D(unsigned target, unsigned level, Image* image,
                                  bool flipY, bool premultiplyAlpha)
{
    ASSERT(image);

    int res = -1;
#if PLATFORM(SKIA)
    NativeImageSkia* skiaImage = image->nativeImageForCurrentFrame();
    if (!skiaImage) {
        ASSERT_NOT_REACHED();
        return -1;
    }
    SkBitmap::Config skiaConfig = skiaImage->config();
    // FIXME: must support more image configurations.
    if (skiaConfig != SkBitmap::kARGB_8888_Config) {
        ASSERT_NOT_REACHED();
        return -1;
    }
    SkBitmap& skiaImageRef = *skiaImage;
    SkAutoLockPixels lock(skiaImageRef);
    int width = skiaImage->width();
    int height = skiaImage->height();
    unsigned char* pixels =
        reinterpret_cast<unsigned char*>(skiaImage->getPixels());
    int rowBytes = skiaImage->rowBytes();
    res = texImage2DHelper(target, level,
                           width, height,
                           rowBytes,
                           flipY, premultiplyAlpha,
                           GL_BGRA,
                           false,
                           pixels);
#elif PLATFORM(CG)
    CGImageRef cgImage = image->nativeImageForCurrentFrame();
    if (!cgImage) {
        ASSERT_NOT_REACHED();
        return -1;
    }
    int width = CGImageGetWidth(cgImage);
    int height = CGImageGetHeight(cgImage);
    int rowBytes = width * 4;
    CGImageAlphaInfo info = CGImageGetAlphaInfo(cgImage);
    bool skipAlpha = (info == kCGImageAlphaNone
                   || info == kCGImageAlphaNoneSkipLast
                   || info == kCGImageAlphaNoneSkipFirst);
    unsigned char* imageData = new unsigned char[height * rowBytes];
    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    CGContextRef tmpContext = CGBitmapContextCreate(imageData, width, height, 8, rowBytes,
                                                    colorSpace,
                                                    kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease(colorSpace);
    CGContextSetBlendMode(tmpContext, kCGBlendModeCopy);
    CGContextDrawImage(tmpContext,
                       CGRectMake(0, 0, static_cast<CGFloat>(width), static_cast<CGFloat>(height)),
                       cgImage);
    CGContextRelease(tmpContext);
    res = texImage2DHelper(target, level, width, height, rowBytes,
                           flipY, premultiplyAlpha, GL_RGBA, skipAlpha, imageData);
    delete[] imageData;
#else
#error Must port to your platform
#endif
    return res;
}

GL_SAME_METHOD_3(TexParameterf, texParameterf, unsigned, unsigned, float);

GL_SAME_METHOD_3(TexParameteri, texParameteri, unsigned, unsigned, int);

int GraphicsContext3D::texSubImage2D(unsigned target,
                                     unsigned level,
                                     unsigned xoffset,
                                     unsigned yoffset,
                                     unsigned width,
                                     unsigned height,
                                     unsigned format,
                                     unsigned type,
                                     void* pixels)
{
    glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
    return 0;
}

int GraphicsContext3D::texSubImage2D(unsigned target,
                                     unsigned level,
                                     unsigned xoffset,
                                     unsigned yoffset,
                                     Image* image,
                                     bool flipY,
                                     bool premultiplyAlpha)
{
    // FIXME: implement.
    notImplemented();
    return -1;
}

GL_SAME_METHOD_2(Uniform1f, uniform1f, long, float)

void GraphicsContext3D::uniform1fv(long location, float* v, int size)
{
    makeContextCurrent();
    glUniform1fv(location, size, v);
}

GL_SAME_METHOD_2(Uniform1i, uniform1i, long, int)

void GraphicsContext3D::uniform1iv(long location, int* v, int size)
{
    makeContextCurrent();
    glUniform1iv(location, size, v);
}

GL_SAME_METHOD_3(Uniform2f, uniform2f, long, float, float)

void GraphicsContext3D::uniform2fv(long location, float* v, int size)
{
    makeContextCurrent();
    glUniform2fv(location, size, v);
}

GL_SAME_METHOD_3(Uniform2i, uniform2i, long, int, int)

void GraphicsContext3D::uniform2iv(long location, int* v, int size)
{
    makeContextCurrent();
    glUniform2iv(location, size, v);
}

GL_SAME_METHOD_4(Uniform3f, uniform3f, long, float, float, float)

void GraphicsContext3D::uniform3fv(long location, float* v, int size)
{
    makeContextCurrent();
    glUniform3fv(location, size, v);
}

GL_SAME_METHOD_4(Uniform3i, uniform3i, long, int, int, int)

void GraphicsContext3D::uniform3iv(long location, int* v, int size)
{
    makeContextCurrent();
    glUniform3iv(location, size, v);
}

GL_SAME_METHOD_5(Uniform4f, uniform4f, long, float, float, float, float)

void GraphicsContext3D::uniform4fv(long location, float* v, int size)
{
    makeContextCurrent();
    glUniform4fv(location, size, v);
}

GL_SAME_METHOD_5(Uniform4i, uniform4i, long, int, int, int, int)

void GraphicsContext3D::uniform4iv(long location, int* v, int size)
{
    makeContextCurrent();
    glUniform4iv(location, size, v);
}

void GraphicsContext3D::uniformMatrix2fv(long location, bool transpose, float* value, int size)
{
    makeContextCurrent();
    glUniformMatrix2fv(location, size, transpose, value);
}

void GraphicsContext3D::uniformMatrix3fv(long location, bool transpose, float* value, int size)
{
    makeContextCurrent();
    glUniformMatrix3fv(location, size, transpose, value);
}

void GraphicsContext3D::uniformMatrix4fv(long location, bool transpose, float* value, int size)
{
    makeContextCurrent();
    glUniformMatrix4fv(location, size, transpose, value);
}

GL_SAME_METHOD_1_X(UseProgram, useProgram, WebGLProgram*)

GL_SAME_METHOD_1_X(ValidateProgram, validateProgram, WebGLProgram*)

GL_SAME_METHOD_2(VertexAttrib1f, vertexAttrib1f, unsigned long, float)

void GraphicsContext3D::vertexAttrib1fv(unsigned long indx, float* values)
{
    makeContextCurrent();
    glVertexAttrib1fv(indx, values);
}

GL_SAME_METHOD_3(VertexAttrib2f, vertexAttrib2f, unsigned long, float, float)

void GraphicsContext3D::vertexAttrib2fv(unsigned long indx, float* values)
{
    makeContextCurrent();
    glVertexAttrib2fv(indx, values);
}

GL_SAME_METHOD_4(VertexAttrib3f, vertexAttrib3f, unsigned long, float, float, float)

void GraphicsContext3D::vertexAttrib3fv(unsigned long indx, float* values)
{
    makeContextCurrent();
    glVertexAttrib3fv(indx, values);
}

GL_SAME_METHOD_5(VertexAttrib4f, vertexAttrib4f, unsigned long, float, float, float, float)

void GraphicsContext3D::vertexAttrib4fv(unsigned long indx, float* values)
{
    makeContextCurrent();
    glVertexAttrib4fv(indx, values);
}

void GraphicsContext3D::vertexAttribPointer(unsigned long indx, int size, int type, bool normalized,
                                            unsigned long stride, unsigned long offset)
{
    m_internal->vertexAttribPointer(indx, size, type, normalized, stride, offset);
}

void GraphicsContext3D::viewport(long x, long y, unsigned long width, unsigned long height)
{
    makeContextCurrent();
    m_internal->viewportImpl(x, y, width, height);
}

}

#endif // ENABLE(3D_CANVAS)