Edit

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

Branch :

  • Show log

    Commit

  • Author : shannonwoods@chromium.org
    Date : 2013-05-30 00:20:58
    Hash : abf14cc3
    Message : Add several cases to API entry points to handle enum values that dEQP uses to reset state. The support for these methods is incomplete and unimplemented, but will not return INVALID_ENUM. TRAC #23089 Signed-off-by: Nicolas Capens Signed-off-by: Shannon Woods Author: Jamie Madill git-svn-id: https://angleproject.googlecode.com/svn/branches/es3proto@2408 736b8ea6-26fd-11df-bfd4-992fa37f6226

  • src/libGLESv2/Context.cpp
  • #include "precompiled.h"
    //
    // Copyright (c) 2002-2013 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.
    //
    
    // Context.cpp: Implements the gl::Context class, managing all GL state and performing
    // rendering operations. It is the GLES2 specific implementation of EGLContext.
    
    #include "libGLESv2/Context.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/Query.h"
    #include "libGLESv2/Texture.h"
    #include "libGLESv2/ResourceManager.h"
    #include "libGLESv2/renderer/IndexDataManager.h"
    #include "libGLESv2/renderer/RenderTarget.h"
    #include "libGLESv2/renderer/Renderer.h"
    
    #include "libEGL/Surface.h"
    
    #undef near
    #undef far
    
    namespace gl
    {
    static const char* makeStaticString(const std::string& str)
    {
        static std::set<std::string> strings;
        std::set<std::string>::iterator it = strings.find(str);
        if (it != strings.end())
          return it->c_str();
    
        return strings.insert(str).first->c_str();
    }
    
    Context::Context(int clientVersion, const gl::Context *shareContext, rx::Renderer *renderer, bool notifyResets, bool robustAccess) : mRenderer(renderer)
    {
        ASSERT(robustAccess == false);   // Unimplemented
    
        mFenceHandleAllocator.setBaseHandle(0);
    
        setClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    
        mClientVersion = clientVersion;
    
        mState.depthClearValue = 1.0f;
        mState.stencilClearValue = 0;
    
        mState.rasterizer.cullFace = false;
        mState.rasterizer.cullMode = GL_BACK;
        mState.rasterizer.frontFace = GL_CCW;
        mState.rasterizer.polygonOffsetFill = false;
        mState.rasterizer.polygonOffsetFactor = 0.0f;
        mState.rasterizer.polygonOffsetUnits = 0.0f;
        mState.rasterizer.pointDrawMode = false;
        mState.scissorTest = false;
        mState.scissor.x = 0;
        mState.scissor.y = 0;
        mState.scissor.width = 0;
        mState.scissor.height = 0;
    
        mState.blend.blend = false;
        mState.blend.sourceBlendRGB = GL_ONE;
        mState.blend.sourceBlendAlpha = GL_ONE;
        mState.blend.destBlendRGB = GL_ZERO;
        mState.blend.destBlendAlpha = GL_ZERO;
        mState.blend.blendEquationRGB = GL_FUNC_ADD;
        mState.blend.blendEquationAlpha = GL_FUNC_ADD;
        mState.blend.sampleAlphaToCoverage = false;
        mState.blend.dither = true;
    
        mState.blendColor.red = 0;
        mState.blendColor.green = 0;
        mState.blendColor.blue = 0;
        mState.blendColor.alpha = 0;
    
        mState.depthStencil.depthTest = false;
        mState.depthStencil.depthFunc = GL_LESS;
        mState.depthStencil.depthMask = true;
        mState.depthStencil.stencilTest = false;
        mState.depthStencil.stencilFunc = GL_ALWAYS;
        mState.depthStencil.stencilMask = -1;
        mState.depthStencil.stencilWritemask = -1;
        mState.depthStencil.stencilBackFunc = GL_ALWAYS;
        mState.depthStencil.stencilBackMask = - 1;
        mState.depthStencil.stencilBackWritemask = -1;
        mState.depthStencil.stencilFail = GL_KEEP;
        mState.depthStencil.stencilPassDepthFail = GL_KEEP;
        mState.depthStencil.stencilPassDepthPass = GL_KEEP;
        mState.depthStencil.stencilBackFail = GL_KEEP;
        mState.depthStencil.stencilBackPassDepthFail = GL_KEEP;
        mState.depthStencil.stencilBackPassDepthPass = GL_KEEP;
    
        mState.stencilRef = 0;
        mState.stencilBackRef = 0;
    
        mState.sampleCoverage = false;
        mState.sampleCoverageValue = 1.0f;
        mState.sampleCoverageInvert = false;
        mState.generateMipmapHint = GL_DONT_CARE;
        mState.fragmentShaderDerivativeHint = GL_DONT_CARE;
    
        mState.lineWidth = 1.0f;
    
        mState.viewport.x = 0;
        mState.viewport.y = 0;
        mState.viewport.width = 0;
        mState.viewport.height = 0;
        mState.zNear = 0.0f;
        mState.zFar = 1.0f;
    
        mState.blend.colorMaskRed = true;
        mState.blend.colorMaskGreen = true;
        mState.blend.colorMaskBlue = true;
        mState.blend.colorMaskAlpha = true;
    
        if (shareContext != NULL)
        {
            mResourceManager = shareContext->mResourceManager;
            mResourceManager->addRef();
        }
        else
        {
            mResourceManager = new ResourceManager(mRenderer);
        }
    
        // [OpenGL ES 2.0.24] section 3.7 page 83:
        // In the initial state, TEXTURE_2D and TEXTURE_CUBE_MAP have twodimensional
        // and cube map texture state vectors respectively associated with them.
        // In order that access to these initial textures not be lost, they are treated as texture
        // objects all of whose names are 0.
    
        mTexture2DZero.set(new Texture2D(mRenderer, 0));
        mTextureCubeMapZero.set(new TextureCubeMap(mRenderer, 0));
        mTexture3DZero.set(new Texture3D(mRenderer, 0));
        mTexture2DArrayZero.set(new Texture2DArray(mRenderer, 0));
    
        mState.activeSampler = 0;
        bindArrayBuffer(0);
        bindElementArrayBuffer(0);
        bindTextureCubeMap(0);
        bindTexture2D(0);
        bindReadFramebuffer(0);
        bindDrawFramebuffer(0);
        bindRenderbuffer(0);
    
        bindGenericUniformBuffer(0);
        for (int i = 0; i < IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS; i++)
        {
            bindIndexedUniformBuffer(0, i, 0, -1);
        }
    
        bindGenericTransformFeedbackBuffer(0);
        for (int i = 0; i < IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS; i++)
        {
            bindIndexedTransformFeedbackBuffer(0, i, 0, -1);
        }
    
        bindCopyReadBuffer(0);
        bindCopyWriteBuffer(0);
        bindPixelPackBuffer(0);
        bindPixelUnpackBuffer(0);
    
        mState.currentProgram = 0;
        mCurrentProgramBinary.set(NULL);
    
        mState.packAlignment = 4;
        mState.unpackAlignment = 4;
        mState.packReverseRowOrder = false;
    
        mCombinedExtensionsString = NULL;
        mRendererString = NULL;
    
        mInvalidEnum = false;
        mInvalidValue = false;
        mInvalidOperation = false;
        mOutOfMemory = false;
        mInvalidFramebufferOperation = false;
    
        mHasBeenCurrent = false;
        mContextLost = false;
        mResetStatus = GL_NO_ERROR;
        mResetStrategy = (notifyResets ? GL_LOSE_CONTEXT_ON_RESET_EXT : GL_NO_RESET_NOTIFICATION_EXT);
        mRobustAccess = robustAccess;
    
        mSupportsBGRATextures = false;
        mSupportsDXT1Textures = false;
        mSupportsDXT3Textures = false;
        mSupportsDXT5Textures = false;
        mSupportsEventQueries = false;
        mSupportsOcclusionQueries = false;
        mNumCompressedTextureFormats = 0;
    }
    
    Context::~Context()
    {
        if (mState.currentProgram != 0)
        {
            Program *programObject = mResourceManager->getProgram(mState.currentProgram);
            if (programObject)
            {
                programObject->release();
            }
            mState.currentProgram = 0;
        }
        mCurrentProgramBinary.set(NULL);
    
        while (!mFramebufferMap.empty())
        {
            deleteFramebuffer(mFramebufferMap.begin()->first);
        }
    
        while (!mFenceMap.empty())
        {
            deleteFence(mFenceMap.begin()->first);
        }
    
        while (!mQueryMap.empty())
        {
            deleteQuery(mQueryMap.begin()->first);
        }
    
        for (int type = 0; type < TEXTURE_TYPE_COUNT; type++)
        {
            for (int sampler = 0; sampler < IMPLEMENTATION_MAX_COMBINED_TEXTURE_IMAGE_UNITS; sampler++)
            {
                mState.samplerTexture[type][sampler].set(NULL);
            }
        }
    
        for (int type = 0; type < TEXTURE_TYPE_COUNT; type++)
        {
            mIncompleteTextures[type].set(NULL);
        }
    
        for (int i = 0; i < MAX_VERTEX_ATTRIBS; i++)
        {
            mState.vertexAttribute[i].mBoundBuffer.set(NULL);
        }
    
        for (int i = 0; i < QUERY_TYPE_COUNT; i++)
        {
            mState.activeQuery[i].set(NULL);
        }
    
        mState.arrayBuffer.set(NULL);
        mState.elementArrayBuffer.set(NULL);
        mState.renderbuffer.set(NULL);
    
        mTexture2DZero.set(NULL);
        mTextureCubeMapZero.set(NULL);
        mTexture3DZero.set(NULL);
        mTexture2DArrayZero.set(NULL);
    
        mState.genericUniformBuffer.set(NULL);
        for (int i = 0; i < IMPLEMENTATION_MAX_COMBINED_SHADER_UNIFORM_BUFFERS; i++)
        {
            mState.uniformBuffers[i].set(NULL);
        }
    
        mState.genericTransformFeedbackBuffer.set(NULL);
        for (int i = 0; i < IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS; i++)
        {
            mState.transformFeedbackBuffers[i].set(NULL);
        }
    
        mState.copyReadBuffer.set(NULL);
        mState.copyWriteBuffer.set(NULL);
    
        mState.pixelPackBuffer.set(NULL);
        mState.pixelUnpackBuffer.set(NULL);
    
        mResourceManager->release();
    }
    
    void Context::makeCurrent(egl::Surface *surface)
    {
        if (!mHasBeenCurrent)
        {
            mMajorShaderModel = mRenderer->getMajorShaderModel();
            mMaximumPointSize = mRenderer->getMaxPointSize();
            mSupportsVertexTexture = mRenderer->getVertexTextureSupport();
            mSupportsNonPower2Texture = mRenderer->getNonPower2TextureSupport();
            mSupportsInstancing = mRenderer->getInstancingSupport();
    
            mMaxViewportDimension = mRenderer->getMaxViewportDimension();
            mMax2DTextureDimension = std::min(std::min(mRenderer->getMaxTextureWidth(), mRenderer->getMaxTextureHeight()),
                                              (int)gl::IMPLEMENTATION_MAX_2D_TEXTURE_SIZE);
            mMaxCubeTextureDimension = std::min(mMax2DTextureDimension, (int)gl::IMPLEMENTATION_MAX_CUBE_MAP_TEXTURE_SIZE);
            mMax3DTextureDimension = std::min(std::min(mMax2DTextureDimension, mRenderer->getMaxTextureDepth()),
                                              (int)gl::IMPLEMENTATION_MAX_3D_TEXTURE_SIZE);
            mMax2DArrayTextureLayers = mRenderer->getMaxTextureArrayLayers();
            mMaxRenderbufferDimension = mMax2DTextureDimension;
            mMaxTextureLevel = log2(mMax2DTextureDimension) + 1;
            mMaxTextureAnisotropy = mRenderer->getTextureMaxAnisotropy();
            TRACE("Max2DTextureDimension=%d, MaxCubeTextureDimension=%d, Max3DTextureDimension=%d, Max2DArrayTextureLayers = %d, "
                  "MaxRenderbufferDimension=%d, MaxTextureLevel=%d, MaxTextureAnisotropy=%f",
                  mMax2DTextureDimension, mMaxCubeTextureDimension, mMax3DTextureDimension, mMax2DArrayTextureLayers,
                  mMaxRenderbufferDimension, mMaxTextureLevel, mMaxTextureAnisotropy);
    
            mSupportsEventQueries = mRenderer->getEventQuerySupport();
            mSupportsOcclusionQueries = mRenderer->getOcclusionQuerySupport();
            mSupportsBGRATextures = mRenderer->getBGRATextureSupport();
            mSupportsDXT1Textures = mRenderer->getDXT1TextureSupport();
            mSupportsDXT3Textures = mRenderer->getDXT3TextureSupport();
            mSupportsDXT5Textures = mRenderer->getDXT5TextureSupport();
            mSupportsFloat32Textures = mRenderer->getFloat32TextureSupport();
            mSupportsFloat32LinearFilter = mRenderer->getFloat32TextureFilteringSupport();
            mSupportsFloat32RenderableTextures = mRenderer->getFloat32TextureRenderingSupport();
            mSupportsFloat16Textures = mRenderer->getFloat16TextureSupport();
            mSupportsFloat16LinearFilter = mRenderer->getFloat16TextureFilteringSupport();
            mSupportsFloat16RenderableTextures = mRenderer->getFloat16TextureRenderingSupport();
            mSupportsLuminanceTextures = mRenderer->getLuminanceTextureSupport();
            mSupportsLuminanceAlphaTextures = mRenderer->getLuminanceAlphaTextureSupport();
            mSupportsDepthTextures = mRenderer->getDepthTextureSupport();
            mSupportsTextureFilterAnisotropy = mRenderer->getTextureFilterAnisotropySupport();
            mSupports32bitIndices = mRenderer->get32BitIndexSupport();
    
            mNumCompressedTextureFormats = 0;
            if (supportsDXT1Textures())
            {
                mNumCompressedTextureFormats += 2;
            }
            if (supportsDXT3Textures())
            {
                mNumCompressedTextureFormats += 1;
            }
            if (supportsDXT5Textures())
            {
                mNumCompressedTextureFormats += 1;
            }
    
            initExtensionString();
            initRendererString();
    
            mState.viewport.x = 0;
            mState.viewport.y = 0;
            mState.viewport.width = surface->getWidth();
            mState.viewport.height = surface->getHeight();
    
            mState.scissor.x = 0;
            mState.scissor.y = 0;
            mState.scissor.width = surface->getWidth();
            mState.scissor.height = surface->getHeight();
    
            mHasBeenCurrent = true;
        }
    
        // Wrap the existing swapchain resources into GL objects and assign them to the '0' names
        rx::SwapChain *swapchain = surface->getSwapChain();
    
        Colorbuffer *colorbufferZero = new Colorbuffer(mRenderer, swapchain);
        DepthStencilbuffer *depthStencilbufferZero = new DepthStencilbuffer(mRenderer, swapchain);
        Framebuffer *framebufferZero = new DefaultFramebuffer(mRenderer, colorbufferZero, depthStencilbufferZero);
    
        setFramebufferZero(framebufferZero);
    
        // Store the current client version in the renderer
        mRenderer->setCurrentClientVersion(mClientVersion);
    }
    
    // NOTE: this function should not assume that this context is current!
    void Context::markContextLost()
    {
        if (mResetStrategy == GL_LOSE_CONTEXT_ON_RESET_EXT)
            mResetStatus = GL_UNKNOWN_CONTEXT_RESET_EXT;
        mContextLost = true;
    }
    
    bool Context::isContextLost()
    {
        return mContextLost;
    }
    
    void Context::setClearColor(float red, float green, float blue, float alpha)
    {
        mState.colorClearValue.red = red;
        mState.colorClearValue.green = green;
        mState.colorClearValue.blue = blue;
        mState.colorClearValue.alpha = alpha;
    }
    
    void Context::setClearDepth(float depth)
    {
        mState.depthClearValue = depth;
    }
    
    void Context::setClearStencil(int stencil)
    {
        mState.stencilClearValue = stencil;
    }
    
    void Context::setCullFace(bool enabled)
    {
        mState.rasterizer.cullFace = enabled;
    }
    
    bool Context::isCullFaceEnabled() const
    {
        return mState.rasterizer.cullFace;
    }
    
    void Context::setCullMode(GLenum mode)
    {
        mState.rasterizer.cullMode = mode;
    }
    
    void Context::setFrontFace(GLenum front)
    {
        mState.rasterizer.frontFace = front;
    }
    
    void Context::setDepthTest(bool enabled)
    {
        mState.depthStencil.depthTest = enabled;
    }
    
    bool Context::isDepthTestEnabled() const
    {
        return mState.depthStencil.depthTest;
    }
    
    void Context::setDepthFunc(GLenum depthFunc)
    {
         mState.depthStencil.depthFunc = depthFunc;
    }
    
    void Context::setDepthRange(float zNear, float zFar)
    {
        mState.zNear = zNear;
        mState.zFar = zFar;
    }
    
    void Context::setBlend(bool enabled)
    {
        mState.blend.blend = enabled;
    }
    
    bool Context::isBlendEnabled() const
    {
        return mState.blend.blend;
    }
    
    void Context::setBlendFactors(GLenum sourceRGB, GLenum destRGB, GLenum sourceAlpha, GLenum destAlpha)
    {
        mState.blend.sourceBlendRGB = sourceRGB;
        mState.blend.destBlendRGB = destRGB;
        mState.blend.sourceBlendAlpha = sourceAlpha;
        mState.blend.destBlendAlpha = destAlpha;
    }
    
    void Context::setBlendColor(float red, float green, float blue, float alpha)
    {
        mState.blendColor.red = red;
        mState.blendColor.green = green;
        mState.blendColor.blue = blue;
        mState.blendColor.alpha = alpha;
    }
    
    void Context::setBlendEquation(GLenum rgbEquation, GLenum alphaEquation)
    {
        mState.blend.blendEquationRGB = rgbEquation;
        mState.blend.blendEquationAlpha = alphaEquation;
    }
    
    void Context::setStencilTest(bool enabled)
    {
        mState.depthStencil.stencilTest = enabled;
    }
    
    bool Context::isStencilTestEnabled() const
    {
        return mState.depthStencil.stencilTest;
    }
    
    void Context::setStencilParams(GLenum stencilFunc, GLint stencilRef, GLuint stencilMask)
    {
        mState.depthStencil.stencilFunc = stencilFunc;
        mState.stencilRef = (stencilRef > 0) ? stencilRef : 0;
        mState.depthStencil.stencilMask = stencilMask;
    }
    
    void Context::setStencilBackParams(GLenum stencilBackFunc, GLint stencilBackRef, GLuint stencilBackMask)
    {
        mState.depthStencil.stencilBackFunc = stencilBackFunc;
        mState.stencilBackRef = (stencilBackRef > 0) ? stencilBackRef : 0;
        mState.depthStencil.stencilBackMask = stencilBackMask;
    }
    
    void Context::setStencilWritemask(GLuint stencilWritemask)
    {
        mState.depthStencil.stencilWritemask = stencilWritemask;
    }
    
    void Context::setStencilBackWritemask(GLuint stencilBackWritemask)
    {
        mState.depthStencil.stencilBackWritemask = stencilBackWritemask;
    }
    
    void Context::setStencilOperations(GLenum stencilFail, GLenum stencilPassDepthFail, GLenum stencilPassDepthPass)
    {
        mState.depthStencil.stencilFail = stencilFail;
        mState.depthStencil.stencilPassDepthFail = stencilPassDepthFail;
        mState.depthStencil.stencilPassDepthPass = stencilPassDepthPass;
    }
    
    void Context::setStencilBackOperations(GLenum stencilBackFail, GLenum stencilBackPassDepthFail, GLenum stencilBackPassDepthPass)
    {
        mState.depthStencil.stencilBackFail = stencilBackFail;
        mState.depthStencil.stencilBackPassDepthFail = stencilBackPassDepthFail;
        mState.depthStencil.stencilBackPassDepthPass = stencilBackPassDepthPass;
    }
    
    void Context::setPolygonOffsetFill(bool enabled)
    {
         mState.rasterizer.polygonOffsetFill = enabled;
    }
    
    bool Context::isPolygonOffsetFillEnabled() const
    {
        return mState.rasterizer.polygonOffsetFill;
    }
    
    void Context::setPolygonOffsetParams(GLfloat factor, GLfloat units)
    {
        // An application can pass NaN values here, so handle this gracefully
        mState.rasterizer.polygonOffsetFactor = factor != factor ? 0.0f : factor;
        mState.rasterizer.polygonOffsetUnits = units != units ? 0.0f : units;
    }
    
    void Context::setSampleAlphaToCoverage(bool enabled)
    {
        mState.blend.sampleAlphaToCoverage = enabled;
    }
    
    bool Context::isSampleAlphaToCoverageEnabled() const
    {
        return mState.blend.sampleAlphaToCoverage;
    }
    
    void Context::setSampleCoverage(bool enabled)
    {
        mState.sampleCoverage = enabled;
    }
    
    bool Context::isSampleCoverageEnabled() const
    {
        return mState.sampleCoverage;
    }
    
    void Context::setSampleCoverageParams(GLclampf value, bool invert)
    {
        mState.sampleCoverageValue = value;
        mState.sampleCoverageInvert = invert;
    }
    
    void Context::setScissorTest(bool enabled)
    {
        mState.scissorTest = enabled;
    }
    
    bool Context::isScissorTestEnabled() const
    {
        return mState.scissorTest;
    }
    
    void Context::setDither(bool enabled)
    {
        mState.blend.dither = enabled;
    }
    
    bool Context::isDitherEnabled() const
    {
        return mState.blend.dither;
    }
    
    void Context::setLineWidth(GLfloat width)
    {
        mState.lineWidth = width;
    }
    
    void Context::setGenerateMipmapHint(GLenum hint)
    {
        mState.generateMipmapHint = hint;
    }
    
    void Context::setFragmentShaderDerivativeHint(GLenum hint)
    {
        mState.fragmentShaderDerivativeHint = hint;
        // TODO: Propagate the hint to shader translator so we can write
        // ddx, ddx_coarse, or ddx_fine depending on the hint.
        // Ignore for now. It is valid for implementations to ignore hint.
    }
    
    void Context::setViewportParams(GLint x, GLint y, GLsizei width, GLsizei height)
    {
        mState.viewport.x = x;
        mState.viewport.y = y;
        mState.viewport.width = width;
        mState.viewport.height = height;
    }
    
    void Context::setScissorParams(GLint x, GLint y, GLsizei width, GLsizei height)
    {
        mState.scissor.x = x;
        mState.scissor.y = y;
        mState.scissor.width = width;
        mState.scissor.height = height;
    }
    
    void Context::setColorMask(bool red, bool green, bool blue, bool alpha)
    {
        mState.blend.colorMaskRed = red;
        mState.blend.colorMaskGreen = green;
        mState.blend.colorMaskBlue = blue;
        mState.blend.colorMaskAlpha = alpha;
    }
    
    void Context::setDepthMask(bool mask)
    {
        mState.depthStencil.depthMask = mask;
    }
    
    void Context::setActiveSampler(unsigned int active)
    {
        mState.activeSampler = active;
    }
    
    GLuint Context::getReadFramebufferHandle() const
    {
        return mState.readFramebuffer;
    }
    
    GLuint Context::getDrawFramebufferHandle() const
    {
        return mState.drawFramebuffer;
    }
    
    GLuint Context::getRenderbufferHandle() const
    {
        return mState.renderbuffer.id();
    }
    
    GLuint Context::getArrayBufferHandle() const
    {
        return mState.arrayBuffer.id();
    }
    
    GLuint Context::getActiveQuery(GLenum target) const
    {
        Query *queryObject = NULL;
        
        switch (target)
        {
          case GL_ANY_SAMPLES_PASSED_EXT:
            queryObject = mState.activeQuery[QUERY_ANY_SAMPLES_PASSED].get();
            break;
          case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT:
            queryObject = mState.activeQuery[QUERY_ANY_SAMPLES_PASSED_CONSERVATIVE].get();
            break;
          default:
            ASSERT(false);
        }
    
        if (queryObject)
        {
            return queryObject->id();
        }
        else
        {
            return 0;
        }
    }
    
    void Context::setEnableVertexAttribArray(unsigned int attribNum, bool enabled)
    {
        mState.vertexAttribute[attribNum].mArrayEnabled = enabled;
    }
    
    const VertexAttribute &Context::getVertexAttribState(unsigned int attribNum)
    {
        return mState.vertexAttribute[attribNum];
    }
    
    void Context::setVertexAttribState(unsigned int attribNum, Buffer *boundBuffer, GLint size, GLenum type, bool normalized,
                                       bool pureInteger, GLsizei stride, const void *pointer)
    {
        mState.vertexAttribute[attribNum].mBoundBuffer.set(boundBuffer);
        mState.vertexAttribute[attribNum].mSize = size;
        mState.vertexAttribute[attribNum].mType = type;
        mState.vertexAttribute[attribNum].mNormalized = normalized;
        mState.vertexAttribute[attribNum].mPureInteger = pureInteger;
        mState.vertexAttribute[attribNum].mStride = stride;
        mState.vertexAttribute[attribNum].mPointer = pointer;
    }
    
    const void *Context::getVertexAttribPointer(unsigned int attribNum) const
    {
        return mState.vertexAttribute[attribNum].mPointer;
    }
    
    void Context::setPackAlignment(GLint alignment)
    {
        mState.packAlignment = alignment;
    }
    
    GLint Context::getPackAlignment() const
    {
        return mState.packAlignment;
    }
    
    void Context::setUnpackAlignment(GLint alignment)
    {
        mState.unpackAlignment = alignment;
    }
    
    GLint Context::getUnpackAlignment() const
    {
        return mState.unpackAlignment;
    }
    
    void Context::setPackReverseRowOrder(bool reverseRowOrder)
    {
        mState.packReverseRowOrder = reverseRowOrder;
    }
    
    bool Context::getPackReverseRowOrder() const
    {
        return mState.packReverseRowOrder;
    }
    
    GLuint Context::createBuffer()
    {
        return mResourceManager->createBuffer();
    }
    
    GLuint Context::createProgram()
    {
        return mResourceManager->createProgram();
    }
    
    GLuint Context::createShader(GLenum type)
    {
        return mResourceManager->createShader(type);
    }
    
    GLuint Context::createTexture()
    {
        return mResourceManager->createTexture();
    }
    
    GLuint Context::createRenderbuffer()
    {
        return mResourceManager->createRenderbuffer();
    }
    
    // Returns an unused framebuffer name
    GLuint Context::createFramebuffer()
    {
        GLuint handle = mFramebufferHandleAllocator.allocate();
    
        mFramebufferMap[handle] = NULL;
    
        return handle;
    }
    
    GLuint Context::createFence()
    {
        GLuint handle = mFenceHandleAllocator.allocate();
    
        mFenceMap[handle] = new Fence(mRenderer);
    
        return handle;
    }
    
    // Returns an unused query name
    GLuint Context::createQuery()
    {
        GLuint handle = mQueryHandleAllocator.allocate();
    
        mQueryMap[handle] = NULL;
    
        return handle;
    }
    
    void Context::deleteBuffer(GLuint buffer)
    {
        if (mResourceManager->getBuffer(buffer))
        {
            detachBuffer(buffer);
        }
        
        mResourceManager->deleteBuffer(buffer);
    }
    
    void Context::deleteShader(GLuint shader)
    {
        mResourceManager->deleteShader(shader);
    }
    
    void Context::deleteProgram(GLuint program)
    {
        mResourceManager->deleteProgram(program);
    }
    
    void Context::deleteTexture(GLuint texture)
    {
        if (mResourceManager->getTexture(texture))
        {
            detachTexture(texture);
        }
    
        mResourceManager->deleteTexture(texture);
    }
    
    void Context::deleteRenderbuffer(GLuint renderbuffer)
    {
        if (mResourceManager->getRenderbuffer(renderbuffer))
        {
            detachRenderbuffer(renderbuffer);
        }
        
        mResourceManager->deleteRenderbuffer(renderbuffer);
    }
    
    void Context::deleteFramebuffer(GLuint framebuffer)
    {
        FramebufferMap::iterator framebufferObject = mFramebufferMap.find(framebuffer);
    
        if (framebufferObject != mFramebufferMap.end())
        {
            detachFramebuffer(framebuffer);
    
            mFramebufferHandleAllocator.release(framebufferObject->first);
            delete framebufferObject->second;
            mFramebufferMap.erase(framebufferObject);
        }
    }
    
    void Context::deleteFence(GLuint fence)
    {
        FenceMap::iterator fenceObject = mFenceMap.find(fence);
    
        if (fenceObject != mFenceMap.end())
        {
            mFenceHandleAllocator.release(fenceObject->first);
            delete fenceObject->second;
            mFenceMap.erase(fenceObject);
        }
    }
    
    void Context::deleteQuery(GLuint query)
    {
        QueryMap::iterator queryObject = mQueryMap.find(query);
        if (queryObject != mQueryMap.end())
        {
            mQueryHandleAllocator.release(queryObject->first);
            if (queryObject->second)
            {
                queryObject->second->release();
            }
            mQueryMap.erase(queryObject);
        }
    }
    
    Buffer *Context::getBuffer(GLuint handle)
    {
        return mResourceManager->getBuffer(handle);
    }
    
    Shader *Context::getShader(GLuint handle)
    {
        return mResourceManager->getShader(handle);
    }
    
    Program *Context::getProgram(GLuint handle)
    {
        return mResourceManager->getProgram(handle);
    }
    
    Texture *Context::getTexture(GLuint handle)
    {
        return mResourceManager->getTexture(handle);
    }
    
    Renderbuffer *Context::getRenderbuffer(GLuint handle)
    {
        return mResourceManager->getRenderbuffer(handle);
    }
    
    Framebuffer *Context::getReadFramebuffer()
    {
        return getFramebuffer(mState.readFramebuffer);
    }
    
    Framebuffer *Context::getDrawFramebuffer()
    {
        return mBoundDrawFramebuffer;
    }
    
    void Context::bindArrayBuffer(unsigned int buffer)
    {
        mResourceManager->checkBufferAllocation(buffer);
    
        mState.arrayBuffer.set(getBuffer(buffer));
    }
    
    void Context::bindElementArrayBuffer(unsigned int buffer)
    {
        mResourceManager->checkBufferAllocation(buffer);
    
        mState.elementArrayBuffer.set(getBuffer(buffer));
    }
    
    void Context::bindTexture2D(GLuint texture)
    {
        mResourceManager->checkTextureAllocation(texture, TEXTURE_2D);
    
        mState.samplerTexture[TEXTURE_2D][mState.activeSampler].set(getTexture(texture));
    }
    
    void Context::bindTextureCubeMap(GLuint texture)
    {
        mResourceManager->checkTextureAllocation(texture, TEXTURE_CUBE);
    
        mState.samplerTexture[TEXTURE_CUBE][mState.activeSampler].set(getTexture(texture));
    }
    
    void Context::bindTexture3D(GLuint texture)
    {
        mResourceManager->checkTextureAllocation(texture, TEXTURE_3D);
    
        mState.samplerTexture[TEXTURE_3D][mState.activeSampler].set(getTexture(texture));
    }
    
    void Context::bindTexture2DArray(GLuint texture)
    {
        mResourceManager->checkTextureAllocation(texture, TEXTURE_2D_ARRAY);
    
        mState.samplerTexture[TEXTURE_2D_ARRAY][mState.activeSampler].set(getTexture(texture));
    }
    
    void Context::bindReadFramebuffer(GLuint framebuffer)
    {
        if (!getFramebuffer(framebuffer))
        {
            mFramebufferMap[framebuffer] = new Framebuffer(mRenderer);
        }
    
        mState.readFramebuffer = framebuffer;
    }
    
    void Context::bindDrawFramebuffer(GLuint framebuffer)
    {
        if (!getFramebuffer(framebuffer))
        {
            mFramebufferMap[framebuffer] = new Framebuffer(mRenderer);
        }
    
        mState.drawFramebuffer = framebuffer;
    
        mBoundDrawFramebuffer = getFramebuffer(framebuffer);
    }
    
    void Context::bindRenderbuffer(GLuint renderbuffer)
    {
        mResourceManager->checkRenderbufferAllocation(renderbuffer);
    
        mState.renderbuffer.set(getRenderbuffer(renderbuffer));
    }
    
    void Context::bindGenericUniformBuffer(GLuint buffer)
    {
        mResourceManager->checkBufferAllocation(buffer);
    
        mState.genericUniformBuffer.set(getBuffer(buffer));
    }
    
    void Context::bindIndexedUniformBuffer(GLuint buffer, GLuint index, GLintptr offset, GLsizeiptr size)
    {
        mResourceManager->checkBufferAllocation(buffer);
    
        mState.uniformBuffers[index].set(getBuffer(buffer), offset, size);
    }
    
    void Context::bindGenericTransformFeedbackBuffer(GLuint buffer)
    {
        mResourceManager->checkBufferAllocation(buffer);
    
        mState.genericTransformFeedbackBuffer.set(getBuffer(buffer));
    }
    
    void Context::bindIndexedTransformFeedbackBuffer(GLuint buffer, GLuint index, GLintptr offset, GLsizeiptr size)
    {
        mResourceManager->checkBufferAllocation(buffer);
    
        mState.transformFeedbackBuffers[index].set(getBuffer(buffer), offset, size);
    }
    
    void Context::bindCopyReadBuffer(GLuint buffer)
    {
        mResourceManager->checkBufferAllocation(buffer);
    
        mState.copyReadBuffer.set(getBuffer(buffer));
    }
    
    void Context::bindCopyWriteBuffer(GLuint buffer)
    {
        mResourceManager->checkBufferAllocation(buffer);
    
        mState.copyWriteBuffer.set(getBuffer(buffer));
    }
    
    void Context::bindPixelPackBuffer(GLuint buffer)
    {
        mResourceManager->checkBufferAllocation(buffer);
    
        mState.pixelPackBuffer.set(getBuffer(buffer));
    }
    
    void Context::bindPixelUnpackBuffer(GLuint buffer)
    {
        mResourceManager->checkBufferAllocation(buffer);
    
        mState.pixelUnpackBuffer.set(getBuffer(buffer));
    }
    
    void Context::useProgram(GLuint program)
    {
        GLuint priorProgram = mState.currentProgram;
        mState.currentProgram = program;               // Must switch before trying to delete, otherwise it only gets flagged.
    
        if (priorProgram != program)
        {
            Program *newProgram = mResourceManager->getProgram(program);
            Program *oldProgram = mResourceManager->getProgram(priorProgram);
            mCurrentProgramBinary.set(NULL);
    
            if (newProgram)
            {
                newProgram->addRef();
                mCurrentProgramBinary.set(newProgram->getProgramBinary());
            }
            
            if (oldProgram)
            {
                oldProgram->release();
            }
        }
    }
    
    void Context::linkProgram(GLuint program)
    {
        Program *programObject = mResourceManager->getProgram(program);
    
        bool linked = programObject->link();
    
        // if the current program was relinked successfully we
        // need to install the new executables
        if (linked && program == mState.currentProgram)
        {
            mCurrentProgramBinary.set(programObject->getProgramBinary());
        }
    }
    
    void Context::setProgramBinary(GLuint program, const void *binary, GLint length)
    {
        Program *programObject = mResourceManager->getProgram(program);
    
        bool loaded = programObject->setProgramBinary(binary, length);
    
        // if the current program was reloaded successfully we
        // need to install the new executables
        if (loaded && program == mState.currentProgram)
        {
            mCurrentProgramBinary.set(programObject->getProgramBinary());
        }
    
    }
    
    void Context::beginQuery(GLenum target, GLuint query)
    {
        // From EXT_occlusion_query_boolean: If BeginQueryEXT is called with an <id>  
        // of zero, if the active query object name for <target> is non-zero (for the  
        // targets ANY_SAMPLES_PASSED_EXT and ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, if  
        // the active query for either target is non-zero), if <id> is the name of an 
        // existing query object whose type does not match <target>, or if <id> is the
        // active query object name for any query type, the error INVALID_OPERATION is
        // generated.
    
        // Ensure no other queries are active
        // NOTE: If other queries than occlusion are supported, we will need to check
        // separately that:
        //    a) The query ID passed is not the current active query for any target/type
        //    b) There are no active queries for the requested target (and in the case
        //       of GL_ANY_SAMPLES_PASSED_EXT and GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT,
        //       no query may be active for either if glBeginQuery targets either.
        for (int i = 0; i < QUERY_TYPE_COUNT; i++)
        {
            if (mState.activeQuery[i].get() != NULL)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
        }
    
        QueryType qType;
        switch (target)
        {
          case GL_ANY_SAMPLES_PASSED_EXT: 
            qType = QUERY_ANY_SAMPLES_PASSED; 
            break;
          case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT: 
            qType = QUERY_ANY_SAMPLES_PASSED_CONSERVATIVE; 
            break;
          default: 
            ASSERT(false);
            return;
        }
    
        Query *queryObject = getQuery(query, true, target);
    
        // check that name was obtained with glGenQueries
        if (!queryObject)
        {
            return gl::error(GL_INVALID_OPERATION);
        }
    
        // check for type mismatch
        if (queryObject->getType() != target)
        {
            return gl::error(GL_INVALID_OPERATION);
        }
    
        // set query as active for specified target
        mState.activeQuery[qType].set(queryObject);
    
        // begin query
        queryObject->begin();
    }
    
    void Context::endQuery(GLenum target)
    {
        QueryType qType;
    
        switch (target)
        {
          case GL_ANY_SAMPLES_PASSED_EXT: 
            qType = QUERY_ANY_SAMPLES_PASSED; 
            break;
          case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT: 
            qType = QUERY_ANY_SAMPLES_PASSED_CONSERVATIVE; 
            break;
          default: 
            ASSERT(false);
            return;
        }
    
        Query *queryObject = mState.activeQuery[qType].get();
    
        if (queryObject == NULL)
        {
            return gl::error(GL_INVALID_OPERATION);
        }
    
        queryObject->end();
    
        mState.activeQuery[qType].set(NULL);
    }
    
    void Context::setFramebufferZero(Framebuffer *buffer)
    {
        delete mFramebufferMap[0];
        mFramebufferMap[0] = buffer;
        if (mState.drawFramebuffer == 0)
        {
            mBoundDrawFramebuffer = buffer;
        }
    }
    
    void Context::setRenderbufferStorage(GLsizei width, GLsizei height, GLenum internalformat, GLsizei samples)
    {
        RenderbufferStorage *renderbuffer = NULL;
        switch (internalformat)
        {
          case GL_DEPTH_COMPONENT16:
            renderbuffer = new gl::Depthbuffer(mRenderer, width, height, samples);
            break;
          case GL_RGBA4:
          case GL_RGB5_A1:
          case GL_RGB565:
          case GL_RGB8_OES:
          case GL_RGBA8_OES:
          case GL_SRGB8_ALPHA8:
          case GL_RGB10_A2:
          case GL_RG8:
          case GL_R8:
            renderbuffer = new gl::Colorbuffer(mRenderer,width, height, internalformat, samples);
            break;
          case GL_STENCIL_INDEX8:
            renderbuffer = new gl::Stencilbuffer(mRenderer, width, height, samples);
            break;
          case GL_DEPTH24_STENCIL8_OES:
            renderbuffer = new gl::DepthStencilbuffer(mRenderer, width, height, samples);
            break;
          default:
            UNIMPLEMENTED();   // TODO: Remaining ES3 formats
            UNREACHABLE(); return;
        }
    
        Renderbuffer *renderbufferObject = mState.renderbuffer.get();
        renderbufferObject->setStorage(renderbuffer);
    }
    
    Framebuffer *Context::getFramebuffer(unsigned int handle)
    {
        FramebufferMap::iterator framebuffer = mFramebufferMap.find(handle);
    
        if (framebuffer == mFramebufferMap.end())
        {
            return NULL;
        }
        else
        {
            return framebuffer->second;
        }
    }
    
    Fence *Context::getFence(unsigned int handle)
    {
        FenceMap::iterator fence = mFenceMap.find(handle);
    
        if (fence == mFenceMap.end())
        {
            return NULL;
        }
        else
        {
            return fence->second;
        }
    }
    
    Query *Context::getQuery(unsigned int handle, bool create, GLenum type)
    {
        QueryMap::iterator query = mQueryMap.find(handle);
    
        if (query == mQueryMap.end())
        {
            return NULL;
        }
        else
        {
            if (!query->second && create)
            {
                query->second = new Query(mRenderer, type, handle);
                query->second->addRef();
            }
            return query->second;
        }
    }
    
    Buffer *Context::getArrayBuffer()
    {
        return mState.arrayBuffer.get();
    }
    
    Buffer *Context::getElementArrayBuffer()
    {
        return mState.elementArrayBuffer.get();
    }
    
    ProgramBinary *Context::getCurrentProgramBinary()
    {
        return mCurrentProgramBinary.get();
    }
    
    Texture2D *Context::getTexture2D()
    {
        return static_cast<Texture2D*>(getSamplerTexture(mState.activeSampler, TEXTURE_2D));
    }
    
    TextureCubeMap *Context::getTextureCubeMap()
    {
        return static_cast<TextureCubeMap*>(getSamplerTexture(mState.activeSampler, TEXTURE_CUBE));
    }
    
    Texture3D *Context::getTexture3D()
    {
        return static_cast<Texture3D*>(getSamplerTexture(mState.activeSampler, TEXTURE_3D));
    }
    
    Texture2DArray *Context::getTexture2DArray()
    {
        return static_cast<Texture2DArray*>(getSamplerTexture(mState.activeSampler, TEXTURE_2D_ARRAY));
    }
    
    Buffer *Context::getGenericUniformBuffer()
    {
        return mState.genericUniformBuffer.get();
    }
    
    Buffer *Context::getGenericTransformFeedbackBuffer()
    {
        return mState.genericTransformFeedbackBuffer.get();
    }
    
    Buffer *Context::getCopyReadBuffer()
    {
        return mState.copyReadBuffer.get();
    }
    
    Buffer *Context::getCopyWriteBuffer()
    {
        return mState.copyWriteBuffer.get();
    }
    
    Buffer *Context::getPixelPackBuffer()
    {
        return mState.pixelPackBuffer.get();
    }
    
    Buffer *Context::getPixelUnpackBuffer()
    {
        return mState.pixelUnpackBuffer.get();
    }
    
    Texture *Context::getSamplerTexture(unsigned int sampler, TextureType type)
    {
        GLuint texid = mState.samplerTexture[type][sampler].id();
    
        if (texid == 0)   // Special case: 0 refers to different initial textures based on the target
        {
            switch (type)
            {
              default: UNREACHABLE();
              case TEXTURE_2D:       return mTexture2DZero.get();
              case TEXTURE_CUBE:     return mTextureCubeMapZero.get();
              case TEXTURE_3D:       return mTexture3DZero.get();
              case TEXTURE_2D_ARRAY: return mTexture2DArrayZero.get();
            }
        }
    
        return mState.samplerTexture[type][sampler].get();
    }
    
    bool Context::getBooleanv(GLenum pname, GLboolean *params)
    {
        switch (pname)
        {
          case GL_SHADER_COMPILER:           *params = GL_TRUE;                             break;
          case GL_SAMPLE_COVERAGE_INVERT:    *params = mState.sampleCoverageInvert;         break;
          case GL_DEPTH_WRITEMASK:           *params = mState.depthStencil.depthMask;       break;
          case GL_COLOR_WRITEMASK:
            params[0] = mState.blend.colorMaskRed;
            params[1] = mState.blend.colorMaskGreen;
            params[2] = mState.blend.colorMaskBlue;
            params[3] = mState.blend.colorMaskAlpha;
            break;
          case GL_CULL_FACE:                 *params = mState.rasterizer.cullFace;          break;
          case GL_POLYGON_OFFSET_FILL:       *params = mState.rasterizer.polygonOffsetFill; break;
          case GL_SAMPLE_ALPHA_TO_COVERAGE:  *params = mState.blend.sampleAlphaToCoverage;  break;
          case GL_SAMPLE_COVERAGE:           *params = mState.sampleCoverage;               break;
          case GL_SCISSOR_TEST:              *params = mState.scissorTest;                  break;
          case GL_STENCIL_TEST:              *params = mState.depthStencil.stencilTest;     break;
          case GL_DEPTH_TEST:                *params = mState.depthStencil.depthTest;       break;
          case GL_BLEND:                     *params = mState.blend.blend;                  break;
          case GL_DITHER:                    *params = mState.blend.dither;                 break;
          case GL_CONTEXT_ROBUST_ACCESS_EXT: *params = mRobustAccess ? GL_TRUE : GL_FALSE;  break;
          case GL_TRANSFORM_FEEDBACK_ACTIVE: *params = GL_FALSE; UNIMPLEMENTED();           break;
          default:
            return false;
        }
    
        return true;
    }
    
    bool Context::getFloatv(GLenum pname, GLfloat *params)
    {
        // Please note: DEPTH_CLEAR_VALUE is included in our internal getFloatv implementation
        // because it is stored as a float, despite the fact that the GL ES 2.0 spec names
        // GetIntegerv as its native query function. As it would require conversion in any
        // case, this should make no difference to the calling application.
        switch (pname)
        {
          case GL_LINE_WIDTH:               *params = mState.lineWidth;                         break;
          case GL_SAMPLE_COVERAGE_VALUE:    *params = mState.sampleCoverageValue;               break;
          case GL_DEPTH_CLEAR_VALUE:        *params = mState.depthClearValue;                   break;
          case GL_POLYGON_OFFSET_FACTOR:    *params = mState.rasterizer.polygonOffsetFactor;    break;
          case GL_POLYGON_OFFSET_UNITS:     *params = mState.rasterizer.polygonOffsetUnits;     break;
          case GL_ALIASED_LINE_WIDTH_RANGE:
            params[0] = gl::ALIASED_LINE_WIDTH_RANGE_MIN;
            params[1] = gl::ALIASED_LINE_WIDTH_RANGE_MAX;
            break;
          case GL_ALIASED_POINT_SIZE_RANGE:
            params[0] = gl::ALIASED_POINT_SIZE_RANGE_MIN;
            params[1] = getMaximumPointSize();
            break;
          case GL_DEPTH_RANGE:
            params[0] = mState.zNear;
            params[1] = mState.zFar;
            break;
          case GL_COLOR_CLEAR_VALUE:
            params[0] = mState.colorClearValue.red;
            params[1] = mState.colorClearValue.green;
            params[2] = mState.colorClearValue.blue;
            params[3] = mState.colorClearValue.alpha;
            break;
          case GL_BLEND_COLOR:
            params[0] = mState.blendColor.red;
            params[1] = mState.blendColor.green;
            params[2] = mState.blendColor.blue;
            params[3] = mState.blendColor.alpha;
            break;
          case GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT:
            if (!supportsTextureFilterAnisotropy())
            {
                return false;
            }
            *params = mMaxTextureAnisotropy;
            break;
          default:
            return false;
        }
    
        return true;
    }
    
    bool Context::getIntegerv(GLenum pname, GLint *params)
    {
        if (pname >= GL_DRAW_BUFFER0_EXT && pname <= GL_DRAW_BUFFER15_EXT)
        {
            unsigned int colorAttachment = (pname - GL_DRAW_BUFFER0_EXT);
    
            if (colorAttachment >= mRenderer->getMaxRenderTargets())
            {
                // return true to stop further operation in the parent call
                return gl::error(GL_INVALID_OPERATION, true);
            }
    
            Framebuffer *framebuffer = getDrawFramebuffer();
    
            *params = framebuffer->getDrawBufferState(colorAttachment);
            return true;
        }
    
        // Please note: DEPTH_CLEAR_VALUE is not included in our internal getIntegerv implementation
        // because it is stored as a float, despite the fact that the GL ES 2.0 spec names
        // GetIntegerv as its native query function. As it would require conversion in any
        // case, this should make no difference to the calling application. You may find it in 
        // Context::getFloatv.
        switch (pname)
        {
          case GL_MAX_VERTEX_ATTRIBS:               *params = gl::MAX_VERTEX_ATTRIBS;               break;
          case GL_MAX_VERTEX_UNIFORM_VECTORS:       *params = mRenderer->getMaxVertexUniformVectors(); break;
          case GL_MAX_VARYING_VECTORS:              *params = mRenderer->getMaxVaryingVectors();    break;
          case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: *params = mRenderer->getMaxCombinedTextureImageUnits(); break;
          case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:   *params = mRenderer->getMaxVertexTextureImageUnits(); break;
          case GL_MAX_TEXTURE_IMAGE_UNITS:          *params = gl::MAX_TEXTURE_IMAGE_UNITS;          break;
          case GL_MAX_FRAGMENT_UNIFORM_VECTORS:     *params = mRenderer->getMaxFragmentUniformVectors(); break;
          case GL_MAX_RENDERBUFFER_SIZE:            *params = getMaximumRenderbufferDimension();    break;
          case GL_MAX_COLOR_ATTACHMENTS_EXT:        *params = mRenderer->getMaxRenderTargets();     break;
          case GL_MAX_DRAW_BUFFERS_EXT:             *params = mRenderer->getMaxRenderTargets();     break;
          case GL_NUM_SHADER_BINARY_FORMATS:        *params = 0;                                    break;
          case GL_SHADER_BINARY_FORMATS:      /* no shader binary formats are supported */          break;
          case GL_ARRAY_BUFFER_BINDING:             *params = mState.arrayBuffer.id();              break;
          case GL_ELEMENT_ARRAY_BUFFER_BINDING:     *params = mState.elementArrayBuffer.id();       break;
          //case GL_FRAMEBUFFER_BINDING:            // now equivalent to GL_DRAW_FRAMEBUFFER_BINDING_ANGLE
          case GL_DRAW_FRAMEBUFFER_BINDING_ANGLE:   *params = mState.drawFramebuffer;               break;
          case GL_READ_FRAMEBUFFER_BINDING_ANGLE:   *params = mState.readFramebuffer;               break;
          case GL_RENDERBUFFER_BINDING:             *params = mState.renderbuffer.id();             break;
          case GL_CURRENT_PROGRAM:                  *params = mState.currentProgram;                break;
          case GL_PACK_ALIGNMENT:                   *params = mState.packAlignment;                 break;
          case GL_PACK_REVERSE_ROW_ORDER_ANGLE:     *params = mState.packReverseRowOrder;           break;
          case GL_UNPACK_ALIGNMENT:                 *params = mState.unpackAlignment;               break;
          case GL_GENERATE_MIPMAP_HINT:             *params = mState.generateMipmapHint;            break;
          case GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES: *params = mState.fragmentShaderDerivativeHint; break;
          case GL_ACTIVE_TEXTURE:                   *params = (mState.activeSampler + GL_TEXTURE0); break;
          case GL_STENCIL_FUNC:                     *params = mState.depthStencil.stencilFunc;             break;
          case GL_STENCIL_REF:                      *params = mState.stencilRef;                           break;
          case GL_STENCIL_VALUE_MASK:               *params = mState.depthStencil.stencilMask;             break;
          case GL_STENCIL_BACK_FUNC:                *params = mState.depthStencil.stencilBackFunc;         break;
          case GL_STENCIL_BACK_REF:                 *params = mState.stencilBackRef;                       break;
          case GL_STENCIL_BACK_VALUE_MASK:          *params = mState.depthStencil.stencilBackMask;         break;
          case GL_STENCIL_FAIL:                     *params = mState.depthStencil.stencilFail;             break;
          case GL_STENCIL_PASS_DEPTH_FAIL:          *params = mState.depthStencil.stencilPassDepthFail;    break;
          case GL_STENCIL_PASS_DEPTH_PASS:          *params = mState.depthStencil.stencilPassDepthPass;    break;
          case GL_STENCIL_BACK_FAIL:                *params = mState.depthStencil.stencilBackFail;         break;
          case GL_STENCIL_BACK_PASS_DEPTH_FAIL:     *params = mState.depthStencil.stencilBackPassDepthFail; break;
          case GL_STENCIL_BACK_PASS_DEPTH_PASS:     *params = mState.depthStencil.stencilBackPassDepthPass; break;
          case GL_DEPTH_FUNC:                       *params = mState.depthStencil.depthFunc;               break;
          case GL_BLEND_SRC_RGB:                    *params = mState.blend.sourceBlendRGB;                 break;
          case GL_BLEND_SRC_ALPHA:                  *params = mState.blend.sourceBlendAlpha;               break;
          case GL_BLEND_DST_RGB:                    *params = mState.blend.destBlendRGB;                   break;
          case GL_BLEND_DST_ALPHA:                  *params = mState.blend.destBlendAlpha;                 break;
          case GL_BLEND_EQUATION_RGB:               *params = mState.blend.blendEquationRGB;               break;
          case GL_BLEND_EQUATION_ALPHA:             *params = mState.blend.blendEquationAlpha;             break;
          case GL_STENCIL_WRITEMASK:                *params = mState.depthStencil.stencilWritemask;        break;
          case GL_STENCIL_BACK_WRITEMASK:           *params = mState.depthStencil.stencilBackWritemask;    break;
          case GL_STENCIL_CLEAR_VALUE:              *params = mState.stencilClearValue;                    break;
          case GL_SUBPIXEL_BITS:                    *params = 4;                                           break;
          case GL_MAX_TEXTURE_SIZE:                 *params = getMaximum2DTextureDimension();              break;
          case GL_MAX_CUBE_MAP_TEXTURE_SIZE:        *params = getMaximumCubeTextureDimension();            break;
          case GL_MAX_3D_TEXTURE_SIZE:              *params = getMaximum3DTextureDimension();              break;
          case GL_MAX_ARRAY_TEXTURE_LAYERS:         *params = getMaximum2DArrayTextureLayers();            break;
          case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:  *params = getUniformBufferOffsetAlignment();           break;
          case GL_MAX_UNIFORM_BUFFER_BINDINGS:      *params = getMaximumCombinedUniformBufferBindings();   break;
          case GL_MAX_VERTEX_UNIFORM_BLOCKS:        *params = mRenderer->getMaxVertexShaderUniformBuffers(); break;
          case GL_MAX_FRAGMENT_UNIFORM_BLOCKS:      *params = mRenderer->getMaxFragmentShaderUniformBuffers(); break;
          case GL_MAX_COMBINED_UNIFORM_BLOCKS:      *params = getMaximumCombinedUniformBufferBindings();   break;
          case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: *params = 0; UNIMPLEMENTED();                   break;
          case GL_NUM_COMPRESSED_TEXTURE_FORMATS:   
            params[0] = mNumCompressedTextureFormats;
            break;
          case GL_MAX_SAMPLES_ANGLE:
            {
                GLsizei maxSamples = getMaxSupportedSamples();
                if (maxSamples != 0)
                {
                    *params = maxSamples;
                }
                else
                {
                    return false;
                }
    
                break;
            }
          case GL_SAMPLE_BUFFERS:                   
          case GL_SAMPLES:
            {
                gl::Framebuffer *framebuffer = getDrawFramebuffer();
                if (framebuffer->completeness() == GL_FRAMEBUFFER_COMPLETE)
                {
                    switch (pname)
                    {
                      case GL_SAMPLE_BUFFERS:
                        if (framebuffer->getSamples() != 0)
                        {
                            *params = 1;
                        }
                        else
                        {
                            *params = 0;
                        }
                        break;
                      case GL_SAMPLES:
                        *params = framebuffer->getSamples();
                        break;
                    }
                }
                else 
                {
                    *params = 0;
                }
            }
            break;
          case GL_IMPLEMENTATION_COLOR_READ_TYPE:
          case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
            {
                GLint internalFormat;
                GLenum format, type;
                if (getCurrentReadFormatType(&internalFormat, &format, &type))
                {
                    if (pname == GL_IMPLEMENTATION_COLOR_READ_FORMAT)
                        *params = format;
                    else
                        *params = type;
                }
            }
            break;
          case GL_MAX_VIEWPORT_DIMS:
            {
                params[0] = mMaxViewportDimension;
                params[1] = mMaxViewportDimension;
            }
            break;
          case GL_COMPRESSED_TEXTURE_FORMATS:
            {
                if (supportsDXT1Textures())
                {
                    *params++ = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
                    *params++ = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
                }
                if (supportsDXT3Textures())
                {
                    *params++ = GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE;
                }
                if (supportsDXT5Textures())
                {
                    *params++ = GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE;
                }
            }
            break;
          case GL_VIEWPORT:
            params[0] = mState.viewport.x;
            params[1] = mState.viewport.y;
            params[2] = mState.viewport.width;
            params[3] = mState.viewport.height;
            break;
          case GL_SCISSOR_BOX:
            params[0] = mState.scissor.x;
            params[1] = mState.scissor.y;
            params[2] = mState.scissor.width;
            params[3] = mState.scissor.height;
            break;
          case GL_CULL_FACE_MODE:                   *params = mState.rasterizer.cullMode;   break;
          case GL_FRONT_FACE:                       *params = mState.rasterizer.frontFace;  break;
          case GL_RED_BITS:
          case GL_GREEN_BITS:
          case GL_BLUE_BITS:
          case GL_ALPHA_BITS:
            {
                gl::Framebuffer *framebuffer = getDrawFramebuffer();
                gl::Renderbuffer *colorbuffer = framebuffer->getFirstColorbuffer();
    
                if (colorbuffer)
                {
                    switch (pname)
                    {
                      case GL_RED_BITS:   *params = colorbuffer->getRedSize();      break;
                      case GL_GREEN_BITS: *params = colorbuffer->getGreenSize();    break;
                      case GL_BLUE_BITS:  *params = colorbuffer->getBlueSize();     break;
                      case GL_ALPHA_BITS: *params = colorbuffer->getAlphaSize();    break;
                    }
                }
                else
                {
                    *params = 0;
                }
            }
            break;
          case GL_DEPTH_BITS:
            {
                gl::Framebuffer *framebuffer = getDrawFramebuffer();
                gl::Renderbuffer *depthbuffer = framebuffer->getDepthbuffer();
    
                if (depthbuffer)
                {
                    *params = depthbuffer->getDepthSize();
                }
                else
                {
                    *params = 0;
                }
            }
            break;
          case GL_STENCIL_BITS:
            {
                gl::Framebuffer *framebuffer = getDrawFramebuffer();
                gl::Renderbuffer *stencilbuffer = framebuffer->getStencilbuffer();
    
                if (stencilbuffer)
                {
                    *params = stencilbuffer->getStencilSize();
                }
                else
                {
                    *params = 0;
                }
            }
            break;
          case GL_TEXTURE_BINDING_2D:
            {
                if (mState.activeSampler > mRenderer->getMaxCombinedTextureImageUnits() - 1)
                {
                    gl::error(GL_INVALID_OPERATION);
                    return false;
                }
    
                *params = mState.samplerTexture[TEXTURE_2D][mState.activeSampler].id();
            }
            break;
          case GL_TEXTURE_BINDING_CUBE_MAP:
            {
                if (mState.activeSampler > mRenderer->getMaxCombinedTextureImageUnits() - 1)
                {
                    gl::error(GL_INVALID_OPERATION);
                    return false;
                }
    
                *params = mState.samplerTexture[TEXTURE_CUBE][mState.activeSampler].id();
            }
            break;
          case GL_TEXTURE_BINDING_3D:
            {
                if (mState.activeSampler > mRenderer->getMaxCombinedTextureImageUnits() - 1)
                {
                    gl::error(GL_INVALID_OPERATION);
                    return false;
                }
    
                *params = mState.samplerTexture[TEXTURE_3D][mState.activeSampler].id();
            }
            break;
          case GL_TEXTURE_BINDING_2D_ARRAY:
            {
                if (mState.activeSampler > mRenderer->getMaxCombinedTextureImageUnits() - 1)
                {
                    gl::error(GL_INVALID_OPERATION);
                    return false;
                }
    
                *params = mState.samplerTexture[TEXTURE_2D_ARRAY][mState.activeSampler].id();
            }
            break;
          case GL_RESET_NOTIFICATION_STRATEGY_EXT:
            *params = mResetStrategy;
            break;
          case GL_NUM_PROGRAM_BINARY_FORMATS_OES:
            *params = 1;
            break;
          case GL_PROGRAM_BINARY_FORMATS_OES:
            *params = GL_PROGRAM_BINARY_ANGLE;
            break;
          case GL_UNIFORM_BUFFER_BINDING:
            *params = mState.genericUniformBuffer.id();
            break;
          case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
            *params = mState.genericTransformFeedbackBuffer.id();
            break;
          case GL_COPY_READ_BUFFER_BINDING:
            *params = mState.copyReadBuffer.id();
            break;
          case GL_COPY_WRITE_BUFFER_BINDING:
            *params = mState.copyWriteBuffer.id();
            break;
          case GL_PIXEL_PACK_BUFFER_BINDING:
            *params = mState.pixelPackBuffer.id();
            break;
          case GL_PIXEL_UNPACK_BUFFER_BINDING:
            *params = mState.pixelUnpackBuffer.id();
            break;
          default:
            return false;
        }
    
        return true;
    }
    
    bool Context::getQueryParameterInfo(GLenum pname, GLenum *type, unsigned int *numParams)
    {
        if (pname >= GL_DRAW_BUFFER0_EXT && pname <= GL_DRAW_BUFFER15_EXT)
        {
            *type = GL_INT;
            *numParams = 1;
            return true;
        }
    
        // Please note: the query type returned for DEPTH_CLEAR_VALUE in this implementation
        // is FLOAT rather than INT, as would be suggested by the GL ES 2.0 spec. This is due
        // to the fact that it is stored internally as a float, and so would require conversion
        // if returned from Context::getIntegerv. Since this conversion is already implemented 
        // in the case that one calls glGetIntegerv to retrieve a float-typed state variable, we
        // place DEPTH_CLEAR_VALUE with the floats. This should make no difference to the calling
        // application.
        switch (pname)
        {
          case GL_COMPRESSED_TEXTURE_FORMATS:
            {
                *type = GL_INT;
                *numParams = mNumCompressedTextureFormats;
            }
            return true;
          case GL_SHADER_BINARY_FORMATS:
            {
                *type = GL_INT;
                *numParams = 0;
            }
            return true;
          case GL_MAX_VERTEX_ATTRIBS:
          case GL_MAX_VERTEX_UNIFORM_VECTORS:
          case GL_MAX_VARYING_VECTORS:
          case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
          case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
          case GL_MAX_TEXTURE_IMAGE_UNITS:
          case GL_MAX_FRAGMENT_UNIFORM_VECTORS:
          case GL_MAX_RENDERBUFFER_SIZE:
          case GL_MAX_COLOR_ATTACHMENTS_EXT:
          case GL_MAX_DRAW_BUFFERS_EXT:
          case GL_NUM_SHADER_BINARY_FORMATS:
          case GL_NUM_COMPRESSED_TEXTURE_FORMATS:
          case GL_ARRAY_BUFFER_BINDING:
          case GL_FRAMEBUFFER_BINDING:
          case GL_RENDERBUFFER_BINDING:
          case GL_CURRENT_PROGRAM:
          case GL_PACK_ALIGNMENT:
          case GL_PACK_REVERSE_ROW_ORDER_ANGLE:
          case GL_UNPACK_ALIGNMENT:
          case GL_GENERATE_MIPMAP_HINT:
          case GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES:
          case GL_RED_BITS:
          case GL_GREEN_BITS:
          case GL_BLUE_BITS:
          case GL_ALPHA_BITS:
          case GL_DEPTH_BITS:
          case GL_STENCIL_BITS:
          case GL_ELEMENT_ARRAY_BUFFER_BINDING:
          case GL_CULL_FACE_MODE:
          case GL_FRONT_FACE:
          case GL_ACTIVE_TEXTURE:
          case GL_STENCIL_FUNC:
          case GL_STENCIL_VALUE_MASK:
          case GL_STENCIL_REF:
          case GL_STENCIL_FAIL:
          case GL_STENCIL_PASS_DEPTH_FAIL:
          case GL_STENCIL_PASS_DEPTH_PASS:
          case GL_STENCIL_BACK_FUNC:
          case GL_STENCIL_BACK_VALUE_MASK:
          case GL_STENCIL_BACK_REF:
          case GL_STENCIL_BACK_FAIL:
          case GL_STENCIL_BACK_PASS_DEPTH_FAIL:
          case GL_STENCIL_BACK_PASS_DEPTH_PASS:
          case GL_DEPTH_FUNC:
          case GL_BLEND_SRC_RGB:
          case GL_BLEND_SRC_ALPHA:
          case GL_BLEND_DST_RGB:
          case GL_BLEND_DST_ALPHA:
          case GL_BLEND_EQUATION_RGB:
          case GL_BLEND_EQUATION_ALPHA:
          case GL_STENCIL_WRITEMASK:
          case GL_STENCIL_BACK_WRITEMASK:
          case GL_STENCIL_CLEAR_VALUE:
          case GL_SUBPIXEL_BITS:
          case GL_MAX_TEXTURE_SIZE:
          case GL_MAX_CUBE_MAP_TEXTURE_SIZE:
          case GL_SAMPLE_BUFFERS:
          case GL_SAMPLES:
          case GL_IMPLEMENTATION_COLOR_READ_TYPE:
          case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
          case GL_TEXTURE_BINDING_2D:
          case GL_TEXTURE_BINDING_CUBE_MAP:
          case GL_RESET_NOTIFICATION_STRATEGY_EXT:
          case GL_NUM_PROGRAM_BINARY_FORMATS_OES:
          case GL_PROGRAM_BINARY_FORMATS_OES:
            {
                *type = GL_INT;
                *numParams = 1;
            }
            return true;
          case GL_MAX_SAMPLES_ANGLE:
            {
                if (getMaxSupportedSamples() != 0)
                {
                    *type = GL_INT;
                    *numParams = 1;
                }
                else
                {
                    return false;
                }
            }
            return true;
          case GL_MAX_VIEWPORT_DIMS:
            {
                *type = GL_INT;
                *numParams = 2;
            }
            return true;
          case GL_VIEWPORT:
          case GL_SCISSOR_BOX:
            {
                *type = GL_INT;
                *numParams = 4;
            }
            return true;
          case GL_SHADER_COMPILER:
          case GL_SAMPLE_COVERAGE_INVERT:
          case GL_DEPTH_WRITEMASK:
          case GL_CULL_FACE:                // CULL_FACE through DITHER are natural to IsEnabled,
          case GL_POLYGON_OFFSET_FILL:      // but can be retrieved through the Get{Type}v queries.
          case GL_SAMPLE_ALPHA_TO_COVERAGE: // For this purpose, they are treated here as bool-natural
          case GL_SAMPLE_COVERAGE:
          case GL_SCISSOR_TEST:
          case GL_STENCIL_TEST:
          case GL_DEPTH_TEST:
          case GL_BLEND:
          case GL_DITHER:
          case GL_CONTEXT_ROBUST_ACCESS_EXT:
            {
                *type = GL_BOOL;
                *numParams = 1;
            }
            return true;
          case GL_COLOR_WRITEMASK:
            {
                *type = GL_BOOL;
                *numParams = 4;
            }
            return true;
          case GL_POLYGON_OFFSET_FACTOR:
          case GL_POLYGON_OFFSET_UNITS:
          case GL_SAMPLE_COVERAGE_VALUE:
          case GL_DEPTH_CLEAR_VALUE:
          case GL_LINE_WIDTH:
            {
                *type = GL_FLOAT;
                *numParams = 1;
            }
            return true;
          case GL_ALIASED_LINE_WIDTH_RANGE:
          case GL_ALIASED_POINT_SIZE_RANGE:
          case GL_DEPTH_RANGE:
            {
                *type = GL_FLOAT;
                *numParams = 2;
            }
            return true;
          case GL_COLOR_CLEAR_VALUE:
          case GL_BLEND_COLOR:
            {
                *type = GL_FLOAT;
                *numParams = 4;
            }
            return true;
          case GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT:
            if (!supportsTextureFilterAnisotropy())
            {
                return false;
            }
            *type = GL_FLOAT;
            *numParams = 1;
            return true;
        }
    
        if (mClientVersion < 3)
        {
            return false;
        }
    
        // Check for ES3.0+ parameter names
        switch (pname)
        {
          case GL_MAX_UNIFORM_BUFFER_BINDINGS:
          case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
          case GL_UNIFORM_BUFFER_BINDING:
          case GL_TRANSFORM_FEEDBACK_BINDING:
          case GL_COPY_READ_BUFFER_BINDING:
          case GL_COPY_WRITE_BUFFER_BINDING:
          case GL_PIXEL_PACK_BUFFER_BINDING:
          case GL_PIXEL_UNPACK_BUFFER_BINDING:
          case GL_TEXTURE_BINDING_3D:
          case GL_TEXTURE_BINDING_2D_ARRAY:
          case GL_MAX_3D_TEXTURE_SIZE:
          case GL_MAX_ARRAY_TEXTURE_LAYERS:
          case GL_MAX_VERTEX_UNIFORM_BLOCKS:
          case GL_MAX_FRAGMENT_UNIFORM_BLOCKS:
          case GL_MAX_COMBINED_UNIFORM_BLOCKS:
            {
                *type = GL_INT;
                *numParams = 1;
            }
            return true;
        }
    
        return false;
    }
    
    // Applies the render target surface, depth stencil surface, viewport rectangle and
    // scissor rectangle to the renderer
    bool Context::applyRenderTarget(GLenum drawMode, bool ignoreViewport)
    {
        Framebuffer *framebufferObject = getDrawFramebuffer();
    
        if (!framebufferObject || framebufferObject->completeness() != GL_FRAMEBUFFER_COMPLETE)
        {
            return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false);
        }
    
        mRenderer->applyRenderTarget(framebufferObject);
    
        if (!mRenderer->setViewport(mState.viewport, mState.zNear, mState.zFar, drawMode, mState.rasterizer.frontFace,
                                    ignoreViewport))
        {
            return false;
        }
    
        mRenderer->setScissorRectangle(mState.scissor, mState.scissorTest);
    
        return true;
    }
    
    // Applies the fixed-function state (culling, depth test, alpha blending, stenciling, etc) to the Direct3D 9 device
    void Context::applyState(GLenum drawMode)
    {
        mState.rasterizer.pointDrawMode = (drawMode == GL_POINTS);
        mRenderer->setRasterizerState(mState.rasterizer);
    
        unsigned int mask = 0;
        if (mState.sampleCoverage)
        {
            if (mState.sampleCoverageValue != 0)
            {
                Framebuffer *framebufferObject = getDrawFramebuffer();
                float threshold = 0.5f;
    
                for (int i = 0; i < framebufferObject->getSamples(); ++i)
                {
                    mask <<= 1;
    
                    if ((i + 1) * mState.sampleCoverageValue >= threshold)
                    {
                        threshold += 1.0f;
                        mask |= 1;
                    }
                }
            }
    
            if (mState.sampleCoverageInvert)
            {
                mask = ~mask;
            }
        }
        else
        {
            mask = 0xFFFFFFFF;
        }
        mRenderer->setBlendState(mState.blend, mState.blendColor, mask);
    
        mRenderer->setDepthStencilState(mState.depthStencil, mState.stencilRef, mState.stencilBackRef,
                                        mState.rasterizer.frontFace == GL_CCW);
    }
    
    // Applies the shaders and shader constants to the Direct3D 9 device
    void Context::applyShaders()
    {
        ProgramBinary *programBinary = getCurrentProgramBinary();
    
        mRenderer->applyShaders(programBinary);
        
        programBinary->applyUniforms();
    }
    
    // Applies the textures and sampler states to the Direct3D 9 device
    void Context::applyTextures()
    {
        applyTextures(SAMPLER_PIXEL);
    
        if (mSupportsVertexTexture)
        {
            applyTextures(SAMPLER_VERTEX);
        }
    }
    
    // For each Direct3D 9 sampler of either the pixel or vertex stage,
    // looks up the corresponding OpenGL texture image unit and texture type,
    // and sets the texture and its addressing/filtering state (or NULL when inactive).
    void Context::applyTextures(SamplerType type)
    {
        ProgramBinary *programBinary = getCurrentProgramBinary();
    
        // Range of Direct3D samplers of given sampler type
        int samplerCount = (type == SAMPLER_PIXEL) ? MAX_TEXTURE_IMAGE_UNITS : mRenderer->getMaxVertexTextureImageUnits();
        int samplerRange = programBinary->getUsedSamplerRange(type);
    
        for (int samplerIndex = 0; samplerIndex < samplerRange; samplerIndex++)
        {
            int textureUnit = programBinary->getSamplerMapping(type, samplerIndex);   // OpenGL texture image unit index
    
            if (textureUnit != -1)
            {
                TextureType textureType = programBinary->getSamplerTextureType(type, samplerIndex);
                Texture *texture = getSamplerTexture(textureUnit, textureType);
    
                if (texture->isSamplerComplete())
                {
                    SamplerState samplerState;
                    texture->getSamplerState(&samplerState);
                    mRenderer->setSamplerState(type, samplerIndex, samplerState);
    
                    mRenderer->setTexture(type, samplerIndex, texture);
    
                    texture->resetDirty();
                }
                else
                {
                    mRenderer->setTexture(type, samplerIndex, getIncompleteTexture(textureType));
                }
            }
            else
            {
                mRenderer->setTexture(type, samplerIndex, NULL);
            }
        }
    
        for (int samplerIndex = samplerRange; samplerIndex < samplerCount; samplerIndex++)
        {
            mRenderer->setTexture(type, samplerIndex, NULL);
        }
    }
    
    bool Context::applyUniformBuffers()
    {
        Program *programObject = getProgram(mState.currentProgram);
        ProgramBinary *programBinary = programObject->getProgramBinary();
    
        std::vector<gl::Buffer*> boundBuffers;
    
        for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < programBinary->getActiveUniformBlockCount(); uniformBlockIndex++)
        {
            GLuint blockBinding = programObject->getUniformBlockBinding(uniformBlockIndex);
            const OffsetBindingPointer<Buffer>& boundBuffer = mState.uniformBuffers[blockBinding];
            if (boundBuffer.id() == 0)
            {
                // undefined behaviour
                return false;
            }
            else
            {
                gl::Buffer *uniformBuffer = boundBuffer.get();
                ASSERT(uniformBuffer);
                boundBuffers.push_back(uniformBuffer);
            }
        }
    
        return programBinary->applyUniformBuffers(boundBuffers);
    }
    
    void Context::readPixels(GLint x, GLint y, GLsizei width, GLsizei height,
                             GLenum format, GLenum type, GLsizei *bufSize, void* pixels)
    {
        Framebuffer *framebuffer = getReadFramebuffer();
    
        if (framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
        {
            return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION);
        }
    
        if (getReadFramebufferHandle() != 0 && framebuffer->getSamples() != 0)
        {
            return gl::error(GL_INVALID_OPERATION);
        }
    
        GLint sizedInternalFormat = IsSizedInternalFormat(format, mClientVersion) ? format
                                                                                  : GetSizedInternalFormat(format, type, mClientVersion);
    
        GLsizei outputPitch = GetRowPitch(sizedInternalFormat, type, mClientVersion, width, getPackAlignment());
        // sized query sanity check
        if (bufSize)
        {
            int requiredSize = outputPitch * height;
            if (requiredSize > *bufSize)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
        }
    
        mRenderer->readPixels(framebuffer, x, y, width, height, format, type, outputPitch, getPackReverseRowOrder(), getPackAlignment(), pixels);
    }
    
    void Context::clear(GLbitfield mask)
    {
        Framebuffer *framebufferObject = getDrawFramebuffer();
    
        if (!framebufferObject || framebufferObject->completeness() != GL_FRAMEBUFFER_COMPLETE)
        {
            return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION);
        }
    
        DWORD flags = 0;
        GLbitfield finalMask = 0;
    
        if (mask & GL_COLOR_BUFFER_BIT)
        {
            mask &= ~GL_COLOR_BUFFER_BIT;
    
            if (framebufferObject->hasEnabledColorAttachment())
            {
                finalMask |= GL_COLOR_BUFFER_BIT;
            }
        }
    
        if (mask & GL_DEPTH_BUFFER_BIT)
        {
            mask &= ~GL_DEPTH_BUFFER_BIT;
            if (mState.depthStencil.depthMask && framebufferObject->getDepthbufferType() != GL_NONE)
            {
                finalMask |= GL_DEPTH_BUFFER_BIT;
            }
        }
    
        if (mask & GL_STENCIL_BUFFER_BIT)
        {
            mask &= ~GL_STENCIL_BUFFER_BIT;
            if (framebufferObject->getStencilbufferType() != GL_NONE)
            {
                rx::RenderTarget *depthStencil = framebufferObject->getStencilbuffer()->getDepthStencil();
                if (!depthStencil)
                {
                    ERR("Depth stencil pointer unexpectedly null.");
                    return;
                }
    
                if (gl::GetStencilBits(depthStencil->getActualFormat(), mClientVersion) > 0)
                {
                    finalMask |= GL_STENCIL_BUFFER_BIT;
                }
            }
        }
    
        if (mask != 0)
        {
            return gl::error(GL_INVALID_VALUE);
        }
    
        if (!applyRenderTarget(GL_TRIANGLES, true))   // Clips the clear to the scissor rectangle but not the viewport
        {
            return;
        }
    
        ClearParameters clearParams;
        clearParams.mask = finalMask;
        clearParams.colorClearValue = mState.colorClearValue;
        clearParams.colorMaskRed = mState.blend.colorMaskRed;
        clearParams.colorMaskGreen = mState.blend.colorMaskGreen;
        clearParams.colorMaskBlue = mState.blend.colorMaskBlue;
        clearParams.colorMaskAlpha = mState.blend.colorMaskAlpha;
        clearParams.depthClearValue = mState.depthClearValue;
        clearParams.stencilClearValue = mState.stencilClearValue;
        clearParams.stencilWriteMask = mState.depthStencil.stencilWritemask;
    
        mRenderer->clear(clearParams, framebufferObject);
    }
    
    void Context::drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instances)
    {
        if (!mState.currentProgram)
        {
            return gl::error(GL_INVALID_OPERATION);
        }
    
        if (!mRenderer->applyPrimitiveType(mode, count))
        {
            return;
        }
    
        if (!applyRenderTarget(mode, false))
        {
            return;
        }
    
        applyState(mode);
    
        ProgramBinary *programBinary = getCurrentProgramBinary();
    
        GLenum err = mRenderer->applyVertexBuffer(programBinary, mState.vertexAttribute, first, count, instances);
        if (err != GL_NO_ERROR)
        {
            return gl::error(err);
        }
    
        applyShaders();
        applyTextures();
    
        if (!applyUniformBuffers())
        {
            return;
        }
    
        if (!programBinary->validateSamplers(NULL))
        {
            return gl::error(GL_INVALID_OPERATION);
        }
    
        if (!skipDraw(mode))
        {
            mRenderer->drawArrays(mode, count, instances);
        }
    }
    
    void Context::drawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instances)
    {
        if (!mState.currentProgram)
        {
            return gl::error(GL_INVALID_OPERATION);
        }
    
        if (!indices && !mState.elementArrayBuffer)
        {
            return gl::error(GL_INVALID_OPERATION);
        }
        
        if (!mRenderer->applyPrimitiveType(mode, count))
        {
            return;
        }
    
        if (!applyRenderTarget(mode, false))
        {
            return;
        }
    
        applyState(mode);
    
        rx::TranslatedIndexData indexInfo;
        GLenum err = mRenderer->applyIndexBuffer(indices, mState.elementArrayBuffer.get(), count, mode, type, &indexInfo);
        if (err != GL_NO_ERROR)
        {
            return gl::error(err);
        }
    
        ProgramBinary *programBinary = getCurrentProgramBinary();
    
        GLsizei vertexCount = indexInfo.maxIndex - indexInfo.minIndex + 1;
        err = mRenderer->applyVertexBuffer(programBinary, mState.vertexAttribute, indexInfo.minIndex, vertexCount, instances);
        if (err != GL_NO_ERROR)
        {
            return gl::error(err);
        }
    
        applyShaders();
        applyTextures();
    
        if (!applyUniformBuffers())
        {
            return;
        }
    
        if (!programBinary->validateSamplers(NULL))
        {
            return gl::error(GL_INVALID_OPERATION);
        }
    
        if (!skipDraw(mode))
        {
            mRenderer->drawElements(mode, count, type, indices, mState.elementArrayBuffer.get(), indexInfo, instances);
        }
    }
    
    // Implements glFlush when block is false, glFinish when block is true
    void Context::sync(bool block)
    {
        mRenderer->sync(block);
    }
    
    void Context::recordInvalidEnum()
    {
        mInvalidEnum = true;
    }
    
    void Context::recordInvalidValue()
    {
        mInvalidValue = true;
    }
    
    void Context::recordInvalidOperation()
    {
        mInvalidOperation = true;
    }
    
    void Context::recordOutOfMemory()
    {
        mOutOfMemory = true;
    }
    
    void Context::recordInvalidFramebufferOperation()
    {
        mInvalidFramebufferOperation = true;
    }
    
    // Get one of the recorded errors and clear its flag, if any.
    // [OpenGL ES 2.0.24] section 2.5 page 13.
    GLenum Context::getError()
    {
        if (mInvalidEnum)
        {
            mInvalidEnum = false;
    
            return GL_INVALID_ENUM;
        }
    
        if (mInvalidValue)
        {
            mInvalidValue = false;
    
            return GL_INVALID_VALUE;
        }
    
        if (mInvalidOperation)
        {
            mInvalidOperation = false;
    
            return GL_INVALID_OPERATION;
        }
    
        if (mOutOfMemory)
        {
            mOutOfMemory = false;
    
            return GL_OUT_OF_MEMORY;
        }
    
        if (mInvalidFramebufferOperation)
        {
            mInvalidFramebufferOperation = false;
    
            return GL_INVALID_FRAMEBUFFER_OPERATION;
        }
    
        return GL_NO_ERROR;
    }
    
    GLenum Context::getResetStatus()
    {
        if (mResetStatus == GL_NO_ERROR && !mContextLost)
        {
            // mResetStatus will be set by the markContextLost callback
            // in the case a notification is sent
            mRenderer->testDeviceLost(true);
        }
    
        GLenum status = mResetStatus;
    
        if (mResetStatus != GL_NO_ERROR)
        {
            ASSERT(mContextLost);
    
            if (mRenderer->testDeviceResettable())
            {
                mResetStatus = GL_NO_ERROR;
            }
        }
        
        return status;
    }
    
    bool Context::isResetNotificationEnabled()
    {
        return (mResetStrategy == GL_LOSE_CONTEXT_ON_RESET_EXT);
    }
    
    int Context::getClientVersion() const
    {
        return mClientVersion;
    }
    
    int Context::getMajorShaderModel() const
    {
        return mMajorShaderModel;
    }
    
    float Context::getMaximumPointSize() const
    {
        return mMaximumPointSize;
    }
    
    unsigned int Context::getMaximumCombinedTextureImageUnits() const
    {
        return mRenderer->getMaxCombinedTextureImageUnits();
    }
    
    unsigned int Context::getMaximumCombinedUniformBufferBindings() const
    {
        return mRenderer->getMaxVertexShaderUniformBuffers() +
               mRenderer->getMaxFragmentShaderUniformBuffers();
    }
    
    int Context::getMaxSupportedSamples() const
    {
        return mRenderer->getMaxSupportedSamples();
    }
    
    unsigned int Context::getMaxTransformFeedbackBufferBindings() const
    {
        return mRenderer->getMaxTransformFeedbackBuffers();
    }
    
    GLintptr Context::getUniformBufferOffsetAlignment() const
    {
        // setting a large alignment forces uniform buffers to bind with zero offset
        return static_cast<GLintptr>(std::numeric_limits<GLint>::max());
    }
    
    unsigned int Context::getMaximumRenderTargets() const
    {
        return mRenderer->getMaxRenderTargets();
    }
    
    bool Context::supportsEventQueries() const
    {
        return mSupportsEventQueries;
    }
    
    bool Context::supportsOcclusionQueries() const
    {
        return mSupportsOcclusionQueries;
    }
    
    bool Context::supportsBGRATextures() const
    {
        return mSupportsBGRATextures;
    }
    
    bool Context::supportsDXT1Textures() const
    {
        return mSupportsDXT1Textures;
    }
    
    bool Context::supportsDXT3Textures() const
    {
        return mSupportsDXT3Textures;
    }
    
    bool Context::supportsDXT5Textures() const
    {
        return mSupportsDXT5Textures;
    }
    
    bool Context::supportsFloat32Textures() const
    {
        return mSupportsFloat32Textures;
    }
    
    bool Context::supportsFloat32LinearFilter() const
    {
        return mSupportsFloat32LinearFilter;
    }
    
    bool Context::supportsFloat32RenderableTextures() const
    {
        return mSupportsFloat32RenderableTextures;
    }
    
    bool Context::supportsFloat16Textures() const
    {
        return mSupportsFloat16Textures;
    }
    
    bool Context::supportsFloat16LinearFilter() const
    {
        return mSupportsFloat16LinearFilter;
    }
    
    bool Context::supportsFloat16RenderableTextures() const
    {
        return mSupportsFloat16RenderableTextures;
    }
    
    int Context::getMaximumRenderbufferDimension() const
    {
        return mMaxRenderbufferDimension;
    }
    
    int Context::getMaximum2DTextureDimension() const
    {
        return mMax2DTextureDimension;
    }
    
    int Context::getMaximumCubeTextureDimension() const
    {
        return mMaxCubeTextureDimension;
    }
    
    int Context::getMaximum3DTextureDimension() const
    {
        return mMax3DTextureDimension;
    }
    
    int Context::getMaximum2DArrayTextureLayers() const
    {
        return mMax2DArrayTextureLayers;
    }
    
    int Context::getMaximumTextureLevel() const
    {
        return mMaxTextureLevel;
    }
    
    bool Context::supportsLuminanceTextures() const
    {
        return mSupportsLuminanceTextures;
    }
    
    bool Context::supportsLuminanceAlphaTextures() const
    {
        return mSupportsLuminanceAlphaTextures;
    }
    
    bool Context::supportsDepthTextures() const
    {
        return mSupportsDepthTextures;
    }
    
    bool Context::supports32bitIndices() const
    {
        return mSupports32bitIndices;
    }
    
    bool Context::supportsNonPower2Texture() const
    {
        return mSupportsNonPower2Texture;
    }
    
    bool Context::supportsInstancing() const
    {
        return mSupportsInstancing;
    }
    
    bool Context::supportsTextureFilterAnisotropy() const
    {
        return mSupportsTextureFilterAnisotropy;
    }
    
    float Context::getTextureMaxAnisotropy() const
    {
        return mMaxTextureAnisotropy;
    }
    
    bool Context::getCurrentReadFormatType(GLint *internalFormat, GLenum *format, GLenum *type)
    {
        Framebuffer *framebuffer = getReadFramebuffer();
        if (!framebuffer || framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
        {
            return gl::error(GL_INVALID_OPERATION, false);
        }
    
        Renderbuffer *renderbuffer = framebuffer->getReadColorbuffer();
        if (!renderbuffer)
        {
            return gl::error(GL_INVALID_OPERATION, false);
        }
    
        *internalFormat = renderbuffer->getActualFormat();
        *format = gl::GetFormat(renderbuffer->getActualFormat(), mClientVersion);
        *type = gl::GetType(renderbuffer->getActualFormat(), mClientVersion);
    
        return true;
    }
    
    void Context::detachBuffer(GLuint buffer)
    {
        // [OpenGL ES 2.0.24] section 2.9 page 22:
        // If a buffer object is deleted while it is bound, all bindings to that object in the current context
        // (i.e. in the thread that called Delete-Buffers) are reset to zero.
    
        if (mState.arrayBuffer.id() == buffer)
        {
            mState.arrayBuffer.set(NULL);
        }
    
        if (mState.elementArrayBuffer.id() == buffer)
        {
            mState.elementArrayBuffer.set(NULL);
        }
    
        for (int attribute = 0; attribute < MAX_VERTEX_ATTRIBS; attribute++)
        {
            if (mState.vertexAttribute[attribute].mBoundBuffer.id() == buffer)
            {
                mState.vertexAttribute[attribute].mBoundBuffer.set(NULL);
            }
        }
    }
    
    void Context::detachTexture(GLuint texture)
    {
        // [OpenGL ES 2.0.24] section 3.8 page 84:
        // If a texture object is deleted, it is as if all texture units which are bound to that texture object are
        // rebound to texture object zero
    
        for (int type = 0; type < TEXTURE_TYPE_COUNT; type++)
        {
            for (int sampler = 0; sampler < IMPLEMENTATION_MAX_COMBINED_TEXTURE_IMAGE_UNITS; sampler++)
            {
                if (mState.samplerTexture[type][sampler].id() == texture)
                {
                    mState.samplerTexture[type][sampler].set(NULL);
                }
            }
        }
    
        // [OpenGL ES 2.0.24] section 4.4 page 112:
        // If a texture object is deleted while its image is attached to the currently bound framebuffer, then it is
        // as if FramebufferTexture2D had been called, with a texture of 0, for each attachment point to which this
        // image was attached in the currently bound framebuffer.
    
        Framebuffer *readFramebuffer = getReadFramebuffer();
        Framebuffer *drawFramebuffer = getDrawFramebuffer();
    
        if (readFramebuffer)
        {
            readFramebuffer->detachTexture(texture);
        }
    
        if (drawFramebuffer && drawFramebuffer != readFramebuffer)
        {
            drawFramebuffer->detachTexture(texture);
        }
    }
    
    void Context::detachFramebuffer(GLuint framebuffer)
    {
        // [OpenGL ES 2.0.24] section 4.4 page 107:
        // If a framebuffer that is currently bound to the target FRAMEBUFFER is deleted, it is as though
        // BindFramebuffer had been executed with the target of FRAMEBUFFER and framebuffer of zero.
    
        if (mState.readFramebuffer == framebuffer)
        {
            bindReadFramebuffer(0);
        }
    
        if (mState.drawFramebuffer == framebuffer)
        {
            bindDrawFramebuffer(0);
        }
    }
    
    void Context::detachRenderbuffer(GLuint renderbuffer)
    {
        // [OpenGL ES 2.0.24] section 4.4 page 109:
        // If a renderbuffer that is currently bound to RENDERBUFFER is deleted, it is as though BindRenderbuffer
        // had been executed with the target RENDERBUFFER and name of zero.
    
        if (mState.renderbuffer.id() == renderbuffer)
        {
            bindRenderbuffer(0);
        }
    
        // [OpenGL ES 2.0.24] section 4.4 page 111:
        // If a renderbuffer object is deleted while its image is attached to the currently bound framebuffer,
        // then it is as if FramebufferRenderbuffer had been called, with a renderbuffer of 0, for each attachment
        // point to which this image was attached in the currently bound framebuffer.
    
        Framebuffer *readFramebuffer = getReadFramebuffer();
        Framebuffer *drawFramebuffer = getDrawFramebuffer();
    
        if (readFramebuffer)
        {
            readFramebuffer->detachRenderbuffer(renderbuffer);
        }
    
        if (drawFramebuffer && drawFramebuffer != readFramebuffer)
        {
            drawFramebuffer->detachRenderbuffer(renderbuffer);
        }
    }
    
    Texture *Context::getIncompleteTexture(TextureType type)
    {
        Texture *t = mIncompleteTextures[type].get();
    
        if (t == NULL)
        {
            static const GLubyte color[] = { 0, 0, 0, 255 };
    
            switch (type)
            {
              default:
                UNREACHABLE();
                // default falls through to TEXTURE_2D
    
              case TEXTURE_2D:
                {
                    Texture2D *incomplete2d = new Texture2D(mRenderer, Texture::INCOMPLETE_TEXTURE_ID);
                    incomplete2d->setImage(0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 1, color);
                    t = incomplete2d;
                }
                break;
    
              case TEXTURE_CUBE:
                {
                  TextureCubeMap *incompleteCube = new TextureCubeMap(mRenderer, Texture::INCOMPLETE_TEXTURE_ID);
    
                  incompleteCube->setImagePosX(0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 1, color);
                  incompleteCube->setImageNegX(0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 1, color);
                  incompleteCube->setImagePosY(0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 1, color);
                  incompleteCube->setImageNegY(0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 1, color);
                  incompleteCube->setImagePosZ(0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 1, color);
                  incompleteCube->setImageNegZ(0, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 1, color);
    
                  t = incompleteCube;
                }
                break;
    
              case TEXTURE_3D:
                {
                    Texture3D *incomplete3d = new Texture3D(mRenderer, Texture::INCOMPLETE_TEXTURE_ID);
                    incomplete3d->setImage(0, 1, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 1, color);
    
                    t = incomplete3d;
                }
                break;
    
              case TEXTURE_2D_ARRAY:
                {
                    Texture2DArray *incomplete2darray = new Texture2DArray(mRenderer, Texture::INCOMPLETE_TEXTURE_ID);
                    incomplete2darray->setImage(0, 1, 1, 1, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 1, color);
    
                    t = incomplete2darray;
                }
                break;
            }
    
            mIncompleteTextures[type].set(t);
        }
    
        return t;
    }
    
    bool Context::skipDraw(GLenum drawMode)
    {
        if (drawMode == GL_POINTS)
        {
            // ProgramBinary assumes non-point rendering if gl_PointSize isn't written,
            // which affects varying interpolation. Since the value of gl_PointSize is
            // undefined when not written, just skip drawing to avoid unexpected results.
            if (!getCurrentProgramBinary()->usesPointSize())
            {
                // This is stictly speaking not an error, but developers should be 
                // notified of risking undefined behavior.
                ERR("Point rendering without writing to gl_PointSize.");
    
                return true;
            }
        }
        else if (IsTriangleMode(drawMode))
        {
            if (mState.rasterizer.cullFace && mState.rasterizer.cullMode == GL_FRONT_AND_BACK)
            {
                return true;
            }
        }
    
        return false;
    }
    
    void Context::setVertexAttribf(GLuint index, const GLfloat values[4])
    {
        ASSERT(index < gl::MAX_VERTEX_ATTRIBS);
    
        mState.vertexAttribute[index].mCurrentValue.FloatValues[0] = values[0];
        mState.vertexAttribute[index].mCurrentValue.FloatValues[1] = values[1];
        mState.vertexAttribute[index].mCurrentValue.FloatValues[2] = values[2];
        mState.vertexAttribute[index].mCurrentValue.FloatValues[3] = values[3];
        mState.vertexAttribute[index].mCurrentValue.Type = GL_FLOAT;
        mState.vertexAttribute[index].mPureInteger = false;
    }
    
    void Context::setVertexAttribu(GLuint index, const GLuint values[4])
    {
        ASSERT(index < gl::MAX_VERTEX_ATTRIBS);
    
        mState.vertexAttribute[index].mCurrentValue.UnsignedIntValues[0] = values[0];
        mState.vertexAttribute[index].mCurrentValue.UnsignedIntValues[1] = values[1];
        mState.vertexAttribute[index].mCurrentValue.UnsignedIntValues[2] = values[2];
        mState.vertexAttribute[index].mCurrentValue.UnsignedIntValues[3] = values[3];
        mState.vertexAttribute[index].mCurrentValue.Type = GL_UNSIGNED_INT;
        mState.vertexAttribute[index].mPureInteger = true;
    }
    
    void Context::setVertexAttribi(GLuint index, const GLint values[4])
    {
        ASSERT(index < gl::MAX_VERTEX_ATTRIBS);
    
        mState.vertexAttribute[index].mCurrentValue.IntValues[0] = values[0];
        mState.vertexAttribute[index].mCurrentValue.IntValues[1] = values[1];
        mState.vertexAttribute[index].mCurrentValue.IntValues[2] = values[2];
        mState.vertexAttribute[index].mCurrentValue.IntValues[3] = values[3];
        mState.vertexAttribute[index].mCurrentValue.Type = GL_INT;
        mState.vertexAttribute[index].mPureInteger = true;
    }
    
    void Context::setVertexAttribDivisor(GLuint index, GLuint divisor)
    {
        ASSERT(index < gl::MAX_VERTEX_ATTRIBS);
    
        mState.vertexAttribute[index].mDivisor = divisor;
    }
    
    // keep list sorted in following order
    // OES extensions
    // EXT extensions
    // Vendor extensions
    void Context::initExtensionString()
    {
        // Do not report extension in GLES 3 contexts for now
        if (mClientVersion == 2)
        {
            // OES extensions
            if (supports32bitIndices())
            {
                mExtensionStringList.push_back("GL_OES_element_index_uint");
            }
    
            mExtensionStringList.push_back("GL_OES_packed_depth_stencil");
            mExtensionStringList.push_back("GL_OES_get_program_binary");
            mExtensionStringList.push_back("GL_OES_rgb8_rgba8");
            if (mRenderer->getDerivativeInstructionSupport())
            {
                mExtensionStringList.push_back("GL_OES_standard_derivatives");
            }
    
            if (supportsFloat16Textures())
            {
                mExtensionStringList.push_back("GL_OES_texture_half_float");
            }
            if (supportsFloat16LinearFilter())
            {
                mExtensionStringList.push_back("GL_OES_texture_half_float_linear");
            }
            if (supportsFloat32Textures())
            {
                mExtensionStringList.push_back("GL_OES_texture_float");
            }
            if (supportsFloat32LinearFilter())
            {
                mExtensionStringList.push_back("GL_OES_texture_float_linear");
            }
    
            if (supportsNonPower2Texture())
            {
                mExtensionStringList.push_back("GL_OES_texture_npot");
            }
    
            // Multi-vendor (EXT) extensions
            if (supportsOcclusionQueries())
            {
                mExtensionStringList.push_back("GL_EXT_occlusion_query_boolean");
            }
    
            mExtensionStringList.push_back("GL_EXT_read_format_bgra");
            mExtensionStringList.push_back("GL_EXT_robustness");
    
            if (supportsDXT1Textures())
            {
                mExtensionStringList.push_back("GL_EXT_texture_compression_dxt1");
            }
    
            if (supportsTextureFilterAnisotropy())
            {
                mExtensionStringList.push_back("GL_EXT_texture_filter_anisotropic");
            }
    
            if (supportsBGRATextures())
            {
                mExtensionStringList.push_back("GL_EXT_texture_format_BGRA8888");
            }
    
            if (mRenderer->getMaxRenderTargets() > 1)
            {
                mExtensionStringList.push_back("GL_EXT_draw_buffers");
            }
    
            mExtensionStringList.push_back("GL_EXT_texture_storage");
    
            // ANGLE-specific extensions
            if (supportsDepthTextures())
            {
                mExtensionStringList.push_back("GL_ANGLE_depth_texture");
            }
    
            mExtensionStringList.push_back("GL_ANGLE_framebuffer_blit");
            if (getMaxSupportedSamples() != 0)
            {
                mExtensionStringList.push_back("GL_ANGLE_framebuffer_multisample");
            }
    
            if (supportsInstancing())
            {
                mExtensionStringList.push_back("GL_ANGLE_instanced_arrays");
            }
    
            mExtensionStringList.push_back("GL_ANGLE_pack_reverse_row_order");
    
            if (supportsDXT3Textures())
            {
                mExtensionStringList.push_back("GL_ANGLE_texture_compression_dxt3");
            }
            if (supportsDXT5Textures())
            {
                mExtensionStringList.push_back("GL_ANGLE_texture_compression_dxt5");
            }
    
            mExtensionStringList.push_back("GL_ANGLE_texture_usage");
            mExtensionStringList.push_back("GL_ANGLE_translated_shader_source");
    
            // Other vendor-specific extensions
            if (supportsEventQueries())
            {
                mExtensionStringList.push_back("GL_NV_fence");
            }
        }
    
        // Join the extension strings to one long string for use with GetString
        std::stringstream strstr;
        for (unsigned int extensionIndex = 0; extensionIndex < mExtensionStringList.size(); extensionIndex++)
        {
            strstr << mExtensionStringList[extensionIndex];
            strstr << " ";
        }
    
        mCombinedExtensionsString = makeStaticString(strstr.str());
    }
    
    const char *Context::getCombinedExtensionsString() const
    {
        return mCombinedExtensionsString;
    }
    
    const char *Context::getExtensionString(const GLuint index) const
    {
        ASSERT(index < mExtensionStringList.size());
        return mExtensionStringList[index].c_str();
    }
    
    unsigned int Context::getNumExtensions() const
    {
        return mExtensionStringList.size();
    }
    
    void Context::initRendererString()
    {
        std::ostringstream rendererString;
        rendererString << "ANGLE (";
        rendererString << mRenderer->getRendererDescription();
        rendererString << ")";
    
        mRendererString = makeStaticString(rendererString.str());
    }
    
    const char *Context::getRendererString() const
    {
        return mRendererString;
    }
    
    void Context::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, 
                                  GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                                  GLbitfield mask)
    {
        Framebuffer *readFramebuffer = getReadFramebuffer();
        Framebuffer *drawFramebuffer = getDrawFramebuffer();
    
        if (!readFramebuffer || readFramebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE ||
            !drawFramebuffer || drawFramebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
        {
            return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION);
        }
    
        if (drawFramebuffer->getSamples() != 0)
        {
            return gl::error(GL_INVALID_OPERATION);
        }
    
        Renderbuffer *readColorBuffer = readFramebuffer->getReadColorbuffer();
        Renderbuffer *drawColorBuffer = drawFramebuffer->getFirstColorbuffer();
    
        if (drawColorBuffer == NULL)
        {
            ERR("Draw buffers formats don't match, which is not supported in this implementation of BlitFramebufferANGLE");
            return gl::error(GL_INVALID_OPERATION);
        }
    
        int readBufferWidth = readColorBuffer->getWidth();
        int readBufferHeight = readColorBuffer->getHeight();
        int drawBufferWidth = drawColorBuffer->getWidth();
        int drawBufferHeight = drawColorBuffer->getHeight();
    
        Rectangle sourceRect;
        Rectangle destRect;
    
        if (srcX0 < srcX1)
        {
            sourceRect.x = srcX0;
            destRect.x = dstX0;
            sourceRect.width = srcX1 - srcX0;
            destRect.width = dstX1 - dstX0;
        }
        else
        {
            sourceRect.x = srcX1;
            destRect.x = dstX1;
            sourceRect.width = srcX0 - srcX1;
            destRect.width = dstX0 - dstX1;
        }
    
        if (srcY0 < srcY1)
        {
            sourceRect.height = srcY1 - srcY0;
            destRect.height = dstY1 - dstY0;
            sourceRect.y = srcY0;
            destRect.y = dstY0;
        }
        else
        {
            sourceRect.height = srcY0 - srcY1;
            destRect.height = dstY0 - srcY1;
            sourceRect.y = srcY1;
            destRect.y = dstY1;
        }
    
        Rectangle sourceScissoredRect = sourceRect;
        Rectangle destScissoredRect = destRect;
    
        if (mState.scissorTest)
        {
            // Only write to parts of the destination framebuffer which pass the scissor test.
            if (destRect.x < mState.scissor.x)
            {
                int xDiff = mState.scissor.x - destRect.x;
                destScissoredRect.x = mState.scissor.x;
                destScissoredRect.width -= xDiff;
                sourceScissoredRect.x += xDiff;
                sourceScissoredRect.width -= xDiff;
    
            }
    
            if (destRect.x + destRect.width > mState.scissor.x + mState.scissor.width)
            {
                int xDiff = (destRect.x + destRect.width) - (mState.scissor.x + mState.scissor.width);
                destScissoredRect.width -= xDiff;
                sourceScissoredRect.width -= xDiff;
            }
    
            if (destRect.y < mState.scissor.y)
            {
                int yDiff = mState.scissor.y - destRect.y;
                destScissoredRect.y = mState.scissor.y;
                destScissoredRect.height -= yDiff;
                sourceScissoredRect.y += yDiff;
                sourceScissoredRect.height -= yDiff;
            }
    
            if (destRect.y + destRect.height > mState.scissor.y + mState.scissor.height)
            {
                int yDiff = (destRect.y + destRect.height) - (mState.scissor.y + mState.scissor.height);
                destScissoredRect.height  -= yDiff;
                sourceScissoredRect.height -= yDiff;
            }
        }
    
        bool blitRenderTarget = false;
        bool blitDepthStencil = false;
    
        Rectangle sourceTrimmedRect = sourceScissoredRect;
        Rectangle destTrimmedRect = destScissoredRect;
    
        // The source & destination rectangles also may need to be trimmed if they fall out of the bounds of 
        // the actual draw and read surfaces.
        if (sourceTrimmedRect.x < 0)
        {
            int xDiff = 0 - sourceTrimmedRect.x;
            sourceTrimmedRect.x = 0;
            sourceTrimmedRect.width -= xDiff;
            destTrimmedRect.x += xDiff;
            destTrimmedRect.width -= xDiff;
        }
    
        if (sourceTrimmedRect.x + sourceTrimmedRect.width > readBufferWidth)
        {
            int xDiff = (sourceTrimmedRect.x + sourceTrimmedRect.width) - readBufferWidth;
            sourceTrimmedRect.width -= xDiff;
            destTrimmedRect.width -= xDiff;
        }
    
        if (sourceTrimmedRect.y < 0)
        {
            int yDiff = 0 - sourceTrimmedRect.y;
            sourceTrimmedRect.y = 0;
            sourceTrimmedRect.height -= yDiff;
            destTrimmedRect.y += yDiff;
            destTrimmedRect.height -= yDiff;
        }
    
        if (sourceTrimmedRect.y + sourceTrimmedRect.height > readBufferHeight)
        {
            int yDiff = (sourceTrimmedRect.y + sourceTrimmedRect.height) - readBufferHeight;
            sourceTrimmedRect.height -= yDiff;
            destTrimmedRect.height -= yDiff;
        }
    
        if (destTrimmedRect.x < 0)
        {
            int xDiff = 0 - destTrimmedRect.x;
            destTrimmedRect.x = 0;
            destTrimmedRect.width -= xDiff;
            sourceTrimmedRect.x += xDiff;
            sourceTrimmedRect.width -= xDiff;
        }
    
        if (destTrimmedRect.x + destTrimmedRect.width > drawBufferWidth)
        {
            int xDiff = (destTrimmedRect.x + destTrimmedRect.width) - drawBufferWidth;
            destTrimmedRect.width -= xDiff;
            sourceTrimmedRect.width -= xDiff;
        }
    
        if (destTrimmedRect.y < 0)
        {
            int yDiff = 0 - destTrimmedRect.y;
            destTrimmedRect.y = 0;
            destTrimmedRect.height -= yDiff;
            sourceTrimmedRect.y += yDiff;
            sourceTrimmedRect.height -= yDiff;
        }
    
        if (destTrimmedRect.y + destTrimmedRect.height > drawBufferHeight)
        {
            int yDiff = (destTrimmedRect.y + destTrimmedRect.height) - drawBufferHeight;
            destTrimmedRect.height -= yDiff;
            sourceTrimmedRect.height -= yDiff;
        }
    
        bool partialBufferCopy = false;
        if (sourceTrimmedRect.height < readBufferHeight ||
            sourceTrimmedRect.width < readBufferWidth || 
            destTrimmedRect.height < drawBufferHeight ||
            destTrimmedRect.width < drawBufferWidth ||
            sourceTrimmedRect.y != 0 || destTrimmedRect.y != 0 || sourceTrimmedRect.x != 0 || destTrimmedRect.x != 0)
        {
            partialBufferCopy = true;
        }
    
        if (mask & GL_COLOR_BUFFER_BIT)
        {
            const GLenum readColorbufferType = readFramebuffer->getReadColorbufferType();
            const bool validReadType = (readColorbufferType == GL_TEXTURE_2D) || (readColorbufferType == GL_RENDERBUFFER);
            bool validDrawType = true;
            bool validDrawFormat = true;
    
            for (unsigned int colorAttachment = 0; colorAttachment < gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; colorAttachment++)
            {
                if (drawFramebuffer->isEnabledColorAttachment(colorAttachment))
                {
                    if (drawFramebuffer->getColorbufferType(colorAttachment) != GL_TEXTURE_2D &&
                        drawFramebuffer->getColorbufferType(colorAttachment) != GL_RENDERBUFFER)
                    {
                        validDrawType = false;
                    }
    
                    if (drawFramebuffer->getColorbuffer(colorAttachment)->getActualFormat() != readColorBuffer->getActualFormat())
                    {
                        validDrawFormat = false;
                    }
                }
            }
    
            if (!validReadType || !validDrawType || !validDrawFormat)
            {
                ERR("Color buffer format conversion in BlitFramebufferANGLE not supported by this implementation");
                return gl::error(GL_INVALID_OPERATION);
            }
            
            if (partialBufferCopy && readFramebuffer->getSamples() != 0)
            {
                return gl::error(GL_INVALID_OPERATION);
            }
    
            blitRenderTarget = true;
    
        }
    
        if (mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT))
        {
            Renderbuffer *readDSBuffer = NULL;
            Renderbuffer *drawDSBuffer = NULL;
    
            // We support OES_packed_depth_stencil, and do not support a separately attached depth and stencil buffer, so if we have
            // both a depth and stencil buffer, it will be the same buffer.
    
            if (mask & GL_DEPTH_BUFFER_BIT)
            {
                if (readFramebuffer->getDepthbuffer() && drawFramebuffer->getDepthbuffer())
                {
                    if (readFramebuffer->getDepthbufferType() != drawFramebuffer->getDepthbufferType() ||
                        readFramebuffer->getDepthbuffer()->getActualFormat() != drawFramebuffer->getDepthbuffer()->getActualFormat())
                    {
                        return gl::error(GL_INVALID_OPERATION);
                    }
    
                    blitDepthStencil = true;
                    readDSBuffer = readFramebuffer->getDepthbuffer();
                    drawDSBuffer = drawFramebuffer->getDepthbuffer();
                }
            }
    
            if (mask & GL_STENCIL_BUFFER_BIT)
            {
                if (readFramebuffer->getStencilbuffer() && drawFramebuffer->getStencilbuffer())
                {
                    if (readFramebuffer->getStencilbufferType() != drawFramebuffer->getStencilbufferType() ||
                        readFramebuffer->getStencilbuffer()->getActualFormat() != drawFramebuffer->getStencilbuffer()->getActualFormat())
                    {
                        return gl::error(GL_INVALID_OPERATION);
                    }
    
                    blitDepthStencil = true;
                    readDSBuffer = readFramebuffer->getStencilbuffer();
                    drawDSBuffer = drawFramebuffer->getStencilbuffer();
                }
            }
    
            if (partialBufferCopy)
            {
                ERR("Only whole-buffer depth and stencil blits are supported by this implementation.");
                return gl::error(GL_INVALID_OPERATION); // only whole-buffer copies are permitted
            }
    
            if ((drawDSBuffer && drawDSBuffer->getSamples() != 0) || 
                (readDSBuffer && readDSBuffer->getSamples() != 0))
            {
                return gl::error(GL_INVALID_OPERATION);
            }
        }
    
        if (blitRenderTarget || blitDepthStencil)
        {
            mRenderer->blitRect(readFramebuffer, sourceTrimmedRect, drawFramebuffer, destTrimmedRect, blitRenderTarget, blitDepthStencil);
        }
    }
    
    void Context::invalidateFrameBuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments,
                                        GLint x, GLint y, GLsizei width, GLsizei height)
    {
        Framebuffer *frameBuffer = NULL;
        switch (target)
        {
          case GL_FRAMEBUFFER:
          case GL_DRAW_FRAMEBUFFER:
            frameBuffer = getDrawFramebuffer();
            break;
          case GL_READ_FRAMEBUFFER:
            frameBuffer = getReadFramebuffer();
            break;
          default:
            UNREACHABLE();
        }
    
        if (frameBuffer && frameBuffer->completeness() == GL_FRAMEBUFFER_COMPLETE)
        {
            for (int i = 0; i < numAttachments; ++i)
            {
                rx::RenderTarget *renderTarget = NULL;
    
                if (attachments[i] >= GL_COLOR_ATTACHMENT0 && attachments[i] <= GL_COLOR_ATTACHMENT15)
                {
                    gl::Renderbuffer *renderBuffer = frameBuffer->getColorbuffer(attachments[i] - GL_COLOR_ATTACHMENT0);
                    if (renderBuffer)
                    {
                        renderTarget = renderBuffer->getRenderTarget();
                    }
                }
                else if (attachments[i] == GL_COLOR)
                {
                     gl::Renderbuffer *renderBuffer = frameBuffer->getColorbuffer(0);
                     if (renderBuffer)
                     {
                         renderTarget = renderBuffer->getRenderTarget();
                     }
                }
                else
                {
                    gl::Renderbuffer *renderBuffer = NULL;
                    switch (attachments[i])
                    {
                      case GL_DEPTH_ATTACHMENT:
                      case GL_DEPTH:
                        renderBuffer = frameBuffer->getDepthbuffer();
                        break;
                      case GL_STENCIL_ATTACHMENT:
                      case GL_STENCIL:
                        renderBuffer = frameBuffer->getStencilbuffer();
                        break;
                      case GL_DEPTH_STENCIL_ATTACHMENT:
                        renderBuffer = frameBuffer->getDepthOrStencilbuffer();
                        break;
                      default:
                        UNREACHABLE();
                    }
    
                    if (renderBuffer)
                    {
                        renderTarget = renderBuffer->getDepthStencil();
                    }
                }
    
                if (renderTarget)
                {
                    renderTarget->invalidate(x, y, width, height);
                }
            }
        }
    }
    
    }
    
    extern "C"
    {
    gl::Context *glCreateContext(int clientVersion, const gl::Context *shareContext, rx::Renderer *renderer, bool notifyResets, bool robustAccess)
    {
        return new gl::Context(clientVersion, shareContext, renderer, notifyResets, robustAccess);
    }
    
    void glDestroyContext(gl::Context *context)
    {
        delete context;
    
        if (context == gl::getContext())
        {
            gl::makeCurrent(NULL, NULL, NULL);
        }
    }
    
    void glMakeCurrent(gl::Context *context, egl::Display *display, egl::Surface *surface)
    {
        gl::makeCurrent(context, display, surface);
    }
    
    gl::Context *glGetCurrentContext()
    {
        return gl::getContext();
    }
    
    }