Edit

kc3-lang/angle/src/libGLESv2/libGLESv2.cpp

Branch :

  • Show log

    Commit

  • Author : Geoff Lang
    Date : 2014-07-22 15:14:06
    Hash : 5d601382
    Message : Simplify formatutils.h by exposing the info structures. Removed all the separate query functions and simply expose the internal info structures. This reduces the number of std::map/std::set operations that were hidden behind the API. Moved the validation tables for ES3 format combinations and effective internal formats into validationES3.cpp so that formatutils.h only has generic GL format queries. BUG=angle:658 Change-Id: Ieb60d42b8eafcdb4f21dcbec130b39478ce5f7c5 Reviewed-on: https://chromium-review.googlesource.com/206835 Reviewed-by: Nicolas Capens <capn@chromium.org> Tested-by: Geoff Lang <geofflang@chromium.org>

  • src/libGLESv2/libGLESv2.cpp
  • #include "precompiled.h"
    //
    // Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    // libGLESv2.cpp: Implements the exported OpenGL ES 2.0 functions.
    
    #include "common/version.h"
    
    #include "libGLESv2/main.h"
    #include "common/utilities.h"
    #include "libGLESv2/formatutils.h"
    #include "libGLESv2/Buffer.h"
    #include "libGLESv2/Fence.h"
    #include "libGLESv2/Framebuffer.h"
    #include "libGLESv2/Renderbuffer.h"
    #include "libGLESv2/Program.h"
    #include "libGLESv2/ProgramBinary.h"
    #include "libGLESv2/Texture.h"
    #include "libGLESv2/Query.h"
    #include "libGLESv2/Context.h"
    #include "libGLESv2/VertexArray.h"
    #include "libGLESv2/VertexAttribute.h"
    #include "libGLESv2/TransformFeedback.h"
    #include "libGLESv2/FramebufferAttachment.h"
    
    #include "libGLESv2/validationES.h"
    #include "libGLESv2/validationES2.h"
    #include "libGLESv2/validationES3.h"
    #include "libGLESv2/queryconversions.h"
    
    extern "C"
    {
    
    // OpenGL ES 2.0 functions
    
    void __stdcall glActiveTexture(GLenum texture)
    {
        EVENT("(GLenum texture = 0x%X)", texture);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (texture < GL_TEXTURE0 || texture > GL_TEXTURE0 + context->getMaximumCombinedTextureImageUnits() - 1)
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            context->getState().setActiveSampler(texture - GL_TEXTURE0);
        }
    }
    
    void __stdcall glAttachShader(GLuint program, GLuint shader)
    {
        EVENT("(GLuint program = %d, GLuint shader = %d)", program, shader);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Program *programObject = context->getProgram(program);
            gl::Shader *shaderObject = context->getShader(shader);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            if (!shaderObject)
            {
                if (context->getProgram(shader))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            if (!programObject->attachShader(shaderObject))
            {
                return gl::error(GL_INVALID_OPERATION);
            }
        }
    }
    
    void __stdcall glBeginQueryEXT(GLenum target, GLuint id)
    {
        EVENT("(GLenum target = 0x%X, GLuint %d)", target, id);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateBeginQuery(context, target, id))
            {
                return;
            }
    
            context->beginQuery(target, id);
        }
    }
    
    void __stdcall glBindAttribLocation(GLuint program, GLuint index, const GLchar* name)
    {
        EVENT("(GLuint program = %d, GLuint index = %d, const GLchar* name = 0x%0.8p)", program, index, name);
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            if (strncmp(name, "gl_", 3) == 0)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            programObject->bindAttributeLocation(index, name);
        }
    }
    
    void __stdcall glBindBuffer(GLenum target, GLuint buffer)
    {
        EVENT("(GLenum target = 0x%X, GLuint buffer = %d)", target, buffer);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!gl::ValidBufferTarget(context, target))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            switch (target)
            {
              case GL_ARRAY_BUFFER:
                context->bindArrayBuffer(buffer);
                return;
              case GL_ELEMENT_ARRAY_BUFFER:
                context->bindElementArrayBuffer(buffer);
                return;
              case GL_COPY_READ_BUFFER:
                context->bindCopyReadBuffer(buffer);
                return;
              case GL_COPY_WRITE_BUFFER:
                context->bindCopyWriteBuffer(buffer);
                return;
              case GL_PIXEL_PACK_BUFFER:
                context->bindPixelPackBuffer(buffer);
                return;
              case GL_PIXEL_UNPACK_BUFFER:
                context->bindPixelUnpackBuffer(buffer);
                return;
              case GL_UNIFORM_BUFFER:
                context->bindGenericUniformBuffer(buffer);
                return;
              case GL_TRANSFORM_FEEDBACK_BUFFER:
                context->bindGenericTransformFeedbackBuffer(buffer);
                return;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glBindFramebuffer(GLenum target, GLuint framebuffer)
    {
        EVENT("(GLenum target = 0x%X, GLuint framebuffer = %d)", target, framebuffer);
    
        if (!gl::ValidFramebufferTarget(target))
        {
            return gl::error(GL_INVALID_ENUM);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (target == GL_READ_FRAMEBUFFER_ANGLE || target == GL_FRAMEBUFFER)
            {
                context->bindReadFramebuffer(framebuffer);
            }
    
            if (target == GL_DRAW_FRAMEBUFFER_ANGLE || target == GL_FRAMEBUFFER)
            {
                context->bindDrawFramebuffer(framebuffer);
            }
        }
    }
    
    void __stdcall glBindRenderbuffer(GLenum target, GLuint renderbuffer)
    {
        EVENT("(GLenum target = 0x%X, GLuint renderbuffer = %d)", target, renderbuffer);
    
        if (target != GL_RENDERBUFFER)
        {
            return gl::error(GL_INVALID_ENUM);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->bindRenderbuffer(renderbuffer);
        }
    }
    
    void __stdcall glBindTexture(GLenum target, GLuint texture)
    {
        EVENT("(GLenum target = 0x%X, GLuint texture = %d)", target, texture);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Texture *textureObject = context->getTexture(texture);
    
            if (textureObject && textureObject->getTarget() != target && texture != 0)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            switch (target)
            {
              case GL_TEXTURE_2D:
                context->bindTexture2D(texture);
                return;
              case GL_TEXTURE_CUBE_MAP:
                context->bindTextureCubeMap(texture);
                return;
              case GL_TEXTURE_3D:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                context->bindTexture3D(texture);
                return;
              case GL_TEXTURE_2D_ARRAY:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                context->bindTexture2DArray(texture);
                return;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
    {
        EVENT("(GLclampf red = %f, GLclampf green = %f, GLclampf blue = %f, GLclampf alpha = %f)",
              red, green, blue, alpha);
    
        gl::Context* context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setBlendColor(gl::clamp01(red), gl::clamp01(green), gl::clamp01(blue), gl::clamp01(alpha));
        }
    }
    
    void __stdcall glBlendEquation(GLenum mode)
    {
        glBlendEquationSeparate(mode, mode);
    }
    
    void __stdcall glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha)
    {
        EVENT("(GLenum modeRGB = 0x%X, GLenum modeAlpha = 0x%X)", modeRGB, modeAlpha);
    
        gl::Context *context = gl::getNonLostContext();
    
        switch (modeRGB)
        {
          case GL_FUNC_ADD:
          case GL_FUNC_SUBTRACT:
          case GL_FUNC_REVERSE_SUBTRACT:
          case GL_MIN:
          case GL_MAX:
            break;
    
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        switch (modeAlpha)
        {
          case GL_FUNC_ADD:
          case GL_FUNC_SUBTRACT:
          case GL_FUNC_REVERSE_SUBTRACT:
          case GL_MIN:
          case GL_MAX:
            break;
    
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        if (context)
        {
            context->getState().setBlendEquation(modeRGB, modeAlpha);
        }
    }
    
    void __stdcall glBlendFunc(GLenum sfactor, GLenum dfactor)
    {
        glBlendFuncSeparate(sfactor, dfactor, sfactor, dfactor);
    }
    
    void __stdcall glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha)
    {
        EVENT("(GLenum srcRGB = 0x%X, GLenum dstRGB = 0x%X, GLenum srcAlpha = 0x%X, GLenum dstAlpha = 0x%X)",
              srcRGB, dstRGB, srcAlpha, dstAlpha);
    
        gl::Context *context = gl::getNonLostContext();
    
        switch (srcRGB)
        {
          case GL_ZERO:
          case GL_ONE:
          case GL_SRC_COLOR:
          case GL_ONE_MINUS_SRC_COLOR:
          case GL_DST_COLOR:
          case GL_ONE_MINUS_DST_COLOR:
          case GL_SRC_ALPHA:
          case GL_ONE_MINUS_SRC_ALPHA:
          case GL_DST_ALPHA:
          case GL_ONE_MINUS_DST_ALPHA:
          case GL_CONSTANT_COLOR:
          case GL_ONE_MINUS_CONSTANT_COLOR:
          case GL_CONSTANT_ALPHA:
          case GL_ONE_MINUS_CONSTANT_ALPHA:
          case GL_SRC_ALPHA_SATURATE:
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        switch (dstRGB)
        {
          case GL_ZERO:
          case GL_ONE:
          case GL_SRC_COLOR:
          case GL_ONE_MINUS_SRC_COLOR:
          case GL_DST_COLOR:
          case GL_ONE_MINUS_DST_COLOR:
          case GL_SRC_ALPHA:
          case GL_ONE_MINUS_SRC_ALPHA:
          case GL_DST_ALPHA:
          case GL_ONE_MINUS_DST_ALPHA:
          case GL_CONSTANT_COLOR:
          case GL_ONE_MINUS_CONSTANT_COLOR:
          case GL_CONSTANT_ALPHA:
          case GL_ONE_MINUS_CONSTANT_ALPHA:
            break;
    
          case GL_SRC_ALPHA_SATURATE:
            if (!context || context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_ENUM);
            }
            break;
    
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        switch (srcAlpha)
        {
          case GL_ZERO:
          case GL_ONE:
          case GL_SRC_COLOR:
          case GL_ONE_MINUS_SRC_COLOR:
          case GL_DST_COLOR:
          case GL_ONE_MINUS_DST_COLOR:
          case GL_SRC_ALPHA:
          case GL_ONE_MINUS_SRC_ALPHA:
          case GL_DST_ALPHA:
          case GL_ONE_MINUS_DST_ALPHA:
          case GL_CONSTANT_COLOR:
          case GL_ONE_MINUS_CONSTANT_COLOR:
          case GL_CONSTANT_ALPHA:
          case GL_ONE_MINUS_CONSTANT_ALPHA:
          case GL_SRC_ALPHA_SATURATE:
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        switch (dstAlpha)
        {
          case GL_ZERO:
          case GL_ONE:
          case GL_SRC_COLOR:
          case GL_ONE_MINUS_SRC_COLOR:
          case GL_DST_COLOR:
          case GL_ONE_MINUS_DST_COLOR:
          case GL_SRC_ALPHA:
          case GL_ONE_MINUS_SRC_ALPHA:
          case GL_DST_ALPHA:
          case GL_ONE_MINUS_DST_ALPHA:
          case GL_CONSTANT_COLOR:
          case GL_ONE_MINUS_CONSTANT_COLOR:
          case GL_CONSTANT_ALPHA:
          case GL_ONE_MINUS_CONSTANT_ALPHA:
            break;
    
          case GL_SRC_ALPHA_SATURATE:
            if (!context || context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_ENUM);
            }
            break;
    
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        bool constantColorUsed = (srcRGB == GL_CONSTANT_COLOR || srcRGB == GL_ONE_MINUS_CONSTANT_COLOR ||
                                  dstRGB == GL_CONSTANT_COLOR || dstRGB == GL_ONE_MINUS_CONSTANT_COLOR);
    
        bool constantAlphaUsed = (srcRGB == GL_CONSTANT_ALPHA || srcRGB == GL_ONE_MINUS_CONSTANT_ALPHA ||
                                  dstRGB == GL_CONSTANT_ALPHA || dstRGB == GL_ONE_MINUS_CONSTANT_ALPHA);
    
        if (constantColorUsed && constantAlphaUsed)
        {
            ERR("Simultaneous use of GL_CONSTANT_ALPHA/GL_ONE_MINUS_CONSTANT_ALPHA and GL_CONSTANT_COLOR/GL_ONE_MINUS_CONSTANT_COLOR invalid under WebGL");
            return gl::error(GL_INVALID_OPERATION);
        }
    
        if (context)
        {
            context->getState().setBlendFactors(srcRGB, dstRGB, srcAlpha, dstAlpha);
        }
    }
    
    void __stdcall glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage)
    {
        EVENT("(GLenum target = 0x%X, GLsizeiptr size = %d, const GLvoid* data = 0x%0.8p, GLenum usage = %d)",
              target, size, data, usage);
    
        if (size < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        switch (usage)
        {
          case GL_STREAM_DRAW:
          case GL_STATIC_DRAW:
          case GL_DYNAMIC_DRAW:
            break;
    
          case GL_STREAM_READ:
          case GL_STREAM_COPY:
          case GL_STATIC_READ:
          case GL_STATIC_COPY:
          case GL_DYNAMIC_READ:
          case GL_DYNAMIC_COPY:
            if (context && context->getClientVersion() < 3)
            {
              return gl::error(GL_INVALID_ENUM);
            }
            break;
    
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        if (context)
        {
            if (!gl::ValidBufferTarget(context, target))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            gl::Buffer *buffer = context->getState().getTargetBuffer(target);
    
            if (!buffer)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            buffer->bufferData(data, size, usage);
        }
    }
    
    void __stdcall glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data)
    {
        EVENT("(GLenum target = 0x%X, GLintptr offset = %d, GLsizeiptr size = %d, const GLvoid* data = 0x%0.8p)",
              target, offset, size, data);
    
        if (size < 0 || offset < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        if (data == NULL)
        {
            return;
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!gl::ValidBufferTarget(context, target))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            gl::Buffer *buffer = context->getState().getTargetBuffer(target);
    
            if (!buffer)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (buffer->isMapped())
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // Check for possible overflow of size + offset
            if (!rx::IsUnsignedAdditionSafe<size_t>(size, offset))
            {
                return gl::error(GL_OUT_OF_MEMORY);
            }
    
            if (size + offset > buffer->getSize())
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            buffer->bufferSubData(data, size, offset);
        }
    }
    
    GLenum __stdcall glCheckFramebufferStatus(GLenum target)
    {
        EVENT("(GLenum target = 0x%X)", target);
    
        if (!gl::ValidFramebufferTarget(target))
        {
            return gl::error(GL_INVALID_ENUM, 0);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target);
            ASSERT(framebuffer);
            return framebuffer->completeness();
        }
    
        return 0;
    }
    
    void __stdcall glClear(GLbitfield mask)
    {
        EVENT("(GLbitfield mask = 0x%X)", mask);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Framebuffer *framebufferObject = context->getState().getDrawFramebuffer();
    
            if (!framebufferObject || framebufferObject->completeness() != GL_FRAMEBUFFER_COMPLETE)
            {
                return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION);
            }
    
            if ((mask & ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            context->clear(mask);
        }
    }
    
    void __stdcall glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
    {
        EVENT("(GLclampf red = %f, GLclampf green = %f, GLclampf blue = %f, GLclampf alpha = %f)",
              red, green, blue, alpha);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setClearColor(red, green, blue, alpha);
        }
    }
    
    void __stdcall glClearDepthf(GLclampf depth)
    {
        EVENT("(GLclampf depth = %f)", depth);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setClearDepth(depth);
        }
    }
    
    void __stdcall glClearStencil(GLint s)
    {
        EVENT("(GLint s = %d)", s);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setClearStencil(s);
        }
    }
    
    void __stdcall glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)
    {
        EVENT("(GLboolean red = %d, GLboolean green = %u, GLboolean blue = %u, GLboolean alpha = %u)",
              red, green, blue, alpha);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setColorMask(red == GL_TRUE, green == GL_TRUE, blue == GL_TRUE, alpha == GL_TRUE);
        }
    }
    
    void __stdcall glCompileShader(GLuint shader)
    {
        EVENT("(GLuint shader = %d)", shader);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Shader *shaderObject = context->getShader(shader);
    
            if (!shaderObject)
            {
                if (context->getProgram(shader))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            shaderObject->compile();
        }
    }
    
    void __stdcall glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
                                          GLint border, GLsizei imageSize, const GLvoid* data)
    {
        EVENT("(GLenum target = 0x%X, GLint level = %d, GLenum internalformat = 0x%X, GLsizei width = %d, "
              "GLsizei height = %d, GLint border = %d, GLsizei imageSize = %d, const GLvoid* data = 0x%0.8p)",
              target, level, internalformat, width, height, border, imageSize, data);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3 &&
                !ValidateES2TexImageParameters(context, target, level, internalformat, true, false,
                                               0, 0, width, height, border, GL_NONE, GL_NONE, data))
            {
                return;
            }
    
            if (context->getClientVersion() >= 3 &&
                !ValidateES3TexImageParameters(context, target, level, internalformat, true, false,
                                               0, 0, 0, width, height, 1, border, GL_NONE, GL_NONE, data))
            {
                return;
            }
    
            const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalformat);
            if (imageSize < 0 || static_cast<GLuint>(imageSize) != formatInfo.computeBlockSize(GL_UNSIGNED_BYTE, width, height))
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            switch (target)
            {
              case GL_TEXTURE_2D:
                {
                    gl::Texture2D *texture = context->getTexture2D();
                    texture->setCompressedImage(level, internalformat, width, height, imageSize, data);
                }
                break;
    
              case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
              case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
              case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
                {
                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
                    texture->setCompressedImage(target, level, internalformat, width, height, imageSize, data);
                }
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                             GLenum format, GLsizei imageSize, const GLvoid* data)
    {
        EVENT("(GLenum target = 0x%X, GLint level = %d, GLint xoffset = %d, GLint yoffset = %d, "
              "GLsizei width = %d, GLsizei height = %d, GLenum format = 0x%X, "
              "GLsizei imageSize = %d, const GLvoid* data = 0x%0.8p)",
              target, level, xoffset, yoffset, width, height, format, imageSize, data);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3 &&
                !ValidateES2TexImageParameters(context, target, level, GL_NONE, true, true,
                                               xoffset, yoffset, width, height, 0, GL_NONE, GL_NONE, data))
            {
                return;
            }
    
            if (context->getClientVersion() >= 3 &&
                !ValidateES3TexImageParameters(context, target, level, GL_NONE, true, true,
                                               xoffset, yoffset, 0, width, height, 1, 0, GL_NONE, GL_NONE, data))
            {
                return;
            }
    
            const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format);
            if (imageSize < 0 || static_cast<GLuint>(imageSize) != formatInfo.computeBlockSize(GL_UNSIGNED_BYTE, width, height))
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            switch (target)
            {
              case GL_TEXTURE_2D:
                {
                    gl::Texture2D *texture = context->getTexture2D();
                    texture->subImageCompressed(level, xoffset, yoffset, width, height, format, imageSize, data);
                }
                break;
    
              case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
              case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
              case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
                {
                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
                    texture->subImageCompressed(target, level, xoffset, yoffset, width, height, format, imageSize, data);
                }
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
    {
        EVENT("(GLenum target = 0x%X, GLint level = %d, GLenum internalformat = 0x%X, "
              "GLint x = %d, GLint y = %d, GLsizei width = %d, GLsizei height = %d, GLint border = %d)",
              target, level, internalformat, x, y, width, height, border);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3 &&
                !ValidateES2CopyTexImageParameters(context, target, level, internalformat, false,
                                                   0, 0, x, y, width, height, border))
            {
                return;
            }
    
            if (context->getClientVersion() >= 3 &&
                !ValidateES3CopyTexImageParameters(context, target, level, internalformat, false,
                                                   0, 0, 0, x, y, width, height, border))
            {
                return;
            }
    
            gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer();
    
            switch (target)
            {
              case GL_TEXTURE_2D:
                {
                    gl::Texture2D *texture = context->getTexture2D();
                    texture->copyImage(level, internalformat, x, y, width, height, framebuffer);
                }
                break;
    
              case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
              case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
              case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
                {
                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
                    texture->copyImage(target, level, internalformat, x, y, width, height, framebuffer);
                }
                break;
    
             default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
    {
        EVENT("(GLenum target = 0x%X, GLint level = %d, GLint xoffset = %d, GLint yoffset = %d, "
              "GLint x = %d, GLint y = %d, GLsizei width = %d, GLsizei height = %d)",
              target, level, xoffset, yoffset, x, y, width, height);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3 &&
                !ValidateES2CopyTexImageParameters(context, target, level, GL_NONE, true,
                                                   xoffset, yoffset, x, y, width, height, 0))
            {
                return;
            }
    
            if (context->getClientVersion() >= 3 &&
                !ValidateES3CopyTexImageParameters(context, target, level, GL_NONE, true,
                                                   xoffset, yoffset, 0, x, y, width, height, 0))
            {
                return;
            }
    
            gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer();
    
            switch (target)
            {
              case GL_TEXTURE_2D:
                {
                    gl::Texture2D *texture = context->getTexture2D();
                    texture->copySubImage(target, level, xoffset, yoffset, 0, x, y, width, height, framebuffer);
                }
                break;
    
              case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
              case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
              case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
                {
                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
                    texture->copySubImage(target, level, xoffset, yoffset, 0, x, y, width, height, framebuffer);
                }
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    GLuint __stdcall glCreateProgram(void)
    {
        EVENT("()");
    
        gl::Context *context = gl::getNonLostContext();
        if (context)
        {
            return context->createProgram();
        }
    
        return 0;
    }
    
    GLuint __stdcall glCreateShader(GLenum type)
    {
        EVENT("(GLenum type = 0x%X)", type);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            switch (type)
            {
              case GL_FRAGMENT_SHADER:
              case GL_VERTEX_SHADER:
                return context->createShader(type);
              default:
                return gl::error(GL_INVALID_ENUM, 0);
            }
        }
    
        return 0;
    }
    
    void __stdcall glCullFace(GLenum mode)
    {
        EVENT("(GLenum mode = 0x%X)", mode);
    
        switch (mode)
        {
          case GL_FRONT:
          case GL_BACK:
          case GL_FRONT_AND_BACK:
            {
                gl::Context *context = gl::getNonLostContext();
    
                if (context)
                {
                    context->getState().setCullMode(mode);
                }
            }
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    }
    
    void __stdcall glDeleteBuffers(GLsizei n, const GLuint* buffers)
    {
        EVENT("(GLsizei n = %d, const GLuint* buffers = 0x%0.8p)", n, buffers);
    
        if (n < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            for (int i = 0; i < n; i++)
            {
                context->deleteBuffer(buffers[i]);
            }
        }
    }
    
    void __stdcall glDeleteFencesNV(GLsizei n, const GLuint* fences)
    {
        EVENT("(GLsizei n = %d, const GLuint* fences = 0x%0.8p)", n, fences);
    
        if (n < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            for (int i = 0; i < n; i++)
            {
                context->deleteFenceNV(fences[i]);
            }
        }
    }
    
    void __stdcall glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers)
    {
        EVENT("(GLsizei n = %d, const GLuint* framebuffers = 0x%0.8p)", n, framebuffers);
    
        if (n < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            for (int i = 0; i < n; i++)
            {
                if (framebuffers[i] != 0)
                {
                    context->deleteFramebuffer(framebuffers[i]);
                }
            }
        }
    }
    
    void __stdcall glDeleteProgram(GLuint program)
    {
        EVENT("(GLuint program = %d)", program);
    
        if (program == 0)
        {
            return;
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!context->getProgram(program))
            {
                if(context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            context->deleteProgram(program);
        }
    }
    
    void __stdcall glDeleteQueriesEXT(GLsizei n, const GLuint *ids)
    {
        EVENT("(GLsizei n = %d, const GLuint *ids = 0x%0.8p)", n, ids);
    
        if (n < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            for (int i = 0; i < n; i++)
            {
                context->deleteQuery(ids[i]);
            }
        }
    }
    
    void __stdcall glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers)
    {
        EVENT("(GLsizei n = %d, const GLuint* renderbuffers = 0x%0.8p)", n, renderbuffers);
    
        if (n < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            for (int i = 0; i < n; i++)
            {
                context->deleteRenderbuffer(renderbuffers[i]);
            }
        }
    }
    
    void __stdcall glDeleteShader(GLuint shader)
    {
        EVENT("(GLuint shader = %d)", shader);
    
        if (shader == 0)
        {
            return;
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!context->getShader(shader))
            {
                if(context->getProgram(shader))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            context->deleteShader(shader);
        }
    }
    
    void __stdcall glDeleteTextures(GLsizei n, const GLuint* textures)
    {
        EVENT("(GLsizei n = %d, const GLuint* textures = 0x%0.8p)", n, textures);
    
        if (n < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            for (int i = 0; i < n; i++)
            {
                if (textures[i] != 0)
                {
                    context->deleteTexture(textures[i]);
                }
            }
        }
    }
    
    void __stdcall glDepthFunc(GLenum func)
    {
        EVENT("(GLenum func = 0x%X)", func);
    
        switch (func)
        {
          case GL_NEVER:
          case GL_ALWAYS:
          case GL_LESS:
          case GL_LEQUAL:
          case GL_EQUAL:
          case GL_GREATER:
          case GL_GEQUAL:
          case GL_NOTEQUAL:
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setDepthFunc(func);
        }
    }
    
    void __stdcall glDepthMask(GLboolean flag)
    {
        EVENT("(GLboolean flag = %u)", flag);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setDepthMask(flag != GL_FALSE);
        }
    }
    
    void __stdcall glDepthRangef(GLclampf zNear, GLclampf zFar)
    {
        EVENT("(GLclampf zNear = %f, GLclampf zFar = %f)", zNear, zFar);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setDepthRange(zNear, zFar);
        }
    }
    
    void __stdcall glDetachShader(GLuint program, GLuint shader)
    {
        EVENT("(GLuint program = %d, GLuint shader = %d)", program, shader);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
    
            gl::Program *programObject = context->getProgram(program);
            gl::Shader *shaderObject = context->getShader(shader);
    
            if (!programObject)
            {
                gl::Shader *shaderByProgramHandle;
                shaderByProgramHandle = context->getShader(program);
                if (!shaderByProgramHandle)
                {
                    return gl::error(GL_INVALID_VALUE);
                }
                else
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
            }
    
            if (!shaderObject)
            {
                gl::Program *programByShaderHandle = context->getProgram(shader);
                if (!programByShaderHandle)
                {
                    return gl::error(GL_INVALID_VALUE);
                }
                else
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
            }
    
            if (!programObject->detachShader(shaderObject))
            {
                return gl::error(GL_INVALID_OPERATION);
            }
        }
    }
    
    void __stdcall glDisable(GLenum cap)
    {
        EVENT("(GLenum cap = 0x%X)", cap);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidCap(context, cap))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            context->getState().setEnableFeature(cap, false);
        }
    }
    
    void __stdcall glDisableVertexAttribArray(GLuint index)
    {
        EVENT("(GLuint index = %d)", index);
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setEnableVertexAttribArray(index, false);
        }
    }
    
    void __stdcall glDrawArrays(GLenum mode, GLint first, GLsizei count)
    {
        EVENT("(GLenum mode = 0x%X, GLint first = %d, GLsizei count = %d)", mode, first, count);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateDrawArrays(context, mode, first, count))
            {
                return;
            }
    
            context->drawArrays(mode, first, count, 0);
        }
    }
    
    void __stdcall glDrawArraysInstancedANGLE(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
    {
        EVENT("(GLenum mode = 0x%X, GLint first = %d, GLsizei count = %d, GLsizei primcount = %d)", mode, first, count, primcount);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateDrawArraysInstanced(context, mode, first, count, primcount))
            {
                return;
            }
    
            context->drawArrays(mode, first, count, primcount);
        }
    }
    
    void __stdcall glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices)
    {
        EVENT("(GLenum mode = 0x%X, GLsizei count = %d, GLenum type = 0x%X, const GLvoid* indices = 0x%0.8p)",
              mode, count, type, indices);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateDrawElements(context, mode, count, type, indices))
            {
                return;
            }
    
            context->drawElements(mode, count, type, indices, 0);
        }
    }
    
    void __stdcall glDrawElementsInstancedANGLE(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei primcount)
    {
        EVENT("(GLenum mode = 0x%X, GLsizei count = %d, GLenum type = 0x%X, const GLvoid* indices = 0x%0.8p, GLsizei primcount = %d)",
              mode, count, type, indices, primcount);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateDrawElementsInstanced(context, mode, count, type, indices, primcount))
            {
                return;
            }
    
            context->drawElements(mode, count, type, indices, primcount);
        }
    }
    
    void __stdcall glEnable(GLenum cap)
    {
        EVENT("(GLenum cap = 0x%X)", cap);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidCap(context, cap))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            context->getState().setEnableFeature(cap, true);
        }
    }
    
    void __stdcall glEnableVertexAttribArray(GLuint index)
    {
        EVENT("(GLuint index = %d)", index);
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setEnableVertexAttribArray(index, true);
        }
    }
    
    void __stdcall glEndQueryEXT(GLenum target)
    {
        EVENT("GLenum target = 0x%X)", target);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateEndQuery(context, target))
            {
                return;
            }
    
            context->endQuery(target);
        }
    }
    
    void __stdcall glFinishFenceNV(GLuint fence)
    {
        EVENT("(GLuint fence = %d)", fence);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::FenceNV *fenceObject = context->getFenceNV(fence);
    
            if (fenceObject == NULL)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (fenceObject->isFence() != GL_TRUE)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            fenceObject->finishFence();
        }
    }
    
    void __stdcall glFinish(void)
    {
        EVENT("()");
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->sync(true);
        }
    }
    
    void __stdcall glFlush(void)
    {
        EVENT("()");
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->sync(false);
        }
    }
    
    void __stdcall glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
    {
        EVENT("(GLenum target = 0x%X, GLenum attachment = 0x%X, GLenum renderbuffertarget = 0x%X, "
              "GLuint renderbuffer = %d)", target, attachment, renderbuffertarget, renderbuffer);
    
        if (!gl::ValidFramebufferTarget(target) || (renderbuffertarget != GL_RENDERBUFFER && renderbuffer != 0))
        {
            return gl::error(GL_INVALID_ENUM);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!gl::ValidateFramebufferRenderbufferParameters(context, target, attachment, renderbuffertarget, renderbuffer))
            {
                return;
            }
    
            gl::Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target);
            ASSERT(framebuffer);
    
            if (attachment >= GL_COLOR_ATTACHMENT0_EXT && attachment <= GL_COLOR_ATTACHMENT15_EXT)
            {
                unsigned int colorAttachment = (attachment - GL_COLOR_ATTACHMENT0_EXT);
                framebuffer->setColorbuffer(colorAttachment, GL_RENDERBUFFER, renderbuffer, 0, 0);
            }
            else
            {
                switch (attachment)
                {
                  case GL_DEPTH_ATTACHMENT:
                    framebuffer->setDepthbuffer(GL_RENDERBUFFER, renderbuffer, 0, 0);
                    break;
                  case GL_STENCIL_ATTACHMENT:
                    framebuffer->setStencilbuffer(GL_RENDERBUFFER, renderbuffer, 0, 0);
                    break;
                  case GL_DEPTH_STENCIL_ATTACHMENT:
                    framebuffer->setDepthStencilBuffer(GL_RENDERBUFFER, renderbuffer, 0, 0);
                    break;
                  default:
                    UNREACHABLE();
                    break;
                }
            }
        }
    }
    
    void __stdcall glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
    {
        EVENT("(GLenum target = 0x%X, GLenum attachment = 0x%X, GLenum textarget = 0x%X, "
              "GLuint texture = %d, GLint level = %d)", target, attachment, textarget, texture, level);
    
        gl::Context *context = gl::getNonLostContext();
        if (context)
        {
            if (!ValidateFramebufferTexture2D(context, target, attachment, textarget, texture, level))
            {
                return;
            }
    
            if (texture == 0)
            {
                textarget = GL_NONE;
            }
    
            gl::Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target);
    
            if (attachment >= GL_COLOR_ATTACHMENT0_EXT && attachment <= GL_COLOR_ATTACHMENT15_EXT)
            {
                const unsigned int colorAttachment = (attachment - GL_COLOR_ATTACHMENT0_EXT);
                framebuffer->setColorbuffer(colorAttachment, textarget, texture, level, 0);
            }
            else
            {
                switch (attachment)
                {
                  case GL_DEPTH_ATTACHMENT:         framebuffer->setDepthbuffer(textarget, texture, level, 0);        break;
                  case GL_STENCIL_ATTACHMENT:       framebuffer->setStencilbuffer(textarget, texture, level, 0);      break;
                  case GL_DEPTH_STENCIL_ATTACHMENT: framebuffer->setDepthStencilBuffer(textarget, texture, level, 0); break;
                }
            }
        }
    }
    
    void __stdcall glFrontFace(GLenum mode)
    {
        EVENT("(GLenum mode = 0x%X)", mode);
    
        switch (mode)
        {
          case GL_CW:
          case GL_CCW:
            {
                gl::Context *context = gl::getNonLostContext();
    
                if (context)
                {
                    context->getState().setFrontFace(mode);
                }
            }
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    }
    
    void __stdcall glGenBuffers(GLsizei n, GLuint* buffers)
    {
        EVENT("(GLsizei n = %d, GLuint* buffers = 0x%0.8p)", n, buffers);
    
        if (n < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            for (int i = 0; i < n; i++)
            {
                buffers[i] = context->createBuffer();
            }
        }
    }
    
    void __stdcall glGenerateMipmap(GLenum target)
    {
        EVENT("(GLenum target = 0x%X)", target);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidTextureTarget(context, target))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            gl::Texture *texture = context->getTargetTexture(target);
    
            if (texture == NULL)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            GLenum internalFormat = texture->getBaseLevelInternalFormat();
            const gl::TextureCaps &formatCaps = context->getTextureCaps().get(internalFormat);
            const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalFormat);
    
            // GenerateMipmap should not generate an INVALID_OPERATION for textures created with
            // unsized formats or that are color renderable and filterable.  Since we do not track if
            // the texture was created with sized or unsized format (only sized formats are stored),
            // it is not possible to make sure the the LUMA formats can generate mipmaps (they should
            // be able to) because they aren't color renderable.  Simply do a special case for LUMA
            // textures since they're the only texture format that can be created with unsized formats
            // that is not color renderable.  New unsized formats are unlikely to be added, since ES2
            // was the last version to use add them.
            bool isLUMA = internalFormat == GL_LUMINANCE8_EXT ||
                          internalFormat == GL_LUMINANCE8_ALPHA8_EXT ||
                          internalFormat == GL_ALPHA8_EXT;
    
            if (formatInfo.depthBits > 0 || formatInfo.stencilBits > 0 || !formatCaps.filterable ||
                (!formatCaps.renderable && !isLUMA) || formatInfo.compressed)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // GL_EXT_sRGB does not support mipmap generation on sRGB textures
            if (context->getClientVersion() == 2 && formatInfo.colorEncoding == GL_SRGB)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // Non-power of 2 ES2 check
            if (!context->getExtensions().textureNPOT && (!gl::isPow2(texture->getBaseLevelWidth()) || !gl::isPow2(texture->getBaseLevelHeight())))
            {
                ASSERT(context->getClientVersion() <= 2 && (target == GL_TEXTURE_2D || target == GL_TEXTURE_CUBE_MAP));
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // Cube completeness check
            if (target == GL_TEXTURE_CUBE_MAP)
            {
                gl::TextureCubeMap *textureCube = static_cast<gl::TextureCubeMap *>(texture);
                if (!textureCube->isCubeComplete())
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
            }
    
            texture->generateMipmaps();
        }
    }
    
    void __stdcall glGenFencesNV(GLsizei n, GLuint* fences)
    {
        EVENT("(GLsizei n = %d, GLuint* fences = 0x%0.8p)", n, fences);
    
        if (n < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            for (int i = 0; i < n; i++)
            {
                fences[i] = context->createFenceNV();
            }
        }
    }
    
    void __stdcall glGenFramebuffers(GLsizei n, GLuint* framebuffers)
    {
        EVENT("(GLsizei n = %d, GLuint* framebuffers = 0x%0.8p)", n, framebuffers);
    
        if (n < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            for (int i = 0; i < n; i++)
            {
                framebuffers[i] = context->createFramebuffer();
            }
        }
    }
    
    void __stdcall glGenQueriesEXT(GLsizei n, GLuint* ids)
    {
        EVENT("(GLsizei n = %d, GLuint* ids = 0x%0.8p)", n, ids);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (n < 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            for (GLsizei i = 0; i < n; i++)
            {
                ids[i] = context->createQuery();
            }
        }
    }
    
    void __stdcall glGenRenderbuffers(GLsizei n, GLuint* renderbuffers)
    {
        EVENT("(GLsizei n = %d, GLuint* renderbuffers = 0x%0.8p)", n, renderbuffers);
    
        if (n < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            for (int i = 0; i < n; i++)
            {
                renderbuffers[i] = context->createRenderbuffer();
            }
        }
    }
    
    void __stdcall glGenTextures(GLsizei n, GLuint* textures)
    {
        EVENT("(GLsizei n = %d, GLuint* textures = 0x%0.8p)", n, textures);
    
        if (n < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            for (int i = 0; i < n; i++)
            {
                textures[i] = context->createTexture();
            }
        }
    }
    
    void __stdcall glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
    {
        EVENT("(GLuint program = %d, GLuint index = %d, GLsizei bufsize = %d, GLsizei *length = 0x%0.8p, "
              "GLint *size = 0x%0.8p, GLenum *type = %0.8p, GLchar *name = %0.8p)",
              program, index, bufsize, length, size, type, name);
    
        if (bufsize < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            if (index >= (GLuint)programObject->getActiveAttributeCount())
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            programObject->getActiveAttribute(index, bufsize, length, size, type, name);
        }
    }
    
    void __stdcall glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name)
    {
        EVENT("(GLuint program = %d, GLuint index = %d, GLsizei bufsize = %d, "
              "GLsizei* length = 0x%0.8p, GLint* size = 0x%0.8p, GLenum* type = 0x%0.8p, GLchar* name = 0x%0.8p)",
              program, index, bufsize, length, size, type, name);
    
        if (bufsize < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            if (index >= (GLuint)programObject->getActiveUniformCount())
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            programObject->getActiveUniform(index, bufsize, length, size, type, name);
        }
    }
    
    void __stdcall glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders)
    {
        EVENT("(GLuint program = %d, GLsizei maxcount = %d, GLsizei* count = 0x%0.8p, GLuint* shaders = 0x%0.8p)",
              program, maxcount, count, shaders);
    
        if (maxcount < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            return programObject->getAttachedShaders(maxcount, count, shaders);
        }
    }
    
    int __stdcall glGetAttribLocation(GLuint program, const GLchar* name)
    {
        EVENT("(GLuint program = %d, const GLchar* name = %s)", program, name);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
    
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION, -1);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE, -1);
                }
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
            if (!programObject->isLinked() || !programBinary)
            {
                return gl::error(GL_INVALID_OPERATION, -1);
            }
    
            return programBinary->getAttributeLocation(name);
        }
    
        return -1;
    }
    
    void __stdcall glGetBooleanv(GLenum pname, GLboolean* params)
    {
        EVENT("(GLenum pname = 0x%X, GLboolean* params = 0x%0.8p)",  pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            GLenum nativeType;
            unsigned int numParams = 0;
            if (!ValidateStateQuery(context, pname, &nativeType, &numParams))
            {
                return;
            }
    
            if (nativeType == GL_BOOL)
            {
                context->getBooleanv(pname, params);
            }
            else
            {
                CastStateValues(context, nativeType, pname, numParams, params);
            }
        }
    }
    
    void __stdcall glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params)
    {
        EVENT("(GLenum target = 0x%X, GLenum pname = 0x%X, GLint* params = 0x%0.8p)", target, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!gl::ValidBufferTarget(context, target))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            if (!gl::ValidBufferParameter(context, pname))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            gl::Buffer *buffer = context->getState().getTargetBuffer(target);
    
            if (!buffer)
            {
                // A null buffer means that "0" is bound to the requested buffer target
                return gl::error(GL_INVALID_OPERATION);
            }
    
            switch (pname)
            {
              case GL_BUFFER_USAGE:
                *params = static_cast<GLint>(buffer->getUsage());
                break;
              case GL_BUFFER_SIZE:
                *params = gl::clampCast<GLint>(buffer->getSize());
                break;
              case GL_BUFFER_ACCESS_FLAGS:
                *params = buffer->getAccessFlags();
                break;
              case GL_BUFFER_MAPPED:
                *params = static_cast<GLint>(buffer->isMapped());
                break;
              case GL_BUFFER_MAP_OFFSET:
                *params = gl::clampCast<GLint>(buffer->getMapOffset());
                break;
              case GL_BUFFER_MAP_LENGTH:
                *params = gl::clampCast<GLint>(buffer->getMapLength());
                break;
              default: UNREACHABLE(); break;
            }
        }
    }
    
    GLenum __stdcall glGetError(void)
    {
        EVENT("()");
    
        gl::Context *context = gl::getContext();
    
        if (context)
        {
            return context->getError();
        }
    
        return GL_NO_ERROR;
    }
    
    void __stdcall glGetFenceivNV(GLuint fence, GLenum pname, GLint *params)
    {
        EVENT("(GLuint fence = %d, GLenum pname = 0x%X, GLint *params = 0x%0.8p)", fence, pname, params);
    
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::FenceNV *fenceObject = context->getFenceNV(fence);
    
            if (fenceObject == NULL)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (fenceObject->isFence() != GL_TRUE)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            switch (pname)
            {
              case GL_FENCE_STATUS_NV:
              case GL_FENCE_CONDITION_NV:
                break;
    
              default: return gl::error(GL_INVALID_ENUM);
            }
    
            params[0] = fenceObject->getFencei(pname);
        }
    }
    
    void __stdcall glGetFloatv(GLenum pname, GLfloat* params)
    {
        EVENT("(GLenum pname = 0x%X, GLfloat* params = 0x%0.8p)", pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            GLenum nativeType;
            unsigned int numParams = 0;
            if (!ValidateStateQuery(context, pname, &nativeType, &numParams))
            {
                return;
            }
    
            if (nativeType == GL_FLOAT)
            {
                context->getFloatv(pname, params);
            }
            else
            {
                CastStateValues(context, nativeType, pname, numParams, params);
            }
        }
    }
    
    void __stdcall glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment, GLenum pname, GLint* params)
    {
        EVENT("(GLenum target = 0x%X, GLenum attachment = 0x%X, GLenum pname = 0x%X, GLint* params = 0x%0.8p)",
              target, attachment, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!gl::ValidFramebufferTarget(target))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            int clientVersion = context->getClientVersion();
    
            switch (pname)
            {
              case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
              case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
              case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
              case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
                break;
              case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
                if (clientVersion < 3 && !context->getExtensions().sRGB)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                break;
              case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
              case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
              case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
              case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
              case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
              case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
              case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
              case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:
                if (clientVersion < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
    
            // Determine if the attachment is a valid enum
            switch (attachment)
            {
              case GL_BACK:
              case GL_FRONT:
              case GL_DEPTH:
              case GL_STENCIL:
              case GL_DEPTH_STENCIL_ATTACHMENT:
                if (clientVersion < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                break;
    
              case GL_DEPTH_ATTACHMENT:
              case GL_STENCIL_ATTACHMENT:
                break;
    
              default:
                if (attachment < GL_COLOR_ATTACHMENT0_EXT ||
                    (attachment - GL_COLOR_ATTACHMENT0_EXT) >= context->getCaps().maxColorAttachments)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                break;
            }
    
            GLuint framebufferHandle = context->getState().getTargetFramebuffer(target)->id();
            gl::Framebuffer *framebuffer = context->getFramebuffer(framebufferHandle);
    
            if (framebufferHandle == 0)
            {
                if (clientVersion < 3)
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
    
                switch (attachment)
                {
                  case GL_BACK:
                  case GL_DEPTH:
                  case GL_STENCIL:
                    break;
                  default:
                    return gl::error(GL_INVALID_OPERATION);
                }
            }
            else
            {
                if (attachment >= GL_COLOR_ATTACHMENT0_EXT && attachment <= GL_COLOR_ATTACHMENT15_EXT)
                {
                    // Valid attachment query
                }
                else
                {
                    switch (attachment)
                    {
                      case GL_DEPTH_ATTACHMENT:
                      case GL_STENCIL_ATTACHMENT:
                        break;
                      case GL_DEPTH_STENCIL_ATTACHMENT:
                        if (framebuffer->hasValidDepthStencil())
                        {
                            return gl::error(GL_INVALID_OPERATION);
                        }
                        break;
                      default:
                        return gl::error(GL_INVALID_OPERATION);
                    }
                }
            }
    
            GLenum attachmentType = GL_NONE;
            GLuint attachmentHandle = 0;
            GLuint attachmentLevel = 0;
            GLuint attachmentLayer = 0;
    
            const gl::FramebufferAttachment *attachmentObject = framebuffer->getAttachment(attachment);
    
            if (attachmentObject)
            {
                attachmentType = attachmentObject->type();
                attachmentHandle = attachmentObject->id();
                attachmentLevel = attachmentObject->mipLevel();
                attachmentLayer = attachmentObject->layer();
            }
    
            GLenum attachmentObjectType;   // Type category
            if (framebufferHandle == 0)
            {
                attachmentObjectType = GL_FRAMEBUFFER_DEFAULT;
            }
            else if (attachmentType == GL_NONE || attachmentType == GL_RENDERBUFFER)
            {
                attachmentObjectType = attachmentType;
            }
            else if (gl::ValidTexture2DDestinationTarget(context, attachmentType))
            {
                attachmentObjectType = GL_TEXTURE;
            }
            else
            {
                UNREACHABLE();
                return;
            }
    
            if (attachmentObjectType == GL_NONE)
            {
                // ES 2.0.25 spec pg 127 states that if the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE
                // is NONE, then querying any other pname will generate INVALID_ENUM.
    
                // ES 3.0.2 spec pg 235 states that if the attachment type is none,
                // GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME will return zero and be an
                // INVALID_OPERATION for all other pnames
    
                switch (pname)
                {
                  case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
                    *params = attachmentObjectType;
                    break;
    
                  case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
                    if (clientVersion < 3)
                    {
                        return gl::error(GL_INVALID_ENUM);
                    }
                    *params = 0;
                    break;
    
                  default:
                    if (clientVersion < 3)
                    {
                        return gl::error(GL_INVALID_ENUM);
                    }
                    else
                    {
                        gl::error(GL_INVALID_OPERATION);
                    }
                }
            }
            else
            {
                ASSERT(attachmentObjectType == GL_RENDERBUFFER || attachmentObjectType == GL_TEXTURE ||
                       attachmentObjectType == GL_FRAMEBUFFER_DEFAULT);
                ASSERT(attachmentObject != NULL);
    
                switch (pname)
                {
                  case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
                    *params = attachmentObjectType;
                    break;
    
                  case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
                    if (attachmentObjectType != GL_RENDERBUFFER && attachmentObjectType != GL_TEXTURE)
                    {
                        return gl::error(GL_INVALID_ENUM);
                    }
                    *params = attachmentHandle;
                    break;
    
                  case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
                    if (attachmentObjectType != GL_TEXTURE)
                    {
                        return gl::error(GL_INVALID_ENUM);
                    }
                    *params = attachmentLevel;
                    break;
    
                  case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
                    if (attachmentObjectType != GL_TEXTURE)
                    {
                        return gl::error(GL_INVALID_ENUM);
                    }
                    *params = gl::IsCubemapTextureTarget(attachmentType) ? attachmentType : 0;
                    break;
    
                  case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
                    *params = attachmentObject->getRedSize();
                    break;
    
                  case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
                    *params = attachmentObject->getGreenSize();
                    break;
    
                  case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
                    *params = attachmentObject->getBlueSize();
                    break;
    
                  case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
                    *params = attachmentObject->getAlphaSize();
                    break;
    
                  case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
                    *params = attachmentObject->getDepthSize();
                    break;
    
                  case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
                    *params = attachmentObject->getStencilSize();
                    break;
    
                  case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
                    if (attachment == GL_DEPTH_STENCIL)
                    {
                        gl::error(GL_INVALID_OPERATION);
                    }
                    *params = attachmentObject->getComponentType();
                    break;
    
                  case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
                    *params = attachmentObject->getColorEncoding();
                    break;
    
                  case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:
                    if (attachmentObjectType != GL_TEXTURE)
                    {
                        return gl::error(GL_INVALID_ENUM);
                    }
                    *params = attachmentLayer;
                    break;
    
                  default:
                    UNREACHABLE();
                    break;
                }
            }
        }
    }
    
    GLenum __stdcall glGetGraphicsResetStatusEXT(void)
    {
        EVENT("()");
    
        gl::Context *context = gl::getContext();
    
        if (context)
        {
            return context->getResetStatus();
        }
    
        return GL_NO_ERROR;
    }
    
    void __stdcall glGetIntegerv(GLenum pname, GLint* params)
    {
        EVENT("(GLenum pname = 0x%X, GLint* params = 0x%0.8p)", pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            GLenum nativeType;
            unsigned int numParams = 0;
    
            if (!ValidateStateQuery(context, pname, &nativeType, &numParams))
            {
                return;
            }
    
            if (nativeType == GL_INT)
            {
                context->getIntegerv(pname, params);
            }
            else
            {
                CastStateValues(context, nativeType, pname, numParams, params);
            }
        }
    }
    
    void __stdcall glGetProgramiv(GLuint program, GLenum pname, GLint* params)
    {
        EVENT("(GLuint program = %d, GLenum pname = %d, GLint* params = 0x%0.8p)", program, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            if (context->getClientVersion() < 3)
            {
                switch (pname)
                {
                  case GL_ACTIVE_UNIFORM_BLOCKS:
                  case GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH:
                  case GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
                  case GL_TRANSFORM_FEEDBACK_VARYINGS:
                  case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH:
                    return gl::error(GL_INVALID_ENUM);
                }
            }
    
            switch (pname)
            {
              case GL_DELETE_STATUS:
                *params = programObject->isFlaggedForDeletion();
                return;
              case GL_LINK_STATUS:
                *params = programObject->isLinked();
                return;
              case GL_VALIDATE_STATUS:
                *params = programObject->isValidated();
                return;
              case GL_INFO_LOG_LENGTH:
                *params = programObject->getInfoLogLength();
                return;
              case GL_ATTACHED_SHADERS:
                *params = programObject->getAttachedShadersCount();
                return;
              case GL_ACTIVE_ATTRIBUTES:
                *params = programObject->getActiveAttributeCount();
                return;
              case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH:
                *params = programObject->getActiveAttributeMaxLength();
                return;
              case GL_ACTIVE_UNIFORMS:
                *params = programObject->getActiveUniformCount();
                return;
              case GL_ACTIVE_UNIFORM_MAX_LENGTH:
                *params = programObject->getActiveUniformMaxLength();
                return;
              case GL_PROGRAM_BINARY_LENGTH_OES:
                *params = programObject->getProgramBinaryLength();
                return;
              case GL_ACTIVE_UNIFORM_BLOCKS:
                *params = programObject->getActiveUniformBlockCount();
                return;
              case GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH:
                *params = programObject->getActiveUniformBlockMaxLength();
                break;
              case GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
                *params = programObject->getTransformFeedbackBufferMode();
                break;
              case GL_TRANSFORM_FEEDBACK_VARYINGS:
                *params = programObject->getTransformFeedbackVaryingCount();
                break;
              case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH:
                *params = programObject->getTransformFeedbackVaryingMaxLength();
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog)
    {
        EVENT("(GLuint program = %d, GLsizei bufsize = %d, GLsizei* length = 0x%0.8p, GLchar* infolog = 0x%0.8p)",
              program, bufsize, length, infolog);
    
        if (bufsize < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            programObject->getInfoLog(bufsize, length, infolog);
        }
    }
    
    void __stdcall glGetQueryivEXT(GLenum target, GLenum pname, GLint *params)
    {
        EVENT("GLenum target = 0x%X, GLenum pname = 0x%X, GLint *params = 0x%0.8p)", target, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidQueryType(context, target))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            switch (pname)
            {
              case GL_CURRENT_QUERY_EXT:
                params[0] = context->getState().getActiveQueryId(target);
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glGetQueryObjectuivEXT(GLuint id, GLenum pname, GLuint *params)
    {
        EVENT("(GLuint id = %d, GLenum pname = 0x%X, GLuint *params = 0x%0.8p)", id, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Query *queryObject = context->getQuery(id, false, GL_NONE);
    
            if (!queryObject)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (context->getState().getActiveQueryId(queryObject->getType()) == id)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            switch(pname)
            {
              case GL_QUERY_RESULT_EXT:
                params[0] = queryObject->getResult();
                break;
              case GL_QUERY_RESULT_AVAILABLE_EXT:
                params[0] = queryObject->isResultAvailable();
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* params)
    {
        EVENT("(GLenum target = 0x%X, GLenum pname = 0x%X, GLint* params = 0x%0.8p)", target, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (target != GL_RENDERBUFFER)
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            if (context->getState().getRenderbufferId() == 0)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            gl::Renderbuffer *renderbuffer = context->getRenderbuffer(context->getState().getRenderbufferId());
    
            switch (pname)
            {
              case GL_RENDERBUFFER_WIDTH:           *params = renderbuffer->getWidth();          break;
              case GL_RENDERBUFFER_HEIGHT:          *params = renderbuffer->getHeight();         break;
              case GL_RENDERBUFFER_INTERNAL_FORMAT: *params = renderbuffer->getInternalFormat(); break;
              case GL_RENDERBUFFER_RED_SIZE:        *params = renderbuffer->getRedSize();        break;
              case GL_RENDERBUFFER_GREEN_SIZE:      *params = renderbuffer->getGreenSize();      break;
              case GL_RENDERBUFFER_BLUE_SIZE:       *params = renderbuffer->getBlueSize();       break;
              case GL_RENDERBUFFER_ALPHA_SIZE:      *params = renderbuffer->getAlphaSize();      break;
              case GL_RENDERBUFFER_DEPTH_SIZE:      *params = renderbuffer->getDepthSize();      break;
              case GL_RENDERBUFFER_STENCIL_SIZE:    *params = renderbuffer->getStencilSize();    break;
              case GL_RENDERBUFFER_SAMPLES_ANGLE:
                if (!context->getExtensions().framebufferMultisample)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = renderbuffer->getSamples();
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glGetShaderiv(GLuint shader, GLenum pname, GLint* params)
    {
        EVENT("(GLuint shader = %d, GLenum pname = %d, GLint* params = 0x%0.8p)", shader, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Shader *shaderObject = context->getShader(shader);
    
            if (!shaderObject)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            switch (pname)
            {
              case GL_SHADER_TYPE:
                *params = shaderObject->getType();
                return;
              case GL_DELETE_STATUS:
                *params = shaderObject->isFlaggedForDeletion();
                return;
              case GL_COMPILE_STATUS:
                *params = shaderObject->isCompiled() ? GL_TRUE : GL_FALSE;
                return;
              case GL_INFO_LOG_LENGTH:
                *params = shaderObject->getInfoLogLength();
                return;
              case GL_SHADER_SOURCE_LENGTH:
                *params = shaderObject->getSourceLength();
                return;
              case GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE:
                *params = shaderObject->getTranslatedSourceLength();
                return;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog)
    {
        EVENT("(GLuint shader = %d, GLsizei bufsize = %d, GLsizei* length = 0x%0.8p, GLchar* infolog = 0x%0.8p)",
              shader, bufsize, length, infolog);
    
        if (bufsize < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Shader *shaderObject = context->getShader(shader);
    
            if (!shaderObject)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            shaderObject->getInfoLog(bufsize, length, infolog);
        }
    }
    
    void __stdcall glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision)
    {
        EVENT("(GLenum shadertype = 0x%X, GLenum precisiontype = 0x%X, GLint* range = 0x%0.8p, GLint* precision = 0x%0.8p)",
              shadertype, precisiontype, range, precision);
    
        switch (shadertype)
        {
          case GL_VERTEX_SHADER:
          case GL_FRAGMENT_SHADER:
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        switch (precisiontype)
        {
          case GL_LOW_FLOAT:
          case GL_MEDIUM_FLOAT:
          case GL_HIGH_FLOAT:
            // Assume IEEE 754 precision
            range[0] = 127;
            range[1] = 127;
            *precision = 23;
            break;
          case GL_LOW_INT:
          case GL_MEDIUM_INT:
          case GL_HIGH_INT:
            // Some (most) hardware only supports single-precision floating-point numbers,
            // which can accurately represent integers up to +/-16777216
            range[0] = 24;
            range[1] = 24;
            *precision = 0;
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    }
    
    void __stdcall glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source)
    {
        EVENT("(GLuint shader = %d, GLsizei bufsize = %d, GLsizei* length = 0x%0.8p, GLchar* source = 0x%0.8p)",
              shader, bufsize, length, source);
    
        if (bufsize < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Shader *shaderObject = context->getShader(shader);
    
            if (!shaderObject)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            shaderObject->getSource(bufsize, length, source);
        }
    }
    
    void __stdcall glGetTranslatedShaderSourceANGLE(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source)
    {
        EVENT("(GLuint shader = %d, GLsizei bufsize = %d, GLsizei* length = 0x%0.8p, GLchar* source = 0x%0.8p)",
              shader, bufsize, length, source);
    
        if (bufsize < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Shader *shaderObject = context->getShader(shader);
    
            if (!shaderObject)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            shaderObject->getTranslatedSource(bufsize, length, source);
        }
    }
    
    const GLubyte* __stdcall glGetString(GLenum name)
    {
        EVENT("(GLenum name = 0x%X)", name);
    
        gl::Context *context = gl::getNonLostContext();
    
        switch (name)
        {
          case GL_VENDOR:
            return (GLubyte*)"Google Inc.";
          case GL_RENDERER:
            return (GLubyte*)((context != NULL) ? context->getRendererString().c_str() : "ANGLE");
          case GL_VERSION:
            if (context->getClientVersion() == 2)
            {
                return (GLubyte*)"OpenGL ES 2.0 (ANGLE " ANGLE_VERSION_STRING ")";
            }
            else
            {
                return (GLubyte*)"OpenGL ES 3.0 (ANGLE " ANGLE_VERSION_STRING ")";
            }
          case GL_SHADING_LANGUAGE_VERSION:
            if (context->getClientVersion() == 2)
            {
                return (GLubyte*)"OpenGL ES GLSL ES 1.00 (ANGLE " ANGLE_VERSION_STRING ")";
            }
            else
            {
                return (GLubyte*)"OpenGL ES GLSL ES 3.00 (ANGLE " ANGLE_VERSION_STRING ")";
            }
          case GL_EXTENSIONS:
            return (GLubyte*)((context != NULL) ? context->getExtensionString().c_str() : "");
          default:
            return gl::error(GL_INVALID_ENUM, (GLubyte*)NULL);
        }
    }
    
    void __stdcall glGetTexParameterfv(GLenum target, GLenum pname, GLfloat* params)
    {
        EVENT("(GLenum target = 0x%X, GLenum pname = 0x%X, GLfloat* params = 0x%0.8p)", target, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Texture *texture = context->getTargetTexture(target);
    
            if (!texture)
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            switch (pname)
            {
              case GL_TEXTURE_MAG_FILTER:
                *params = (GLfloat)texture->getSamplerState().magFilter;
                break;
              case GL_TEXTURE_MIN_FILTER:
                *params = (GLfloat)texture->getSamplerState().minFilter;
                break;
              case GL_TEXTURE_WRAP_S:
                *params = (GLfloat)texture->getSamplerState().wrapS;
                break;
              case GL_TEXTURE_WRAP_T:
                *params = (GLfloat)texture->getSamplerState().wrapT;
                break;
              case GL_TEXTURE_WRAP_R:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = (GLfloat)texture->getSamplerState().wrapR;
                break;
              case GL_TEXTURE_IMMUTABLE_FORMAT:
                // Exposed to ES2.0 through EXT_texture_storage, no client version validation.
                *params = (GLfloat)(texture->isImmutable() ? GL_TRUE : GL_FALSE);
                break;
              case GL_TEXTURE_IMMUTABLE_LEVELS:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = (GLfloat)texture->immutableLevelCount();
                break;
              case GL_TEXTURE_USAGE_ANGLE:
                *params = (GLfloat)texture->getUsage();
                break;
              case GL_TEXTURE_MAX_ANISOTROPY_EXT:
                if (!context->getExtensions().textureFilterAnisotropic)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = (GLfloat)texture->getSamplerState().maxAnisotropy;
                break;
              case GL_TEXTURE_SWIZZLE_R:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = (GLfloat)texture->getSamplerState().swizzleRed;
                break;
              case GL_TEXTURE_SWIZZLE_G:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = (GLfloat)texture->getSamplerState().swizzleGreen;
                break;
              case GL_TEXTURE_SWIZZLE_B:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = (GLfloat)texture->getSamplerState().swizzleBlue;
                break;
              case GL_TEXTURE_SWIZZLE_A:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = (GLfloat)texture->getSamplerState().swizzleAlpha;
                break;
              case GL_TEXTURE_BASE_LEVEL:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = (GLfloat)texture->getSamplerState().baseLevel;
                break;
              case GL_TEXTURE_MAX_LEVEL:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = (GLfloat)texture->getSamplerState().maxLevel;
                break;
              case GL_TEXTURE_MIN_LOD:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = texture->getSamplerState().minLod;
                break;
              case GL_TEXTURE_MAX_LOD:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = texture->getSamplerState().maxLod;
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glGetTexParameteriv(GLenum target, GLenum pname, GLint* params)
    {
        EVENT("(GLenum target = 0x%X, GLenum pname = 0x%X, GLint* params = 0x%0.8p)", target, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Texture *texture = context->getTargetTexture(target);
    
            if (!texture)
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            switch (pname)
            {
              case GL_TEXTURE_MAG_FILTER:
                *params = texture->getSamplerState().magFilter;
                break;
              case GL_TEXTURE_MIN_FILTER:
                *params = texture->getSamplerState().minFilter;
                break;
              case GL_TEXTURE_WRAP_S:
                *params = texture->getSamplerState().wrapS;
                break;
              case GL_TEXTURE_WRAP_T:
                *params = texture->getSamplerState().wrapT;
                break;
              case GL_TEXTURE_WRAP_R:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = texture->getSamplerState().wrapR;
                break;
              case GL_TEXTURE_IMMUTABLE_FORMAT:
                // Exposed to ES2.0 through EXT_texture_storage, no client version validation.
                *params = texture->isImmutable() ? GL_TRUE : GL_FALSE;
                break;
              case GL_TEXTURE_IMMUTABLE_LEVELS:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = texture->immutableLevelCount();
                break;
              case GL_TEXTURE_USAGE_ANGLE:
                *params = texture->getUsage();
                break;
              case GL_TEXTURE_MAX_ANISOTROPY_EXT:
                if (!context->getExtensions().textureFilterAnisotropic)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = (GLint)texture->getSamplerState().maxAnisotropy;
                break;
              case GL_TEXTURE_SWIZZLE_R:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = texture->getSamplerState().swizzleRed;
                break;
              case GL_TEXTURE_SWIZZLE_G:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = texture->getSamplerState().swizzleGreen;
                break;
              case GL_TEXTURE_SWIZZLE_B:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = texture->getSamplerState().swizzleBlue;
                break;
              case GL_TEXTURE_SWIZZLE_A:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = texture->getSamplerState().swizzleAlpha;
                break;
              case GL_TEXTURE_BASE_LEVEL:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = texture->getSamplerState().baseLevel;
                break;
              case GL_TEXTURE_MAX_LEVEL:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = texture->getSamplerState().maxLevel;
                break;
              case GL_TEXTURE_MIN_LOD:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = (GLint)texture->getSamplerState().minLod;
                break;
              case GL_TEXTURE_MAX_LOD:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                *params = (GLint)texture->getSamplerState().maxLod;
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glGetnUniformfvEXT(GLuint program, GLint location, GLsizei bufSize, GLfloat* params)
    {
        EVENT("(GLuint program = %d, GLint location = %d, GLsizei bufSize = %d, GLfloat* params = 0x%0.8p)",
              program, location, bufSize, params);
    
        if (bufSize < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (program == 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject || !programObject->isLinked())
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
            if (!programBinary)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!programBinary->getUniformfv(location, &bufSize, params))
            {
                return gl::error(GL_INVALID_OPERATION);
            }
        }
    }
    
    void __stdcall glGetUniformfv(GLuint program, GLint location, GLfloat* params)
    {
        EVENT("(GLuint program = %d, GLint location = %d, GLfloat* params = 0x%0.8p)", program, location, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (program == 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject || !programObject->isLinked())
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
            if (!programBinary)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!programBinary->getUniformfv(location, NULL, params))
            {
                return gl::error(GL_INVALID_OPERATION);
            }
        }
    }
    
    void __stdcall glGetnUniformivEXT(GLuint program, GLint location, GLsizei bufSize, GLint* params)
    {
        EVENT("(GLuint program = %d, GLint location = %d, GLsizei bufSize = %d, GLint* params = 0x%0.8p)",
              program, location, bufSize, params);
    
        if (bufSize < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (program == 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject || !programObject->isLinked())
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
            if (!programBinary)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!programBinary->getUniformiv(location, &bufSize, params))
            {
                return gl::error(GL_INVALID_OPERATION);
            }
        }
    }
    
    void __stdcall glGetUniformiv(GLuint program, GLint location, GLint* params)
    {
        EVENT("(GLuint program = %d, GLint location = %d, GLint* params = 0x%0.8p)", program, location, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (program == 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject || !programObject->isLinked())
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
            if (!programBinary)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!programBinary->getUniformiv(location, NULL, params))
            {
                return gl::error(GL_INVALID_OPERATION);
            }
        }
    }
    
    int __stdcall glGetUniformLocation(GLuint program, const GLchar* name)
    {
        EVENT("(GLuint program = %d, const GLchar* name = 0x%0.8p)", program, name);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (strstr(name, "gl_") == name)
        {
            return -1;
        }
    
        if (context)
        {
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION, -1);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE, -1);
                }
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
            if (!programObject->isLinked() || !programBinary)
            {
                return gl::error(GL_INVALID_OPERATION, -1);
            }
    
            return programBinary->getUniformLocation(name);
        }
    
        return -1;
    }
    
    void __stdcall glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params)
    {
        EVENT("(GLuint index = %d, GLenum pname = 0x%X, GLfloat* params = 0x%0.8p)", index, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (index >= gl::MAX_VERTEX_ATTRIBS)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            const gl::VertexAttribute &attribState = context->getState().getVertexAttribState(index);
            if (!gl::ValidateGetVertexAttribParameters(pname, context->getClientVersion()))
            {
                return;
            }
    
            if (pname == GL_CURRENT_VERTEX_ATTRIB)
            {
                const gl::VertexAttribCurrentValueData &currentValueData = context->getState().getVertexAttribCurrentValue(index);
                for (int i = 0; i < 4; ++i)
                {
                    params[i] = currentValueData.FloatValues[i];
                }
            }
            else
            {
                *params = gl::QuerySingleVertexAttributeParameter<GLfloat>(attribState, pname);
            }
        }
    }
    
    void __stdcall glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params)
    {
        EVENT("(GLuint index = %d, GLenum pname = 0x%X, GLint* params = 0x%0.8p)", index, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (index >= gl::MAX_VERTEX_ATTRIBS)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            const gl::VertexAttribute &attribState = context->getState().getVertexAttribState(index);
    
            if (!gl::ValidateGetVertexAttribParameters(pname, context->getClientVersion()))
            {
                return;
            }
    
            if (pname == GL_CURRENT_VERTEX_ATTRIB)
            {
                const gl::VertexAttribCurrentValueData &currentValueData = context->getState().getVertexAttribCurrentValue(index);
                for (int i = 0; i < 4; ++i)
                {
                    float currentValue = currentValueData.FloatValues[i];
                    params[i] = gl::iround<GLint>(currentValue);
                }
            }
            else
            {
                *params = gl::QuerySingleVertexAttributeParameter<GLint>(attribState, pname);
            }
        }
    }
    
    void __stdcall glGetVertexAttribPointerv(GLuint index, GLenum pname, GLvoid** pointer)
    {
        EVENT("(GLuint index = %d, GLenum pname = 0x%X, GLvoid** pointer = 0x%0.8p)", index, pname, pointer);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (index >= gl::MAX_VERTEX_ATTRIBS)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            if (pname != GL_VERTEX_ATTRIB_ARRAY_POINTER)
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            *pointer = const_cast<GLvoid*>(context->getState().getVertexAttribPointer(index));
        }
    }
    
    void __stdcall glHint(GLenum target, GLenum mode)
    {
        EVENT("(GLenum target = 0x%X, GLenum mode = 0x%X)", target, mode);
    
        switch (mode)
        {
          case GL_FASTEST:
          case GL_NICEST:
          case GL_DONT_CARE:
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        gl::Context *context = gl::getNonLostContext();
        switch (target)
        {
          case GL_GENERATE_MIPMAP_HINT:
            if (context) context->getState().setGenerateMipmapHint(mode);
            break;
          case GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES:
            if (context) context->getState().setFragmentShaderDerivativeHint(mode);
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    }
    
    GLboolean __stdcall glIsBuffer(GLuint buffer)
    {
        EVENT("(GLuint buffer = %d)", buffer);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context && buffer)
        {
            gl::Buffer *bufferObject = context->getBuffer(buffer);
    
            if (bufferObject)
            {
                return GL_TRUE;
            }
        }
    
        return GL_FALSE;
    }
    
    GLboolean __stdcall glIsEnabled(GLenum cap)
    {
        EVENT("(GLenum cap = 0x%X)", cap);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidCap(context, cap))
            {
                return gl::error(GL_INVALID_ENUM, false);
            }
    
            return context->getState().getEnableFeature(cap);
        }
    
        return false;
    }
    
    GLboolean __stdcall glIsFenceNV(GLuint fence)
    {
        EVENT("(GLuint fence = %d)", fence);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::FenceNV *fenceObject = context->getFenceNV(fence);
    
            if (fenceObject == NULL)
            {
                return GL_FALSE;
            }
    
            return fenceObject->isFence();
        }
    
        return GL_FALSE;
    }
    
    GLboolean __stdcall glIsFramebuffer(GLuint framebuffer)
    {
        EVENT("(GLuint framebuffer = %d)", framebuffer);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context && framebuffer)
        {
            gl::Framebuffer *framebufferObject = context->getFramebuffer(framebuffer);
    
            if (framebufferObject)
            {
                return GL_TRUE;
            }
        }
    
        return GL_FALSE;
    }
    
    GLboolean __stdcall glIsProgram(GLuint program)
    {
        EVENT("(GLuint program = %d)", program);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context && program)
        {
            gl::Program *programObject = context->getProgram(program);
    
            if (programObject)
            {
                return GL_TRUE;
            }
        }
    
        return GL_FALSE;
    }
    
    GLboolean __stdcall glIsQueryEXT(GLuint id)
    {
        EVENT("(GLuint id = %d)", id);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            return (context->getQuery(id, false, GL_NONE) != NULL) ? GL_TRUE : GL_FALSE;
        }
    
        return GL_FALSE;
    }
    
    GLboolean __stdcall glIsRenderbuffer(GLuint renderbuffer)
    {
        EVENT("(GLuint renderbuffer = %d)", renderbuffer);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context && renderbuffer)
        {
            gl::Renderbuffer *renderbufferObject = context->getRenderbuffer(renderbuffer);
    
            if (renderbufferObject)
            {
                return GL_TRUE;
            }
        }
    
        return GL_FALSE;
    }
    
    GLboolean __stdcall glIsShader(GLuint shader)
    {
        EVENT("(GLuint shader = %d)", shader);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context && shader)
        {
            gl::Shader *shaderObject = context->getShader(shader);
    
            if (shaderObject)
            {
                return GL_TRUE;
            }
        }
    
        return GL_FALSE;
    }
    
    GLboolean __stdcall glIsTexture(GLuint texture)
    {
        EVENT("(GLuint texture = %d)", texture);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context && texture)
        {
            gl::Texture *textureObject = context->getTexture(texture);
    
            if (textureObject)
            {
                return GL_TRUE;
            }
        }
    
        return GL_FALSE;
    }
    
    void __stdcall glLineWidth(GLfloat width)
    {
        EVENT("(GLfloat width = %f)", width);
    
        if (width <= 0.0f)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setLineWidth(width);
        }
    }
    
    void __stdcall glLinkProgram(GLuint program)
    {
        EVENT("(GLuint program = %d)", program);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            context->linkProgram(program);
        }
    }
    
    void __stdcall glPixelStorei(GLenum pname, GLint param)
    {
        EVENT("(GLenum pname = 0x%X, GLint param = %d)", pname, param);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            switch (pname)
            {
              case GL_UNPACK_ALIGNMENT:
                if (param != 1 && param != 2 && param != 4 && param != 8)
                {
                    return gl::error(GL_INVALID_VALUE);
                }
    
                context->getState().setUnpackAlignment(param);
                break;
    
              case GL_PACK_ALIGNMENT:
                if (param != 1 && param != 2 && param != 4 && param != 8)
                {
                    return gl::error(GL_INVALID_VALUE);
                }
    
                context->getState().setPackAlignment(param);
                break;
    
              case GL_PACK_REVERSE_ROW_ORDER_ANGLE:
                context->getState().setPackReverseRowOrder(param != 0);
                break;
    
              case GL_UNPACK_IMAGE_HEIGHT:
              case GL_UNPACK_SKIP_IMAGES:
              case GL_UNPACK_ROW_LENGTH:
              case GL_UNPACK_SKIP_ROWS:
              case GL_UNPACK_SKIP_PIXELS:
              case GL_PACK_ROW_LENGTH:
              case GL_PACK_SKIP_ROWS:
              case GL_PACK_SKIP_PIXELS:
                if (context->getClientVersion() < 3)
                {
                    return gl::error(GL_INVALID_ENUM);
                }
                UNIMPLEMENTED();
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glPolygonOffset(GLfloat factor, GLfloat units)
    {
        EVENT("(GLfloat factor = %f, GLfloat units = %f)", factor, units);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setPolygonOffsetParams(factor, units);
        }
    }
    
    void __stdcall glReadnPixelsEXT(GLint x, GLint y, GLsizei width, GLsizei height,
                                    GLenum format, GLenum type, GLsizei bufSize,
                                    GLvoid *data)
    {
        EVENT("(GLint x = %d, GLint y = %d, GLsizei width = %d, GLsizei height = %d, "
              "GLenum format = 0x%X, GLenum type = 0x%X, GLsizei bufSize = 0x%d, GLvoid *data = 0x%0.8p)",
              x, y, width, height, format, type, bufSize, data);
    
        if (width < 0 || height < 0 || bufSize < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!gl::ValidateReadPixelsParameters(context, x, y, width, height,
                                                  format, type, &bufSize, data))
            {
                return;
            }
    
            context->readPixels(x, y, width, height, format, type, &bufSize, data);
        }
    }
    
    void __stdcall glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
                                GLenum format, GLenum type, GLvoid* pixels)
    {
        EVENT("(GLint x = %d, GLint y = %d, GLsizei width = %d, GLsizei height = %d, "
              "GLenum format = 0x%X, GLenum type = 0x%X, GLvoid* pixels = 0x%0.8p)",
              x, y, width, height, format, type,  pixels);
    
        if (width < 0 || height < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!gl::ValidateReadPixelsParameters(context, x, y, width, height,
                                                  format, type, NULL, pixels))
            {
                return;
            }
    
            context->readPixels(x, y, width, height, format, type, NULL, pixels);
        }
    }
    
    void __stdcall glReleaseShaderCompiler(void)
    {
        EVENT("()");
    
        gl::Shader::releaseCompiler();
    }
    
    void __stdcall glRenderbufferStorageMultisampleANGLE(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
    {
        EVENT("(GLenum target = 0x%X, GLsizei samples = %d, GLenum internalformat = 0x%X, GLsizei width = %d, GLsizei height = %d)",
              target, samples, internalformat, width, height);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateRenderbufferStorageParameters(context, target, samples, internalformat,
                                                       width, height, true))
            {
                return;
            }
    
            context->setRenderbufferStorage(width, height, internalformat, samples);
        }
    }
    
    void __stdcall glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
    {
        glRenderbufferStorageMultisampleANGLE(target, 0, internalformat, width, height);
    }
    
    void __stdcall glSampleCoverage(GLclampf value, GLboolean invert)
    {
        EVENT("(GLclampf value = %f, GLboolean invert = %u)", value, invert);
    
        gl::Context* context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setSampleCoverageParams(gl::clamp01(value), invert == GL_TRUE);
        }
    }
    
    void __stdcall glSetFenceNV(GLuint fence, GLenum condition)
    {
        EVENT("(GLuint fence = %d, GLenum condition = 0x%X)", fence, condition);
    
        if (condition != GL_ALL_COMPLETED_NV)
        {
            return gl::error(GL_INVALID_ENUM);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::FenceNV *fenceObject = context->getFenceNV(fence);
    
            if (fenceObject == NULL)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            fenceObject->setFence(condition);
        }
    }
    
    void __stdcall glScissor(GLint x, GLint y, GLsizei width, GLsizei height)
    {
        EVENT("(GLint x = %d, GLint y = %d, GLsizei width = %d, GLsizei height = %d)", x, y, width, height);
    
        if (width < 0 || height < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context* context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setScissorParams(x, y, width, height);
        }
    }
    
    void __stdcall glShaderBinary(GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length)
    {
        EVENT("(GLsizei n = %d, const GLuint* shaders = 0x%0.8p, GLenum binaryformat = 0x%X, "
              "const GLvoid* binary = 0x%0.8p, GLsizei length = %d)",
              n, shaders, binaryformat, binary, length);
    
        // No binary shader formats are supported.
        return gl::error(GL_INVALID_ENUM);
    }
    
    void __stdcall glShaderSource(GLuint shader, GLsizei count, const GLchar* const* string, const GLint* length)
    {
        EVENT("(GLuint shader = %d, GLsizei count = %d, const GLchar** string = 0x%0.8p, const GLint* length = 0x%0.8p)",
              shader, count, string, length);
    
        if (count < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Shader *shaderObject = context->getShader(shader);
    
            if (!shaderObject)
            {
                if (context->getProgram(shader))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            shaderObject->setSource(count, string, length);
        }
    }
    
    void __stdcall glStencilFunc(GLenum func, GLint ref, GLuint mask)
    {
        glStencilFuncSeparate(GL_FRONT_AND_BACK, func, ref, mask);
    }
    
    void __stdcall glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask)
    {
        EVENT("(GLenum face = 0x%X, GLenum func = 0x%X, GLint ref = %d, GLuint mask = %d)", face, func, ref, mask);
    
        switch (face)
        {
          case GL_FRONT:
          case GL_BACK:
          case GL_FRONT_AND_BACK:
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        switch (func)
        {
          case GL_NEVER:
          case GL_ALWAYS:
          case GL_LESS:
          case GL_LEQUAL:
          case GL_EQUAL:
          case GL_GEQUAL:
          case GL_GREATER:
          case GL_NOTEQUAL:
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (face == GL_FRONT || face == GL_FRONT_AND_BACK)
            {
                context->getState().setStencilParams(func, ref, mask);
            }
    
            if (face == GL_BACK || face == GL_FRONT_AND_BACK)
            {
                context->getState().setStencilBackParams(func, ref, mask);
            }
        }
    }
    
    void __stdcall glStencilMask(GLuint mask)
    {
        glStencilMaskSeparate(GL_FRONT_AND_BACK, mask);
    }
    
    void __stdcall glStencilMaskSeparate(GLenum face, GLuint mask)
    {
        EVENT("(GLenum face = 0x%X, GLuint mask = %d)", face, mask);
    
        switch (face)
        {
          case GL_FRONT:
          case GL_BACK:
          case GL_FRONT_AND_BACK:
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (face == GL_FRONT || face == GL_FRONT_AND_BACK)
            {
                context->getState().setStencilWritemask(mask);
            }
    
            if (face == GL_BACK || face == GL_FRONT_AND_BACK)
            {
                context->getState().setStencilBackWritemask(mask);
            }
        }
    }
    
    void __stdcall glStencilOp(GLenum fail, GLenum zfail, GLenum zpass)
    {
        glStencilOpSeparate(GL_FRONT_AND_BACK, fail, zfail, zpass);
    }
    
    void __stdcall glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum zpass)
    {
        EVENT("(GLenum face = 0x%X, GLenum fail = 0x%X, GLenum zfail = 0x%X, GLenum zpas = 0x%Xs)",
              face, fail, zfail, zpass);
    
        switch (face)
        {
          case GL_FRONT:
          case GL_BACK:
          case GL_FRONT_AND_BACK:
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        switch (fail)
        {
          case GL_ZERO:
          case GL_KEEP:
          case GL_REPLACE:
          case GL_INCR:
          case GL_DECR:
          case GL_INVERT:
          case GL_INCR_WRAP:
          case GL_DECR_WRAP:
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        switch (zfail)
        {
          case GL_ZERO:
          case GL_KEEP:
          case GL_REPLACE:
          case GL_INCR:
          case GL_DECR:
          case GL_INVERT:
          case GL_INCR_WRAP:
          case GL_DECR_WRAP:
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        switch (zpass)
        {
          case GL_ZERO:
          case GL_KEEP:
          case GL_REPLACE:
          case GL_INCR:
          case GL_DECR:
          case GL_INVERT:
          case GL_INCR_WRAP:
          case GL_DECR_WRAP:
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (face == GL_FRONT || face == GL_FRONT_AND_BACK)
            {
                context->getState().setStencilOperations(fail, zfail, zpass);
            }
    
            if (face == GL_BACK || face == GL_FRONT_AND_BACK)
            {
                context->getState().setStencilBackOperations(fail, zfail, zpass);
            }
        }
    }
    
    GLboolean __stdcall glTestFenceNV(GLuint fence)
    {
        EVENT("(GLuint fence = %d)", fence);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::FenceNV *fenceObject = context->getFenceNV(fence);
    
            if (fenceObject == NULL)
            {
                return gl::error(GL_INVALID_OPERATION, GL_TRUE);
            }
    
            if (fenceObject->isFence() != GL_TRUE)
            {
                return gl::error(GL_INVALID_OPERATION, GL_TRUE);
            }
    
            return fenceObject->testFence();
        }
    
        return GL_TRUE;
    }
    
    void __stdcall glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height,
                                GLint border, GLenum format, GLenum type, const GLvoid* pixels)
    {
        EVENT("(GLenum target = 0x%X, GLint level = %d, GLint internalformat = %d, GLsizei width = %d, GLsizei height = %d, "
              "GLint border = %d, GLenum format = 0x%X, GLenum type = 0x%X, const GLvoid* pixels = 0x%0.8p)",
              target, level, internalformat, width, height, border, format, type, pixels);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3 &&
                !ValidateES2TexImageParameters(context, target, level, internalformat, false, false,
                                               0, 0, width, height, border, format, type, pixels))
            {
                return;
            }
    
            if (context->getClientVersion() >= 3 &&
                !ValidateES3TexImageParameters(context, target, level, internalformat, false, false,
                                               0, 0, 0, width, height, 1, border, format, type, pixels))
            {
                return;
            }
    
            switch (target)
            {
              case GL_TEXTURE_2D:
                {
                    gl::Texture2D *texture = context->getTexture2D();
                    texture->setImage(level, width, height, internalformat, format, type, context->getState().getUnpackState(), pixels);
                }
                break;
              case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
                {
                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
                    texture->setImagePosX(level, width, height, internalformat, format, type, context->getState().getUnpackState(), pixels);
                }
                break;
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
                {
                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
                    texture->setImageNegX(level, width, height, internalformat, format, type, context->getState().getUnpackState(), pixels);
                }
                break;
              case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
                {
                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
                    texture->setImagePosY(level, width, height, internalformat, format, type, context->getState().getUnpackState(), pixels);
                }
                break;
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
                {
                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
                    texture->setImageNegY(level, width, height, internalformat, format, type, context->getState().getUnpackState(), pixels);
                }
                break;
              case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
                {
                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
                    texture->setImagePosZ(level, width, height, internalformat, format, type, context->getState().getUnpackState(), pixels);
                }
                break;
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
                {
                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
                    texture->setImageNegZ(level, width, height, internalformat, format, type, context->getState().getUnpackState(), pixels);
                }
                break;
              default: UNREACHABLE();
            }
        }
    }
    
    void __stdcall glTexParameterf(GLenum target, GLenum pname, GLfloat param)
    {
        EVENT("(GLenum target = 0x%X, GLenum pname = 0x%X, GLint param = %f)", target, pname, param);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateTexParamParameters(context, pname, static_cast<GLint>(param)))
            {
                return;
            }
    
            gl::Texture *texture = context->getTargetTexture(target);
    
            if (!texture)
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            switch (pname)
            {
              case GL_TEXTURE_WRAP_S:               texture->getSamplerState().wrapS = gl::uiround<GLenum>(param);        break;
              case GL_TEXTURE_WRAP_T:               texture->getSamplerState().wrapT = gl::uiround<GLenum>(param);        break;
              case GL_TEXTURE_WRAP_R:               texture->getSamplerState().wrapR = gl::uiround<GLenum>(param);        break;
              case GL_TEXTURE_MIN_FILTER:           texture->getSamplerState().minFilter = gl::uiround<GLenum>(param);    break;
              case GL_TEXTURE_MAG_FILTER:           texture->getSamplerState().magFilter = gl::uiround<GLenum>(param);    break;
              case GL_TEXTURE_USAGE_ANGLE:          texture->setUsage(gl::uiround<GLenum>(param));                        break;
              case GL_TEXTURE_MAX_ANISOTROPY_EXT:   texture->getSamplerState().maxAnisotropy = std::min(param, context->getExtensions().maxTextureAnisotropy); break;
              case GL_TEXTURE_COMPARE_MODE:         texture->getSamplerState().compareMode = gl::uiround<GLenum>(param);  break;
              case GL_TEXTURE_COMPARE_FUNC:         texture->getSamplerState().compareFunc = gl::uiround<GLenum>(param);  break;
              case GL_TEXTURE_SWIZZLE_R:            texture->getSamplerState().swizzleRed = gl::uiround<GLenum>(param);   break;
              case GL_TEXTURE_SWIZZLE_G:            texture->getSamplerState().swizzleGreen = gl::uiround<GLenum>(param); break;
              case GL_TEXTURE_SWIZZLE_B:            texture->getSamplerState().swizzleBlue = gl::uiround<GLenum>(param);  break;
              case GL_TEXTURE_SWIZZLE_A:            texture->getSamplerState().swizzleAlpha = gl::uiround<GLenum>(param); break;
              case GL_TEXTURE_BASE_LEVEL:           texture->getSamplerState().baseLevel = gl::iround<GLint>(param);      break;
              case GL_TEXTURE_MAX_LEVEL:            texture->getSamplerState().maxLevel = gl::iround<GLint>(param);       break;
              case GL_TEXTURE_MIN_LOD:              texture->getSamplerState().minLod = param;                            break;
              case GL_TEXTURE_MAX_LOD:              texture->getSamplerState().maxLod = param;                            break;
              default: UNREACHABLE(); break;
            }
        }
    }
    
    void __stdcall glTexParameterfv(GLenum target, GLenum pname, const GLfloat* params)
    {
        glTexParameterf(target, pname, (GLfloat)*params);
    }
    
    void __stdcall glTexParameteri(GLenum target, GLenum pname, GLint param)
    {
        EVENT("(GLenum target = 0x%X, GLenum pname = 0x%X, GLint param = %d)", target, pname, param);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateTexParamParameters(context, pname, param))
            {
                return;
            }
    
            gl::Texture *texture = context->getTargetTexture(target);
    
            if (!texture)
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            switch (pname)
            {
              case GL_TEXTURE_WRAP_S:               texture->getSamplerState().wrapS = (GLenum)param;        break;
              case GL_TEXTURE_WRAP_T:               texture->getSamplerState().wrapT = (GLenum)param;        break;
              case GL_TEXTURE_WRAP_R:               texture->getSamplerState().wrapR = (GLenum)param;        break;
              case GL_TEXTURE_MIN_FILTER:           texture->getSamplerState().minFilter = (GLenum)param;    break;
              case GL_TEXTURE_MAG_FILTER:           texture->getSamplerState().magFilter = (GLenum)param;    break;
              case GL_TEXTURE_USAGE_ANGLE:          texture->setUsage((GLenum)param);                        break;
              case GL_TEXTURE_MAX_ANISOTROPY_EXT:   texture->getSamplerState().maxAnisotropy = std::min((float)param, context->getExtensions().maxTextureAnisotropy); break;
              case GL_TEXTURE_COMPARE_MODE:         texture->getSamplerState().compareMode = (GLenum)param;  break;
              case GL_TEXTURE_COMPARE_FUNC:         texture->getSamplerState().compareFunc = (GLenum)param;  break;
              case GL_TEXTURE_SWIZZLE_R:            texture->getSamplerState().swizzleRed = (GLenum)param;   break;
              case GL_TEXTURE_SWIZZLE_G:            texture->getSamplerState().swizzleGreen = (GLenum)param; break;
              case GL_TEXTURE_SWIZZLE_B:            texture->getSamplerState().swizzleBlue = (GLenum)param;  break;
              case GL_TEXTURE_SWIZZLE_A:            texture->getSamplerState().swizzleAlpha = (GLenum)param; break;
              case GL_TEXTURE_BASE_LEVEL:           texture->getSamplerState().baseLevel = param;            break;
              case GL_TEXTURE_MAX_LEVEL:            texture->getSamplerState().maxLevel = param;             break;
              case GL_TEXTURE_MIN_LOD:              texture->getSamplerState().minLod = (GLfloat)param;      break;
              case GL_TEXTURE_MAX_LOD:              texture->getSamplerState().maxLod = (GLfloat)param;      break;
              default: UNREACHABLE(); break;
            }
        }
    }
    
    void __stdcall glTexParameteriv(GLenum target, GLenum pname, const GLint* params)
    {
        glTexParameteri(target, pname, *params);
    }
    
    void __stdcall glTexStorage2DEXT(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
    {
        EVENT("(GLenum target = 0x%X, GLsizei levels = %d, GLenum internalformat = 0x%X, GLsizei width = %d, GLsizei height = %d)",
               target, levels, internalformat, width, height);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!context->getExtensions().textureStorage)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (context->getClientVersion() < 3 &&
                !ValidateES2TexStorageParameters(context, target, levels, internalformat, width, height))
            {
                return;
            }
    
            if (context->getClientVersion() >= 3 &&
                !ValidateES3TexStorageParameters(context, target, levels, internalformat, width, height, 1))
            {
                return;
            }
    
            switch (target)
            {
              case GL_TEXTURE_2D:
                {
                    gl::Texture2D *texture2d = context->getTexture2D();
                    texture2d->storage(levels, internalformat, width, height);
                }
                break;
    
              case GL_TEXTURE_CUBE_MAP:
                {
                    gl::TextureCubeMap *textureCube = context->getTextureCubeMap();
                    textureCube->storage(levels, internalformat, width);
                }
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                   GLenum format, GLenum type, const GLvoid* pixels)
    {
        EVENT("(GLenum target = 0x%X, GLint level = %d, GLint xoffset = %d, GLint yoffset = %d, "
              "GLsizei width = %d, GLsizei height = %d, GLenum format = 0x%X, GLenum type = 0x%X, "
              "const GLvoid* pixels = 0x%0.8p)",
               target, level, xoffset, yoffset, width, height, format, type, pixels);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3 &&
                !ValidateES2TexImageParameters(context, target, level, GL_NONE, false, true,
                                               xoffset, yoffset, width, height, 0, format, type, pixels))
            {
                return;
            }
    
            if (context->getClientVersion() >= 3 &&
                !ValidateES3TexImageParameters(context, target, level, GL_NONE, false, true,
                                               xoffset, yoffset, 0, width, height, 1, 0, format, type, pixels))
            {
                return;
            }
    
            // Zero sized uploads are valid but no-ops
            if (width == 0 || height == 0)
            {
                return;
            }
    
            switch (target)
            {
              case GL_TEXTURE_2D:
                {
                    gl::Texture2D *texture = context->getTexture2D();
                    texture->subImage(level, xoffset, yoffset, width, height, format, type, context->getState().getUnpackState(), pixels);
                }
                break;
    
              case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
              case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
              case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
                {
                    gl::TextureCubeMap *texture = context->getTextureCubeMap();
                    texture->subImage(target, level, xoffset, yoffset, width, height, format, type, context->getState().getUnpackState(), pixels);
                }
                break;
    
              default:
                UNREACHABLE();
            }
        }
    }
    
    void __stdcall glUniform1f(GLint location, GLfloat x)
    {
        glUniform1fv(location, 1, &x);
    }
    
    void __stdcall glUniform1fv(GLint location, GLsizei count, const GLfloat* v)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, const GLfloat* v = 0x%0.8p)", location, count, v);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniform(context, GL_FLOAT, location, count))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniform1fv(location, count, v);
        }
    }
    
    void __stdcall glUniform1i(GLint location, GLint x)
    {
        glUniform1iv(location, 1, &x);
    }
    
    void __stdcall glUniform1iv(GLint location, GLsizei count, const GLint* v)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, const GLint* v = 0x%0.8p)", location, count, v);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniform(context, GL_INT, location, count))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniform1iv(location, count, v);
        }
    }
    
    void __stdcall glUniform2f(GLint location, GLfloat x, GLfloat y)
    {
        GLfloat xy[2] = {x, y};
    
        glUniform2fv(location, 1, xy);
    }
    
    void __stdcall glUniform2fv(GLint location, GLsizei count, const GLfloat* v)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, const GLfloat* v = 0x%0.8p)", location, count, v);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniform(context, GL_FLOAT_VEC2, location, count))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniform2fv(location, count, v);
        }
    }
    
    void __stdcall glUniform2i(GLint location, GLint x, GLint y)
    {
        GLint xy[2] = {x, y};
    
        glUniform2iv(location, 1, xy);
    }
    
    void __stdcall glUniform2iv(GLint location, GLsizei count, const GLint* v)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, const GLint* v = 0x%0.8p)", location, count, v);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniform(context, GL_INT_VEC2, location, count))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniform2iv(location, count, v);
        }
    }
    
    void __stdcall glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z)
    {
        GLfloat xyz[3] = {x, y, z};
    
        glUniform3fv(location, 1, xyz);
    }
    
    void __stdcall glUniform3fv(GLint location, GLsizei count, const GLfloat* v)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, const GLfloat* v = 0x%0.8p)", location, count, v);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniform(context, GL_FLOAT_VEC3, location, count))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniform3fv(location, count, v);
        }
    }
    
    void __stdcall glUniform3i(GLint location, GLint x, GLint y, GLint z)
    {
        GLint xyz[3] = {x, y, z};
    
        glUniform3iv(location, 1, xyz);
    }
    
    void __stdcall glUniform3iv(GLint location, GLsizei count, const GLint* v)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, const GLint* v = 0x%0.8p)", location, count, v);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniform(context, GL_INT_VEC3, location, count))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniform3iv(location, count, v);
        }
    }
    
    void __stdcall glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
    {
        GLfloat xyzw[4] = {x, y, z, w};
    
        glUniform4fv(location, 1, xyzw);
    }
    
    void __stdcall glUniform4fv(GLint location, GLsizei count, const GLfloat* v)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, const GLfloat* v = 0x%0.8p)", location, count, v);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniform(context, GL_FLOAT_VEC4, location, count))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniform4fv(location, count, v);
        }
    }
    
    void __stdcall glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w)
    {
        GLint xyzw[4] = {x, y, z, w};
    
        glUniform4iv(location, 1, xyzw);
    }
    
    void __stdcall glUniform4iv(GLint location, GLsizei count, const GLint* v)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, const GLint* v = 0x%0.8p)", location, count, v);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniform(context, GL_INT_VEC4, location, count))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniform4iv(location, count, v);
        }
    }
    
    void __stdcall glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, GLboolean transpose = %u, const GLfloat* value = 0x%0.8p)",
              location, count, transpose, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniformMatrix(context, GL_FLOAT_MAT2, location, count, transpose))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniformMatrix2fv(location, count, transpose, value);
        }
    }
    
    void __stdcall glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, GLboolean transpose = %u, const GLfloat* value = 0x%0.8p)",
              location, count, transpose, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniformMatrix(context, GL_FLOAT_MAT3, location, count, transpose))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniformMatrix3fv(location, count, transpose, value);
        }
    }
    
    void __stdcall glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, GLboolean transpose = %u, const GLfloat* value = 0x%0.8p)",
              location, count, transpose, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniformMatrix(context, GL_FLOAT_MAT4, location, count, transpose))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniformMatrix4fv(location, count, transpose, value);
        }
    }
    
    void __stdcall glUseProgram(GLuint program)
    {
        EVENT("(GLuint program = %d)", program);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject && program != 0)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            if (program != 0 && !programObject->isLinked())
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            context->useProgram(program);
        }
    }
    
    void __stdcall glValidateProgram(GLuint program)
    {
        EVENT("(GLuint program = %d)", program);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            programObject->validate();
        }
    }
    
    void __stdcall glVertexAttrib1f(GLuint index, GLfloat x)
    {
        EVENT("(GLuint index = %d, GLfloat x = %f)", index, x);
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            GLfloat vals[4] = { x, 0, 0, 1 };
            context->getState().setVertexAttribf(index, vals);
        }
    }
    
    void __stdcall glVertexAttrib1fv(GLuint index, const GLfloat* values)
    {
        EVENT("(GLuint index = %d, const GLfloat* values = 0x%0.8p)", index, values);
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            GLfloat vals[4] = { values[0], 0, 0, 1 };
            context->getState().setVertexAttribf(index, vals);
        }
    }
    
    void __stdcall glVertexAttrib2f(GLuint index, GLfloat x, GLfloat y)
    {
        EVENT("(GLuint index = %d, GLfloat x = %f, GLfloat y = %f)", index, x, y);
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            GLfloat vals[4] = { x, y, 0, 1 };
            context->getState().setVertexAttribf(index, vals);
        }
    }
    
    void __stdcall glVertexAttrib2fv(GLuint index, const GLfloat* values)
    {
        EVENT("(GLuint index = %d, const GLfloat* values = 0x%0.8p)", index, values);
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            GLfloat vals[4] = { values[0], values[1], 0, 1 };
            context->getState().setVertexAttribf(index, vals);
        }
    }
    
    void __stdcall glVertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z)
    {
        EVENT("(GLuint index = %d, GLfloat x = %f, GLfloat y = %f, GLfloat z = %f)", index, x, y, z);
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            GLfloat vals[4] = { x, y, z, 1 };
            context->getState().setVertexAttribf(index, vals);
        }
    }
    
    void __stdcall glVertexAttrib3fv(GLuint index, const GLfloat* values)
    {
        EVENT("(GLuint index = %d, const GLfloat* values = 0x%0.8p)", index, values);
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            GLfloat vals[4] = { values[0], values[1], values[2], 1 };
            context->getState().setVertexAttribf(index, vals);
        }
    }
    
    void __stdcall glVertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
    {
        EVENT("(GLuint index = %d, GLfloat x = %f, GLfloat y = %f, GLfloat z = %f, GLfloat w = %f)", index, x, y, z, w);
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            GLfloat vals[4] = { x, y, z, w };
            context->getState().setVertexAttribf(index, vals);
        }
    }
    
    void __stdcall glVertexAttrib4fv(GLuint index, const GLfloat* values)
    {
        EVENT("(GLuint index = %d, const GLfloat* values = 0x%0.8p)", index, values);
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setVertexAttribf(index, values);
        }
    }
    
    void __stdcall glVertexAttribDivisorANGLE(GLuint index, GLuint divisor)
    {
        EVENT("(GLuint index = %d, GLuint divisor = %d)", index, divisor);
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->setVertexAttribDivisor(index, divisor);
        }
    }
    
    void __stdcall glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
    {
        EVENT("(GLuint index = %d, GLint size = %d, GLenum type = 0x%X, "
              "GLboolean normalized = %u, GLsizei stride = %d, const GLvoid* ptr = 0x%0.8p)",
              index, size, type, normalized, stride, ptr);
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        if (size < 1 || size > 4)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        switch (type)
        {
          case GL_BYTE:
          case GL_UNSIGNED_BYTE:
          case GL_SHORT:
          case GL_UNSIGNED_SHORT:
          case GL_FIXED:
          case GL_FLOAT:
            break;
          case GL_HALF_FLOAT:
          case GL_INT:
          case GL_UNSIGNED_INT:
          case GL_INT_2_10_10_10_REV:
          case GL_UNSIGNED_INT_2_10_10_10_REV:
            if (context && context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_ENUM);
            }
            else
            {
                break;
            }
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        if (stride < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        if ((type == GL_INT_2_10_10_10_REV || type == GL_UNSIGNED_INT_2_10_10_10_REV) && size != 4)
        {
            return gl::error(GL_INVALID_OPERATION);
        }
    
        if (context)
        {
            // [OpenGL ES 3.0.2] Section 2.8 page 24:
            // An INVALID_OPERATION error is generated when a non-zero vertex array object
            // is bound, zero is bound to the ARRAY_BUFFER buffer object binding point,
            // and the pointer argument is not NULL.
            if (context->getState().getVertexArray()->id() != 0 && context->getState().getArrayBufferId() == 0 && ptr != NULL)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            context->getState().setVertexAttribState(index, context->getState().getTargetBuffer(GL_ARRAY_BUFFER), size, type,
                                                     normalized == GL_TRUE, false, stride, ptr);
        }
    }
    
    void __stdcall glViewport(GLint x, GLint y, GLsizei width, GLsizei height)
    {
        EVENT("(GLint x = %d, GLint y = %d, GLsizei width = %d, GLsizei height = %d)", x, y, width, height);
    
        if (width < 0 || height < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            context->getState().setViewportParams(x, y, width, height);
        }
    }
    
    // OpenGL ES 3.0 functions
    
    void __stdcall glReadBuffer(GLenum mode)
    {
        EVENT("(GLenum mode = 0x%X)", mode);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // glReadBuffer
            UNIMPLEMENTED();
        }
    }
    
    void __stdcall glDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid* indices)
    {
        EVENT("(GLenum mode = 0x%X, GLuint start = %u, GLuint end = %u, GLsizei count = %d, GLenum type = 0x%X, "
              "const GLvoid* indices = 0x%0.8p)", mode, start, end, count, type, indices);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // glDrawRangeElements
            UNIMPLEMENTED();
        }
    }
    
    void __stdcall glTexImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels)
    {
        EVENT("(GLenum target = 0x%X, GLint level = %d, GLint internalformat = %d, GLsizei width = %d, "
              "GLsizei height = %d, GLsizei depth = %d, GLint border = %d, GLenum format = 0x%X, "
              "GLenum type = 0x%X, const GLvoid* pixels = 0x%0.8p)",
              target, level, internalformat, width, height, depth, border, format, type, pixels);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // validateES3TexImageFormat sets the error code if there is an error
            if (!ValidateES3TexImageParameters(context, target, level, internalformat, false, false,
                                               0, 0, 0, width, height, depth, border, format, type, pixels))
            {
                return;
            }
    
            switch(target)
            {
              case GL_TEXTURE_3D:
                {
                    gl::Texture3D *texture = context->getTexture3D();
                    texture->setImage(level, width, height, depth, internalformat, format, type, context->getState().getUnpackState(), pixels);
                }
                break;
    
              case GL_TEXTURE_2D_ARRAY:
                {
                    gl::Texture2DArray *texture = context->getTexture2DArray();
                    texture->setImage(level, width, height, depth, internalformat, format, type, context->getState().getUnpackState(), pixels);
                }
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels)
    {
        EVENT("(GLenum target = 0x%X, GLint level = %d, GLint xoffset = %d, GLint yoffset = %d, "
              "GLint zoffset = %d, GLsizei width = %d, GLsizei height = %d, GLsizei depth = %d, "
              "GLenum format = 0x%X, GLenum type = 0x%X, const GLvoid* pixels = 0x%0.8p)",
              target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // validateES3TexImageFormat sets the error code if there is an error
            if (!ValidateES3TexImageParameters(context, target, level, GL_NONE, false, true,
                                               xoffset, yoffset, zoffset, width, height, depth, 0,
                                               format, type, pixels))
            {
                return;
            }
    
            // Zero sized uploads are valid but no-ops
            if (width == 0 || height == 0 || depth == 0)
            {
                return;
            }
    
            switch(target)
            {
              case GL_TEXTURE_3D:
                {
                    gl::Texture3D *texture = context->getTexture3D();
                    texture->subImage(level, xoffset, yoffset, zoffset, width, height, depth, format, type, context->getState().getUnpackState(), pixels);
                }
                break;
    
              case GL_TEXTURE_2D_ARRAY:
                {
                    gl::Texture2DArray *texture = context->getTexture2DArray();
                    texture->subImage(level, xoffset, yoffset, zoffset, width, height, depth, format, type, context->getState().getUnpackState(), pixels);
                }
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glCopyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height)
    {
        EVENT("(GLenum target = 0x%X, GLint level = %d, GLint xoffset = %d, GLint yoffset = %d, "
              "GLint zoffset = %d, GLint x = %d, GLint y = %d, GLsizei width = %d, GLsizei height = %d)",
              target, level, xoffset, yoffset, zoffset, x, y, width, height);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!ValidateES3CopyTexImageParameters(context, target, level, GL_NONE, true, xoffset, yoffset, zoffset,
                                                   x, y, width, height, 0))
            {
                return;
            }
    
            gl::Framebuffer *framebuffer = context->getState().getReadFramebuffer();
            gl::Texture *texture = NULL;
            switch (target)
            {
              case GL_TEXTURE_3D:
                texture = context->getTexture3D();
                break;
    
              case GL_TEXTURE_2D_ARRAY:
                texture = context->getTexture2DArray();
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
    
            texture->copySubImage(target, level, xoffset, yoffset, zoffset, x, y, width, height, framebuffer);
        }
    }
    
    void __stdcall glCompressedTexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid* data)
    {
        EVENT("(GLenum target = 0x%X, GLint level = %d, GLenum internalformat = 0x%X, GLsizei width = %d, "
              "GLsizei height = %d, GLsizei depth = %d, GLint border = %d, GLsizei imageSize = %d, "
              "const GLvoid* data = 0x%0.8p)",
              target, level, internalformat, width, height, depth, border, imageSize, data);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(internalformat);
            if (imageSize < 0 || static_cast<GLuint>(imageSize) != formatInfo.computeBlockSize(GL_UNSIGNED_BYTE, width, height))
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            // validateES3TexImageFormat sets the error code if there is an error
            if (!ValidateES3TexImageParameters(context, target, level, internalformat, true, false,
                                               0, 0, 0, width, height, depth, border, GL_NONE, GL_NONE, data))
            {
                return;
            }
    
            switch(target)
            {
              case GL_TEXTURE_3D:
                {
                    gl::Texture3D *texture = context->getTexture3D();
                    texture->setCompressedImage(level, internalformat, width, height, depth, imageSize, data);
                }
                break;
    
              case GL_TEXTURE_2D_ARRAY:
                {
                    gl::Texture2DArray *texture = context->getTexture2DArray();
                    texture->setCompressedImage(level, internalformat, width, height, depth, imageSize, data);
                }
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glCompressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data)
    {
        EVENT("(GLenum target = 0x%X, GLint level = %d, GLint xoffset = %d, GLint yoffset = %d, "
            "GLint zoffset = %d, GLsizei width = %d, GLsizei height = %d, GLsizei depth = %d, "
            "GLenum format = 0x%X, GLsizei imageSize = %d, const GLvoid* data = 0x%0.8p)",
            target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(format);
            if (imageSize < 0 || static_cast<GLuint>(imageSize) != formatInfo.computeBlockSize(GL_UNSIGNED_BYTE, width, height))
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            if (!data)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            // validateES3TexImageFormat sets the error code if there is an error
            if (!ValidateES3TexImageParameters(context, target, level, GL_NONE, true, true,
                                               0, 0, 0, width, height, depth, 0, GL_NONE, GL_NONE, data))
            {
                return;
            }
    
            // Zero sized uploads are valid but no-ops
            if (width == 0 || height == 0)
            {
                return;
            }
    
            switch(target)
            {
              case GL_TEXTURE_3D:
                {
                    gl::Texture3D *texture = context->getTexture3D();
                    texture->subImageCompressed(level, xoffset, yoffset, zoffset, width, height, depth,
                                                format, imageSize, data);
                }
                break;
    
              case GL_TEXTURE_2D_ARRAY:
                {
                    gl::Texture2DArray *texture = context->getTexture2DArray();
                    texture->subImageCompressed(level, xoffset, yoffset, zoffset, width, height, depth,
                                                format, imageSize, data);
                }
                break;
    
            default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glGenQueries(GLsizei n, GLuint* ids)
    {
        EVENT("(GLsizei n = %d, GLuint* ids = 0x%0.8p)", n, ids);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (n < 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            for (GLsizei i = 0; i < n; i++)
            {
                ids[i] = context->createQuery();
            }
        }
    }
    
    void __stdcall glDeleteQueries(GLsizei n, const GLuint* ids)
    {
        EVENT("(GLsizei n = %d, GLuint* ids = 0x%0.8p)", n, ids);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (n < 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            for (GLsizei i = 0; i < n; i++)
            {
                context->deleteQuery(ids[i]);
            }
        }
    }
    
    GLboolean __stdcall glIsQuery(GLuint id)
    {
        EVENT("(GLuint id = %u)", id);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION, GL_FALSE);
            }
    
            return (context->getQuery(id, false, GL_NONE) != NULL) ? GL_TRUE : GL_FALSE;
        }
    
        return GL_FALSE;
    }
    
    void __stdcall glBeginQuery(GLenum target, GLuint id)
    {
        EVENT("(GLenum target = 0x%X, GLuint id = %u)", target, id);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!ValidateBeginQuery(context, target, id))
            {
                return;
            }
            context->beginQuery(target, id);
        }
    }
    
    void __stdcall glEndQuery(GLenum target)
    {
        EVENT("(GLenum target = 0x%X)", target);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!ValidateEndQuery(context, target))
            {
                return;
            }
    
            context->endQuery(target);
        }
    }
    
    void __stdcall glGetQueryiv(GLenum target, GLenum pname, GLint* params)
    {
        EVENT("(GLenum target = 0x%X, GLenum pname = 0x%X, GLint* params = 0x%0.8p)", target, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!ValidQueryType(context, target))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            switch (pname)
            {
              case GL_CURRENT_QUERY:
                params[0] = static_cast<GLint>(context->getState().getActiveQueryId(target));
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glGetQueryObjectuiv(GLuint id, GLenum pname, GLuint* params)
    {
        EVENT("(GLuint id = %u, GLenum pname = 0x%X, GLint* params = 0x%0.8p)", id, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            gl::Query *queryObject = context->getQuery(id, false, GL_NONE);
    
            if (!queryObject)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (context->getState().getActiveQueryId(queryObject->getType()) == id)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            switch(pname)
            {
              case GL_QUERY_RESULT:
                params[0] = queryObject->getResult();
                break;
              case GL_QUERY_RESULT_AVAILABLE:
                params[0] = queryObject->isResultAvailable();
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    GLboolean __stdcall glUnmapBuffer(GLenum target)
    {
        EVENT("(GLenum target = 0x%X)", target);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION, GL_FALSE);
            }
    
            return glUnmapBufferOES(target);
        }
    
        return GL_FALSE;
    }
    
    void __stdcall glGetBufferPointerv(GLenum target, GLenum pname, GLvoid** params)
    {
        EVENT("(GLenum target = 0x%X, GLenum pname = 0x%X, GLvoid** params = 0x%0.8p)", target, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            glGetBufferPointervOES(target, pname, params);
        }
    }
    
    void __stdcall glDrawBuffers(GLsizei n, const GLenum* bufs)
    {
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            glDrawBuffersEXT(n, bufs);
        }
    }
    
    void __stdcall glUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, GLboolean transpose = %u, const GLfloat* value = 0x%0.8p)",
              location, count, transpose, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniformMatrix(context, GL_FLOAT_MAT2x3, location, count, transpose))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniformMatrix2x3fv(location, count, transpose, value);
        }
    }
    
    void __stdcall glUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, GLboolean transpose = %u, const GLfloat* value = 0x%0.8p)",
              location, count, transpose, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniformMatrix(context, GL_FLOAT_MAT3x2, location, count, transpose))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniformMatrix3x2fv(location, count, transpose, value);
        }
    }
    
    void __stdcall glUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, GLboolean transpose = %u, const GLfloat* value = 0x%0.8p)",
              location, count, transpose, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniformMatrix(context, GL_FLOAT_MAT2x4, location, count, transpose))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniformMatrix2x4fv(location, count, transpose, value);
        }
    }
    
    void __stdcall glUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, GLboolean transpose = %u, const GLfloat* value = 0x%0.8p)",
              location, count, transpose, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniformMatrix(context, GL_FLOAT_MAT4x2, location, count, transpose))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniformMatrix4x2fv(location, count, transpose, value);
        }
    }
    
    void __stdcall glUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, GLboolean transpose = %u, const GLfloat* value = 0x%0.8p)",
              location, count, transpose, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniformMatrix(context, GL_FLOAT_MAT3x4, location, count, transpose))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniformMatrix3x4fv(location, count, transpose, value);
        }
    }
    
    void __stdcall glUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, GLboolean transpose = %u, const GLfloat* value = 0x%0.8p)",
              location, count, transpose, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniformMatrix(context, GL_FLOAT_MAT4x3, location, count, transpose))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniformMatrix4x3fv(location, count, transpose, value);
        }
    }
    
    void __stdcall glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
    {
        EVENT("(GLint srcX0 = %d, GLint srcY0 = %d, GLint srcX1 = %d, GLint srcY1 = %d, GLint dstX0 = %d, "
              "GLint dstY0 = %d, GLint dstX1 = %d, GLint dstY1 = %d, GLbitfield mask = 0x%X, GLenum filter = 0x%X)",
              srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
    
        gl::Context *context = gl::getNonLostContext();
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!ValidateBlitFramebufferParameters(context, srcX0, srcY0, srcX1, srcY1,
                                                   dstX0, dstY0, dstX1, dstY1, mask, filter,
                                                   false))
            {
                return;
            }
    
            context->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1,
                                     mask, filter);
        }
    }
    
    void __stdcall glRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
    {
        EVENT("(GLenum target = 0x%X, GLsizei samples = %d, GLenum internalformat = 0x%X, GLsizei width = %d, GLsizei height = %d)",
            target, samples, internalformat, width, height);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!ValidateRenderbufferStorageParameters(context, target, samples, internalformat,
                                                       width, height, false))
            {
                return;
            }
    
            context->setRenderbufferStorage(width, height, internalformat, samples);
        }
    }
    
    void __stdcall glFramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer)
    {
        EVENT("(GLenum target = 0x%X, GLenum attachment = 0x%X, GLuint texture = %u, GLint level = %d, GLint layer = %d)",
            target, attachment, texture, level, layer);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateFramebufferTextureLayer(context, target, attachment, texture,
                                                 level, layer))
            {
                return;
            }
    
            gl::Framebuffer *framebuffer = context->getState().getTargetFramebuffer(target);
            ASSERT(framebuffer);
    
            gl::Texture *textureObject = context->getTexture(texture);
            GLenum textarget = textureObject ? textureObject->getTarget() : GL_NONE;
    
            if (attachment >= GL_COLOR_ATTACHMENT0_EXT && attachment <= GL_COLOR_ATTACHMENT15_EXT)
            {
                const unsigned int colorAttachment = (attachment - GL_COLOR_ATTACHMENT0_EXT);
                framebuffer->setColorbuffer(colorAttachment, textarget, texture, level, layer);
            }
            else
            {
                switch (attachment)
                {
                  case GL_DEPTH_ATTACHMENT:         framebuffer->setDepthbuffer(textarget, texture, level, layer);        break;
                  case GL_STENCIL_ATTACHMENT:       framebuffer->setStencilbuffer(textarget, texture, level, layer);      break;
                  case GL_DEPTH_STENCIL_ATTACHMENT: framebuffer->setDepthStencilBuffer(textarget, texture, level, layer); break;
                }
            }
        }
    }
    
    GLvoid* __stdcall glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)
    {
        EVENT("(GLenum target = 0x%X, GLintptr offset = %d, GLsizeiptr length = %d, GLbitfield access = 0x%X)",
              target, offset, length, access);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION, reinterpret_cast<GLvoid*>(NULL));
            }
    
            return glMapBufferRangeEXT(target, offset, length, access);
        }
    
        return NULL;
    }
    
    void __stdcall glFlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length)
    {
        EVENT("(GLenum target = 0x%X, GLintptr offset = %d, GLsizeiptr length = %d)", target, offset, length);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            glFlushMappedBufferRangeEXT(target, offset, length);
        }
    }
    
    void __stdcall glBindVertexArray(GLuint array)
    {
        EVENT("(GLuint array = %u)", array);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            gl::VertexArray *vao = context->getVertexArray(array);
    
            if (!vao)
            {
                // The default VAO should always exist
                ASSERT(array != 0);
                return gl::error(GL_INVALID_OPERATION);
            }
    
            context->bindVertexArray(array);
        }
    }
    
    void __stdcall glDeleteVertexArrays(GLsizei n, const GLuint* arrays)
    {
        EVENT("(GLsizei n = %d, const GLuint* arrays = 0x%0.8p)", n, arrays);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (n < 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            for (int arrayIndex = 0; arrayIndex < n; arrayIndex++)
            {
                if (arrays[arrayIndex] != 0)
                {
                    context->deleteVertexArray(arrays[arrayIndex]);
                }
            }
        }
    }
    
    void __stdcall glGenVertexArrays(GLsizei n, GLuint* arrays)
    {
        EVENT("(GLsizei n = %d, GLuint* arrays = 0x%0.8p)", n, arrays);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (n < 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            for (int arrayIndex = 0; arrayIndex < n; arrayIndex++)
            {
                arrays[arrayIndex] = context->createVertexArray();
            }
        }
    }
    
    GLboolean __stdcall glIsVertexArray(GLuint array)
    {
        EVENT("(GLuint array = %u)", array);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION, GL_FALSE);
            }
    
            if (array == 0)
            {
                return GL_FALSE;
            }
    
            gl::VertexArray *vao = context->getVertexArray(array);
    
            return (vao != NULL ? GL_TRUE : GL_FALSE);
        }
    
        return GL_FALSE;
    }
    
    void __stdcall glGetIntegeri_v(GLenum target, GLuint index, GLint* data)
    {
        EVENT("(GLenum target = 0x%X, GLuint index = %u, GLint* data = 0x%0.8p)",
              target, index, data);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            switch (target)
            {
              case GL_TRANSFORM_FEEDBACK_BUFFER_START:
              case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
              case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
                if (index >= context->getMaxTransformFeedbackBufferBindings())
                    return gl::error(GL_INVALID_VALUE);
                break;
              case GL_UNIFORM_BUFFER_START:
              case GL_UNIFORM_BUFFER_SIZE:
              case GL_UNIFORM_BUFFER_BINDING:
                if (index >= context->getMaximumCombinedUniformBufferBindings())
                    return gl::error(GL_INVALID_VALUE);
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
    
            if (!(context->getIndexedIntegerv(target, index, data)))
            {
                GLenum nativeType;
                unsigned int numParams = 0;
                if (!context->getIndexedQueryParameterInfo(target, &nativeType, &numParams))
                    return gl::error(GL_INVALID_ENUM);
    
                if (numParams == 0)
                    return; // it is known that pname is valid, but there are no parameters to return
    
                if (nativeType == GL_INT_64_ANGLEX)
                {
                    GLint64 minIntValue = static_cast<GLint64>(std::numeric_limits<int>::min());
                    GLint64 maxIntValue = static_cast<GLint64>(std::numeric_limits<int>::max());
                    GLint64 *int64Params = new GLint64[numParams];
    
                    context->getIndexedInteger64v(target, index, int64Params);
    
                    for (unsigned int i = 0; i < numParams; ++i)
                    {
                        GLint64 clampedValue = std::max(std::min(int64Params[i], maxIntValue), minIntValue);
                        data[i] = static_cast<GLint>(clampedValue);
                    }
    
                    delete [] int64Params;
                }
                else
                {
                    UNREACHABLE();
                }
            }
        }
    }
    
    void __stdcall glBeginTransformFeedback(GLenum primitiveMode)
    {
        EVENT("(GLenum primitiveMode = 0x%X)", primitiveMode);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            switch (primitiveMode)
            {
              case GL_TRIANGLES:
              case GL_LINES:
              case GL_POINTS:
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
    
            gl::TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
            ASSERT(transformFeedback != NULL);
    
            if (transformFeedback->isStarted())
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (transformFeedback->isPaused())
            {
                transformFeedback->resume();
            }
            else
            {
                transformFeedback->start(primitiveMode);
            }
        }
    }
    
    void __stdcall glEndTransformFeedback(void)
    {
        EVENT("(void)");
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            gl::TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
            ASSERT(transformFeedback != NULL);
    
            if (!transformFeedback->isStarted())
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            transformFeedback->stop();
        }
    }
    
    void __stdcall glBindBufferRange(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size)
    {
        EVENT("(GLenum target = 0x%X, GLuint index = %u, GLuint buffer = %u, GLintptr offset = %d, GLsizeiptr size = %d)",
              target, index, buffer, offset, size);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            switch (target)
            {
              case GL_TRANSFORM_FEEDBACK_BUFFER:
                if (index >= context->getMaxTransformFeedbackBufferBindings())
                {
                    return gl::error(GL_INVALID_VALUE);
                }
                break;
    
              case GL_UNIFORM_BUFFER:
                if (index >= context->getMaximumCombinedUniformBufferBindings())
                {
                    return gl::error(GL_INVALID_VALUE);
                }
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
    
            if (buffer != 0 && size <= 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            switch (target)
            {
              case GL_TRANSFORM_FEEDBACK_BUFFER:
    
                // size and offset must be a multiple of 4
                if (buffer != 0 && ((offset % 4) != 0 || (size % 4) != 0))
                {
                    return gl::error(GL_INVALID_VALUE);
                }
    
                context->bindIndexedTransformFeedbackBuffer(buffer, index, offset, size);
                context->bindGenericTransformFeedbackBuffer(buffer);
                break;
    
              case GL_UNIFORM_BUFFER:
    
                // it is an error to bind an offset not a multiple of the alignment
                if (buffer != 0 && (offset % context->getUniformBufferOffsetAlignment()) != 0)
                {
                    return gl::error(GL_INVALID_VALUE);
                }
    
                context->bindIndexedUniformBuffer(buffer, index, offset, size);
                context->bindGenericUniformBuffer(buffer);
                break;
    
              default:
                UNREACHABLE();
            }
        }
    }
    
    void __stdcall glBindBufferBase(GLenum target, GLuint index, GLuint buffer)
    {
        EVENT("(GLenum target = 0x%X, GLuint index = %u, GLuint buffer = %u)",
              target, index, buffer);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            switch (target)
            {
              case GL_TRANSFORM_FEEDBACK_BUFFER:
                if (index >= context->getMaxTransformFeedbackBufferBindings())
                {
                    return gl::error(GL_INVALID_VALUE);
                }
                break;
    
              case GL_UNIFORM_BUFFER:
                if (index >= context->getMaximumCombinedUniformBufferBindings())
                {
                    return gl::error(GL_INVALID_VALUE);
                }
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
    
            switch (target)
            {
              case GL_TRANSFORM_FEEDBACK_BUFFER:
                context->bindIndexedTransformFeedbackBuffer(buffer, index, 0, 0);
                context->bindGenericTransformFeedbackBuffer(buffer);
                break;
    
              case GL_UNIFORM_BUFFER:
                context->bindIndexedUniformBuffer(buffer, index, 0, 0);
                context->bindGenericUniformBuffer(buffer);
                break;
    
              default:
                UNREACHABLE();
            }
        }
    }
    
    void __stdcall glTransformFeedbackVaryings(GLuint program, GLsizei count, const GLchar* const* varyings, GLenum bufferMode)
    {
        EVENT("(GLuint program = %u, GLsizei count = %d, const GLchar* const* varyings = 0x%0.8p, GLenum bufferMode = 0x%X)",
              program, count, varyings, bufferMode);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (count < 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            switch (bufferMode)
            {
              case GL_INTERLEAVED_ATTRIBS:
                break;
              case GL_SEPARATE_ATTRIBS:
                if (static_cast<GLuint>(count) > context->getMaxTransformFeedbackBufferBindings())
                {
                    return gl::error(GL_INVALID_VALUE);
                }
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
    
            if (!gl::ValidProgram(context, program))
            {
                return;
            }
    
            gl::Program *programObject = context->getProgram(program);
            ASSERT(programObject);
    
            programObject->setTransformFeedbackVaryings(count, varyings, bufferMode);
        }
    }
    
    void __stdcall glGetTransformFeedbackVarying(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLsizei* size, GLenum* type, GLchar* name)
    {
        EVENT("(GLuint program = %u, GLuint index = %u, GLsizei bufSize = %d, GLsizei* length = 0x%0.8p, "
              "GLsizei* size = 0x%0.8p, GLenum* type = 0x%0.8p, GLchar* name = 0x%0.8p)",
              program, index, bufSize, length, size, type, name);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (bufSize < 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            if (!gl::ValidProgram(context, program))
            {
                return;
            }
    
            gl::Program *programObject = context->getProgram(program);
            ASSERT(programObject);
    
            if (index >= static_cast<GLuint>(programObject->getTransformFeedbackVaryingCount()))
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            programObject->getTransformFeedbackVarying(index, bufSize, length, size, type, name);
        }
    }
    
    void __stdcall glVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid* pointer)
    {
        EVENT("(GLuint index = %u, GLint size = %d, GLenum type = 0x%X, GLsizei stride = %d, const GLvoid* pointer = 0x%0.8p)",
              index, size, type, stride, pointer);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
        }
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        if (size < 1 || size > 4)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        switch (type)
        {
          case GL_BYTE:
          case GL_UNSIGNED_BYTE:
          case GL_SHORT:
          case GL_UNSIGNED_SHORT:
          case GL_INT:
          case GL_UNSIGNED_INT:
          case GL_INT_2_10_10_10_REV:
          case GL_UNSIGNED_INT_2_10_10_10_REV:
            break;
          default:
            return gl::error(GL_INVALID_ENUM);
        }
    
        if (stride < 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        if ((type == GL_INT_2_10_10_10_REV || type == GL_UNSIGNED_INT_2_10_10_10_REV) && size != 4)
        {
            return gl::error(GL_INVALID_OPERATION);
        }
    
        if (context)
        {
            // [OpenGL ES 3.0.2] Section 2.8 page 24:
            // An INVALID_OPERATION error is generated when a non-zero vertex array object
            // is bound, zero is bound to the ARRAY_BUFFER buffer object binding point,
            // and the pointer argument is not NULL.
            if (context->getState().getVertexArray()->id() != 0 && context->getState().getArrayBufferId() == 0 && pointer != NULL)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            context->getState().setVertexAttribState(index, context->getState().getTargetBuffer(GL_ARRAY_BUFFER), size, type, false, true,
                                                     stride, pointer);
        }
    }
    
    void __stdcall glGetVertexAttribIiv(GLuint index, GLenum pname, GLint* params)
    {
        EVENT("(GLuint index = %u, GLenum pname = 0x%X, GLint* params = 0x%0.8p)",
              index, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (index >= gl::MAX_VERTEX_ATTRIBS)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            const gl::VertexAttribute &attribState = context->getState().getVertexAttribState(index);
    
            if (!gl::ValidateGetVertexAttribParameters(pname, context->getClientVersion()))
            {
                return;
            }
    
            if (pname == GL_CURRENT_VERTEX_ATTRIB)
            {
                const gl::VertexAttribCurrentValueData &currentValueData = context->getState().getVertexAttribCurrentValue(index);
                for (int i = 0; i < 4; ++i)
                {
                    params[i] = currentValueData.IntValues[i];
                }
            }
            else
            {
                *params = gl::QuerySingleVertexAttributeParameter<GLint>(attribState, pname);
            }
        }
    }
    
    void __stdcall glGetVertexAttribIuiv(GLuint index, GLenum pname, GLuint* params)
    {
        EVENT("(GLuint index = %u, GLenum pname = 0x%X, GLuint* params = 0x%0.8p)",
              index, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (index >= gl::MAX_VERTEX_ATTRIBS)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            const gl::VertexAttribute &attribState = context->getState().getVertexAttribState(index);
    
            if (!gl::ValidateGetVertexAttribParameters(pname, context->getClientVersion()))
            {
                return;
            }
    
            if (pname == GL_CURRENT_VERTEX_ATTRIB)
            {
                const gl::VertexAttribCurrentValueData &currentValueData = context->getState().getVertexAttribCurrentValue(index);
                for (int i = 0; i < 4; ++i)
                {
                    params[i] = currentValueData.UnsignedIntValues[i];
                }
            }
            else
            {
                *params = gl::QuerySingleVertexAttributeParameter<GLuint>(attribState, pname);
            }
        }
    }
    
    void __stdcall glVertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w)
    {
        EVENT("(GLuint index = %u, GLint x = %d, GLint y = %d, GLint z = %d, GLint w = %d)",
              index, x, y, z, w);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (index >= gl::MAX_VERTEX_ATTRIBS)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            GLint vals[4] = { x, y, z, w };
            context->getState().setVertexAttribi(index, vals);
        }
    }
    
    void __stdcall glVertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w)
    {
        EVENT("(GLuint index = %u, GLuint x = %u, GLuint y = %u, GLuint z = %u, GLuint w = %u)",
              index, x, y, z, w);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (index >= gl::MAX_VERTEX_ATTRIBS)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            GLuint vals[4] = { x, y, z, w };
            context->getState().setVertexAttribu(index, vals);
        }
    }
    
    void __stdcall glVertexAttribI4iv(GLuint index, const GLint* v)
    {
        EVENT("(GLuint index = %u, const GLint* v = 0x%0.8p)", index, v);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (index >= gl::MAX_VERTEX_ATTRIBS)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            context->getState().setVertexAttribi(index, v);
        }
    }
    
    void __stdcall glVertexAttribI4uiv(GLuint index, const GLuint* v)
    {
        EVENT("(GLuint index = %u, const GLuint* v = 0x%0.8p)", index, v);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (index >= gl::MAX_VERTEX_ATTRIBS)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            context->getState().setVertexAttribu(index, v);
        }
    }
    
    void __stdcall glGetUniformuiv(GLuint program, GLint location, GLuint* params)
    {
        EVENT("(GLuint program = %u, GLint location = %d, GLuint* params = 0x%0.8p)",
              program, location, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (program == 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject || !programObject->isLinked())
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
            if (!programBinary)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!programBinary->getUniformuiv(location, NULL, params))
            {
                return gl::error(GL_INVALID_OPERATION);
            }
        }
    }
    
    GLint __stdcall glGetFragDataLocation(GLuint program, const GLchar *name)
    {
        EVENT("(GLuint program = %u, const GLchar *name = 0x%0.8p)",
              program, name);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION, -1);
            }
    
            if (program == 0)
            {
                return gl::error(GL_INVALID_VALUE, -1);
            }
    
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject || !programObject->isLinked())
            {
                return gl::error(GL_INVALID_OPERATION, -1);
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
            if (!programBinary)
            {
                return gl::error(GL_INVALID_OPERATION, -1);
            }
    
            return programBinary->getFragDataLocation(name);
        }
    
        return 0;
    }
    
    void __stdcall glUniform1ui(GLint location, GLuint v0)
    {
        glUniform1uiv(location, 1, &v0);
    }
    
    void __stdcall glUniform2ui(GLint location, GLuint v0, GLuint v1)
    {
        const GLuint xy[] = { v0, v1 };
        glUniform2uiv(location, 1, xy);
    }
    
    void __stdcall glUniform3ui(GLint location, GLuint v0, GLuint v1, GLuint v2)
    {
        const GLuint xyz[] = { v0, v1, v2 };
        glUniform3uiv(location, 1, xyz);
    }
    
    void __stdcall glUniform4ui(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3)
    {
        const GLuint xyzw[] = { v0, v1, v2, v3 };
        glUniform4uiv(location, 1, xyzw);
    }
    
    void __stdcall glUniform1uiv(GLint location, GLsizei count, const GLuint* value)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, const GLuint* value = 0x%0.8p)",
              location, count, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniform(context, GL_UNSIGNED_INT, location, count))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniform1uiv(location, count, value);
        }
    }
    
    void __stdcall glUniform2uiv(GLint location, GLsizei count, const GLuint* value)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, const GLuint* value = 0x%0.8p)",
              location, count, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniform(context, GL_UNSIGNED_INT_VEC2, location, count))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniform2uiv(location, count, value);
        }
    }
    
    void __stdcall glUniform3uiv(GLint location, GLsizei count, const GLuint* value)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, const GLuint* value)",
              location, count, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniform(context, GL_UNSIGNED_INT_VEC3, location, count))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniform3uiv(location, count, value);
        }
    }
    
    void __stdcall glUniform4uiv(GLint location, GLsizei count, const GLuint* value)
    {
        EVENT("(GLint location = %d, GLsizei count = %d, const GLuint* value = 0x%0.8p)",
              location, count, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateUniform(context, GL_UNSIGNED_INT_VEC4, location, count))
            {
                return;
            }
    
            gl::ProgramBinary *programBinary = context->getState().getCurrentProgramBinary();
            programBinary->setUniform4uiv(location, count, value);
        }
    }
    
    void __stdcall glClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value)
    {
        EVENT("(GLenum buffer = 0x%X, GLint drawbuffer = %d, const GLint* value = 0x%0.8p)",
              buffer, drawbuffer, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateClearBuffer(context))
            {
                return;
            }
    
            switch (buffer)
            {
              case GL_COLOR:
                if (drawbuffer < 0 || static_cast<GLuint>(drawbuffer) >= context->getCaps().maxDrawBuffers)
                {
                    return gl::error(GL_INVALID_VALUE);
                }
                break;
              case GL_STENCIL:
                if (drawbuffer != 0)
                {
                    return gl::error(GL_INVALID_VALUE);
                }
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
    
            context->clearBufferiv(buffer, drawbuffer, value);
        }
    }
    
    void __stdcall glClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value)
    {
        EVENT("(GLenum buffer = 0x%X, GLint drawbuffer = %d, const GLuint* value = 0x%0.8p)",
              buffer, drawbuffer, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateClearBuffer(context))
            {
                return;
            }
    
            switch (buffer)
            {
              case GL_COLOR:
                if (drawbuffer < 0 || static_cast<GLuint>(drawbuffer) >= context->getCaps().maxDrawBuffers)
                {
                    return gl::error(GL_INVALID_VALUE);
                }
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
    
            context->clearBufferuiv(buffer, drawbuffer, value);
        }
    }
    
    void __stdcall glClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value)
    {
        EVENT("(GLenum buffer = 0x%X, GLint drawbuffer = %d, const GLfloat* value = 0x%0.8p)",
              buffer, drawbuffer, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateClearBuffer(context))
            {
                return;
            }
    
            switch (buffer)
            {
              case GL_COLOR:
                if (drawbuffer < 0 || static_cast<GLuint>(drawbuffer) >= context->getCaps().maxDrawBuffers)
                {
                    return gl::error(GL_INVALID_VALUE);
                }
                break;
              case GL_DEPTH:
                if (drawbuffer != 0)
                {
                    return gl::error(GL_INVALID_VALUE);
                }
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
    
            context->clearBufferfv(buffer, drawbuffer, value);
        }
    }
    
    void __stdcall glClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)
    {
        EVENT("(GLenum buffer = 0x%X, GLint drawbuffer = %d, GLfloat depth, GLint stencil = %d)",
              buffer, drawbuffer, depth, stencil);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateClearBuffer(context))
            {
                return;
            }
    
            switch (buffer)
            {
              case GL_DEPTH_STENCIL:
                if (drawbuffer != 0)
                {
                    return gl::error(GL_INVALID_VALUE);
                }
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
    
            context->clearBufferfi(buffer, drawbuffer, depth, stencil);
        }
    }
    
    const GLubyte* __stdcall glGetStringi(GLenum name, GLuint index)
    {
        EVENT("(GLenum name = 0x%X, GLuint index = %u)", name, index);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION, reinterpret_cast<GLubyte*>(NULL));
            }
    
            if (name != GL_EXTENSIONS)
            {
                return gl::error(GL_INVALID_ENUM, reinterpret_cast<GLubyte*>(NULL));
            }
    
            if (index >= context->getExtensionStringCount())
            {
                return gl::error(GL_INVALID_VALUE, reinterpret_cast<GLubyte*>(NULL));
            }
    
            return reinterpret_cast<const GLubyte*>(context->getExtensionString(index).c_str());
        }
    
        return NULL;
    }
    
    void __stdcall glCopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size)
    {
        EVENT("(GLenum readTarget = 0x%X, GLenum writeTarget = 0x%X, GLintptr readOffset = %d, GLintptr writeOffset = %d, GLsizeiptr size = %d)",
              readTarget, writeTarget, readOffset, writeOffset, size);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!gl::ValidBufferTarget(context, readTarget) || !gl::ValidBufferTarget(context, readTarget))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            gl::Buffer *readBuffer = context->getState().getTargetBuffer(readTarget);
            gl::Buffer *writeBuffer = context->getState().getTargetBuffer(writeTarget);
    
            if (!readBuffer || !writeBuffer)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // Verify that readBuffer and writeBuffer are not currently mapped
            if (readBuffer->isMapped() || writeBuffer->isMapped())
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (readOffset < 0 || writeOffset < 0 || size < 0 ||
                static_cast<unsigned int>(readOffset + size) > readBuffer->getSize() ||
                static_cast<unsigned int>(writeOffset + size) > writeBuffer->getSize())
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            if (readBuffer == writeBuffer && abs(readOffset - writeOffset) < size)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            // if size is zero, the copy is a successful no-op
            if (size > 0)
            {
                writeBuffer->copyBufferSubData(readBuffer, readOffset, writeOffset, size);
            }
        }
    }
    
    void __stdcall glGetUniformIndices(GLuint program, GLsizei uniformCount, const GLchar* const* uniformNames, GLuint* uniformIndices)
    {
        EVENT("(GLuint program = %u, GLsizei uniformCount = %d, const GLchar* const* uniformNames = 0x%0.8p, GLuint* uniformIndices = 0x%0.8p)",
              program, uniformCount, uniformNames, uniformIndices);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (uniformCount < 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
            if (!programObject->isLinked() || !programBinary)
            {
                for (int uniformId = 0; uniformId < uniformCount; uniformId++)
                {
                    uniformIndices[uniformId] = GL_INVALID_INDEX;
                }
            }
            else
            {
                for (int uniformId = 0; uniformId < uniformCount; uniformId++)
                {
                    uniformIndices[uniformId] = programBinary->getUniformIndex(uniformNames[uniformId]);
                }
            }
        }
    }
    
    void __stdcall glGetActiveUniformsiv(GLuint program, GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname, GLint* params)
    {
        EVENT("(GLuint program = %u, GLsizei uniformCount = %d, const GLuint* uniformIndices = 0x%0.8p, GLenum pname = 0x%X, GLint* params = 0x%0.8p)",
              program, uniformCount, uniformIndices, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (uniformCount < 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            switch (pname)
            {
              case GL_UNIFORM_TYPE:
              case GL_UNIFORM_SIZE:
              case GL_UNIFORM_NAME_LENGTH:
              case GL_UNIFORM_BLOCK_INDEX:
              case GL_UNIFORM_OFFSET:
              case GL_UNIFORM_ARRAY_STRIDE:
              case GL_UNIFORM_MATRIX_STRIDE:
              case GL_UNIFORM_IS_ROW_MAJOR:
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
    
            if (!programBinary && uniformCount > 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            for (int uniformId = 0; uniformId < uniformCount; uniformId++)
            {
                const GLuint index = uniformIndices[uniformId];
    
                if (index >= (GLuint)programBinary->getActiveUniformCount())
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            for (int uniformId = 0; uniformId < uniformCount; uniformId++)
            {
                const GLuint index = uniformIndices[uniformId];
                params[uniformId] = programBinary->getActiveUniformi(index, pname);
            }
        }
    }
    
    GLuint __stdcall glGetUniformBlockIndex(GLuint program, const GLchar* uniformBlockName)
    {
        EVENT("(GLuint program = %u, const GLchar* uniformBlockName = 0x%0.8p)", program, uniformBlockName);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION, GL_INVALID_INDEX);
            }
    
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION, GL_INVALID_INDEX);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE, GL_INVALID_INDEX);
                }
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
            if (!programBinary)
            {
                return GL_INVALID_INDEX;
            }
    
            return programBinary->getUniformBlockIndex(uniformBlockName);
        }
    
        return 0;
    }
    
    void __stdcall glGetActiveUniformBlockiv(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint* params)
    {
        EVENT("(GLuint program = %u, GLuint uniformBlockIndex = %u, GLenum pname = 0x%X, GLint* params = 0x%0.8p)",
              program, uniformBlockIndex, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
    
            if (!programBinary || uniformBlockIndex >= programBinary->getActiveUniformBlockCount())
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            switch (pname)
            {
              case GL_UNIFORM_BLOCK_BINDING:
                *params = static_cast<GLint>(programObject->getUniformBlockBinding(uniformBlockIndex));
                break;
    
              case GL_UNIFORM_BLOCK_DATA_SIZE:
              case GL_UNIFORM_BLOCK_NAME_LENGTH:
              case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
              case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES:
              case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
              case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
                programBinary->getActiveUniformBlockiv(uniformBlockIndex, pname, params);
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glGetActiveUniformBlockName(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformBlockName)
    {
        EVENT("(GLuint program = %u, GLuint uniformBlockIndex = %u, GLsizei bufSize = %d, GLsizei* length = 0x%0.8p, GLchar* uniformBlockName = 0x%0.8p)",
              program, uniformBlockIndex, bufSize, length, uniformBlockName);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
    
            if (!programBinary || uniformBlockIndex >= programBinary->getActiveUniformBlockCount())
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            programBinary->getActiveUniformBlockName(uniformBlockIndex, bufSize, length, uniformBlockName);
        }
    }
    
    void __stdcall glUniformBlockBinding(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding)
    {
        EVENT("(GLuint program = %u, GLuint uniformBlockIndex = %u, GLuint uniformBlockBinding = %u)",
              program, uniformBlockIndex, uniformBlockBinding);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (uniformBlockBinding >= context->getMaximumCombinedUniformBufferBindings())
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                if (context->getShader(program))
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
                else
                {
                    return gl::error(GL_INVALID_VALUE);
                }
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
    
            // if never linked, there won't be any uniform blocks
            if (!programBinary || uniformBlockIndex >= programBinary->getActiveUniformBlockCount())
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            programObject->bindUniformBlock(uniformBlockIndex, uniformBlockBinding);
        }
    }
    
    void __stdcall glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount)
    {
        EVENT("(GLenum mode = 0x%X, GLint first = %d, GLsizei count = %d, GLsizei instanceCount = %d)",
              mode, first, count, instanceCount);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // glDrawArraysInstanced
            UNIMPLEMENTED();
        }
    }
    
    void __stdcall glDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLsizei instanceCount)
    {
        EVENT("(GLenum mode = 0x%X, GLsizei count = %d, GLenum type = 0x%X, const GLvoid* indices = 0x%0.8p, GLsizei instanceCount = %d)",
              mode, count, type, indices, instanceCount);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // glDrawElementsInstanced
            UNIMPLEMENTED();
        }
    }
    
    GLsync __stdcall glFenceSync(GLenum condition, GLbitfield flags)
    {
        EVENT("(GLenum condition = 0x%X, GLbitfield flags = 0x%X)", condition, flags);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION, reinterpret_cast<GLsync>(0));
            }
    
            if (condition != GL_SYNC_GPU_COMMANDS_COMPLETE)
            {
                return gl::error(GL_INVALID_ENUM, reinterpret_cast<GLsync>(0));
            }
    
            if (flags != 0)
            {
                return gl::error(GL_INVALID_VALUE, reinterpret_cast<GLsync>(0));
            }
    
            return context->createFenceSync(condition);
        }
    
        return NULL;
    }
    
    GLboolean __stdcall glIsSync(GLsync sync)
    {
        EVENT("(GLsync sync = 0x%0.8p)", sync);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION, GL_FALSE);
            }
    
            return (context->getFenceSync(sync) != NULL);
        }
    
        return GL_FALSE;
    }
    
    void __stdcall glDeleteSync(GLsync sync)
    {
        EVENT("(GLsync sync = 0x%0.8p)", sync);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (sync != static_cast<GLsync>(0) && !context->getFenceSync(sync))
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            context->deleteFenceSync(sync);
        }
    }
    
    GLenum __stdcall glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout)
    {
        EVENT("(GLsync sync = 0x%0.8p, GLbitfield flags = 0x%X, GLuint64 timeout = %llu)",
              sync, flags, timeout);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION, GL_WAIT_FAILED);
            }
    
            if ((flags & ~(GL_SYNC_FLUSH_COMMANDS_BIT)) != 0)
            {
                return gl::error(GL_INVALID_VALUE, GL_WAIT_FAILED);
            }
    
            gl::FenceSync *fenceSync = context->getFenceSync(sync);
    
            if (!fenceSync)
            {
                return gl::error(GL_INVALID_VALUE, GL_WAIT_FAILED);
            }
    
            return fenceSync->clientWait(flags, timeout);
        }
    
        return GL_FALSE;
    }
    
    void __stdcall glWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout)
    {
        EVENT("(GLsync sync = 0x%0.8p, GLbitfield flags = 0x%X, GLuint64 timeout = %llu)",
              sync, flags, timeout);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (flags != 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            if (timeout != GL_TIMEOUT_IGNORED)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            gl::FenceSync *fenceSync = context->getFenceSync(sync);
    
            if (!fenceSync)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            fenceSync->serverWait();
        }
    }
    
    void __stdcall glGetInteger64v(GLenum pname, GLint64* params)
    {
        EVENT("(GLenum pname = 0x%X, GLint64* params = 0x%0.8p)",
              pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            GLenum nativeType;
            unsigned int numParams = 0;
            if (!ValidateStateQuery(context, pname, &nativeType, &numParams))
            {
                return;
            }
    
            if (nativeType == GL_INT_64_ANGLEX)
            {
                context->getInteger64v(pname, params);
            }
            else
            {
                CastStateValues(context, nativeType, pname, numParams, params);
            }
        }
    }
    
    void __stdcall glGetSynciv(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values)
    {
        EVENT("(GLsync sync = 0x%0.8p, GLenum pname = 0x%X, GLsizei bufSize = %d, GLsizei* length = 0x%0.8p, GLint* values = 0x%0.8p)",
              sync, pname, bufSize, length, values);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (bufSize < 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            gl::FenceSync *fenceSync = context->getFenceSync(sync);
    
            if (!fenceSync)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            switch (pname)
            {
              case GL_OBJECT_TYPE:     values[0] = static_cast<GLint>(GL_SYNC_FENCE);              break;
              case GL_SYNC_STATUS:     values[0] = static_cast<GLint>(fenceSync->getStatus());     break;
              case GL_SYNC_CONDITION:  values[0] = static_cast<GLint>(fenceSync->getCondition());  break;
              case GL_SYNC_FLAGS:      values[0] = 0;                                              break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glGetInteger64i_v(GLenum target, GLuint index, GLint64* data)
    {
        EVENT("(GLenum target = 0x%X, GLuint index = %u, GLint64* data = 0x%0.8p)",
              target, index, data);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            switch (target)
            {
              case GL_TRANSFORM_FEEDBACK_BUFFER_START:
              case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
              case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
                if (index >= context->getMaxTransformFeedbackBufferBindings())
                    return gl::error(GL_INVALID_VALUE);
                break;
              case GL_UNIFORM_BUFFER_START:
              case GL_UNIFORM_BUFFER_SIZE:
              case GL_UNIFORM_BUFFER_BINDING:
                if (index >= context->getMaximumCombinedUniformBufferBindings())
                    return gl::error(GL_INVALID_VALUE);
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
    
            if (!(context->getIndexedInteger64v(target, index, data)))
            {
                GLenum nativeType;
                unsigned int numParams = 0;
                if (!context->getIndexedQueryParameterInfo(target, &nativeType, &numParams))
                    return gl::error(GL_INVALID_ENUM);
    
                if (numParams == 0)
                    return; // it is known that pname is valid, but there are no parameters to return
    
                if (nativeType == GL_INT)
                {
                    GLint *intParams = new GLint[numParams];
    
                    context->getIndexedIntegerv(target, index, intParams);
    
                    for (unsigned int i = 0; i < numParams; ++i)
                    {
                        data[i] = static_cast<GLint64>(intParams[i]);
                    }
    
                    delete [] intParams;
                }
                else
                {
                    UNREACHABLE();
                }
            }
        }
    }
    
    void __stdcall glGetBufferParameteri64v(GLenum target, GLenum pname, GLint64* params)
    {
        EVENT("(GLenum target = 0x%X, GLenum pname = 0x%X, GLint64* params = 0x%0.8p)",
              target, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!gl::ValidBufferTarget(context, target))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            if (!gl::ValidBufferParameter(context, pname))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            gl::Buffer *buffer = context->getState().getTargetBuffer(target);
    
            if (!buffer)
            {
                // A null buffer means that "0" is bound to the requested buffer target
                return gl::error(GL_INVALID_OPERATION);
            }
    
            switch (pname)
            {
              case GL_BUFFER_USAGE:
                *params = static_cast<GLint64>(buffer->getUsage());
                break;
              case GL_BUFFER_SIZE:
                *params = buffer->getSize();
                break;
              case GL_BUFFER_ACCESS_FLAGS:
                *params = static_cast<GLint64>(buffer->getAccessFlags());
                break;
              case GL_BUFFER_MAPPED:
                *params = static_cast<GLint64>(buffer->isMapped());
                break;
              case GL_BUFFER_MAP_OFFSET:
                *params = buffer->getMapOffset();
                break;
              case GL_BUFFER_MAP_LENGTH:
                *params = buffer->getMapLength();
                break;
              default: UNREACHABLE(); break;
            }
        }
    }
    
    void __stdcall glGenSamplers(GLsizei count, GLuint* samplers)
    {
        EVENT("(GLsizei count = %d, GLuint* samplers = 0x%0.8p)", count, samplers);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (count < 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            for (int i = 0; i < count; i++)
            {
                samplers[i] = context->createSampler();
            }
        }
    }
    
    void __stdcall glDeleteSamplers(GLsizei count, const GLuint* samplers)
    {
        EVENT("(GLsizei count = %d, const GLuint* samplers = 0x%0.8p)", count, samplers);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (count < 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            for (int i = 0; i < count; i++)
            {
                context->deleteSampler(samplers[i]);
            }
        }
    }
    
    GLboolean __stdcall glIsSampler(GLuint sampler)
    {
        EVENT("(GLuint sampler = %u)", sampler);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION, GL_FALSE);
            }
    
            return context->isSampler(sampler);
        }
    
        return GL_FALSE;
    }
    
    void __stdcall glBindSampler(GLuint unit, GLuint sampler)
    {
        EVENT("(GLuint unit = %u, GLuint sampler = %u)", unit, sampler);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (sampler != 0 && !context->isSampler(sampler))
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (unit >= context->getMaximumCombinedTextureImageUnits())
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            context->bindSampler(unit, sampler);
        }
    }
    
    void __stdcall glSamplerParameteri(GLuint sampler, GLenum pname, GLint param)
    {
        EVENT("(GLuint sampler = %u, GLenum pname = 0x%X, GLint param = %d)", sampler, pname, param);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!gl::ValidateSamplerObjectParameter(pname))
            {
                return;
            }
    
            if (!gl::ValidateTexParamParameters(context, pname, param))
            {
                return;
            }
    
            if (!context->isSampler(sampler))
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            context->samplerParameteri(sampler, pname, param);
        }
    }
    
    void __stdcall glSamplerParameteriv(GLuint sampler, GLenum pname, const GLint* param)
    {
        glSamplerParameteri(sampler, pname, *param);
    }
    
    void __stdcall glSamplerParameterf(GLuint sampler, GLenum pname, GLfloat param)
    {
        EVENT("(GLuint sampler = %u, GLenum pname = 0x%X, GLfloat param = %g)", sampler, pname, param);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!gl::ValidateSamplerObjectParameter(pname))
            {
                return;
            }
    
            if (!gl::ValidateTexParamParameters(context, pname, static_cast<GLint>(param)))
            {
                return;
            }
    
            if (!context->isSampler(sampler))
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            context->samplerParameterf(sampler, pname, param);
        }
    }
    
    void __stdcall glSamplerParameterfv(GLuint sampler, GLenum pname, const GLfloat* param)
    {
        glSamplerParameterf(sampler, pname, *param);
    }
    
    void __stdcall glGetSamplerParameteriv(GLuint sampler, GLenum pname, GLint* params)
    {
        EVENT("(GLuint sampler = %u, GLenum pname = 0x%X, GLint* params = 0x%0.8p)", sampler, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!gl::ValidateSamplerObjectParameter(pname))
            {
                return;
            }
    
            if (!context->isSampler(sampler))
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            *params = context->getSamplerParameteri(sampler, pname);
        }
    }
    
    void __stdcall glGetSamplerParameterfv(GLuint sampler, GLenum pname, GLfloat* params)
    {
        EVENT("(GLuint sample = %ur, GLenum pname = 0x%X, GLfloat* params = 0x%0.8p)", sampler, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!gl::ValidateSamplerObjectParameter(pname))
            {
                return;
            }
    
            if (!context->isSampler(sampler))
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            *params = context->getSamplerParameterf(sampler, pname);
        }
    }
    
    void __stdcall glVertexAttribDivisor(GLuint index, GLuint divisor)
    {
        EVENT("(GLuint index = %u, GLuint divisor = %u)", index, divisor);
    
        if (index >= gl::MAX_VERTEX_ATTRIBS)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            context->setVertexAttribDivisor(index, divisor);
        }
    }
    
    void __stdcall glBindTransformFeedback(GLenum target, GLuint id)
    {
        EVENT("(GLenum target = 0x%X, GLuint id = %u)", target, id);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            switch (target)
            {
              case GL_TRANSFORM_FEEDBACK:
                {
                    // Cannot bind a transform feedback object if the current one is started and not paused (3.0.2 pg 85 section 2.14.1)
                    gl::TransformFeedback *curTransformFeedback = context->getState().getCurrentTransformFeedback();
                    if (curTransformFeedback && curTransformFeedback->isStarted() && !curTransformFeedback->isPaused())
                    {
                        return gl::error(GL_INVALID_OPERATION);
                    }
    
                    // Cannot bind a transform feedback object that does not exist (3.0.2 pg 85 section 2.14.1)
                    if (context->getTransformFeedback(id) == NULL)
                    {
                        return gl::error(GL_INVALID_OPERATION);
                    }
    
                    context->bindTransformFeedback(id);
                }
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glDeleteTransformFeedbacks(GLsizei n, const GLuint* ids)
    {
        EVENT("(GLsizei n = %d, const GLuint* ids = 0x%0.8p)", n, ids);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            for (int i = 0; i < n; i++)
            {
                context->deleteTransformFeedback(ids[i]);
            }
        }
    }
    
    void __stdcall glGenTransformFeedbacks(GLsizei n, GLuint* ids)
    {
        EVENT("(GLsizei n = %d, GLuint* ids = 0x%0.8p)", n, ids);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            for (int i = 0; i < n; i++)
            {
                ids[i] = context->createTransformFeedback();
            }
        }
    }
    
    GLboolean __stdcall glIsTransformFeedback(GLuint id)
    {
        EVENT("(GLuint id = %u)", id);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION, GL_FALSE);
            }
    
            return ((context->getTransformFeedback(id) != NULL) ? GL_TRUE : GL_FALSE);
        }
    
        return GL_FALSE;
    }
    
    void __stdcall glPauseTransformFeedback(void)
    {
        EVENT("(void)");
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            gl::TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
            ASSERT(transformFeedback != NULL);
    
            // Current transform feedback must be started and not paused in order to pause (3.0.2 pg 86)
            if (!transformFeedback->isStarted() || transformFeedback->isPaused())
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            transformFeedback->pause();
        }
    }
    
    void __stdcall glResumeTransformFeedback(void)
    {
        EVENT("(void)");
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            gl::TransformFeedback *transformFeedback = context->getState().getCurrentTransformFeedback();
            ASSERT(transformFeedback != NULL);
    
            // Current transform feedback must be started and paused in order to resume (3.0.2 pg 86)
            if (!transformFeedback->isStarted() || !transformFeedback->isPaused())
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            transformFeedback->resume();
        }
    }
    
    void __stdcall glGetProgramBinary(GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, GLvoid* binary)
    {
        EVENT("(GLuint program = %u, GLsizei bufSize = %d, GLsizei* length = 0x%0.8p, GLenum* binaryFormat = 0x%0.8p, GLvoid* binary = 0x%0.8p)",
              program, bufSize, length, binaryFormat, binary);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // glGetProgramBinary
            UNIMPLEMENTED();
        }
    }
    
    void __stdcall glProgramBinary(GLuint program, GLenum binaryFormat, const GLvoid* binary, GLsizei length)
    {
        EVENT("(GLuint program = %u, GLenum binaryFormat = 0x%X, const GLvoid* binary = 0x%0.8p, GLsizei length = %d)",
              program, binaryFormat, binary, length);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // glProgramBinary
            UNIMPLEMENTED();
        }
    }
    
    void __stdcall glProgramParameteri(GLuint program, GLenum pname, GLint value)
    {
        EVENT("(GLuint program = %u, GLenum pname = 0x%X, GLint value = %d)",
              program, pname, value);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // glProgramParameteri
            UNIMPLEMENTED();
        }
    }
    
    void __stdcall glInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments)
    {
        EVENT("(GLenum target = 0x%X, GLsizei numAttachments = %d, const GLenum* attachments = 0x%0.8p)",
              target, numAttachments, attachments);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!ValidateInvalidateFramebufferParameters(context, target, numAttachments, attachments))
            {
                return;
            }
    
            GLuint maxDimension = context->getCaps().maxRenderbufferSize;
            context->invalidateFrameBuffer(target, numAttachments, attachments, 0, 0, maxDimension, maxDimension);
        }
    }
    
    void __stdcall glInvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height)
    {
        EVENT("(GLenum target = 0x%X, GLsizei numAttachments = %d, const GLenum* attachments = 0x%0.8p, GLint x = %d, "
              "GLint y = %d, GLsizei width = %d, GLsizei height = %d)",
              target, numAttachments, attachments, x, y, width, height);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!ValidateInvalidateFramebufferParameters(context, target, numAttachments, attachments))
            {
                return;
            }
    
            context->invalidateFrameBuffer(target, numAttachments, attachments, x, y, width, height);
        }
    }
    
    void __stdcall glTexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
    {
        EVENT("(GLenum target = 0x%X, GLsizei levels = %d, GLenum internalformat = 0x%X, GLsizei width = %d, GLsizei height = %d)",
              target, levels, internalformat, width, height);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!ValidateES3TexStorageParameters(context, target, levels, internalformat, width, height, 1))
            {
                return;
            }
    
            switch (target)
            {
              case GL_TEXTURE_2D:
                {
                    gl::Texture2D *texture2d = context->getTexture2D();
                    texture2d->storage(levels, internalformat, width, height);
                }
                break;
    
              case GL_TEXTURE_CUBE_MAP:
                {
                    gl::TextureCubeMap *textureCube = context->getTextureCubeMap();
                    textureCube->storage(levels, internalformat, width);
                }
                break;
    
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    void __stdcall glTexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
    {
        EVENT("(GLenum target = 0x%X, GLsizei levels = %d, GLenum internalformat = 0x%X, GLsizei width = %d, "
              "GLsizei height = %d, GLsizei depth = %d)",
              target, levels, internalformat, width, height, depth);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!ValidateES3TexStorageParameters(context, target, levels, internalformat, width, height, depth))
            {
                return;
            }
    
            switch (target)
            {
              case GL_TEXTURE_3D:
                {
                    gl::Texture3D *texture3d = context->getTexture3D();
                    texture3d->storage(levels, internalformat, width, height, depth);
                }
                break;
    
              case GL_TEXTURE_2D_ARRAY:
                {
                    gl::Texture2DArray *texture2darray = context->getTexture2DArray();
                    texture2darray->storage(levels, internalformat, width, height, depth);
                }
                break;
    
              default:
                UNREACHABLE();
            }
        }
    }
    
    void __stdcall glGetInternalformativ(GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params)
    {
        EVENT("(GLenum target = 0x%X, GLenum internalformat = 0x%X, GLenum pname = 0x%X, GLsizei bufSize = %d, "
              "GLint* params = 0x%0.8p)",
              target, internalformat, pname, bufSize, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (context->getClientVersion() < 3)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            const gl::TextureCaps &formatCaps = context->getTextureCaps().get(internalformat);
            if (!formatCaps.renderable)
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            if (target != GL_RENDERBUFFER)
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            if (bufSize < 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            switch (pname)
            {
              case GL_NUM_SAMPLE_COUNTS:
                if (bufSize != 0)
                {
                    *params = formatCaps.sampleCounts.size();
                }
                break;
              case GL_SAMPLES:
                std::copy_n(formatCaps.sampleCounts.rbegin(), std::min<size_t>(bufSize, formatCaps.sampleCounts.size()), params);
                break;
              default:
                return gl::error(GL_INVALID_ENUM);
            }
        }
    }
    
    // Extension functions
    
    void __stdcall glBlitFramebufferANGLE(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                                          GLbitfield mask, GLenum filter)
    {
        EVENT("(GLint srcX0 = %d, GLint srcY0 = %d, GLint srcX1 = %d, GLint srcY1 = %d, "
              "GLint dstX0 = %d, GLint dstY0 = %d, GLint dstX1 = %d, GLint dstY1 = %d, "
              "GLbitfield mask = 0x%X, GLenum filter = 0x%X)",
              srcX0, srcY0, srcX1, srcX1, dstX0, dstY0, dstX1, dstY1, mask, filter);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!ValidateBlitFramebufferParameters(context, srcX0, srcY0, srcX1, srcY1,
                                                   dstX0, dstY0, dstX1, dstY1, mask, filter,
                                                   true))
            {
                return;
            }
    
            context->blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1,
                                     mask, filter);
        }
    }
    
    void __stdcall glTexImage3DOES(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth,
                                   GLint border, GLenum format, GLenum type, const GLvoid* pixels)
    {
        EVENT("(GLenum target = 0x%X, GLint level = %d, GLenum internalformat = 0x%X, "
              "GLsizei width = %d, GLsizei height = %d, GLsizei depth = %d, GLint border = %d, "
              "GLenum format = 0x%X, GLenum type = 0x%x, const GLvoid* pixels = 0x%0.8p)",
              target, level, internalformat, width, height, depth, border, format, type, pixels);
    
        UNIMPLEMENTED();   // FIXME
    }
    
    void __stdcall glGetProgramBinaryOES(GLuint program, GLsizei bufSize, GLsizei *length,
                                         GLenum *binaryFormat, void *binary)
    {
        EVENT("(GLenum program = 0x%X, bufSize = %d, length = 0x%0.8p, binaryFormat = 0x%0.8p, binary = 0x%0.8p)",
              program, bufSize, length, binaryFormat, binary);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject || !programObject->isLinked())
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            gl::ProgramBinary *programBinary = programObject->getProgramBinary();
    
            if (!programBinary)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!programBinary->save(binary, bufSize, length))
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            *binaryFormat = GL_PROGRAM_BINARY_ANGLE;
        }
    }
    
    void __stdcall glProgramBinaryOES(GLuint program, GLenum binaryFormat,
                                      const void *binary, GLint length)
    {
        EVENT("(GLenum program = 0x%X, binaryFormat = 0x%x, binary = 0x%0.8p, length = %d)",
              program, binaryFormat, binary, length);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (binaryFormat != GL_PROGRAM_BINARY_ANGLE)
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            gl::Program *programObject = context->getProgram(program);
    
            if (!programObject)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            context->setProgramBinary(program, binary, length);
        }
    }
    
    void __stdcall glDrawBuffersEXT(GLsizei n, const GLenum *bufs)
    {
        EVENT("(GLenum n = %d, bufs = 0x%0.8p)", n, bufs);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (n < 0 || static_cast<GLuint>(n) > context->getCaps().maxDrawBuffers)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            if (context->getState().getDrawFramebuffer()->id() == 0)
            {
                if (n != 1)
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
    
                if (bufs[0] != GL_NONE && bufs[0] != GL_BACK)
                {
                    return gl::error(GL_INVALID_OPERATION);
                }
            }
            else
            {
                for (int colorAttachment = 0; colorAttachment < n; colorAttachment++)
                {
                    const GLenum attachment = GL_COLOR_ATTACHMENT0_EXT + colorAttachment;
                    if (bufs[colorAttachment] != GL_NONE && bufs[colorAttachment] != attachment)
                    {
                        return gl::error(GL_INVALID_OPERATION);
                    }
                }
            }
    
            gl::Framebuffer *framebuffer = context->getState().getDrawFramebuffer();
    
            for (unsigned int colorAttachment = 0; colorAttachment < static_cast<unsigned int>(n); colorAttachment++)
            {
                framebuffer->setDrawBufferState(colorAttachment, bufs[colorAttachment]);
            }
    
            for (unsigned int colorAttachment = n; colorAttachment < context->getCaps().maxDrawBuffers; colorAttachment++)
            {
                framebuffer->setDrawBufferState(colorAttachment, GL_NONE);
            }
        }
    }
    
    void __stdcall glGetBufferPointervOES(GLenum target, GLenum pname, void** params)
    {
        EVENT("(GLenum target = 0x%X, GLenum pname = 0x%X, GLvoid** params = 0x%0.8p)", target, pname, params);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!gl::ValidBufferTarget(context, target))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            if (pname != GL_BUFFER_MAP_POINTER)
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            gl::Buffer *buffer = context->getState().getTargetBuffer(target);
    
            if (!buffer || !buffer->isMapped())
            {
                *params = NULL;
            }
            else
            {
                *params = buffer->getMapPointer();
            }
        }
    }
    
    void * __stdcall glMapBufferOES(GLenum target, GLenum access)
    {
        EVENT("(GLenum target = 0x%X, GLbitfield access = 0x%X)", target, access);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!gl::ValidBufferTarget(context, target))
            {
                return gl::error(GL_INVALID_ENUM, reinterpret_cast<GLvoid*>(NULL));
            }
    
            gl::Buffer *buffer = context->getState().getTargetBuffer(target);
    
            if (buffer == NULL)
            {
                return gl::error(GL_INVALID_OPERATION, reinterpret_cast<GLvoid*>(NULL));
            }
    
            if (access != GL_WRITE_ONLY_OES)
            {
                return gl::error(GL_INVALID_ENUM, reinterpret_cast<GLvoid*>(NULL));
            }
    
            if (buffer->isMapped())
            {
                return gl::error(GL_INVALID_OPERATION, reinterpret_cast<GLvoid*>(NULL));
            }
    
            return buffer->mapRange(0, buffer->getSize(), GL_MAP_WRITE_BIT);
        }
    
        return NULL;
    }
    
    GLboolean __stdcall glUnmapBufferOES(GLenum target)
    {
        EVENT("(GLenum target = 0x%X)", target);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!gl::ValidBufferTarget(context, target))
            {
                return gl::error(GL_INVALID_ENUM, GL_FALSE);
            }
    
            gl::Buffer *buffer = context->getState().getTargetBuffer(target);
    
            if (buffer == NULL || !buffer->isMapped())
            {
                return gl::error(GL_INVALID_OPERATION, GL_FALSE);
            }
    
            // TODO: detect if we had corruption. if so, throw an error and return false.
    
            buffer->unmap();
    
            return GL_TRUE;
        }
    
        return GL_FALSE;
    }
    
    void* __stdcall glMapBufferRangeEXT (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)
    {
        EVENT("(GLenum target = 0x%X, GLintptr offset = %d, GLsizeiptr length = %d, GLbitfield access = 0x%X)",
              target, offset, length, access);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (!gl::ValidBufferTarget(context, target))
            {
                return gl::error(GL_INVALID_ENUM, reinterpret_cast<GLvoid*>(NULL));
            }
    
            if (offset < 0 || length < 0)
            {
                return gl::error(GL_INVALID_VALUE, reinterpret_cast<GLvoid*>(NULL));
            }
    
            gl::Buffer *buffer = context->getState().getTargetBuffer(target);
    
            if (buffer == NULL)
            {
                return gl::error(GL_INVALID_OPERATION, reinterpret_cast<GLvoid*>(NULL));
            }
    
            // Check for buffer overflow
            size_t offsetSize = static_cast<size_t>(offset);
            size_t lengthSize = static_cast<size_t>(length);
    
            if (!rx::IsUnsignedAdditionSafe(offsetSize, lengthSize) ||
                offsetSize + lengthSize > static_cast<size_t>(buffer->getSize()))
            {
                return gl::error(GL_INVALID_VALUE, reinterpret_cast<GLvoid*>(NULL));
            }
    
            // Check for invalid bits in the mask
            GLbitfield allAccessBits = GL_MAP_READ_BIT |
                                       GL_MAP_WRITE_BIT |
                                       GL_MAP_INVALIDATE_RANGE_BIT |
                                       GL_MAP_INVALIDATE_BUFFER_BIT |
                                       GL_MAP_FLUSH_EXPLICIT_BIT |
                                       GL_MAP_UNSYNCHRONIZED_BIT;
    
            if (access & ~(allAccessBits))
            {
                return gl::error(GL_INVALID_VALUE, reinterpret_cast<GLvoid*>(NULL));
            }
    
            if (length == 0 || buffer->isMapped())
            {
                return gl::error(GL_INVALID_OPERATION, reinterpret_cast<GLvoid*>(NULL));
            }
    
            // Check for invalid bit combinations
            if ((access & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT)) == 0)
            {
                return gl::error(GL_INVALID_OPERATION, reinterpret_cast<GLvoid*>(NULL));
            }
    
            GLbitfield writeOnlyBits = GL_MAP_INVALIDATE_RANGE_BIT |
                                       GL_MAP_INVALIDATE_BUFFER_BIT |
                                       GL_MAP_UNSYNCHRONIZED_BIT;
    
            if ((access & GL_MAP_READ_BIT) != 0 && (access & writeOnlyBits) != 0)
            {
                return gl::error(GL_INVALID_OPERATION, reinterpret_cast<GLvoid*>(NULL));
            }
    
            if ((access & GL_MAP_WRITE_BIT) == 0 && (access & GL_MAP_FLUSH_EXPLICIT_BIT) != 0)
            {
                return gl::error(GL_INVALID_OPERATION, reinterpret_cast<GLvoid*>(NULL));
            }
    
            return buffer->mapRange(offset, length, access);
        }
    
        return NULL;
    }
    
    void __stdcall glFlushMappedBufferRangeEXT (GLenum target, GLintptr offset, GLsizeiptr length)
    {
        EVENT("(GLenum target = 0x%X, GLintptr offset = %d, GLsizeiptr length = %d)", target, offset, length);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            if (offset < 0 || length < 0)
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            if (!gl::ValidBufferTarget(context, target))
            {
                return gl::error(GL_INVALID_ENUM);
            }
    
            gl::Buffer *buffer = context->getState().getTargetBuffer(target);
    
            if (buffer == NULL)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            if (!buffer->isMapped() || (buffer->getAccessFlags() & GL_MAP_FLUSH_EXPLICIT_BIT) == 0)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            // Check for buffer overflow
            size_t offsetSize = static_cast<size_t>(offset);
            size_t lengthSize = static_cast<size_t>(length);
    
            if (!rx::IsUnsignedAdditionSafe(offsetSize, lengthSize) ||
                offsetSize + lengthSize > static_cast<size_t>(buffer->getMapLength()))
            {
                return gl::error(GL_INVALID_VALUE);
            }
    
            // We do not currently support a non-trivial implementation of FlushMappedBufferRange
        }
    }
    
    __eglMustCastToProperFunctionPointerType __stdcall glGetProcAddress(const char *procname)
    {
        struct Extension
        {
            const char *name;
            __eglMustCastToProperFunctionPointerType address;
        };
    
        static const Extension glExtensions[] =
        {
            {"glTexImage3DOES", (__eglMustCastToProperFunctionPointerType)glTexImage3DOES},
            {"glBlitFramebufferANGLE", (__eglMustCastToProperFunctionPointerType)glBlitFramebufferANGLE},
            {"glRenderbufferStorageMultisampleANGLE", (__eglMustCastToProperFunctionPointerType)glRenderbufferStorageMultisampleANGLE},
            {"glDeleteFencesNV", (__eglMustCastToProperFunctionPointerType)glDeleteFencesNV},
            {"glGenFencesNV", (__eglMustCastToProperFunctionPointerType)glGenFencesNV},
            {"glIsFenceNV", (__eglMustCastToProperFunctionPointerType)glIsFenceNV},
            {"glTestFenceNV", (__eglMustCastToProperFunctionPointerType)glTestFenceNV},
            {"glGetFenceivNV", (__eglMustCastToProperFunctionPointerType)glGetFenceivNV},
            {"glFinishFenceNV", (__eglMustCastToProperFunctionPointerType)glFinishFenceNV},
            {"glSetFenceNV", (__eglMustCastToProperFunctionPointerType)glSetFenceNV},
            {"glGetTranslatedShaderSourceANGLE", (__eglMustCastToProperFunctionPointerType)glGetTranslatedShaderSourceANGLE},
            {"glTexStorage2DEXT", (__eglMustCastToProperFunctionPointerType)glTexStorage2DEXT},
            {"glGetGraphicsResetStatusEXT", (__eglMustCastToProperFunctionPointerType)glGetGraphicsResetStatusEXT},
            {"glReadnPixelsEXT", (__eglMustCastToProperFunctionPointerType)glReadnPixelsEXT},
            {"glGetnUniformfvEXT", (__eglMustCastToProperFunctionPointerType)glGetnUniformfvEXT},
            {"glGetnUniformivEXT", (__eglMustCastToProperFunctionPointerType)glGetnUniformivEXT},
            {"glGenQueriesEXT", (__eglMustCastToProperFunctionPointerType)glGenQueriesEXT},
            {"glDeleteQueriesEXT", (__eglMustCastToProperFunctionPointerType)glDeleteQueriesEXT},
            {"glIsQueryEXT", (__eglMustCastToProperFunctionPointerType)glIsQueryEXT},
            {"glBeginQueryEXT", (__eglMustCastToProperFunctionPointerType)glBeginQueryEXT},
            {"glEndQueryEXT", (__eglMustCastToProperFunctionPointerType)glEndQueryEXT},
            {"glGetQueryivEXT", (__eglMustCastToProperFunctionPointerType)glGetQueryivEXT},
            {"glGetQueryObjectuivEXT", (__eglMustCastToProperFunctionPointerType)glGetQueryObjectuivEXT},
            {"glDrawBuffersEXT", (__eglMustCastToProperFunctionPointerType)glDrawBuffersEXT},
            {"glVertexAttribDivisorANGLE", (__eglMustCastToProperFunctionPointerType)glVertexAttribDivisorANGLE},
            {"glDrawArraysInstancedANGLE", (__eglMustCastToProperFunctionPointerType)glDrawArraysInstancedANGLE},
            {"glDrawElementsInstancedANGLE", (__eglMustCastToProperFunctionPointerType)glDrawElementsInstancedANGLE},
            {"glGetProgramBinaryOES", (__eglMustCastToProperFunctionPointerType)glGetProgramBinaryOES},
            {"glProgramBinaryOES", (__eglMustCastToProperFunctionPointerType)glProgramBinaryOES},
            {"glGetBufferPointervOES", (__eglMustCastToProperFunctionPointerType)glGetBufferPointervOES},
            {"glMapBufferOES", (__eglMustCastToProperFunctionPointerType)glMapBufferOES},
            {"glUnmapBufferOES", (__eglMustCastToProperFunctionPointerType)glUnmapBufferOES},
            {"glMapBufferRangeEXT", (__eglMustCastToProperFunctionPointerType)glMapBufferRangeEXT},
            {"glFlushMappedBufferRangeEXT", (__eglMustCastToProperFunctionPointerType)glFlushMappedBufferRangeEXT},    };
    
        for (unsigned int ext = 0; ext < ArraySize(glExtensions); ext++)
        {
            if (strcmp(procname, glExtensions[ext].name) == 0)
            {
                return (__eglMustCastToProperFunctionPointerType)glExtensions[ext].address;
            }
        }
    
        return NULL;
    }
    
    // Non-public functions used by EGL
    
    bool __stdcall glBindTexImage(egl::Surface *surface)
    {
        EVENT("(egl::Surface* surface = 0x%0.8p)",
              surface);
    
        gl::Context *context = gl::getNonLostContext();
    
        if (context)
        {
            gl::Texture2D *textureObject = context->getTexture2D();
            ASSERT(textureObject != NULL);
    
            if (textureObject->isImmutable())
            {
                return false;
            }
    
            textureObject->bindTexImage(surface);
        }
    
        return true;
    }
    
    }