//
// Copyright 2016 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.
//

// queryutils.cpp: Utilities for querying values from GL objects

#ifdef UNSAFE_BUFFERS_BUILD
#    pragma allow_unsafe_buffers
#endif

#include "libANGLE/queryutils.h"

#include <algorithm>

#include "common/utilities.h"

#include "libANGLE/Buffer.h"
#include "libANGLE/Config.h"
#include "libANGLE/Context.h"
#include "libANGLE/Display.h"
#include "libANGLE/EGLSync.h"
#include "libANGLE/Fence.h"
#include "libANGLE/Framebuffer.h"
#include "libANGLE/GLES1State.h"
#include "libANGLE/MemoryObject.h"
#include "libANGLE/PixelLocalStorage.h"
#include "libANGLE/Program.h"
#include "libANGLE/Renderbuffer.h"
#include "libANGLE/Sampler.h"
#include "libANGLE/Shader.h"
#include "libANGLE/Surface.h"
#include "libANGLE/Texture.h"
#include "libANGLE/Uniform.h"
#include "libANGLE/VertexAttribute.h"
#include "libANGLE/queryconversions.h"

namespace gl
{

namespace
{

template <bool isPureInteger>
ColorGeneric ConvertToColor(const GLfloat *params)
{
    if (isPureInteger)
    {
        UNREACHABLE();
        return ColorGeneric(ColorI());
    }
    else
    {
        return ColorGeneric(ColorF::fromData(params));
    }
}

template <bool isPureInteger>
ColorGeneric ConvertToColor(const GLint *params)
{
    if (isPureInteger)
    {
        return ColorGeneric(ColorI(params[0], params[1], params[2], params[3]));
    }
    else
    {
        return ColorGeneric(ColorF(normalizedToFloat(params[0]), normalizedToFloat(params[1]),
                                   normalizedToFloat(params[2]), normalizedToFloat(params[3])));
    }
}

template <bool isPureInteger>
ColorGeneric ConvertToColor(const GLuint *params)
{
    if (isPureInteger)
    {
        return ColorGeneric(ColorUI(params[0], params[1], params[2], params[3]));
    }
    else
    {
        UNREACHABLE();
        return ColorGeneric(ColorF());
    }
}

template <bool isPureInteger>
void ConvertFromColor(const ColorGeneric &color, GLfloat *outParams)
{
    if (isPureInteger)
    {
        UNREACHABLE();
    }
    else
    {
        color.colorF.writeData(outParams);
    }
}

template <bool isPureInteger>
void ConvertFromColor(const ColorGeneric &color, GLint *outParams)
{
    if (isPureInteger)
    {
        outParams[0] = color.colorI.red;
        outParams[1] = color.colorI.green;
        outParams[2] = color.colorI.blue;
        outParams[3] = color.colorI.alpha;
    }
    else
    {
        outParams[0] = floatToNormalized<GLint>(color.colorF.red);
        outParams[1] = floatToNormalized<GLint>(color.colorF.green);
        outParams[2] = floatToNormalized<GLint>(color.colorF.blue);
        outParams[3] = floatToNormalized<GLint>(color.colorF.alpha);
    }
}

template <bool isPureInteger>
void ConvertFromColor(const ColorGeneric &color, GLuint *outParams)
{
    if (isPureInteger)
    {
        constexpr unsigned int kMinValue = 0;

        outParams[0] = std::max(color.colorUI.red, kMinValue);
        outParams[1] = std::max(color.colorUI.green, kMinValue);
        outParams[2] = std::max(color.colorUI.blue, kMinValue);
        outParams[3] = std::max(color.colorUI.alpha, kMinValue);
    }
    else
    {
        UNREACHABLE();
    }
}

template <typename ParamType>
void QueryTexLevelParameterBase(const Texture *texture,
                                TextureTarget targetPacked,
                                GLint level,
                                TextureImageParameter pnamePacked,
                                ParamType *params)
{
    ASSERT(texture != nullptr);
    const InternalFormat *info =
        texture->getTextureState().getImageDesc(targetPacked, level).format.info;

    switch (pnamePacked)
    {
        case TextureImageParameter::Width:
            *params = clampCast<ParamType>(texture->getWidth(targetPacked, level));
            break;
        case TextureImageParameter::Height:
            *params = clampCast<ParamType>(texture->getHeight(targetPacked, level));
            break;
        case TextureImageParameter::Depth:
            *params = clampCast<ParamType>(texture->getDepth(targetPacked, level));
            break;
        case TextureImageParameter::InternalFormat:
            *params = static_cast<ParamType>(info->internalFormat ? info->internalFormat : GL_RGBA);
            break;
        case TextureImageParameter::RedSize:
            *params = static_cast<ParamType>(info->redBits);
            break;
        case TextureImageParameter::GreenSize:
            *params = static_cast<ParamType>(info->greenBits);
            break;
        case TextureImageParameter::BlueSize:
            *params = static_cast<ParamType>(info->blueBits);
            break;
        case TextureImageParameter::AlphaSize:
            *params = static_cast<ParamType>(info->alphaBits);
            break;
        case TextureImageParameter::DepthSize:
            *params = static_cast<ParamType>(info->depthBits);
            break;
        case TextureImageParameter::StencilSize:
            *params = static_cast<ParamType>(info->stencilBits);
            break;
        case TextureImageParameter::SharedSize:
            *params = static_cast<ParamType>(info->sharedBits);
            break;
        case TextureImageParameter::RedType:
            *params = static_cast<ParamType>(info->redBits != 0 ? info->componentType : GL_NONE);
            break;
        case TextureImageParameter::GreenType:
            *params = static_cast<ParamType>(info->greenBits != 0 ? info->componentType : GL_NONE);
            break;
        case TextureImageParameter::BlueType:
            *params = static_cast<ParamType>(info->blueBits != 0 ? info->componentType : GL_NONE);
            break;
        case TextureImageParameter::AlphaType:
            *params = static_cast<ParamType>(info->alphaBits != 0 ? info->componentType : GL_NONE);
            break;
        case TextureImageParameter::DepthType:
            *params = static_cast<ParamType>(info->depthBits != 0 ? info->componentType : GL_NONE);
            break;
        case TextureImageParameter::Compressed:
            *params = static_cast<ParamType>(info->compressed);
            break;
        case TextureImageParameter::Samples:
            *params = static_cast<ParamType>(texture->getSamples(targetPacked, level));
            break;
        case TextureImageParameter::FixedSampleLocations:
            *params = static_cast<ParamType>(texture->getFixedSampleLocations(targetPacked, level));
            break;
        case TextureImageParameter::BufferDataStoreBinding:
            *params = static_cast<ParamType>(texture->getBuffer().id().value);
            break;
        case TextureImageParameter::BufferOffset:
            *params = clampCast<ParamType>(texture->getBuffer().getOffset());
            break;
        case TextureImageParameter::BufferSize:
            *params = clampCast<ParamType>(GetBoundBufferAvailableSize(texture->getBuffer()));
            break;
        case TextureImageParameter::MemorySize:
            *params = clampCast<ParamType>(texture->getLevelMemorySize(targetPacked, level));
            break;
        case TextureImageParameter::ResourceInitialized:
            *params = static_cast<ParamType>(
                texture->initState(GL_NONE, ImageIndex::MakeFromTarget(targetPacked, level)) ==
                InitState::Initialized);
            break;
        default:
            UNREACHABLE();
            break;
    }
}

// This function is needed to handle fixed_point data.
// It can be used when some pname need special conversion from int/float/bool to fixed_point.
template <bool isGLfixed, typename QueryT, typename ParamType>
QueryT CastFromSpecialValue(GLenum pname, const ParamType param)
{
    if (isGLfixed)
    {
        return static_cast<QueryT>(ConvertFloatToFixed(CastFromStateValue<GLfloat>(pname, param)));
    }
    else
    {
        return CastFromStateValue<QueryT>(pname, param);
    }
}

template <bool isPureInteger, bool isGLfixed, typename ParamType>
void QueryTexParameterBase(const Context *context,
                           const Texture *texture,
                           GLenum pname,
                           ParamType *params)
{
    ASSERT(texture != nullptr);

    switch (pname)
    {
        case GL_TEXTURE_MAG_FILTER:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getMagFilter());
            break;
        case GL_TEXTURE_MIN_FILTER:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getMinFilter());
            break;
        case GL_TEXTURE_WRAP_S:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getWrapS());
            break;
        case GL_TEXTURE_WRAP_T:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getWrapT());
            break;
        case GL_TEXTURE_WRAP_R:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getWrapR());
            break;
        case GL_TEXTURE_IMMUTABLE_FORMAT:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getImmutableFormat());
            break;
        case GL_TEXTURE_IMMUTABLE_LEVELS:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getImmutableLevels());
            break;
        case GL_TEXTURE_USAGE_ANGLE:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getUsage());
            break;
        case GL_TEXTURE_MAX_ANISOTROPY_EXT:
            *params =
                CastFromSpecialValue<isGLfixed, ParamType>(pname, texture->getMaxAnisotropy());
            break;
        case GL_TEXTURE_SWIZZLE_R:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getSwizzleRed());
            break;
        case GL_TEXTURE_SWIZZLE_G:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getSwizzleGreen());
            break;
        case GL_TEXTURE_SWIZZLE_B:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getSwizzleBlue());
            break;
        case GL_TEXTURE_SWIZZLE_A:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getSwizzleAlpha());
            break;
        case GL_TEXTURE_BASE_LEVEL:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getBaseLevel());
            break;
        case GL_TEXTURE_MAX_LEVEL:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getMaxLevel());
            break;
        case GL_TEXTURE_LOD_BIAS_QCOM:
            *params = CastFromStateValue<ParamType>(pname, texture->getLodBias());
            break;
        case GL_TEXTURE_MIN_LOD:
            *params = CastFromSpecialValue<isGLfixed, ParamType>(pname, texture->getMinLod());
            break;
        case GL_TEXTURE_MAX_LOD:
            *params = CastFromSpecialValue<isGLfixed, ParamType>(pname, texture->getMaxLod());
            break;
        case GL_TEXTURE_COMPARE_MODE:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getCompareMode());
            break;
        case GL_TEXTURE_COMPARE_FUNC:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getCompareFunc());
            break;
        case GL_TEXTURE_SRGB_DECODE_EXT:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getSRGBDecode());
            break;
        case GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getSRGBOverride());
            break;
        case GL_DEPTH_STENCIL_TEXTURE_MODE:
            *params =
                CastFromGLintStateValue<ParamType>(pname, texture->getDepthStencilTextureMode());
            break;
        case GL_TEXTURE_CROP_RECT_OES:
        {
            const gl::Rectangle &crop = texture->getCrop();
            params[0]                 = CastFromSpecialValue<isGLfixed, ParamType>(pname, crop.x);
            params[1]                 = CastFromSpecialValue<isGLfixed, ParamType>(pname, crop.y);
            params[2] = CastFromSpecialValue<isGLfixed, ParamType>(pname, crop.width);
            params[3] = CastFromSpecialValue<isGLfixed, ParamType>(pname, crop.height);
            break;
        }
        case GL_GENERATE_MIPMAP:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getGenerateMipmapHint());
            break;
        case GL_MEMORY_SIZE_ANGLE:
            *params = CastFromSpecialValue<isGLfixed, ParamType>(pname, texture->getMemorySize());
            break;
        case GL_TEXTURE_BORDER_COLOR:
            ConvertFromColor<isPureInteger>(texture->getBorderColor(), params);
            break;
        case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
            *params = CastFromGLintStateValue<ParamType>(
                pname, texture->getImplementationColorReadFormat(context));
            break;
        case GL_IMPLEMENTATION_COLOR_READ_TYPE:
            *params = CastFromGLintStateValue<ParamType>(
                pname, texture->getImplementationColorReadType(context));
            break;
        case GL_IMAGE_FORMAT_COMPATIBILITY_TYPE:
            *params =
                CastFromGLintStateValue<ParamType>(pname, GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE);
            break;
        case GL_RESOURCE_INITIALIZED_ANGLE:
            *params = CastFromGLintStateValue<ParamType>(
                pname, texture->initState() == InitState::Initialized);
            break;
        case GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES:
            *params = CastFromGLintStateValue<ParamType>(
                pname, texture->getRequiredTextureImageUnits(context));
            break;
        case GL_TEXTURE_PROTECTED_EXT:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->hasProtectedContent());
            break;
        case GL_TEXTURE_TILING_EXT:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getTilingMode());
            break;
        case GL_TEXTURE_FOVEATED_FEATURE_BITS_QCOM:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getFoveatedFeatureBits());
            break;
        case GL_TEXTURE_FOVEATED_FEATURE_QUERY_QCOM:
            *params =
                CastFromGLintStateValue<ParamType>(pname, texture->getSupportedFoveationFeatures());
            break;
        case GL_TEXTURE_FOVEATED_MIN_PIXEL_DENSITY_QCOM:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getMinPixelDensity());
            break;
        case GL_TEXTURE_FOVEATED_NUM_FOCAL_POINTS_QUERY_QCOM:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getNumFocalPoints());
            break;
        case GL_SURFACE_COMPRESSION_EXT:
            *params = CastFromGLintStateValue<ParamType>(pname,
                                                         texture->getImageCompressionRate(context));
            break;
        case GL_TEXTURE_ASTC_DECODE_PRECISION_EXT:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getASTCDecodePrecision());
            break;
        default:
            UNREACHABLE();
            break;
    }
}

// this function is needed to handle OES_FIXED_POINT.
// Some pname values can take in GLfixed values and may need to be converted
template <bool isGLfixed, typename ReturnType, typename ParamType>
ReturnType ConvertTexParam(GLenum pname, const ParamType param)
{
    if (isGLfixed)
    {
        return CastQueryValueTo<ReturnType>(pname,
                                            ConvertFixedToFloat(static_cast<GLfixed>(param)));
    }
    else
    {
        return CastQueryValueTo<ReturnType>(pname, param);
    }
}

template <bool isPureInteger, bool isGLfixed, typename ParamType>
void SetTexParameterBase(Context *context, Texture *texture, GLenum pname, const ParamType *params)
{
    ASSERT(texture != nullptr);

    switch (pname)
    {
        case GL_TEXTURE_WRAP_S:
            texture->setWrapS(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_WRAP_T:
            texture->setWrapT(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_WRAP_R:
            texture->setWrapR(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_MIN_FILTER:
            texture->setMinFilter(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_MAG_FILTER:
            texture->setMagFilter(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_USAGE_ANGLE:
            texture->setUsage(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_MAX_ANISOTROPY_EXT:
            texture->setMaxAnisotropy(context,
                                      ConvertTexParam<isGLfixed, GLfloat>(pname, params[0]));
            break;
        case GL_TEXTURE_COMPARE_MODE:
            texture->setCompareMode(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_COMPARE_FUNC:
            texture->setCompareFunc(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_SWIZZLE_R:
            texture->setSwizzleRed(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_SWIZZLE_G:
            texture->setSwizzleGreen(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_SWIZZLE_B:
            texture->setSwizzleBlue(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_SWIZZLE_A:
            texture->setSwizzleAlpha(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_BASE_LEVEL:
        {
            (void)(texture->setBaseLevel(
                context, clampCast<GLuint>(CastQueryValueTo<GLint>(pname, params[0]))));
            break;
        }
        case GL_TEXTURE_MAX_LEVEL:
            texture->setMaxLevel(context,
                                 clampCast<GLuint>(CastQueryValueTo<GLint>(pname, params[0])));
            break;
        case GL_TEXTURE_LOD_BIAS_QCOM:
            texture->setLodBias(context, CastQueryValueTo<GLfloat>(pname, params[0]));
            break;
        case GL_TEXTURE_MIN_LOD:
            texture->setMinLod(context, CastQueryValueTo<GLfloat>(pname, params[0]));
            break;
        case GL_TEXTURE_MAX_LOD:
            texture->setMaxLod(context, CastQueryValueTo<GLfloat>(pname, params[0]));
            break;
        case GL_DEPTH_STENCIL_TEXTURE_MODE:
            texture->setDepthStencilTextureMode(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_SRGB_DECODE_EXT:
            texture->setSRGBDecode(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT:
            texture->setSRGBOverride(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_CROP_RECT_OES:
            texture->setCrop(gl::Rectangle(ConvertTexParam<isGLfixed, GLint>(pname, params[0]),
                                           ConvertTexParam<isGLfixed, GLint>(pname, params[1]),
                                           ConvertTexParam<isGLfixed, GLint>(pname, params[2]),
                                           ConvertTexParam<isGLfixed, GLint>(pname, params[3])));
            break;
        case GL_GENERATE_MIPMAP:
            texture->setGenerateMipmapHint(ConvertToGLenum(params[0]));
            break;
        case GL_TEXTURE_BORDER_COLOR:
            texture->setBorderColor(context, ConvertToColor<isPureInteger>(params));
            break;
        case GL_RESOURCE_INITIALIZED_ANGLE:
            texture->setInitState(ConvertToBool(params[0]) ? InitState::Initialized
                                                           : InitState::MayNeedInit);
            break;
        case GL_TEXTURE_PROTECTED_EXT:
            texture->setProtectedContent(context, (params[0] == GL_TRUE));
            break;
        case GL_RENDERABILITY_VALIDATION_ANGLE:
            texture->setRenderabilityValidation(context, (params[0] == GL_TRUE));
            break;
        case GL_TEXTURE_TILING_EXT:
            texture->setTilingMode(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_FOVEATED_FEATURE_BITS_QCOM:
            texture->setFoveatedFeatureBits(ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_FOVEATED_MIN_PIXEL_DENSITY_QCOM:
            texture->setMinPixelDensity(ConvertToGLfloat(params[0]));
            break;
        case GL_TEXTURE_ASTC_DECODE_PRECISION_EXT:
            texture->setASTCDecodePrecision(context, ConvertToGLenum(pname, params[0]));
            break;
        default:
            UNREACHABLE();
            break;
    }
}

template <bool isPureInteger, typename ParamType>
void QuerySamplerParameterBase(const Sampler *sampler,
                               SamplerParameter pnamePacked,
                               ParamType *params)
{
    switch (pnamePacked)
    {
        case SamplerParameter::MagFilter:
            *params = static_cast<ParamType>(sampler->getMagFilter());
            break;
        case SamplerParameter::MinFilter:
            *params = static_cast<ParamType>(sampler->getMinFilter());
            break;
        case SamplerParameter::WrapS:
            *params = static_cast<ParamType>(sampler->getWrapS());
            break;
        case SamplerParameter::WrapT:
            *params = static_cast<ParamType>(sampler->getWrapT());
            break;
        case SamplerParameter::WrapR:
            *params = static_cast<ParamType>(sampler->getWrapR());
            break;
        case SamplerParameter::MinLod:
            // TODO(http://anglebug.com/487944776): remove the first parameter
            *params = CastFromStateValue<ParamType>(0, sampler->getMinLod());
            break;
        case SamplerParameter::MaxLod:
            // TODO(http://anglebug.com/487944776): remove the first parameter
            *params = CastFromStateValue<ParamType>(0, sampler->getMaxLod());
            break;
        case SamplerParameter::CompareMode:
            *params = static_cast<ParamType>(sampler->getCompareMode());
            break;
        case SamplerParameter::CompareFunc:
            *params = static_cast<ParamType>(sampler->getCompareFunc());
            break;
        case SamplerParameter::BorderColor:
            ConvertFromColor<isPureInteger>(sampler->getBorderColor(), params);
            break;
        case SamplerParameter::MaxAnisotropy:
            // TODO(http://anglebug.com/487944776): remove the first parameter
            *params = CastFromStateValue<ParamType>(0, sampler->getMaxAnisotropy());
            break;
        case SamplerParameter::SrgbDecode:
            *params = static_cast<ParamType>(sampler->getSRGBDecode());
            break;
        case SamplerParameter::LodBiasQCOM:
            *params = CastFromStateValue<ParamType>(0, sampler->getLodBias());
            break;
        default:
            UNREACHABLE();
            break;
    }
}

template <bool isPureInteger, typename ParamType>
void SetSamplerParameterBase(Context *context,
                             Sampler *sampler,
                             SamplerParameter pnamePacked,
                             const ParamType *params)
{
    switch (pnamePacked)
    {
        case SamplerParameter::MagFilter:
            sampler->setMagFilter(context, ConvertToGLenum(params[0]));
            break;
        case SamplerParameter::MinFilter:
            sampler->setMinFilter(context, ConvertToGLenum(params[0]));
            break;
        case SamplerParameter::WrapS:
            sampler->setWrapS(context, ConvertToGLenum(params[0]));
            break;
        case SamplerParameter::WrapT:
            sampler->setWrapT(context, ConvertToGLenum(params[0]));
            break;
        case SamplerParameter::WrapR:
            sampler->setWrapR(context, ConvertToGLenum(params[0]));
            break;
        case SamplerParameter::MinLod:
            sampler->setMinLod(context, static_cast<GLfloat>(params[0]));
            break;
        case SamplerParameter::MaxLod:
            sampler->setMaxLod(context, static_cast<GLfloat>(params[0]));
            break;
        case SamplerParameter::CompareMode:
            sampler->setCompareMode(context, ConvertToGLenum(params[0]));
            break;
        case SamplerParameter::CompareFunc:
            sampler->setCompareFunc(context, ConvertToGLenum(params[0]));
            break;
        case SamplerParameter::BorderColor:
            sampler->setBorderColor(context, ConvertToColor<isPureInteger>(params));
            break;
        case SamplerParameter::MaxAnisotropy:
            sampler->setMaxAnisotropy(context, static_cast<GLfloat>(params[0]));
            break;
        case SamplerParameter::SrgbDecode:
            sampler->setSRGBDecode(context, ConvertToGLenum(params[0]));
            break;
        case SamplerParameter::LodBiasQCOM:
            sampler->setLodBias(context, static_cast<GLfloat>(params[0]));
            break;
        default:
            UNREACHABLE();
            break;
    }

    sampler->onStateChange(angle::SubjectMessage::ContentsChanged);
}

// Warning: you should ensure binding really matches attrib.bindingIndex before using this function.
template <typename ParamType, typename CurrentDataType, size_t CurrentValueCount>
void QueryVertexAttribBase(const VertexAttribute &attrib,
                           const VertexBinding &binding,
                           const Buffer *buffer,
                           const CurrentDataType (&currentValueData)[CurrentValueCount],
                           GLenum pname,
                           ParamType *params)
{
    switch (pname)
    {
        case GL_CURRENT_VERTEX_ATTRIB:
            for (size_t i = 0; i < CurrentValueCount; ++i)
            {
                params[i] = CastFromStateValue<ParamType>(pname, currentValueData[i]);
            }
            break;
        case GL_VERTEX_ATTRIB_ARRAY_ENABLED:
            *params = CastFromStateValue<ParamType>(pname, static_cast<GLint>(attrib.enabled));
            break;
        case GL_VERTEX_ATTRIB_ARRAY_SIZE:
            *params = CastFromGLintStateValue<ParamType>(pname, attrib.format->channelCount);
            break;
        case GL_VERTEX_ATTRIB_ARRAY_STRIDE:
            *params = CastFromGLintStateValue<ParamType>(pname, attrib.vertexAttribArrayStride);
            break;
        case GL_VERTEX_ATTRIB_ARRAY_TYPE:
            *params = CastFromGLintStateValue<ParamType>(
                pname, gl::ToGLenum(attrib.format->vertexAttribType));
            break;
        case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
            *params =
                CastFromStateValue<ParamType>(pname, static_cast<GLint>(attrib.format->isNorm()));
            break;
        case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
            *params = CastFromGLintStateValue<ParamType>(pname, buffer ? buffer->id().value : 0);
            break;
        case GL_VERTEX_ATTRIB_ARRAY_DIVISOR:
            *params = CastFromStateValue<ParamType>(pname, binding.getDivisor());
            break;
        case GL_VERTEX_ATTRIB_ARRAY_INTEGER:
            *params = CastFromGLintStateValue<ParamType>(pname, attrib.format->isPureInt());
            break;
        case GL_VERTEX_ATTRIB_BINDING:
            *params = CastFromGLintStateValue<ParamType>(pname, attrib.bindingIndex);
            break;
        case GL_VERTEX_ATTRIB_RELATIVE_OFFSET:
            *params = CastFromGLintStateValue<ParamType>(pname, attrib.relativeOffset);
            break;
        default:
            UNREACHABLE();
            break;
    }
}

template <typename ParamType>
void QueryBufferParameterBase(const Buffer *buffer, BufferParam pnamePacked, ParamType *params)
{
    static_assert(std::is_same_v<ParamType, GLint> || std::is_same_v<ParamType, GLint64>);
    ASSERT(buffer != nullptr);

    switch (pnamePacked)
    {
        case BufferParam::ImmutableStorage:
            *params = static_cast<ParamType>(buffer->isImmutable());
            break;
        case BufferParam::StorageFlags:
            *params = static_cast<ParamType>(buffer->getStorageExtUsageFlags());
            break;
        case BufferParam::BufferSize:
            *params = clampCast<ParamType>(buffer->getSize());
            break;
        case BufferParam::BufferUsage:
            *params = static_cast<ParamType>(ToGLenum(buffer->getUsage()));
            break;
        case BufferParam::BufferAccess:
            *params = static_cast<ParamType>(buffer->getAccess());
            break;
        case BufferParam::BufferMapped:
            *params = static_cast<ParamType>(buffer->isMapped());
            break;
        case BufferParam::BufferAccessFlags:
            *params = static_cast<ParamType>(buffer->getAccessFlags());
            break;
        case BufferParam::BufferMapLength:
            *params = clampCast<ParamType>(buffer->getMapLength());
            break;
        case BufferParam::BufferMapOffset:
            *params = clampCast<ParamType>(buffer->getMapOffset());
            break;
        case BufferParam::MemorySize:
            *params = clampCast<ParamType>(buffer->getMemorySize());
            break;
        case BufferParam::ResourceInitialized:
            *params = static_cast<ParamType>(buffer->initState() == InitState::Initialized);
            break;
        default:
            UNREACHABLE();
            break;
    }
}

template <typename T>
GLint GetCommonVariableProperty(const T &var, GLenum prop)
{
    switch (prop)
    {
        case GL_TYPE:
            return clampCast<GLint>(var.pod.type);

        case GL_ARRAY_SIZE:
            // Queryable variables are guaranteed not to be arrays of arrays or arrays of structs,
            // see GLES 3.1 spec section 7.3.1.1 page 77.
            return clampCast<GLint>(var.getBasicTypeElementCount());

        case GL_NAME_LENGTH:
            // ES31 spec p84: This counts the terminating null char.
            return clampCast<GLint>(var.name.size() + 1u);

        default:
            UNREACHABLE();
            return GL_INVALID_VALUE;
    }
}

GLint GetInputResourceProperty(const Program *program, GLuint index, GLenum prop)
{
    const ProgramExecutable &executable = program->getExecutable();
    const ProgramInput &variable        = executable.getInputResource(index);

    switch (prop)
    {
        case GL_TYPE:
            return clampCast<GLint>(variable.getType());
        case GL_ARRAY_SIZE:
            return clampCast<GLint>(variable.getBasicTypeElementCount());

        case GL_NAME_LENGTH:
            return clampCast<GLint>(executable.getInputResourceName(index).size() + 1u);

        case GL_LOCATION:
            return variable.isBuiltIn() ? GL_INVALID_INDEX : variable.getLocation();

        // The query is targeted at the set of active input variables used by the first shader stage
        // of program. If program contains multiple shader stages then input variables from any
        // stage other than the first will not be enumerated. Since we found the variable to get
        // this far, we know it exists in the first attached shader stage.
        case GL_REFERENCED_BY_VERTEX_SHADER:
            return executable.getFirstLinkedShaderStageType() == ShaderType::Vertex;
        case GL_REFERENCED_BY_FRAGMENT_SHADER:
            return executable.getFirstLinkedShaderStageType() == ShaderType::Fragment;
        case GL_REFERENCED_BY_COMPUTE_SHADER:
            return executable.getFirstLinkedShaderStageType() == ShaderType::Compute;
        case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT:
            return executable.getFirstLinkedShaderStageType() == ShaderType::Geometry;
        case GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT:
            return executable.getFirstLinkedShaderStageType() == ShaderType::TessControl;
        case GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT:
            return executable.getFirstLinkedShaderStageType() == ShaderType::TessEvaluation;
        case GL_IS_PER_PATCH_EXT:
            return variable.isPatch();

        default:
            UNREACHABLE();
            return GL_INVALID_VALUE;
    }
}

GLint GetOutputResourceProperty(const Program *program, GLuint index, const GLenum prop)
{
    const ProgramExecutable &executable = program->getExecutable();
    const ProgramOutput &outputVariable = executable.getOutputResource(index);

    switch (prop)
    {
        case GL_TYPE:
            return clampCast<GLint>(outputVariable.pod.type);
        case GL_ARRAY_SIZE:
            return clampCast<GLint>(outputVariable.pod.basicTypeElementCount);

        case GL_NAME_LENGTH:
            return clampCast<GLint>(executable.getOutputResourceName(index).size() + 1u);

        case GL_LOCATION:
            return outputVariable.pod.location;

        case GL_LOCATION_INDEX_EXT:
            // EXT_blend_func_extended
            if (executable.getLastLinkedShaderStageType() == gl::ShaderType::Fragment)
            {
                return executable.getFragDataIndex(outputVariable.name);
            }
            return GL_INVALID_INDEX;

        // The set of active user-defined outputs from the final shader stage in this program. If
        // the final stage is a Fragment Shader, then this represents the fragment outputs that get
        // written to individual color buffers. If the program only contains a Compute Shader, then
        // there are no user-defined outputs.
        case GL_REFERENCED_BY_VERTEX_SHADER:
            return executable.getLastLinkedShaderStageType() == ShaderType::Vertex;
        case GL_REFERENCED_BY_FRAGMENT_SHADER:
            return executable.getLastLinkedShaderStageType() == ShaderType::Fragment;
        case GL_REFERENCED_BY_COMPUTE_SHADER:
            return executable.getLastLinkedShaderStageType() == ShaderType::Compute;
        case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT:
            return executable.getLastLinkedShaderStageType() == ShaderType::Geometry;
        case GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT:
            return executable.getLastLinkedShaderStageType() == ShaderType::TessControl;
        case GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT:
            return executable.getLastLinkedShaderStageType() == ShaderType::TessEvaluation;
        case GL_IS_PER_PATCH_EXT:
            return outputVariable.pod.isPatch;

        default:
            UNREACHABLE();
            return GL_INVALID_VALUE;
    }
}

GLint GetTransformFeedbackVaryingResourceProperty(const Program *program,
                                                  GLuint index,
                                                  const GLenum prop)
{
    const ProgramExecutable &executable = program->getExecutable();
    const TransformFeedbackVarying &tfVariable =
        executable.getTransformFeedbackVaryingResource(index);
    switch (prop)
    {
        case GL_TYPE:
            return clampCast<GLint>(tfVariable.type);

        case GL_ARRAY_SIZE:
            return clampCast<GLint>(tfVariable.size());

        case GL_NAME_LENGTH:
            return clampCast<GLint>(tfVariable.nameWithArrayIndex().size() + 1);

        default:
            UNREACHABLE();
            return GL_INVALID_VALUE;
    }
}

GLint QueryProgramInterfaceActiveResources(const Program *program, GLenum programInterface)
{
    const ProgramExecutable &executable = program->getExecutable();
    switch (programInterface)
    {
        case GL_PROGRAM_INPUT:
            return clampCast<GLint>(executable.getProgramInputs().size());

        case GL_PROGRAM_OUTPUT:
            return clampCast<GLint>(executable.getOutputVariables().size());

        case GL_UNIFORM:
            return clampCast<GLint>(executable.getUniforms().size());

        case GL_UNIFORM_BLOCK:
            return clampCast<GLint>(executable.getUniformBlocks().size());

        case GL_ATOMIC_COUNTER_BUFFER:
            return clampCast<GLint>(executable.getAtomicCounterBuffers().size());

        case GL_BUFFER_VARIABLE:
            return clampCast<GLint>(executable.getBufferVariables().size());

        case GL_SHADER_STORAGE_BLOCK:
            return clampCast<GLint>(executable.getShaderStorageBlocks().size());

        case GL_TRANSFORM_FEEDBACK_VARYING:
            return clampCast<GLint>(executable.getLinkedTransformFeedbackVaryings().size());

        default:
            UNREACHABLE();
            return 0;
    }
}

template <typename T, typename M>
GLint FindMaxSize(const std::vector<T> &resources, M member)
{
    GLint max = 0;
    for (const T &resource : resources)
    {
        max = std::max(max, clampCast<GLint>((resource.*member).size()));
    }
    return max;
}

GLint FindMaxNameLength(const std::vector<std::string> &names)
{
    GLint max = 0;
    for (const std::string &name : names)
    {
        max = std::max(max, clampCast<GLint>(name.size()));
    }
    return max;
}

GLint QueryProgramInterfaceMaxNameLength(const Program *program, GLenum programInterface)
{
    const ProgramExecutable &executable = program->getExecutable();

    GLint maxNameLength = 0;
    switch (programInterface)
    {
        case GL_PROGRAM_INPUT:
            maxNameLength = executable.getInputResourceMaxNameSize();
            break;

        case GL_PROGRAM_OUTPUT:
            maxNameLength = executable.getOutputResourceMaxNameSize();
            break;

        case GL_UNIFORM:
            maxNameLength = FindMaxNameLength(executable.getUniformNames());
            break;

        case GL_UNIFORM_BLOCK:
            return executable.getActiveUniformBlockMaxNameLength();

        case GL_BUFFER_VARIABLE:
            maxNameLength = FindMaxSize(executable.getBufferVariables(), &BufferVariable::name);
            break;

        case GL_SHADER_STORAGE_BLOCK:
            return executable.getActiveShaderStorageBlockMaxNameLength();

        case GL_TRANSFORM_FEEDBACK_VARYING:
            return clampCast<GLint>(executable.getTransformFeedbackVaryingMaxLength());

        default:
            UNREACHABLE();
            return 0;
    }
    // This length includes an extra character for the null terminator.
    return (maxNameLength == 0 ? 0 : maxNameLength + 1);
}

GLint QueryProgramInterfaceMaxNumActiveVariables(const Program *program, GLenum programInterface)
{
    const ProgramExecutable &executable = program->getExecutable();

    switch (programInterface)
    {
        case GL_UNIFORM_BLOCK:
            return FindMaxSize(executable.getUniformBlocks(), &InterfaceBlock::memberIndexes);
        case GL_ATOMIC_COUNTER_BUFFER:
            return FindMaxSize(executable.getAtomicCounterBuffers(),
                               &AtomicCounterBuffer::memberIndexes);

        case GL_SHADER_STORAGE_BLOCK:
            return FindMaxSize(executable.getShaderStorageBlocks(), &InterfaceBlock::memberIndexes);

        default:
            UNREACHABLE();
            return 0;
    }
}

GLenum GetUniformPropertyEnum(GLenum prop)
{
    switch (prop)
    {
        case GL_UNIFORM_TYPE:
            return GL_TYPE;
        case GL_UNIFORM_SIZE:
            return GL_ARRAY_SIZE;
        case GL_UNIFORM_NAME_LENGTH:
            return GL_NAME_LENGTH;
        case GL_UNIFORM_BLOCK_INDEX:
            return GL_BLOCK_INDEX;
        case GL_UNIFORM_OFFSET:
            return GL_OFFSET;
        case GL_UNIFORM_ARRAY_STRIDE:
            return GL_ARRAY_STRIDE;
        case GL_UNIFORM_MATRIX_STRIDE:
            return GL_MATRIX_STRIDE;
        case GL_UNIFORM_IS_ROW_MAJOR:
            return GL_IS_ROW_MAJOR;

        default:
            return prop;
    }
}

GLenum GetUniformBlockPropertyEnum(UniformBlockParameter pnamePacked)
{
    switch (pnamePacked)
    {
        case UniformBlockParameter::Binding:
            return GL_BUFFER_BINDING;
        case UniformBlockParameter::DataSize:
            return GL_BUFFER_DATA_SIZE;
        case UniformBlockParameter::NameLength:
            return GL_NAME_LENGTH;
        case UniformBlockParameter::ActiveUniforms:
            return GL_NUM_ACTIVE_VARIABLES;
        case UniformBlockParameter::ActiveUniformIndices:
            return GL_ACTIVE_VARIABLES;
        case UniformBlockParameter::ReferencedByVertexShader:
            return GL_REFERENCED_BY_VERTEX_SHADER;
        case UniformBlockParameter::ReferencedByFragmentShader:
            return GL_REFERENCED_BY_FRAGMENT_SHADER;
        default:
            UNREACHABLE();
            return 0;
    }
}

template <typename ShaderVariableT>
void GetShaderVariableBufferResourceProperty(const ShaderVariableT &buffer,
                                             GLenum pname,
                                             GLint *params,
                                             GLsizei count,
                                             GLsizei *outputPosition)

{
    switch (pname)
    {
        case GL_BUFFER_DATA_SIZE:
            params[(*outputPosition)++] = clampCast<GLint>(buffer.pod.dataSize);
            break;
        case GL_NUM_ACTIVE_VARIABLES:
            params[(*outputPosition)++] = buffer.numActiveVariables();
            break;
        case GL_ACTIVE_VARIABLES:
            for (size_t memberIndex = 0;
                 memberIndex < buffer.memberIndexes.size() && *outputPosition < count;
                 ++memberIndex)
            {
                params[(*outputPosition)++] = clampCast<GLint>(buffer.memberIndexes[memberIndex]);
            }
            break;
        case GL_REFERENCED_BY_VERTEX_SHADER:
            params[(*outputPosition)++] = static_cast<GLint>(buffer.isActive(ShaderType::Vertex));
            break;
        case GL_REFERENCED_BY_FRAGMENT_SHADER:
            params[(*outputPosition)++] = static_cast<GLint>(buffer.isActive(ShaderType::Fragment));
            break;
        case GL_REFERENCED_BY_COMPUTE_SHADER:
            params[(*outputPosition)++] = static_cast<GLint>(buffer.isActive(ShaderType::Compute));
            break;
        case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT:
            params[(*outputPosition)++] = static_cast<GLint>(buffer.isActive(ShaderType::Geometry));
            break;
        case GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT:
            params[(*outputPosition)++] =
                static_cast<GLint>(buffer.isActive(ShaderType::TessControl));
            break;
        case GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT:
            params[(*outputPosition)++] =
                static_cast<GLint>(buffer.isActive(ShaderType::TessEvaluation));
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void GetInterfaceBlockResourceProperty(const InterfaceBlock &block,
                                       GLenum pname,
                                       GLint *params,
                                       GLsizei count,
                                       GLsizei *outputPosition)
{
    if (pname == GL_NAME_LENGTH)
    {
        params[(*outputPosition)++] = clampCast<GLint>(block.nameWithArrayIndex().size() + 1);
        return;
    }
    GetShaderVariableBufferResourceProperty(block, pname, params, count, outputPosition);
}

void GetUniformBlockResourceProperty(const Program *program,
                                     GLuint blockIndex,
                                     GLenum pname,
                                     GLint *params,
                                     GLsizei count,
                                     GLsizei *outputPosition)

{
    ASSERT(*outputPosition < count);

    if (pname == GL_BUFFER_BINDING)
    {
        params[(*outputPosition)++] = program->getExecutable().getUniformBlockBinding(blockIndex);
        return;
    }

    const auto &block = program->getExecutable().getUniformBlockByIndex(blockIndex);
    GetInterfaceBlockResourceProperty(block, pname, params, count, outputPosition);
}

void GetShaderStorageBlockResourceProperty(const Program *program,
                                           GLuint blockIndex,
                                           GLenum pname,
                                           GLint *params,
                                           GLsizei count,
                                           GLsizei *outputPosition)

{
    ASSERT(*outputPosition < count);

    if (pname == GL_BUFFER_BINDING)
    {
        params[(*outputPosition)++] =
            program->getExecutable().getShaderStorageBlockBinding(blockIndex);
        return;
    }

    const auto &block = program->getExecutable().getShaderStorageBlockByIndex(blockIndex);
    GetInterfaceBlockResourceProperty(block, pname, params, count, outputPosition);
}

void GetAtomicCounterBufferResourceProperty(const Program *program,
                                            GLuint index,
                                            GLenum pname,
                                            GLint *params,
                                            GLsizei count,
                                            GLsizei *outputPosition)

{
    ASSERT(*outputPosition < count);

    if (pname == GL_BUFFER_BINDING)
    {
        params[(*outputPosition)++] = program->getExecutable().getAtomicCounterBufferBinding(index);
        return;
    }

    const auto &buffer = program->getExecutable().getAtomicCounterBuffers()[index];
    GetShaderVariableBufferResourceProperty(buffer, pname, params, count, outputPosition);
}

bool IsTextureEnvEnumParameter(TextureEnvParameter pname)
{
    switch (pname)
    {
        case TextureEnvParameter::Mode:
        case TextureEnvParameter::CombineRgb:
        case TextureEnvParameter::CombineAlpha:
        case TextureEnvParameter::Src0Rgb:
        case TextureEnvParameter::Src1Rgb:
        case TextureEnvParameter::Src2Rgb:
        case TextureEnvParameter::Src0Alpha:
        case TextureEnvParameter::Src1Alpha:
        case TextureEnvParameter::Src2Alpha:
        case TextureEnvParameter::Op0Rgb:
        case TextureEnvParameter::Op1Rgb:
        case TextureEnvParameter::Op2Rgb:
        case TextureEnvParameter::Op0Alpha:
        case TextureEnvParameter::Op1Alpha:
        case TextureEnvParameter::Op2Alpha:
        case TextureEnvParameter::PointCoordReplace:
            return true;
        default:
            return false;
    }
}

void GetShaderProgramId(ProgramPipeline *programPipeline, ShaderType shaderType, GLint *params)
{
    ASSERT(params);

    *params = 0;
    if (programPipeline)
    {
        const Program *program = programPipeline->getShaderProgram(shaderType);
        if (program)
        {
            *params = program->id().value;
        }
    }
}

}  // namespace

void QueryFramebufferAttachmentParameteriv(const Context *context,
                                           const Framebuffer *framebuffer,
                                           GLenum attachment,
                                           GLenum pname,
                                           GLint *params)
{
    ASSERT(framebuffer);

    const FramebufferAttachment *attachmentObject = framebuffer->getAttachment(context, attachment);

    if (attachmentObject == nullptr)
    {
        // ES 2.0.25 spec pg 127 states that if the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE
        // is NONE, then querying any other pname will generate INVALID_ENUM.

        // ES 3.0.2 spec pg 235 states that if the attachment type is none,
        // GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME will return zero and be an
        // INVALID_OPERATION for all other pnames

        switch (pname)
        {
            case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
                *params = GL_NONE;
                break;

            case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
                *params = 0;
                break;

            default:
                UNREACHABLE();
                break;
        }

        return;
    }

    switch (pname)
    {
        case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
            *params = attachmentObject->type();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
            *params = attachmentObject->id();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
            *params = attachmentObject->mipLevel();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
        {
            TextureTarget face = attachmentObject->cubeMapFace();
            if (face != TextureTarget::InvalidEnum)
            {
                *params = ToGLenum(attachmentObject->cubeMapFace());
            }
            else
            {
                // This happens when the attachment isn't a texture cube map face
                *params = GL_NONE;
            }
        }
        break;

        case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
            *params = attachmentObject->getRedSize();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
            *params = attachmentObject->getGreenSize();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
            *params = attachmentObject->getBlueSize();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
            *params = attachmentObject->getAlphaSize();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
            *params = attachmentObject->getDepthSize();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
            *params = attachmentObject->getStencilSize();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
            *params = attachmentObject->getComponentType();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
            *params = attachmentObject->getColorEncoding();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:
            *params = attachmentObject->layer();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR:
            *params = attachmentObject->getNumViews();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR:
            *params = attachmentObject->getBaseViewIndex();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT:
            *params = attachmentObject->isLayered();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT:
            if (attachmentObject->type() == GL_TEXTURE)
            {
                *params = attachmentObject->getSamples();
            }
            else
            {
                *params = 0;
            }
            break;

        default:
            UNREACHABLE();
            break;
    }
}

void QueryBufferParameteriv(const Buffer *buffer, BufferParam pnamePacked, GLint *params)
{
    QueryBufferParameterBase(buffer, pnamePacked, params);
}

void QueryBufferParameteri64v(const Buffer *buffer, BufferParam pnamePacked, GLint64 *params)
{
    QueryBufferParameterBase(buffer, pnamePacked, params);
}

void QueryProgramiv(Context *context, Program *program, GLenum pname, GLint *params)
{
    ASSERT(program != nullptr || pname == GL_COMPLETION_STATUS_KHR);

    switch (pname)
    {
        case GL_DELETE_STATUS:
            *params = program->isFlaggedForDeletion();
            return;
        case GL_LINK_STATUS:
            *params = program->isLinked();
            return;
        case GL_COMPLETION_STATUS_KHR:
            if (context->isContextLost())
            {
                context->contextLostErrorOnBlockingCall(angle::EntryPoint::GLGetProgramiv);
                *params = GL_TRUE;
            }
            else
            {
                *params = program->isLinking() ? GL_FALSE : GL_TRUE;
            }
            return;
        case GL_VALIDATE_STATUS:
            *params = program->isValidated();
            return;
        case GL_INFO_LOG_LENGTH:
            *params = program->getInfoLogLength();
            return;
        case GL_ATTACHED_SHADERS:
            *params = program->getAttachedShadersCount();
            return;
        case GL_ACTIVE_ATTRIBUTES:
            *params = static_cast<GLint>(program->getExecutable().getProgramInputs().size());
            return;
        case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH:
            *params = program->getExecutable().getActiveAttributeMaxLength();
            return;
        case GL_ACTIVE_UNIFORMS:
            *params = static_cast<GLint>(program->getExecutable().getUniforms().size());
            return;
        case GL_ACTIVE_UNIFORM_MAX_LENGTH:
            *params = program->getExecutable().getActiveUniformMaxLength();
            return;
        case GL_PROGRAM_BINARY_READY_ANGLE:
            *params = program->isBinaryReady(context);
            return;
        case GL_PROGRAM_BINARY_LENGTH_OES:
            *params = context->getCaps().programBinaryFormats.empty()
                          ? 0
                          : program->getBinaryLength(context);
            return;
        case GL_ACTIVE_UNIFORM_BLOCKS:
            *params = static_cast<GLint>(program->getExecutable().getUniformBlocks().size());
            return;
        case GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH:
            *params = program->getExecutable().getActiveUniformBlockMaxNameLength();
            break;
        case GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
            *params = program->getExecutable().getTransformFeedbackBufferMode();
            break;
        case GL_TRANSFORM_FEEDBACK_VARYINGS:
            *params = clampCast<GLint>(
                program->getExecutable().getLinkedTransformFeedbackVaryings().size());
            break;
        case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH:
            *params = program->getExecutable().getTransformFeedbackVaryingMaxLength();
            break;
        case GL_PROGRAM_BINARY_RETRIEVABLE_HINT:
            *params = program->getBinaryRetrievableHint();
            break;
        case GL_PROGRAM_SEPARABLE:
            // From es31cSeparateShaderObjsTests.cpp:
            // ProgramParameteri PROGRAM_SEPARABLE
            // NOTE: The query for PROGRAM_SEPARABLE must query latched
            //       state. In other words, the state of the binary after
            //       it was linked. So in the tests below, the queries
            //       should return the default state GL_FALSE since the
            //       program has no linked binary.
            *params = program->isSeparable() && program->isLinked();
            break;
        case GL_COMPUTE_WORK_GROUP_SIZE:
        {
            const sh::WorkGroupSize &localSize =
                program->getExecutable().getComputeShaderLocalSize();
            params[0] = localSize[0];
            params[1] = localSize[1];
            params[2] = localSize[2];
        }
        break;
        case GL_ACTIVE_ATOMIC_COUNTER_BUFFERS:
            *params = static_cast<GLint>(program->getExecutable().getAtomicCounterBuffers().size());
            break;
        case GL_GEOMETRY_LINKED_INPUT_TYPE_EXT:
            *params = ToGLenum(program->getExecutable().getGeometryShaderInputPrimitiveType());
            break;
        case GL_GEOMETRY_LINKED_OUTPUT_TYPE_EXT:
            *params = ToGLenum(program->getExecutable().getGeometryShaderOutputPrimitiveType());
            break;
        case GL_GEOMETRY_LINKED_VERTICES_OUT_EXT:
            *params = program->getExecutable().getGeometryShaderMaxVertices();
            break;
        case GL_GEOMETRY_SHADER_INVOCATIONS_EXT:
            *params = program->getExecutable().getGeometryShaderInvocations();
            break;
        case GL_TESS_CONTROL_OUTPUT_VERTICES_EXT:
            *params = program->getExecutable().getTessControlShaderVertices();
            break;
        case GL_TESS_GEN_MODE_EXT:
            *params = program->getExecutable().getTessGenMode();
            break;
        case GL_TESS_GEN_SPACING_EXT:
            *params = program->getExecutable().getTessGenSpacing()
                          ? program->getExecutable().getTessGenSpacing()
                          : GL_EQUAL;
            break;
        case GL_TESS_GEN_VERTEX_ORDER:
            *params = program->getExecutable().getTessGenVertexOrder()
                          ? program->getExecutable().getTessGenVertexOrder()
                          : GL_CCW;
            break;
        case GL_TESS_GEN_POINT_MODE_EXT:
            *params = program->getExecutable().getTessGenPointMode() ? GL_TRUE : GL_FALSE;
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void QueryRenderbufferiv(const Context *context,
                         const Renderbuffer *renderbuffer,
                         GLenum pname,
                         GLint *params)
{
    ASSERT(renderbuffer != nullptr);

    switch (pname)
    {
        case GL_RENDERBUFFER_WIDTH:
            *params = renderbuffer->getWidth();
            break;
        case GL_RENDERBUFFER_HEIGHT:
            *params = renderbuffer->getHeight();
            break;
        case GL_RENDERBUFFER_INTERNAL_FORMAT:
            // Special case the WebGL 1 DEPTH_STENCIL format.
            if (context->isWebGL1() &&
                renderbuffer->getFormat().info->internalFormat == GL_DEPTH24_STENCIL8)
            {
                *params = GL_DEPTH_STENCIL;
            }
            else
            {
                *params = (renderbuffer->getFormat().info->internalFormat == GL_NONE)
                              ? GL_RGBA4
                              : renderbuffer->getFormat().info->internalFormat;
            }
            break;
        case GL_RENDERBUFFER_RED_SIZE:
            *params = renderbuffer->getRedSize();
            break;
        case GL_RENDERBUFFER_GREEN_SIZE:
            *params = renderbuffer->getGreenSize();
            break;
        case GL_RENDERBUFFER_BLUE_SIZE:
            *params = renderbuffer->getBlueSize();
            break;
        case GL_RENDERBUFFER_ALPHA_SIZE:
            *params = renderbuffer->getAlphaSize();
            break;
        case GL_RENDERBUFFER_DEPTH_SIZE:
            *params = renderbuffer->getDepthSize();
            break;
        case GL_RENDERBUFFER_STENCIL_SIZE:
            *params = renderbuffer->getStencilSize();
            break;
        case GL_RENDERBUFFER_SAMPLES_ANGLE:
            *params = renderbuffer->getState().getSamples();
            break;
        case GL_MEMORY_SIZE_ANGLE:
            *params = renderbuffer->getMemorySize();
            break;
        case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
            *params = static_cast<GLint>(renderbuffer->getImplementationColorReadFormat(context));
            break;
        case GL_IMPLEMENTATION_COLOR_READ_TYPE:
            *params = static_cast<GLint>(renderbuffer->getImplementationColorReadType(context));
            break;
        case GL_RESOURCE_INITIALIZED_ANGLE:
            *params = (renderbuffer->initState(GL_NONE, ImageIndex()) == InitState::Initialized);
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void QueryShaderiv(const Context *context,
                   Shader *shader,
                   ShaderParameter pnamePacked,
                   GLint *params)
{
    ASSERT(shader != nullptr || pnamePacked == ShaderParameter::CompletionStatus);

    switch (pnamePacked)
    {
        case ShaderParameter::ShaderType:
            *params = static_cast<GLint>(ToGLenum(shader->getType()));
            break;
        case ShaderParameter::DeleteStatus:
            *params = shader->isFlaggedForDeletion() ? GL_TRUE : GL_FALSE;
            break;
        case ShaderParameter::CompileStatus:
            *params = shader->isCompiled(context) ? GL_TRUE : GL_FALSE;
            break;
        case ShaderParameter::InfoLogLength:
            *params = shader->getInfoLogLength(context);
            break;
        case ShaderParameter::ShaderSourceLength:
            *params = shader->getSourceLength();
            break;
        case ShaderParameter::CompletionStatus:
            if (context->isContextLost())
            {
                context->contextLostErrorOnBlockingCall(angle::EntryPoint::GLGetShaderiv);
                *params = GL_TRUE;
            }
            else
            {
                *params = shader->isCompleted() ? GL_TRUE : GL_FALSE;
            }
            break;
        case ShaderParameter::TranslatedShaderSourceLength:
            *params = shader->getTranslatedSourceWithDebugInfoLength(context);
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void QueryTexLevelParameterfv(const Texture *texture,
                              TextureTarget targetPacked,
                              GLint level,
                              TextureImageParameter pnamePacked,
                              GLfloat *params)
{
    QueryTexLevelParameterBase(texture, targetPacked, level, pnamePacked, params);
}

void QueryTexLevelParameteriv(const Texture *texture,
                              TextureTarget targetPacked,
                              GLint level,
                              TextureImageParameter pnamePacked,
                              GLint *params)
{
    QueryTexLevelParameterBase(texture, targetPacked, level, pnamePacked, params);
}

void QueryTexParameterfv(const Context *context,
                         const Texture *texture,
                         GLenum pname,
                         GLfloat *params)
{
    QueryTexParameterBase<false, false>(context, texture, pname, params);
}

void QueryTexParameterxv(const Context *context,
                         const Texture *texture,
                         GLenum pname,
                         GLfixed *params)
{
    QueryTexParameterBase<false, true>(context, texture, pname, params);
}

void QueryTexParameteriv(const Context *context,
                         const Texture *texture,
                         GLenum pname,
                         GLint *params)
{
    QueryTexParameterBase<false, false>(context, texture, pname, params);
}

void QueryTexParameterIiv(const Context *context,
                          const Texture *texture,
                          GLenum pname,
                          GLint *params)
{
    QueryTexParameterBase<true, false>(context, texture, pname, params);
}

void QueryTexParameterIuiv(const Context *context,
                           const Texture *texture,
                           GLenum pname,
                           GLuint *params)
{
    QueryTexParameterBase<true, false>(context, texture, pname, params);
}

void QuerySamplerParameterfv(const Sampler *sampler, SamplerParameter pnamePacked, GLfloat *params)
{
    QuerySamplerParameterBase<false>(sampler, pnamePacked, params);
}

void QuerySamplerParameteriv(const Sampler *sampler, SamplerParameter pnamePacked, GLint *params)
{
    QuerySamplerParameterBase<false>(sampler, pnamePacked, params);
}

void QuerySamplerParameterIiv(const Sampler *sampler, SamplerParameter pnamePacked, GLint *params)
{
    QuerySamplerParameterBase<true>(sampler, pnamePacked, params);
}

void QuerySamplerParameterIuiv(const Sampler *sampler, SamplerParameter pnamePacked, GLuint *params)
{
    QuerySamplerParameterBase<true>(sampler, pnamePacked, params);
}

void QueryVertexAttribfv(const VertexAttribute &attrib,
                         const VertexBinding &binding,
                         const Buffer *buffer,
                         const VertexAttribCurrentValueData &currentValueData,
                         GLenum pname,
                         GLfloat *params)
{
    QueryVertexAttribBase(attrib, binding, buffer, currentValueData.Values.FloatValues, pname,
                          params);
}

void QueryVertexAttribiv(const VertexAttribute &attrib,
                         const VertexBinding &binding,
                         const Buffer *buffer,
                         const VertexAttribCurrentValueData &currentValueData,
                         GLenum pname,
                         GLint *params)
{
    QueryVertexAttribBase(attrib, binding, buffer, currentValueData.Values.FloatValues, pname,
                          params);
}

void QueryVertexAttribIiv(const VertexAttribute &attrib,
                          const VertexBinding &binding,
                          const Buffer *buffer,
                          const VertexAttribCurrentValueData &currentValueData,
                          GLenum pname,
                          GLint *params)
{
    QueryVertexAttribBase(attrib, binding, buffer, currentValueData.Values.IntValues, pname,
                          params);
}

void QueryVertexAttribIuiv(const VertexAttribute &attrib,
                           const VertexBinding &binding,
                           const Buffer *buffer,
                           const VertexAttribCurrentValueData &currentValueData,
                           GLenum pname,
                           GLuint *params)
{
    QueryVertexAttribBase(attrib, binding, buffer, currentValueData.Values.UnsignedIntValues, pname,
                          params);
}

void QueryActiveUniformBlockiv(const Program *program,
                               UniformBlockIndex uniformBlockIndexPacked,
                               UniformBlockParameter pnamePacked,
                               GLsizei *length,
                               GLint *params)
{
    const GLenum prop = GetUniformBlockPropertyEnum(pnamePacked);
    QueryProgramResourceiv(program, GL_UNIFORM_BLOCK, uniformBlockIndexPacked, 1, &prop,
                           std::numeric_limits<GLsizei>::max(), length, params);
}

void QueryInternalFormativ(const Context *context,
                           const Texture *texture,
                           GLenum internalformat,
                           const TextureCaps &format,
                           GLenum pname,
                           GLsizei count,
                           GLint *params)
{
    if (count < 1)
    {
        return;
    }

    switch (pname)
    {
        case GL_NUM_SAMPLE_COUNTS:
            *params = clampCast<GLint>(format.sampleCounts.size());
            break;

        case GL_SAMPLES:
        {
            size_t returnCount   = std::min<size_t>(count, format.sampleCounts.size());
            auto sampleCounts    = format.sampleCounts.sampleCounts();
            auto sampleReverseIt = sampleCounts.rbegin();
            for (size_t sampleIndex = 0; sampleIndex < returnCount; ++sampleIndex)
            {
                params[sampleIndex] = *sampleReverseIt++;
            }
        }
        break;

        case GL_NUM_SURFACE_COMPRESSION_FIXED_RATES_EXT:
            if (texture != nullptr)
            {
                *params = texture->getFormatSupportedCompressionRates(context, internalformat,
                                                                      count, nullptr);
            }
            break;

        case GL_SURFACE_COMPRESSION_EXT:
            if (texture != nullptr)
            {
                texture->getFormatSupportedCompressionRates(context, internalformat, count, params);
            }
            break;

        default:
            UNREACHABLE();
            break;
    }
}

void QueryFramebufferParameteriv(const Framebuffer *framebuffer,
                                 FramebufferParameter pnamePacked,
                                 GLint *params)
{
    ASSERT(framebuffer != nullptr);

    switch (pnamePacked)
    {
        case FramebufferParameter::DefaultWidth:
            *params = framebuffer->getDefaultWidth();
            break;
        case FramebufferParameter::DefaultHeight:
            *params = framebuffer->getDefaultHeight();
            break;
        case FramebufferParameter::DefaultLayers:
            *params = framebuffer->getDefaultLayers();
            break;
        case FramebufferParameter::DefaultSamples:
            *params = framebuffer->getDefaultSamples();
            break;
        case FramebufferParameter::DefaultFixedSampleLocations:
            *params = ConvertToGLBoolean(framebuffer->getDefaultFixedSampleLocations());
            break;
        case FramebufferParameter::FlipY:
            *params = ConvertToGLBoolean(framebuffer->getFlipY());
            break;
        default:
            UNREACHABLE();
            break;
    }
}

template <typename ParamType>
void QueryFramebufferPixelLocalStorageParameterBase(Context *context,
                                                    GLint plane,
                                                    PlaneParameter pnamePacked,
                                                    GLsizei *length,
                                                    ParamType *params)
{
    const PixelLocalStoragePlane &planeObject =
        context->getState().getDrawFramebuffer()->getPixelLocalStorage(context).getPlane(plane);
    switch (pnamePacked)
    {
        case PlaneParameter::InternalFormat:
            *params = clampCast<ParamType>(planeObject.getInternalformat());
            break;
        case PlaneParameter::TextureName:
            *params = clampCast<ParamType>(planeObject.getTextureName());
            break;
        case PlaneParameter::TextureLevel:
            *params = clampCast<ParamType>(planeObject.getTextureLevel());
            break;
        case PlaneParameter::TextureLayer:
            *params = clampCast<ParamType>(planeObject.getTextureLayer());
            break;
        case PlaneParameter::Usage:
            *params = clampCast<ParamType>(planeObject.getUsage());
            break;
        case PlaneParameter::ClearValueFloat:
        {
            GLfloat p[4];
            planeObject.getClearValuef(p);
            params[0] = clampCast<ParamType>(p[0]);
            params[1] = clampCast<ParamType>(p[1]);
            params[2] = clampCast<ParamType>(p[2]);
            params[3] = clampCast<ParamType>(p[3]);
            break;
        }
        case PlaneParameter::ClearValueInt:
        {
            GLint p[4];
            planeObject.getClearValuei(p);
            params[0] = clampCast<ParamType>(p[0]);
            params[1] = clampCast<ParamType>(p[1]);
            params[2] = clampCast<ParamType>(p[2]);
            params[3] = clampCast<ParamType>(p[3]);
            break;
        }
        case PlaneParameter::ClearValueUnsignedInt:
        {
            GLuint p[4];
            planeObject.getClearValueui(p);
            params[0] = clampCast<ParamType>(p[0]);
            params[1] = clampCast<ParamType>(p[1]);
            params[2] = clampCast<ParamType>(p[2]);
            params[3] = clampCast<ParamType>(p[3]);
            break;
        }
        default:
            UNREACHABLE();
            break;
    }

    if (length != nullptr)
    {
        switch (pnamePacked)
        {
            case PlaneParameter::ClearValueFloat:
            case PlaneParameter::ClearValueInt:
            case PlaneParameter::ClearValueUnsignedInt:
                *length = 4;
                break;
            default:
                *length = 1;
                break;
        }
    }
}

void QueryFramebufferPixelLocalStorageParameterfv(Context *context,
                                                  GLint plane,
                                                  PlaneParameter pnamePacked,
                                                  GLsizei *length,
                                                  GLfloat *params)
{
    QueryFramebufferPixelLocalStorageParameterBase(context, plane, pnamePacked, length, params);
}

void QueryFramebufferPixelLocalStorageParameteriv(Context *context,
                                                  GLint plane,
                                                  PlaneParameter pnamePacked,
                                                  GLsizei *length,
                                                  GLint *params)
{
    QueryFramebufferPixelLocalStorageParameterBase(context, plane, pnamePacked, length, params);
}

void QueryFramebufferPixelLocalStorageParameteruiv(Context *context,
                                                   GLint plane,
                                                   PlaneParameter pnamePacked,
                                                   GLsizei *length,
                                                   GLuint *params)
{
    QueryFramebufferPixelLocalStorageParameterBase(context, plane, pnamePacked, length, params);
}

angle::Result QuerySynciv(const Context *context,
                          const Sync *sync,
                          GLenum pname,
                          GLsizei count,
                          GLsizei *length,
                          GLint *values)
{
    ASSERT(sync != nullptr || pname == GL_SYNC_STATUS);

    // All queries return one value, exit early if the requested count is less.
    if (count < 1)
    {
        if (length != nullptr)
        {
            *length = 0;
        }
        return angle::Result::Continue;
    }

    switch (pname)
    {
        case GL_OBJECT_TYPE:
            *values = clampCast<GLint>(GL_SYNC_FENCE);
            break;
        case GL_SYNC_CONDITION:
            *values = clampCast<GLint>(sync->getCondition());
            break;
        case GL_SYNC_FLAGS:
            *values = clampCast<GLint>(sync->getFlags());
            break;
        case GL_SYNC_STATUS:
            if (context->isContextLost())
            {
                context->contextLostErrorOnBlockingCall(angle::EntryPoint::GLGetSynciv);
                *values = GL_SIGNALED;
            }
            else
            {
                ANGLE_TRY(sync->getStatus(context, values));
            }
            break;

        default:
            UNREACHABLE();
            break;
    }

    if (length != nullptr)
    {
        *length = 1;
    }

    return angle::Result::Continue;
}

void SetTexParameterx(Context *context, Texture *texture, GLenum pname, GLfixed param)
{
    SetTexParameterBase<false, true>(context, texture, pname, &param);
}

void SetTexParameterxv(Context *context, Texture *texture, GLenum pname, const GLfixed *params)
{
    SetTexParameterBase<false, true>(context, texture, pname, params);
}

void SetTexParameterf(Context *context, Texture *texture, GLenum pname, GLfloat param)
{
    SetTexParameterBase<false, false>(context, texture, pname, &param);
}

void SetTexParameterfv(Context *context, Texture *texture, GLenum pname, const GLfloat *params)
{
    SetTexParameterBase<false, false>(context, texture, pname, params);
}

void SetTexParameteri(Context *context, Texture *texture, GLenum pname, GLint param)
{
    SetTexParameterBase<false, false>(context, texture, pname, &param);
}

void SetTexParameteriv(Context *context, Texture *texture, GLenum pname, const GLint *params)
{
    SetTexParameterBase<false, false>(context, texture, pname, params);
}

void SetTexParameterIiv(Context *context, Texture *texture, GLenum pname, const GLint *params)
{
    SetTexParameterBase<true, false>(context, texture, pname, params);
}

void SetTexParameterIuiv(Context *context, Texture *texture, GLenum pname, const GLuint *params)
{
    SetTexParameterBase<true, false>(context, texture, pname, params);
}

void SetSamplerParameterfv(Context *context,
                           Sampler *sampler,
                           SamplerParameter pnamePacked,
                           const GLfloat *params)
{
    SetSamplerParameterBase<false>(context, sampler, pnamePacked, params);
}

void SetSamplerParameteriv(Context *context,
                           Sampler *sampler,
                           SamplerParameter pnamePacked,
                           const GLint *params)
{
    SetSamplerParameterBase<false>(context, sampler, pnamePacked, params);
}

void SetSamplerParameterIiv(Context *context,
                            Sampler *sampler,
                            SamplerParameter pnamePacked,
                            const GLint *params)
{
    SetSamplerParameterBase<true>(context, sampler, pnamePacked, params);
}

void SetSamplerParameterIuiv(Context *context,
                             Sampler *sampler,
                             SamplerParameter pnamePacked,
                             const GLuint *params)
{
    SetSamplerParameterBase<true>(context, sampler, pnamePacked, params);
}

void SetFramebufferParameteri(const Context *context,
                              Framebuffer *framebuffer,
                              FramebufferParameter pnamePacked,
                              GLint param)
{
    ASSERT(framebuffer != nullptr);

    switch (pnamePacked)
    {
        case FramebufferParameter::DefaultWidth:
            framebuffer->setDefaultWidth(context, param);
            break;
        case FramebufferParameter::DefaultHeight:
            framebuffer->setDefaultHeight(context, param);
            break;
        case FramebufferParameter::DefaultLayers:
            framebuffer->setDefaultLayers(param);
            break;
        case FramebufferParameter::DefaultSamples:
            framebuffer->setDefaultSamples(context, param);
            break;
        case FramebufferParameter::DefaultFixedSampleLocations:
            framebuffer->setDefaultFixedSampleLocations(context, ConvertToBool(param));
            break;
        case FramebufferParameter::FlipY:
            framebuffer->setFlipY(ConvertToBool(param));
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void SetProgramParameteri(const Context *context, Program *program, GLenum pname, GLint value)
{
    ASSERT(program);

    switch (pname)
    {
        case GL_PROGRAM_BINARY_RETRIEVABLE_HINT:
            program->setBinaryRetrievableHint(ConvertToBool(value));
            break;
        case GL_PROGRAM_SEPARABLE:
            program->setSeparable(context, ConvertToBool(value));
            break;
        default:
            UNREACHABLE();
            break;
    }
}

GLint GetUniformResourceProperty(const Program *program, GLuint index, const GLenum prop)
{
    const ProgramExecutable &executable = program->getExecutable();
    const LinkedUniform &uniform        = executable.getUniformByIndex(index);

    GLenum resourceProp = GetUniformPropertyEnum(prop);
    switch (resourceProp)
    {
        case GL_TYPE:
            return clampCast<GLint>(uniform.getType());

        case GL_ARRAY_SIZE:
            return clampCast<GLint>(uniform.getBasicTypeElementCount());

        case GL_NAME_LENGTH:
            return clampCast<GLint>(executable.getUniformNameByIndex(index).size() + 1u);

        case GL_LOCATION:
            return executable.getUniformLocation(executable.getUniformNameByIndex(index)).value;

        case GL_BLOCK_INDEX:
            return (uniform.isAtomicCounter() ? -1 : uniform.getBufferIndex());

        case GL_OFFSET:
            return uniform.pod.flagBits.isBlock ? uniform.pod.blockOffset : -1;

        case GL_ARRAY_STRIDE:
            return uniform.pod.flagBits.isBlock ? uniform.pod.blockArrayStride : -1;

        case GL_MATRIX_STRIDE:
            return uniform.pod.flagBits.isBlock ? uniform.pod.blockMatrixStride : -1;

        case GL_IS_ROW_MAJOR:
            return uniform.pod.flagBits.blockIsRowMajorMatrix ? 1 : 0;

        case GL_REFERENCED_BY_VERTEX_SHADER:
            return uniform.isActive(ShaderType::Vertex);

        case GL_REFERENCED_BY_FRAGMENT_SHADER:
            return uniform.isActive(ShaderType::Fragment);

        case GL_REFERENCED_BY_COMPUTE_SHADER:
            return uniform.isActive(ShaderType::Compute);

        case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT:
            return uniform.isActive(ShaderType::Geometry);

        case GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT:
            return uniform.isActive(ShaderType::TessControl);

        case GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT:
            return uniform.isActive(ShaderType::TessEvaluation);

        case GL_ATOMIC_COUNTER_BUFFER_INDEX:
            return (uniform.isAtomicCounter() ? uniform.getBufferIndex() : -1);

        default:
            UNREACHABLE();
            return 0;
    }
}

GLint GetBufferVariableResourceProperty(const Program *program, GLuint index, const GLenum prop)
{
    const ProgramExecutable &executable  = program->getExecutable();
    const BufferVariable &bufferVariable = executable.getBufferVariableByIndex(index);

    switch (prop)
    {
        case GL_TYPE:
        case GL_ARRAY_SIZE:
        case GL_NAME_LENGTH:
            return GetCommonVariableProperty(bufferVariable, prop);

        case GL_BLOCK_INDEX:
            return bufferVariable.pod.bufferIndex;

        case GL_OFFSET:
            return bufferVariable.pod.blockInfo.offset;

        case GL_ARRAY_STRIDE:
            return bufferVariable.pod.blockInfo.arrayStride;

        case GL_MATRIX_STRIDE:
            return bufferVariable.pod.blockInfo.matrixStride;

        case GL_IS_ROW_MAJOR:
            return static_cast<GLint>(bufferVariable.pod.blockInfo.isRowMajorMatrix);

        case GL_REFERENCED_BY_VERTEX_SHADER:
            return bufferVariable.isActive(ShaderType::Vertex);

        case GL_REFERENCED_BY_FRAGMENT_SHADER:
            return bufferVariable.isActive(ShaderType::Fragment);

        case GL_REFERENCED_BY_COMPUTE_SHADER:
            return bufferVariable.isActive(ShaderType::Compute);

        case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT:
            return bufferVariable.isActive(ShaderType::Geometry);

        case GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT:
            return bufferVariable.isActive(ShaderType::TessControl);

        case GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT:
            return bufferVariable.isActive(ShaderType::TessEvaluation);

        case GL_TOP_LEVEL_ARRAY_SIZE:
            return bufferVariable.pod.topLevelArraySize;

        case GL_TOP_LEVEL_ARRAY_STRIDE:
            return bufferVariable.pod.blockInfo.topLevelArrayStride;

        default:
            UNREACHABLE();
            return 0;
    }
}

GLuint QueryProgramResourceIndex(const Program *program,
                                 GLenum programInterface,
                                 const GLchar *name)
{
    const ProgramExecutable &executable = program->getExecutable();

    switch (programInterface)
    {
        case GL_PROGRAM_INPUT:
            return executable.getInputResourceIndex(name);

        case GL_PROGRAM_OUTPUT:
            return executable.getOutputResourceIndex(name);

        case GL_UNIFORM:
            return executable.getUniformIndexFromName(name);

        case GL_BUFFER_VARIABLE:
            return executable.getBufferVariableIndexFromName(name);

        case GL_SHADER_STORAGE_BLOCK:
            return executable.getShaderStorageBlockIndex(name);

        case GL_UNIFORM_BLOCK:
            return executable.getUniformBlockIndex(name);

        case GL_TRANSFORM_FEEDBACK_VARYING:
            return executable.getTransformFeedbackVaryingResourceIndex(name);

        default:
            UNREACHABLE();
            return GL_INVALID_INDEX;
    }
}

void QueryProgramResourceName(const Context *context,
                              const Program *program,
                              GLenum programInterface,
                              GLuint index,
                              GLsizei bufSize,
                              GLsizei *length,
                              GLchar *name)
{
    const ProgramExecutable &executable = program->getExecutable();

    switch (programInterface)
    {
        case GL_PROGRAM_INPUT:
            executable.getInputResourceName(index, bufSize, length, name);
            break;

        case GL_PROGRAM_OUTPUT:
            executable.getOutputResourceName(index, bufSize, length, name);
            break;

        case GL_UNIFORM:
            executable.getUniformResourceName(index, bufSize, length, name);
            break;

        case GL_BUFFER_VARIABLE:
            executable.getBufferVariableResourceName(index, bufSize, length, name);
            break;

        case GL_SHADER_STORAGE_BLOCK:
            executable.getActiveShaderStorageBlockName(index, bufSize, length, name);
            break;

        case GL_UNIFORM_BLOCK:
            executable.getActiveUniformBlockName(context, {index}, bufSize, length, name);
            break;

        case GL_TRANSFORM_FEEDBACK_VARYING:
            executable.getTransformFeedbackVarying(index, bufSize, length, nullptr, nullptr, name);
            break;

        default:
            UNREACHABLE();
    }
}

GLint QueryProgramResourceLocation(const Program *program,
                                   GLenum programInterface,
                                   const GLchar *name)
{
    const ProgramExecutable &executable = program->getExecutable();

    switch (programInterface)
    {
        case GL_PROGRAM_INPUT:
            return executable.getInputResourceLocation(name);

        case GL_PROGRAM_OUTPUT:
            return executable.getOutputResourceLocation(name);

        case GL_UNIFORM:
            return executable.getUniformLocation(name).value;

        default:
            UNREACHABLE();
            return -1;
    }
}

void QueryProgramResourceiv(const Program *program,
                            GLenum programInterface,
                            UniformBlockIndex index,
                            GLsizei propCount,
                            const GLenum *props,
                            GLsizei count,
                            GLsizei *length,
                            GLint *params)
{
    if (!program->isLinked())
    {
        return;
    }

    if (length != nullptr)
    {
        *length = 0;
    }

    if (count < 1)
    {
        return;
    }

    GLsizei pos = 0;
    for (GLsizei i = 0; i < propCount; i++)
    {
        switch (programInterface)
        {
            case GL_PROGRAM_INPUT:
                params[i] = GetInputResourceProperty(program, index.value, props[i]);
                ++pos;
                break;

            case GL_PROGRAM_OUTPUT:
                params[i] = GetOutputResourceProperty(program, index.value, props[i]);
                ++pos;
                break;

            case GL_UNIFORM:
                params[i] = GetUniformResourceProperty(program, index.value, props[i]);
                ++pos;
                break;

            case GL_BUFFER_VARIABLE:
                params[i] = GetBufferVariableResourceProperty(program, index.value, props[i]);
                ++pos;
                break;

            case GL_UNIFORM_BLOCK:
                GetUniformBlockResourceProperty(program, index.value, props[i], params, count,
                                                &pos);
                break;

            case GL_SHADER_STORAGE_BLOCK:
                GetShaderStorageBlockResourceProperty(program, index.value, props[i], params, count,
                                                      &pos);
                break;

            case GL_ATOMIC_COUNTER_BUFFER:
                GetAtomicCounterBufferResourceProperty(program, index.value, props[i], params,
                                                       count, &pos);
                break;

            case GL_TRANSFORM_FEEDBACK_VARYING:
                params[i] =
                    GetTransformFeedbackVaryingResourceProperty(program, index.value, props[i]);
                ++pos;
                break;

            default:
                UNREACHABLE();
                params[i] = GL_INVALID_VALUE;
        }
        if (pos == count)
        {
            // Most properties return one value, but GL_ACTIVE_VARIABLES returns an array of values.
            // This checks not to break buffer bounds for such case.
            break;
        }
    }

    if (length != nullptr)
    {
        *length = pos;
    }
}

void QueryProgramInterfaceiv(const Program *program,
                             GLenum programInterface,
                             GLenum pname,
                             GLint *params)
{
    switch (pname)
    {
        case GL_ACTIVE_RESOURCES:
            *params = QueryProgramInterfaceActiveResources(program, programInterface);
            break;

        case GL_MAX_NAME_LENGTH:
            *params = QueryProgramInterfaceMaxNameLength(program, programInterface);
            break;

        case GL_MAX_NUM_ACTIVE_VARIABLES:
            *params = QueryProgramInterfaceMaxNumActiveVariables(program, programInterface);
            break;

        default:
            UNREACHABLE();
    }
}

angle::Result SetMemoryObjectParameteriv(const Context *context,
                                         MemoryObject *memoryObject,
                                         GLenum pname,
                                         const GLint *params)
{
    switch (pname)
    {
        case GL_DEDICATED_MEMORY_OBJECT_EXT:
            ANGLE_TRY(memoryObject->setDedicatedMemory(context, ConvertToBool(params[0])));
            break;

        case GL_PROTECTED_MEMORY_OBJECT_EXT:
            ANGLE_TRY(memoryObject->setProtectedMemory(context, ConvertToBool(params[0])));
            break;

        default:
            UNREACHABLE();
    }

    return angle::Result::Continue;
}

void QueryMemoryObjectParameteriv(const MemoryObject *memoryObject, GLenum pname, GLint *params)
{
    switch (pname)
    {
        case GL_DEDICATED_MEMORY_OBJECT_EXT:
            *params = memoryObject->isDedicatedMemory();
            break;

        case GL_PROTECTED_MEMORY_OBJECT_EXT:
            *params = memoryObject->isProtectedMemory();
            break;

        default:
            UNREACHABLE();
    }
}

ClientVertexArrayType ParamToVertexArrayType(GLenum param)
{
    switch (param)
    {
        case GL_VERTEX_ARRAY:
        case GL_VERTEX_ARRAY_BUFFER_BINDING:
        case GL_VERTEX_ARRAY_STRIDE:
        case GL_VERTEX_ARRAY_SIZE:
        case GL_VERTEX_ARRAY_TYPE:
        case GL_VERTEX_ARRAY_POINTER:
            return ClientVertexArrayType::Vertex;
        case GL_NORMAL_ARRAY:
        case GL_NORMAL_ARRAY_BUFFER_BINDING:
        case GL_NORMAL_ARRAY_STRIDE:
        case GL_NORMAL_ARRAY_TYPE:
        case GL_NORMAL_ARRAY_POINTER:
            return ClientVertexArrayType::Normal;
        case GL_COLOR_ARRAY:
        case GL_COLOR_ARRAY_BUFFER_BINDING:
        case GL_COLOR_ARRAY_STRIDE:
        case GL_COLOR_ARRAY_SIZE:
        case GL_COLOR_ARRAY_TYPE:
        case GL_COLOR_ARRAY_POINTER:
            return ClientVertexArrayType::Color;
        case GL_POINT_SIZE_ARRAY_OES:
        case GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES:
        case GL_POINT_SIZE_ARRAY_STRIDE_OES:
        case GL_POINT_SIZE_ARRAY_TYPE_OES:
        case GL_POINT_SIZE_ARRAY_POINTER_OES:
            return ClientVertexArrayType::PointSize;
        case GL_TEXTURE_COORD_ARRAY:
        case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING:
        case GL_TEXTURE_COORD_ARRAY_STRIDE:
        case GL_TEXTURE_COORD_ARRAY_SIZE:
        case GL_TEXTURE_COORD_ARRAY_TYPE:
        case GL_TEXTURE_COORD_ARRAY_POINTER:
            return ClientVertexArrayType::TextureCoord;
        default:
            UNREACHABLE();
            return ClientVertexArrayType::InvalidEnum;
    }
}

void SetLightModelParameters(GLES1State *state, GLenum pname, const GLfloat *params)
{
    LightModelParameters &lightModel = state->lightModelParameters();

    switch (pname)
    {
        case GL_LIGHT_MODEL_AMBIENT:
            lightModel.color = ColorF::fromData(params);
            break;
        case GL_LIGHT_MODEL_TWO_SIDE:
            lightModel.twoSided = *params == 1.0f ? true : false;
            break;
        default:
            break;
    }
}

void GetLightModelParameters(const GLES1State *state, GLenum pname, GLfloat *params)
{
    const LightModelParameters &lightModel = state->lightModelParameters();

    switch (pname)
    {
        case GL_LIGHT_MODEL_TWO_SIDE:
            *params = lightModel.twoSided ? 1.0f : 0.0f;
            break;
        case GL_LIGHT_MODEL_AMBIENT:
            lightModel.color.writeData(params);
            break;
        default:
            break;
    }
}

bool IsLightModelTwoSided(const GLES1State *state)
{
    return state->lightModelParameters().twoSided;
}

void SetLightParameters(GLES1State *state,
                        GLenum light,
                        LightParameter pname,
                        const GLfloat *params)
{
    uint32_t lightIndex = light - GL_LIGHT0;

    LightParameters &lightParams = state->lightParameters(lightIndex);

    switch (pname)
    {
        case LightParameter::Ambient:
            lightParams.ambient = ColorF::fromData(params);
            break;
        case LightParameter::Diffuse:
            lightParams.diffuse = ColorF::fromData(params);
            break;
        case LightParameter::Specular:
            lightParams.specular = ColorF::fromData(params);
            break;
        case LightParameter::Position:
        {
            angle::Mat4 mv = state->getModelviewMatrix();
            angle::Vector4 transformedPos =
                mv.product(angle::Vector4(params[0], params[1], params[2], params[3]));
            lightParams.position[0] = transformedPos[0];
            lightParams.position[1] = transformedPos[1];
            lightParams.position[2] = transformedPos[2];
            lightParams.position[3] = transformedPos[3];
        }
        break;
        case LightParameter::SpotDirection:
        {
            angle::Mat4 mv = state->getModelviewMatrix();
            angle::Vector4 transformedPos =
                mv.product(angle::Vector4(params[0], params[1], params[2], 0.0f));
            lightParams.direction[0] = transformedPos[0];
            lightParams.direction[1] = transformedPos[1];
            lightParams.direction[2] = transformedPos[2];
        }
        break;
        case LightParameter::SpotExponent:
            lightParams.spotlightExponent = *params;
            break;
        case LightParameter::SpotCutoff:
            lightParams.spotlightCutoffAngle = *params;
            break;
        case LightParameter::ConstantAttenuation:
            lightParams.attenuationConst = *params;
            break;
        case LightParameter::LinearAttenuation:
            lightParams.attenuationLinear = *params;
            break;
        case LightParameter::QuadraticAttenuation:
            lightParams.attenuationQuadratic = *params;
            break;
        default:
            return;
    }
}

void GetLightParameters(const GLES1State *state,
                        GLenum light,
                        LightParameter pname,
                        GLfloat *params)
{
    uint32_t lightIndex                = light - GL_LIGHT0;
    const LightParameters &lightParams = state->lightParameters(lightIndex);

    switch (pname)
    {
        case LightParameter::Ambient:
            lightParams.ambient.writeData(params);
            break;
        case LightParameter::Diffuse:
            lightParams.diffuse.writeData(params);
            break;
        case LightParameter::Specular:
            lightParams.specular.writeData(params);
            break;
        case LightParameter::Position:
            memcpy(params, lightParams.position.data(), 4 * sizeof(GLfloat));
            break;
        case LightParameter::SpotDirection:
            memcpy(params, lightParams.direction.data(), 3 * sizeof(GLfloat));
            break;
        case LightParameter::SpotExponent:
            *params = lightParams.spotlightExponent;
            break;
        case LightParameter::SpotCutoff:
            *params = lightParams.spotlightCutoffAngle;
            break;
        case LightParameter::ConstantAttenuation:
            *params = lightParams.attenuationConst;
            break;
        case LightParameter::LinearAttenuation:
            *params = lightParams.attenuationLinear;
            break;
        case LightParameter::QuadraticAttenuation:
            *params = lightParams.attenuationQuadratic;
            break;
        default:
            break;
    }
}

void SetMaterialParameters(GLES1State *state,
                           GLenum face,
                           MaterialParameter pname,
                           const GLfloat *params)
{
    // Note: Ambient and diffuse colors are inherited from glColor when COLOR_MATERIAL is enabled,
    // and can only be modified by this function if that is disabled:
    //
    // > the replaced values remain until changed by either sending a new color or by setting a
    // > new material value when COLOR_MATERIAL is not currently enabled, to override that
    // particular value.

    MaterialParameters &material = state->materialParameters();
    switch (pname)
    {
        case MaterialParameter::Ambient:
            if (!state->isColorMaterialEnabled())
            {
                material.ambient = ColorF::fromData(params);
            }
            break;
        case MaterialParameter::Diffuse:
            if (!state->isColorMaterialEnabled())
            {
                material.diffuse = ColorF::fromData(params);
            }
            break;
        case MaterialParameter::AmbientAndDiffuse:
            if (!state->isColorMaterialEnabled())
            {
                material.ambient = ColorF::fromData(params);
                material.diffuse = ColorF::fromData(params);
            }
            break;
        case MaterialParameter::Specular:
            material.specular = ColorF::fromData(params);
            break;
        case MaterialParameter::Emission:
            material.emissive = ColorF::fromData(params);
            break;
        case MaterialParameter::Shininess:
            material.specularExponent = *params;
            break;
        default:
            return;
    }
}

void GetMaterialParameters(const GLES1State *state,
                           GLenum face,
                           MaterialParameter pname,
                           GLfloat *params)
{
    const ColorF &currentColor         = state->getCurrentColor();
    const MaterialParameters &material = state->materialParameters();
    const bool colorMaterialEnabled    = state->isColorMaterialEnabled();

    switch (pname)
    {
        case MaterialParameter::Ambient:
            if (colorMaterialEnabled)
            {
                currentColor.writeData(params);
            }
            else
            {
                material.ambient.writeData(params);
            }
            break;
        case MaterialParameter::Diffuse:
            if (colorMaterialEnabled)
            {
                currentColor.writeData(params);
            }
            else
            {
                material.diffuse.writeData(params);
            }
            break;
        case MaterialParameter::Specular:
            material.specular.writeData(params);
            break;
        case MaterialParameter::Emission:
            material.emissive.writeData(params);
            break;
        case MaterialParameter::Shininess:
            *params = material.specularExponent;
            break;
        default:
            return;
    }
}

unsigned int GetLightModelParameterCount(GLenum pname)
{
    switch (pname)
    {
        case GL_LIGHT_MODEL_AMBIENT:
            return 4;
        case GL_LIGHT_MODEL_TWO_SIDE:
            return 1;
        default:
            UNREACHABLE();
            return 0;
    }
}

unsigned int GetLightParameterCount(LightParameter pname)
{
    switch (pname)
    {
        case LightParameter::Ambient:
        case LightParameter::Diffuse:
        case LightParameter::AmbientAndDiffuse:
        case LightParameter::Specular:
        case LightParameter::Position:
            return 4;
        case LightParameter::SpotDirection:
            return 3;
        case LightParameter::SpotExponent:
        case LightParameter::SpotCutoff:
        case LightParameter::ConstantAttenuation:
        case LightParameter::LinearAttenuation:
        case LightParameter::QuadraticAttenuation:
            return 1;
        default:
            UNREACHABLE();
            return 0;
    }
}

unsigned int GetMaterialParameterCount(MaterialParameter pname)
{
    switch (pname)
    {
        case MaterialParameter::Ambient:
        case MaterialParameter::Diffuse:
        case MaterialParameter::AmbientAndDiffuse:
        case MaterialParameter::Specular:
        case MaterialParameter::Emission:
            return 4;
        case MaterialParameter::Shininess:
            return 1;
        default:
            UNREACHABLE();
            return 0;
    }
}

void SetFogParameters(GLES1State *state, GLenum pname, const GLfloat *params)
{
    FogParameters &fog = state->fogParameters();
    switch (pname)
    {
        case GL_FOG_MODE:
            fog.mode = FromGLenum<FogMode>(static_cast<GLenum>(params[0]));
            break;
        case GL_FOG_DENSITY:
            fog.density = params[0];
            break;
        case GL_FOG_START:
            fog.start = params[0];
            break;
        case GL_FOG_END:
            fog.end = params[0];
            break;
        case GL_FOG_COLOR:
            fog.color = ColorF::fromData(params);
            break;
        default:
            return;
    }
}

void GetFogParameters(const GLES1State *state, GLenum pname, GLfloat *params)
{
    const FogParameters &fog = state->fogParameters();
    switch (pname)
    {
        case GL_FOG_MODE:
            params[0] = static_cast<GLfloat>(ToGLenum(fog.mode));
            break;
        case GL_FOG_DENSITY:
            params[0] = fog.density;
            break;
        case GL_FOG_START:
            params[0] = fog.start;
            break;
        case GL_FOG_END:
            params[0] = fog.end;
            break;
        case GL_FOG_COLOR:
            fog.color.writeData(params);
            break;
        default:
            return;
    }
}

unsigned int GetFogParameterCount(GLenum pname)
{
    switch (pname)
    {
        case GL_FOG_MODE:
        case GL_FOG_DENSITY:
        case GL_FOG_START:
        case GL_FOG_END:
            return 1;
        case GL_FOG_COLOR:
            return 4;
        default:
            return 0;
    }
}

unsigned int GetTextureEnvParameterCount(TextureEnvParameter pname)
{
    switch (pname)
    {
        case TextureEnvParameter::Mode:
        case TextureEnvParameter::CombineRgb:
        case TextureEnvParameter::CombineAlpha:
        case TextureEnvParameter::Src0Rgb:
        case TextureEnvParameter::Src1Rgb:
        case TextureEnvParameter::Src2Rgb:
        case TextureEnvParameter::Src0Alpha:
        case TextureEnvParameter::Src1Alpha:
        case TextureEnvParameter::Src2Alpha:
        case TextureEnvParameter::Op0Rgb:
        case TextureEnvParameter::Op1Rgb:
        case TextureEnvParameter::Op2Rgb:
        case TextureEnvParameter::Op0Alpha:
        case TextureEnvParameter::Op1Alpha:
        case TextureEnvParameter::Op2Alpha:
        case TextureEnvParameter::RgbScale:
        case TextureEnvParameter::AlphaScale:
        case TextureEnvParameter::PointCoordReplace:
        case TextureEnvParameter::LodBias:
            return 1;
        case TextureEnvParameter::Color:
            return 4;
        default:
            return 0;
    }
}

void ConvertTextureEnvFromInt(TextureEnvParameter pname, const GLint *input, GLfloat *output)
{
    if (IsTextureEnvEnumParameter(pname))
    {
        ConvertGLenumValue(input[0], output);
        return;
    }

    switch (pname)
    {
        case TextureEnvParameter::RgbScale:
        case TextureEnvParameter::AlphaScale:
        case TextureEnvParameter::LodBias:
            output[0] = static_cast<GLfloat>(input[0]);
            break;
        case TextureEnvParameter::Color:
            for (int i = 0; i < 4; i++)
            {
                output[i] = input[i] / 255.0f;
            }
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void ConvertTextureEnvFromFixed(TextureEnvParameter pname, const GLfixed *input, GLfloat *output)
{
    if (IsTextureEnvEnumParameter(pname))
    {
        ConvertGLenumValue(input[0], output);
        return;
    }

    switch (pname)
    {
        case TextureEnvParameter::RgbScale:
        case TextureEnvParameter::AlphaScale:
        case TextureEnvParameter::LodBias:
            output[0] = ConvertFixedToFloat(input[0]);
            break;
        case TextureEnvParameter::Color:
            for (int i = 0; i < 4; i++)
            {
                output[i] = ConvertFixedToFloat(input[i]);
            }
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void ConvertTextureEnvToInt(TextureEnvParameter pname, const GLfloat *input, GLint *output)
{
    if (IsTextureEnvEnumParameter(pname))
    {
        ConvertGLenumValue(input[0], output);
        return;
    }

    switch (pname)
    {
        case TextureEnvParameter::RgbScale:
        case TextureEnvParameter::AlphaScale:
        case TextureEnvParameter::LodBias:
            output[0] = static_cast<GLint>(input[0]);
            break;
        case TextureEnvParameter::Color:
            for (int i = 0; i < 4; i++)
            {
                output[i] = static_cast<GLint>(input[i] * 255.0f);
            }
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void ConvertTextureEnvToFixed(TextureEnvParameter pname, const GLfloat *input, GLfixed *output)
{
    if (IsTextureEnvEnumParameter(pname))
    {
        ConvertGLenumValue(input[0], output);
        return;
    }

    switch (pname)
    {
        case TextureEnvParameter::RgbScale:
        case TextureEnvParameter::AlphaScale:
        case TextureEnvParameter::LodBias:
            output[0] = ConvertFloatToFixed(input[0]);
            break;
        case TextureEnvParameter::Color:
            for (int i = 0; i < 4; i++)
            {
                output[i] = ConvertFloatToFixed(input[i]);
            }
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void SetTextureEnv(unsigned int unit,
                   GLES1State *state,
                   TextureEnvTarget target,
                   TextureEnvParameter pname,
                   const GLfloat *params)
{
    TextureEnvironmentParameters &env = state->textureEnvironment(unit);
    GLenum asEnum                     = ConvertToGLenum(params[0]);

    switch (target)
    {
        case TextureEnvTarget::Env:
            switch (pname)
            {
                case TextureEnvParameter::Mode:
                    env.mode = FromGLenum<TextureEnvMode>(asEnum);
                    break;
                case TextureEnvParameter::CombineRgb:
                    env.combineRgb = FromGLenum<TextureCombine>(asEnum);
                    break;
                case TextureEnvParameter::CombineAlpha:
                    env.combineAlpha = FromGLenum<TextureCombine>(asEnum);
                    break;
                case TextureEnvParameter::Src0Rgb:
                    env.src0Rgb = FromGLenum<TextureSrc>(asEnum);
                    break;
                case TextureEnvParameter::Src1Rgb:
                    env.src1Rgb = FromGLenum<TextureSrc>(asEnum);
                    break;
                case TextureEnvParameter::Src2Rgb:
                    env.src2Rgb = FromGLenum<TextureSrc>(asEnum);
                    break;
                case TextureEnvParameter::Src0Alpha:
                    env.src0Alpha = FromGLenum<TextureSrc>(asEnum);
                    break;
                case TextureEnvParameter::Src1Alpha:
                    env.src1Alpha = FromGLenum<TextureSrc>(asEnum);
                    break;
                case TextureEnvParameter::Src2Alpha:
                    env.src2Alpha = FromGLenum<TextureSrc>(asEnum);
                    break;
                case TextureEnvParameter::Op0Rgb:
                    env.op0Rgb = FromGLenum<TextureOp>(asEnum);
                    break;
                case TextureEnvParameter::Op1Rgb:
                    env.op1Rgb = FromGLenum<TextureOp>(asEnum);
                    break;
                case TextureEnvParameter::Op2Rgb:
                    env.op2Rgb = FromGLenum<TextureOp>(asEnum);
                    break;
                case TextureEnvParameter::Op0Alpha:
                    env.op0Alpha = FromGLenum<TextureOp>(asEnum);
                    break;
                case TextureEnvParameter::Op1Alpha:
                    env.op1Alpha = FromGLenum<TextureOp>(asEnum);
                    break;
                case TextureEnvParameter::Op2Alpha:
                    env.op2Alpha = FromGLenum<TextureOp>(asEnum);
                    break;
                case TextureEnvParameter::Color:
                    env.color = ColorF::fromData(params);
                    break;
                case TextureEnvParameter::RgbScale:
                    env.rgbScale = params[0];
                    break;
                case TextureEnvParameter::AlphaScale:
                    env.alphaScale = params[0];
                    break;
                default:
                    UNREACHABLE();
                    break;
            }
            break;
        case TextureEnvTarget::PointSprite:
            switch (pname)
            {
                case TextureEnvParameter::PointCoordReplace:
                    env.pointSpriteCoordReplace = static_cast<bool>(params[0]);
                    break;
                default:
                    UNREACHABLE();
                    break;
            }
            break;
        case TextureEnvTarget::TextureFilterControl:
            switch (pname)
            {
                case TextureEnvParameter::LodBias:
                    env.lodBias = params[0];
                    break;
                default:
                    UNREACHABLE();
                    break;
            }
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void GetTextureEnv(unsigned int unit,
                   const GLES1State *state,
                   TextureEnvTarget target,
                   TextureEnvParameter pname,
                   GLfloat *params)
{
    const TextureEnvironmentParameters &env = state->textureEnvironment(unit);

    switch (target)
    {
        case TextureEnvTarget::Env:
            switch (pname)
            {
                case TextureEnvParameter::Mode:
                    ConvertPackedEnum(env.mode, params);
                    break;
                case TextureEnvParameter::CombineRgb:
                    ConvertPackedEnum(env.combineRgb, params);
                    break;
                case TextureEnvParameter::CombineAlpha:
                    ConvertPackedEnum(env.combineAlpha, params);
                    break;
                case TextureEnvParameter::Src0Rgb:
                    ConvertPackedEnum(env.src0Rgb, params);
                    break;
                case TextureEnvParameter::Src1Rgb:
                    ConvertPackedEnum(env.src1Rgb, params);
                    break;
                case TextureEnvParameter::Src2Rgb:
                    ConvertPackedEnum(env.src2Rgb, params);
                    break;
                case TextureEnvParameter::Src0Alpha:
                    ConvertPackedEnum(env.src0Alpha, params);
                    break;
                case TextureEnvParameter::Src1Alpha:
                    ConvertPackedEnum(env.src1Alpha, params);
                    break;
                case TextureEnvParameter::Src2Alpha:
                    ConvertPackedEnum(env.src2Alpha, params);
                    break;
                case TextureEnvParameter::Op0Rgb:
                    ConvertPackedEnum(env.op0Rgb, params);
                    break;
                case TextureEnvParameter::Op1Rgb:
                    ConvertPackedEnum(env.op1Rgb, params);
                    break;
                case TextureEnvParameter::Op2Rgb:
                    ConvertPackedEnum(env.op2Rgb, params);
                    break;
                case TextureEnvParameter::Op0Alpha:
                    ConvertPackedEnum(env.op0Alpha, params);
                    break;
                case TextureEnvParameter::Op1Alpha:
                    ConvertPackedEnum(env.op1Alpha, params);
                    break;
                case TextureEnvParameter::Op2Alpha:
                    ConvertPackedEnum(env.op2Alpha, params);
                    break;
                case TextureEnvParameter::Color:
                    env.color.writeData(params);
                    break;
                case TextureEnvParameter::RgbScale:
                    *params = env.rgbScale;
                    break;
                case TextureEnvParameter::AlphaScale:
                    *params = env.alphaScale;
                    break;
                default:
                    UNREACHABLE();
                    break;
            }
            break;
        case TextureEnvTarget::PointSprite:
            switch (pname)
            {
                case TextureEnvParameter::PointCoordReplace:
                    *params = static_cast<GLfloat>(env.pointSpriteCoordReplace);
                    break;
                default:
                    UNREACHABLE();
                    break;
            }
            break;
        case TextureEnvTarget::TextureFilterControl:
            switch (pname)
            {
                case TextureEnvParameter::LodBias:
                    *params = env.lodBias;
                    break;
                default:
                    UNREACHABLE();
                    break;
            }
            break;
        default:
            UNREACHABLE();
            break;
    }
}

unsigned int GetPointParameterCount(PointParameter pname)
{
    switch (pname)
    {
        case PointParameter::PointSizeMin:
        case PointParameter::PointSizeMax:
        case PointParameter::PointFadeThresholdSize:
            return 1;
        case PointParameter::PointDistanceAttenuation:
            return 3;
        default:
            return 0;
    }
}

void SetPointParameter(GLES1State *state, PointParameter pname, const GLfloat *params)
{

    PointParameters &pointParams = state->pointParameters();

    switch (pname)
    {
        case PointParameter::PointSizeMin:
            pointParams.pointSizeMin = params[0];
            break;
        case PointParameter::PointSizeMax:
            pointParams.pointSizeMax = params[0];
            break;
        case PointParameter::PointFadeThresholdSize:
            pointParams.pointFadeThresholdSize = params[0];
            break;
        case PointParameter::PointDistanceAttenuation:
            for (unsigned int i = 0; i < 3; i++)
            {
                pointParams.pointDistanceAttenuation[i] = params[i];
            }
            break;
        default:
            UNREACHABLE();
    }
}

void GetPointParameter(const GLES1State *state, PointParameter pname, GLfloat *params)
{
    const PointParameters &pointParams = state->pointParameters();

    switch (pname)
    {
        case PointParameter::PointSizeMin:
            params[0] = pointParams.pointSizeMin;
            break;
        case PointParameter::PointSizeMax:
            params[0] = pointParams.pointSizeMax;
            break;
        case PointParameter::PointFadeThresholdSize:
            params[0] = pointParams.pointFadeThresholdSize;
            break;
        case PointParameter::PointDistanceAttenuation:
            for (unsigned int i = 0; i < 3; i++)
            {
                params[i] = pointParams.pointDistanceAttenuation[i];
            }
            break;
        default:
            UNREACHABLE();
    }
}

void SetPointSize(GLES1State *state, GLfloat size)
{
    PointParameters &params = state->pointParameters();
    params.pointSize        = size;
}

void GetPointSize(const GLES1State *state, GLfloat *sizeOut)
{
    const PointParameters &params = state->pointParameters();
    *sizeOut                      = params.pointSize;
}

bool GetQueryParameterInfo(const State &glState,
                           GLenum pname,
                           GLenum *type,
                           unsigned int *numParams)
{
    const Caps &caps             = glState.getCaps();
    const Extensions &extensions = glState.getExtensions();
    const Version &clientVersion = glState.getClientVersion();

    // 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 = static_cast<unsigned int>(caps.compressedTextureFormats.size());
            return true;
        }
        case GL_SHADER_BINARY_FORMATS:
        {
            *type      = GL_INT;
            *numParams = static_cast<unsigned int>(caps.shaderBinaryFormats.size());
            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_NUM_SHADER_BINARY_FORMATS:
        case GL_NUM_COMPRESSED_TEXTURE_FORMATS:
        case GL_ARRAY_BUFFER_BINDING:
        case GL_FRAMEBUFFER_BINDING:  // GL_FRAMEBUFFER_BINDING now equivalent to
                                      // GL_DRAW_FRAMEBUFFER_BINDING
        case GL_RENDERBUFFER_BINDING:
        case GL_CURRENT_PROGRAM:
        case GL_PACK_ALIGNMENT:
        case GL_UNPACK_ALIGNMENT:
        case GL_GENERATE_MIPMAP_HINT:
        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_QUERY_COUNTER_BITS_EXT:
        {
            *type      = GL_INT;
            *numParams = 1;
            return true;
        }
        case GL_PACK_REVERSE_ROW_ORDER_ANGLE:
        {
            if (!extensions.packReverseRowOrderANGLE)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        }
        case GL_MAX_RECTANGLE_TEXTURE_SIZE_ANGLE:
        case GL_TEXTURE_BINDING_RECTANGLE_ANGLE:
        {
            if (!extensions.textureRectangleANGLE)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        }
        case GL_MAX_DRAW_BUFFERS_EXT:
        case GL_MAX_COLOR_ATTACHMENTS_EXT:
        {
            if ((clientVersion < ES_3_0) && !extensions.drawBuffersEXT)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        }
        case GL_BLEND_ADVANCED_COHERENT_KHR:
        {
            if (!extensions.blendEquationAdvancedCoherentKHR)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            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_POLYGON_OFFSET_POINT_NV:
        {
            if (!extensions.polygonModeNV)
            {
                return false;
            }
            *type      = GL_BOOL;
            *numParams = 1;
            return true;
        }
        case GL_POLYGON_OFFSET_LINE_NV:  // = GL_POLYGON_OFFSET_LINE_ANGLE
        {
            if (!extensions.polygonModeAny())
            {
                return false;
            }
            *type      = GL_BOOL;
            *numParams = 1;
            return true;
        }
        case GL_DEPTH_CLAMP_EXT:
        {
            if (!extensions.depthClampEXT)
            {
                return false;
            }
            *type      = GL_BOOL;
            *numParams = 1;
            return true;
        }
        case GL_COLOR_LOGIC_OP:
        {
            if (clientVersion < ES_2_0)
            {
                // Handle logicOp in GLES1 through GLES1 state management.
                break;
            }

            if (!extensions.logicOpANGLE)
            {
                return false;
            }
            *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_MULTISAMPLE_LINE_WIDTH_GRANULARITY:
        case GL_LINE_WIDTH:
        {
            *type      = GL_FLOAT;
            *numParams = 1;
            return true;
        }
        case GL_POLYGON_OFFSET_CLAMP_EXT:
            if (!extensions.polygonOffsetClampEXT)
            {
                return false;
            }
            *type      = GL_FLOAT;
            *numParams = 1;
            return true;
        case GL_ALIASED_LINE_WIDTH_RANGE:
        case GL_MULTISAMPLE_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 (!extensions.textureFilterAnisotropicEXT)
            {
                return false;
            }
            *type      = GL_FLOAT;
            *numParams = 1;
            return true;
        case GL_TIMESTAMP_EXT:
            if (!extensions.disjointTimerQueryEXT)
            {
                return false;
            }
            *type      = GL_INT64;
            *numParams = 1;
            return true;
        case GL_GPU_DISJOINT_EXT:
            if (!extensions.disjointTimerQueryEXT)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_COVERAGE_MODULATION_CHROMIUM:
            if (!extensions.framebufferMixedSamplesCHROMIUM)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_TEXTURE_BINDING_EXTERNAL_OES:
            if (!extensions.EGLStreamConsumerExternalNV && !extensions.EGLImageExternalOES)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_MAX_CLIP_DISTANCES_EXT:  // case GL_MAX_CLIP_PLANES
        case GL_CLIP_DISTANCE0_EXT:
        case GL_CLIP_DISTANCE1_EXT:
        case GL_CLIP_DISTANCE2_EXT:
        case GL_CLIP_DISTANCE3_EXT:
        case GL_CLIP_DISTANCE4_EXT:
        case GL_CLIP_DISTANCE5_EXT:
        case GL_CLIP_DISTANCE6_EXT:
        case GL_CLIP_DISTANCE7_EXT:
            if (clientVersion < ES_2_0)
            {
                break;
            }
            if (!extensions.clipDistanceAPPLE && !extensions.clipCullDistanceAny())
            {
                // NOTE(hqle): if client version is 1. GL_MAX_CLIP_DISTANCES_EXT is equal
                // to GL_MAX_CLIP_PLANES which is a valid enum.
                return false;
            }
            *type      = (pname == GL_MAX_CLIP_DISTANCES_EXT) ? GL_INT : GL_BOOL;
            *numParams = 1;
            return true;
        case GL_MAX_CULL_DISTANCES_EXT:
        case GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES_EXT:
            if (!extensions.clipCullDistanceAny())
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_CLIP_ORIGIN_EXT:
        case GL_CLIP_DEPTH_MODE_EXT:
            if (!extensions.clipControlEXT)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_POLYGON_MODE_NV:  // = GL_POLYGON_MODE_ANGLE
        {
            if (!extensions.polygonModeAny())
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        }
        case GL_PRIMITIVE_BOUNDING_BOX:
            if (!extensions.primitiveBoundingBoxAny())
            {
                return false;
            }
            *type      = GL_FLOAT;
            *numParams = 8;
            return true;
        case GL_SHADING_RATE_QCOM:
            if (!extensions.shadingRateQCOM)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_SHADING_RATE_EXT:
            if (!extensions.fragmentShadingRateEXT)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_FRAGMENT_SHADING_RATE_NON_TRIVIAL_COMBINERS_SUPPORTED_EXT:
            if (!extensions.fragmentShadingRateEXT)
            {
                return false;
            }
            *type      = GL_BOOL;
            *numParams = 1;
            return true;
    }

    if (glState.getClientVersion() >= Version(3, 2))
    {
        switch (pname)
        {
            case GL_CONTEXT_FLAGS:
            {
                *type      = GL_INT;
                *numParams = 1;
                return true;
            }
        }
    }

    if (extensions.debugKHR)
    {
        switch (pname)
        {
            case GL_DEBUG_LOGGED_MESSAGES:
            case GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH:
            case GL_DEBUG_GROUP_STACK_DEPTH:
            case GL_MAX_DEBUG_MESSAGE_LENGTH:
            case GL_MAX_DEBUG_LOGGED_MESSAGES:
            case GL_MAX_DEBUG_GROUP_STACK_DEPTH:
            case GL_MAX_LABEL_LENGTH:
                *type      = GL_INT;
                *numParams = 1;
                return true;

            case GL_DEBUG_OUTPUT_SYNCHRONOUS:
            case GL_DEBUG_OUTPUT:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.multisampleCompatibilityEXT)
    {
        switch (pname)
        {
            case GL_MULTISAMPLE_EXT:
            case GL_SAMPLE_ALPHA_TO_ONE_EXT:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.bindGeneratesResourceCHROMIUM)
    {
        switch (pname)
        {
            case GL_BIND_GENERATES_RESOURCE_CHROMIUM:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.clientArraysANGLE)
    {
        switch (pname)
        {
            case GL_CLIENT_ARRAYS_ANGLE:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.sRGBWriteControlEXT)
    {
        switch (pname)
        {
            case GL_FRAMEBUFFER_SRGB_EXT:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.robustResourceInitializationANGLE &&
        pname == GL_ROBUST_RESOURCE_INITIALIZATION_ANGLE)
    {
        *type      = GL_BOOL;
        *numParams = 1;
        return true;
    }

    if (extensions.programCacheControlANGLE && pname == GL_PROGRAM_CACHE_ENABLED_ANGLE)
    {
        *type      = GL_BOOL;
        *numParams = 1;
        return true;
    }

    if (extensions.parallelShaderCompileKHR && pname == GL_MAX_SHADER_COMPILER_THREADS_KHR)
    {
        *type      = GL_INT;
        *numParams = 1;
        return true;
    }

    if (extensions.blendFuncExtendedEXT && pname == GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT)
    {
        *type      = GL_INT;
        *numParams = 1;
        return true;
    }

    if (extensions.robustFragmentShaderOutputANGLE &&
        pname == GL_ROBUST_FRAGMENT_SHADER_OUTPUT_ANGLE)
    {
        *type      = GL_BOOL;
        *numParams = 1;
        return true;
    }

    // Check for ES3.0+ parameter names which are also exposed as ES2 extensions
    switch (pname)
    {
        // GL_DRAW_FRAMEBUFFER_BINDING equivalent to GL_FRAMEBUFFER_BINDING
        case GL_READ_FRAMEBUFFER_BINDING:
            if ((clientVersion < ES_3_0) && !extensions.framebufferBlitAny())
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;

        case GL_NUM_PROGRAM_BINARY_FORMATS_OES:
            if ((clientVersion < ES_3_0) && !extensions.getProgramBinaryOES)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;

        case GL_PROGRAM_BINARY_FORMATS_OES:
            if ((clientVersion < ES_3_0) && !extensions.getProgramBinaryOES)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = static_cast<unsigned int>(caps.programBinaryFormats.size());
            return true;

        case GL_PACK_ROW_LENGTH:
        case GL_PACK_SKIP_ROWS:
        case GL_PACK_SKIP_PIXELS:
            if ((clientVersion < ES_3_0) && !extensions.packSubimageNV)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_UNPACK_ROW_LENGTH:
        case GL_UNPACK_SKIP_ROWS:
        case GL_UNPACK_SKIP_PIXELS:
            if ((clientVersion < ES_3_0) && !extensions.unpackSubimageEXT)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_VERTEX_ARRAY_BINDING:
            if ((clientVersion < ES_3_0) && !extensions.vertexArrayObjectOES)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_PIXEL_PACK_BUFFER_BINDING:
        case GL_PIXEL_UNPACK_BUFFER_BINDING:
            if ((clientVersion < ES_3_0) && !extensions.pixelBufferObjectNV)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_MAX_SAMPLES:
            static_assert(GL_MAX_SAMPLES_ANGLE == GL_MAX_SAMPLES,
                          "GL_MAX_SAMPLES_ANGLE not equal to GL_MAX_SAMPLES");
            if ((clientVersion < ES_3_0) && !(extensions.framebufferMultisampleANGLE ||
                                              extensions.multisampledRenderToTextureEXT))
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
            if ((clientVersion < ES_3_0) && !extensions.standardDerivativesOES)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_TEXTURE_BINDING_3D:
            if ((clientVersion < ES_3_0) && !extensions.texture3DOES)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_MAX_3D_TEXTURE_SIZE:
            if ((clientVersion < ES_3_0) && !extensions.texture3DOES)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
    }

    if (pname >= GL_DRAW_BUFFER0_EXT && pname <= GL_DRAW_BUFFER15_EXT)
    {
        if ((glState.getClientVersion() < Version(3, 0)) && !extensions.drawBuffersEXT)
        {
            return false;
        }
        *type      = GL_INT;
        *numParams = 1;
        return true;
    }

    if (extensions.multiviewOVR && pname == GL_MAX_VIEWS_OVR)
    {
        *type      = GL_INT;
        *numParams = 1;
        return true;
    }

    if (extensions.provokingVertexANGLE && pname == GL_PROVOKING_VERTEX_ANGLE)
    {
        *type      = GL_INT;
        *numParams = 1;
        return true;
    }

    if (extensions.shaderFramebufferFetchARM &&
        (pname == GL_FETCH_PER_SAMPLE_ARM || pname == GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM))
    {
        *type      = GL_BOOL;
        *numParams = 1;
        return true;
    }

    if (extensions.textureLodBiasEXT && pname == GL_MAX_TEXTURE_LOD_BIAS_EXT)
    {
        *type      = GL_FLOAT;
        *numParams = 1;
        return true;
    }

    if (glState.getClientVersion() < Version(2, 0))
    {
        switch (pname)
        {
            case GL_ALPHA_TEST_FUNC:
            case GL_CLIENT_ACTIVE_TEXTURE:
            case GL_MATRIX_MODE:
            case GL_MAX_TEXTURE_UNITS:
            case GL_MAX_MODELVIEW_STACK_DEPTH:
            case GL_MAX_PROJECTION_STACK_DEPTH:
            case GL_MAX_TEXTURE_STACK_DEPTH:
            case GL_MAX_LIGHTS:
            case GL_MAX_CLIP_PLANES:
            case GL_VERTEX_ARRAY_STRIDE:
            case GL_NORMAL_ARRAY_STRIDE:
            case GL_COLOR_ARRAY_STRIDE:
            case GL_TEXTURE_COORD_ARRAY_STRIDE:
            case GL_VERTEX_ARRAY_SIZE:
            case GL_COLOR_ARRAY_SIZE:
            case GL_TEXTURE_COORD_ARRAY_SIZE:
            case GL_VERTEX_ARRAY_TYPE:
            case GL_NORMAL_ARRAY_TYPE:
            case GL_COLOR_ARRAY_TYPE:
            case GL_TEXTURE_COORD_ARRAY_TYPE:
            case GL_VERTEX_ARRAY_BUFFER_BINDING:
            case GL_NORMAL_ARRAY_BUFFER_BINDING:
            case GL_COLOR_ARRAY_BUFFER_BINDING:
            case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING:
            case GL_POINT_SIZE_ARRAY_STRIDE_OES:
            case GL_POINT_SIZE_ARRAY_TYPE_OES:
            case GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES:
            case GL_SHADE_MODEL:
            case GL_MODELVIEW_STACK_DEPTH:
            case GL_PROJECTION_STACK_DEPTH:
            case GL_TEXTURE_STACK_DEPTH:
            case GL_LOGIC_OP_MODE:
            case GL_BLEND_SRC:
            case GL_BLEND_DST:
            case GL_PERSPECTIVE_CORRECTION_HINT:
            case GL_POINT_SMOOTH_HINT:
            case GL_LINE_SMOOTH_HINT:
            case GL_FOG_HINT:
                *type      = GL_INT;
                *numParams = 1;
                return true;
            case GL_ALPHA_TEST_REF:
            case GL_FOG_DENSITY:
            case GL_FOG_START:
            case GL_FOG_END:
            case GL_FOG_MODE:
            case GL_POINT_SIZE:
            case GL_POINT_SIZE_MIN:
            case GL_POINT_SIZE_MAX:
            case GL_POINT_FADE_THRESHOLD_SIZE:
                *type      = GL_FLOAT;
                *numParams = 1;
                return true;
            case GL_SMOOTH_POINT_SIZE_RANGE:
            case GL_SMOOTH_LINE_WIDTH_RANGE:
                *type      = GL_FLOAT;
                *numParams = 2;
                return true;
            case GL_CURRENT_COLOR:
            case GL_CURRENT_TEXTURE_COORDS:
            case GL_LIGHT_MODEL_AMBIENT:
            case GL_FOG_COLOR:
                *type      = GL_FLOAT;
                *numParams = 4;
                return true;
            case GL_CURRENT_NORMAL:
            case GL_POINT_DISTANCE_ATTENUATION:
                *type      = GL_FLOAT;
                *numParams = 3;
                return true;
            case GL_MODELVIEW_MATRIX:
            case GL_PROJECTION_MATRIX:
            case GL_TEXTURE_MATRIX:
                *type      = GL_FLOAT;
                *numParams = 16;
                return true;
            case GL_ALPHA_TEST:
            case GL_CLIP_PLANE0:
            case GL_CLIP_PLANE1:
            case GL_CLIP_PLANE2:
            case GL_CLIP_PLANE3:
            case GL_CLIP_PLANE4:
            case GL_CLIP_PLANE5:
            case GL_COLOR_ARRAY:
            case GL_COLOR_LOGIC_OP:
            case GL_COLOR_MATERIAL:
            case GL_FOG:
            case GL_LIGHT_MODEL_TWO_SIDE:
            case GL_LIGHT0:
            case GL_LIGHT1:
            case GL_LIGHT2:
            case GL_LIGHT3:
            case GL_LIGHT4:
            case GL_LIGHT5:
            case GL_LIGHT6:
            case GL_LIGHT7:
            case GL_LIGHTING:
            case GL_LINE_SMOOTH:
            case GL_NORMAL_ARRAY:
            case GL_NORMALIZE:
            case GL_POINT_SIZE_ARRAY_OES:
            case GL_POINT_SMOOTH:
            case GL_POINT_SPRITE_OES:
            case GL_RESCALE_NORMAL:
            case GL_TEXTURE_2D:
            case GL_TEXTURE_CUBE_MAP:
            case GL_TEXTURE_COORD_ARRAY:
            case GL_VERTEX_ARRAY:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
        }
    }

    if (glState.getClientVersion() < Version(3, 0))
    {
        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_TRANSFORM_FEEDBACK_BUFFER_BINDING:
        case GL_COPY_READ_BUFFER_BINDING:
        case GL_COPY_WRITE_BUFFER_BINDING:
        case GL_SAMPLER_BINDING:
        case GL_READ_BUFFER:
        case GL_TEXTURE_BINDING_3D:
        case GL_TEXTURE_BINDING_2D_ARRAY:
        case GL_MAX_ARRAY_TEXTURE_LAYERS:
        case GL_MAX_VERTEX_UNIFORM_BLOCKS:
        case GL_MAX_FRAGMENT_UNIFORM_BLOCKS:
        case GL_MAX_COMBINED_UNIFORM_BLOCKS:
        case GL_MAX_VERTEX_OUTPUT_COMPONENTS:
        case GL_MAX_FRAGMENT_INPUT_COMPONENTS:
        case GL_MAX_VARYING_COMPONENTS:
        case GL_MAX_VERTEX_UNIFORM_COMPONENTS:
        case GL_MAX_FRAGMENT_UNIFORM_COMPONENTS:
        case GL_MIN_PROGRAM_TEXEL_OFFSET:
        case GL_MAX_PROGRAM_TEXEL_OFFSET:
        case GL_NUM_EXTENSIONS:
        case GL_MAJOR_VERSION:
        case GL_MINOR_VERSION:
        case GL_MAX_ELEMENTS_INDICES:
        case GL_MAX_ELEMENTS_VERTICES:
        case GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS:
        case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS:
        case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS:
        case GL_UNPACK_IMAGE_HEIGHT:
        case GL_UNPACK_SKIP_IMAGES:
        {
            *type      = GL_INT;
            *numParams = 1;
            return true;
        }

        case GL_MAX_ELEMENT_INDEX:
        case GL_MAX_UNIFORM_BLOCK_SIZE:
        case GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS:
        case GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS:
        case GL_MAX_SERVER_WAIT_TIMEOUT:
        {
            *type      = GL_INT64;
            *numParams = 1;
            return true;
        }

        case GL_TRANSFORM_FEEDBACK_ACTIVE:
        case GL_TRANSFORM_FEEDBACK_PAUSED:
        case GL_PRIMITIVE_RESTART_FIXED_INDEX:
        case GL_RASTERIZER_DISCARD:
        {
            *type      = GL_BOOL;
            *numParams = 1;
            return true;
        }

        case GL_MAX_TEXTURE_LOD_BIAS:
        {
            *type      = GL_FLOAT;
            *numParams = 1;
            return true;
        }
    }

    if (extensions.shaderMultisampleInterpolationOES)
    {
        switch (pname)
        {
            case GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_OES:
            case GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_OES:
            {
                *type      = GL_FLOAT;
                *numParams = 1;
                return true;
            }
            case GL_FRAGMENT_INTERPOLATION_OFFSET_BITS_OES:
            {
                *type      = GL_INT;
                *numParams = 1;
                return true;
            }
        }
    }

    if (extensions.requestExtensionANGLE)
    {
        switch (pname)
        {
            case GL_NUM_REQUESTABLE_EXTENSIONS_ANGLE:
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
    }

    if (glState.getClientVersion() >= Version(3, 1) || extensions.textureMultisampleANGLE)
    {
        static_assert(GL_SAMPLE_MASK_ANGLE == GL_SAMPLE_MASK);
        static_assert(GL_MAX_SAMPLE_MASK_WORDS_ANGLE == GL_MAX_SAMPLE_MASK_WORDS);
        static_assert(GL_MAX_COLOR_TEXTURE_SAMPLES_ANGLE == GL_MAX_COLOR_TEXTURE_SAMPLES);
        static_assert(GL_MAX_DEPTH_TEXTURE_SAMPLES_ANGLE == GL_MAX_DEPTH_TEXTURE_SAMPLES);
        static_assert(GL_MAX_INTEGER_SAMPLES_ANGLE == GL_MAX_INTEGER_SAMPLES);
        static_assert(GL_TEXTURE_BINDING_2D_MULTISAMPLE_ANGLE == GL_TEXTURE_BINDING_2D_MULTISAMPLE);

        switch (pname)
        {
            case GL_SAMPLE_MASK:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
            case GL_MAX_SAMPLE_MASK_WORDS:
            case GL_MAX_COLOR_TEXTURE_SAMPLES:
            case GL_MAX_DEPTH_TEXTURE_SAMPLES:
            case GL_MAX_INTEGER_SAMPLES:
            case GL_TEXTURE_BINDING_2D_MULTISAMPLE:
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.textureCubeMapArrayAny())
    {
        switch (pname)
        {
            case GL_TEXTURE_BINDING_CUBE_MAP_ARRAY:
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.textureBufferAny())
    {
        switch (pname)
        {
            case GL_TEXTURE_BUFFER_BINDING:
            case GL_TEXTURE_BINDING_BUFFER:
            case GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT:
            case GL_MAX_TEXTURE_BUFFER_SIZE:
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.shaderPixelLocalStorageANGLE)
    {
        switch (pname)
        {
            case GL_MAX_PIXEL_LOCAL_STORAGE_PLANES_ANGLE:
            case GL_MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES_ANGLE:
            case GL_PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_ANGLE:
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
    }

    if (glState.getClientVersion() >= Version(3, 2) ||
        extensions.textureStorageMultisample2dArrayOES)
    {
        switch (pname)
        {
            case GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY:
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
    }

    if (glState.getClientVersion() < Version(3, 1))
    {
        return false;
    }

    // Check for ES3.1+ parameter names
    switch (pname)
    {
        case GL_ATOMIC_COUNTER_BUFFER_BINDING:
        case GL_DRAW_INDIRECT_BUFFER_BINDING:
        case GL_DISPATCH_INDIRECT_BUFFER_BINDING:
        case GL_MAX_FRAMEBUFFER_WIDTH:
        case GL_MAX_FRAMEBUFFER_HEIGHT:
        case GL_MAX_FRAMEBUFFER_SAMPLES:
        case GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET:
        case GL_MAX_VERTEX_ATTRIB_BINDINGS:
        case GL_MAX_VERTEX_ATTRIB_STRIDE:
        case GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS:
        case GL_MAX_VERTEX_ATOMIC_COUNTERS:
        case GL_MAX_VERTEX_IMAGE_UNIFORMS:
        case GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS:
        case GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS:
        case GL_MAX_FRAGMENT_ATOMIC_COUNTERS:
        case GL_MAX_FRAGMENT_IMAGE_UNIFORMS:
        case GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS:
        case GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET:
        case GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET:
        case GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS:
        case GL_MAX_COMPUTE_UNIFORM_BLOCKS:
        case GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS:
        case GL_MAX_COMPUTE_SHARED_MEMORY_SIZE:
        case GL_MAX_COMPUTE_UNIFORM_COMPONENTS:
        case GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS:
        case GL_MAX_COMPUTE_ATOMIC_COUNTERS:
        case GL_MAX_COMPUTE_IMAGE_UNIFORMS:
        case GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS:
        case GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS:
        case GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES:
        case GL_MAX_UNIFORM_LOCATIONS:
        case GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS:
        case GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE:
        case GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS:
        case GL_MAX_COMBINED_ATOMIC_COUNTERS:
        case GL_MAX_IMAGE_UNITS:
        case GL_MAX_COMBINED_IMAGE_UNIFORMS:
        case GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS:
        case GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS:
        case GL_SHADER_STORAGE_BUFFER_BINDING:
        case GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT:
        case GL_PROGRAM_PIPELINE_BINDING:
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_MAX_SHADER_STORAGE_BLOCK_SIZE:
            *type      = GL_INT64;
            *numParams = 1;
            return true;
        case GL_SAMPLE_SHADING:
            *type      = GL_BOOL;
            *numParams = 1;
            return true;
        case GL_MIN_SAMPLE_SHADING_VALUE:
            *type      = GL_FLOAT;
            *numParams = 1;
            return true;
    }

    if (extensions.geometryShaderAny())
    {
        switch (pname)
        {
            case GL_MAX_FRAMEBUFFER_LAYERS_EXT:
            case GL_LAYER_PROVOKING_VERTEX_EXT:
            case GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT:
            case GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT:
            case GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_EXT:
            case GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT:
            case GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT:
            case GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT:
            case GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT:
            case GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT:
            case GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT:
            case GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT:
            case GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT:
            case GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT:
            case GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT:
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.tessellationShaderAny())
    {
        switch (pname)
        {
            case GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
            case GL_PATCH_VERTICES:
            case GL_MAX_PATCH_VERTICES_EXT:
            case GL_MAX_TESS_GEN_LEVEL_EXT:
            case GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS_EXT:
            case GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT:
            case GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS_EXT:
            case GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS_EXT:
            case GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_EXT:
            case GL_MAX_TESS_PATCH_COMPONENTS_EXT:
            case GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS_EXT:
            case GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_EXT:
            case GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS_EXT:
            case GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS_EXT:
            case GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_EXT:
            case GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_EXT:
            case GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_EXT:
            case GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT:
            case GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_EXT:
            case GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_EXT:
            case GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_EXT:
            case GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_EXT:
            case GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_EXT:
            case GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS_EXT:
            case GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_EXT:
            case GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_EXT:
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
    }

    return false;
}

bool GetIndexedQueryParameterInfo(const State &glState,
                                  GLenum target,
                                  GLenum *type,
                                  unsigned int *numParams)
{
    const Extensions &extensions = glState.getExtensions();
    const Version &clientVersion = glState.getClientVersion();

    ASSERT(clientVersion >= ES_3_0);

    switch (target)
    {
        case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
        case GL_UNIFORM_BUFFER_BINDING:
        {
            *type      = GL_INT;
            *numParams = 1;
            return true;
        }
        case GL_TRANSFORM_FEEDBACK_BUFFER_START:
        case GL_TRANSFORM_FEEDBACK_BUFFER_SIZE:
        case GL_UNIFORM_BUFFER_START:
        case GL_UNIFORM_BUFFER_SIZE:
        {
            *type      = GL_INT64;
            *numParams = 1;
            return true;
        }
    }

    if (clientVersion >= ES_3_1 || extensions.textureMultisampleANGLE)
    {
        static_assert(GL_SAMPLE_MASK_VALUE_ANGLE == GL_SAMPLE_MASK_VALUE);
        switch (target)
        {
            case GL_SAMPLE_MASK_VALUE:
            {
                *type      = GL_INT;
                *numParams = 1;
                return true;
            }
        }
    }

    if (clientVersion >= ES_3_2 || extensions.drawBuffersIndexedAny())
    {
        switch (target)
        {
            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:
            {
                *type      = GL_INT;
                *numParams = 1;
                return true;
            }
            case GL_COLOR_WRITEMASK:
            {
                *type      = GL_BOOL;
                *numParams = 4;
                return true;
            }
        }
    }

    if (clientVersion < ES_3_1)
    {
        return false;
    }

    switch (target)
    {
        case GL_IMAGE_BINDING_LAYERED:
        {
            *type      = GL_BOOL;
            *numParams = 1;
            return true;
        }
        case GL_MAX_COMPUTE_WORK_GROUP_COUNT:
        case GL_MAX_COMPUTE_WORK_GROUP_SIZE:
        case GL_ATOMIC_COUNTER_BUFFER_BINDING:
        case GL_SHADER_STORAGE_BUFFER_BINDING:
        case GL_VERTEX_BINDING_BUFFER:
        case GL_VERTEX_BINDING_DIVISOR:
        case GL_VERTEX_BINDING_OFFSET:
        case GL_VERTEX_BINDING_STRIDE:
        case GL_IMAGE_BINDING_NAME:
        case GL_IMAGE_BINDING_LEVEL:
        case GL_IMAGE_BINDING_LAYER:
        case GL_IMAGE_BINDING_ACCESS:
        case GL_IMAGE_BINDING_FORMAT:
        {
            *type      = GL_INT;
            *numParams = 1;
            return true;
        }
        case GL_ATOMIC_COUNTER_BUFFER_START:
        case GL_ATOMIC_COUNTER_BUFFER_SIZE:
        case GL_SHADER_STORAGE_BUFFER_START:
        case GL_SHADER_STORAGE_BUFFER_SIZE:
        {
            *type      = GL_INT64;
            *numParams = 1;
            return true;
        }
    }

    return false;
}

void QueryProgramPipelineiv(const Context *context,
                            ProgramPipeline *programPipeline,
                            GLenum pname,
                            GLint *params)
{
    if (!params)
    {
        // Can't write the result anywhere, so just return immediately.
        return;
    }

    switch (pname)
    {
        case GL_ACTIVE_PROGRAM:
        {
            // the name of the active program object of the program pipeline object is returned in
            // params
            *params = 0;
            if (programPipeline)
            {
                const Program *program = programPipeline->getActiveShaderProgram();
                if (program)
                {
                    *params = program->id().value;
                }
            }
            break;
        }

        case GL_VERTEX_SHADER:
        {
            // the name of the current program object for the vertex shader type of the program
            // pipeline object is returned in params
            GetShaderProgramId(programPipeline, ShaderType::Vertex, params);
            break;
        }

        case GL_FRAGMENT_SHADER:
        {
            // the name of the current program object for the fragment shader type of the program
            // pipeline object is returned in params
            GetShaderProgramId(programPipeline, ShaderType::Fragment, params);
            break;
        }

        case GL_TESS_CONTROL_SHADER:
        {
            // the name of the current program object for the tessellation control shader type of
            // the program pipeline object is returned in params
            GetShaderProgramId(programPipeline, ShaderType::TessControl, params);
            break;
        }

        case GL_TESS_EVALUATION_SHADER:
        {
            // the name of the current program object for the tessellation evaluation shader type of
            // the program pipeline object is returned in params
            GetShaderProgramId(programPipeline, ShaderType::TessEvaluation, params);
            break;
        }

        case GL_COMPUTE_SHADER:
        {
            // the name of the current program object for the compute shader type of the program
            // pipeline object is returned in params
            GetShaderProgramId(programPipeline, ShaderType::Compute, params);
            break;
        }

        case GL_GEOMETRY_SHADER:
        {
            // the name of the current program object for the geometry shader type of the program
            // pipeline object is returned in params
            GetShaderProgramId(programPipeline, ShaderType::Geometry, params);
            break;
        }

        case GL_INFO_LOG_LENGTH:
        {
            // the length of the info log, including the null terminator, is returned in params. If
            // there is no info log, zero is returned.
            *params = 0;
            if (programPipeline)
            {
                *params = programPipeline->getInfoLogLength();
            }
            break;
        }

        case GL_VALIDATE_STATUS:
        {
            // the validation status of pipeline, as determined by glValidateProgramPipeline, is
            // returned in params
            *params = 0;
            if (programPipeline)
            {
                *params = programPipeline->isValid();
            }
            break;
        }

        default:
            break;
    }
}

}  // namespace gl

namespace egl
{

void QueryConfigAttrib(const Config *config, EGLint attribute, EGLint *value)
{
    ASSERT(config != nullptr);
    switch (attribute)
    {
        case EGL_BUFFER_SIZE:
            *value = config->bufferSize;
            break;
        case EGL_ALPHA_SIZE:
            *value = config->alphaSize;
            break;
        case EGL_BLUE_SIZE:
            *value = config->blueSize;
            break;
        case EGL_GREEN_SIZE:
            *value = config->greenSize;
            break;
        case EGL_RED_SIZE:
            *value = config->redSize;
            break;
        case EGL_DEPTH_SIZE:
            *value = config->depthSize;
            break;
        case EGL_STENCIL_SIZE:
            *value = config->stencilSize;
            break;
        case EGL_CONFIG_CAVEAT:
            *value = config->configCaveat;
            break;
        case EGL_CONFIG_ID:
            *value = config->configID;
            break;
        case EGL_LEVEL:
            *value = config->level;
            break;
        case EGL_NATIVE_RENDERABLE:
            *value = config->nativeRenderable;
            break;
        case EGL_NATIVE_VISUAL_ID:
            *value = config->nativeVisualID;
            break;
        case EGL_NATIVE_VISUAL_TYPE:
            *value = config->nativeVisualType;
            break;
        case EGL_SAMPLES:
            *value = config->samples;
            break;
        case EGL_SAMPLE_BUFFERS:
            *value = config->sampleBuffers;
            break;
        case EGL_SURFACE_TYPE:
            *value = config->surfaceType;
            break;
        case EGL_BIND_TO_TEXTURE_TARGET_ANGLE:
            *value = config->bindToTextureTarget;
            break;
        case EGL_TRANSPARENT_TYPE:
            *value = config->transparentType;
            break;
        case EGL_TRANSPARENT_BLUE_VALUE:
            *value = config->transparentBlueValue;
            break;
        case EGL_TRANSPARENT_GREEN_VALUE:
            *value = config->transparentGreenValue;
            break;
        case EGL_TRANSPARENT_RED_VALUE:
            *value = config->transparentRedValue;
            break;
        case EGL_BIND_TO_TEXTURE_RGB:
            *value = config->bindToTextureRGB;
            break;
        case EGL_BIND_TO_TEXTURE_RGBA:
            *value = config->bindToTextureRGBA;
            break;
        case EGL_MIN_SWAP_INTERVAL:
            *value = config->minSwapInterval;
            break;
        case EGL_MAX_SWAP_INTERVAL:
            *value = config->maxSwapInterval;
            break;
        case EGL_LUMINANCE_SIZE:
            *value = config->luminanceSize;
            break;
        case EGL_ALPHA_MASK_SIZE:
            *value = config->alphaMaskSize;
            break;
        case EGL_COLOR_BUFFER_TYPE:
            *value = config->colorBufferType;
            break;
        case EGL_RENDERABLE_TYPE:
            *value = config->renderableType;
            break;
        case EGL_MATCH_NATIVE_PIXMAP:
            *value = false;
            UNIMPLEMENTED();
            break;
        case EGL_CONFORMANT:
            *value = config->conformant;
            break;
        case EGL_MAX_PBUFFER_WIDTH:
            *value = config->maxPBufferWidth;
            break;
        case EGL_MAX_PBUFFER_HEIGHT:
            *value = config->maxPBufferHeight;
            break;
        case EGL_MAX_PBUFFER_PIXELS:
            *value = config->maxPBufferPixels;
            break;
        case EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE:
            *value = config->optimalOrientation;
            break;
        case EGL_COLOR_COMPONENT_TYPE_EXT:
            *value = config->colorComponentType;
            break;
        case EGL_RECORDABLE_ANDROID:
            *value = config->recordable;
            break;
        case EGL_FRAMEBUFFER_TARGET_ANDROID:
            *value = config->framebufferTarget;
            break;
        case EGL_MATCH_FORMAT_KHR:
            *value = config->matchFormat;
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void QueryContextAttrib(const gl::Context *context, EGLint attribute, EGLint *value)
{
    switch (attribute)
    {
        case EGL_CONFIG_ID:
            if (context->getConfig() != EGL_NO_CONFIG_KHR)
            {
                *value = context->getConfig()->configID;
            }
            else
            {
                *value = 0;
            }
            break;
        case EGL_CONTEXT_CLIENT_TYPE:
            *value = EGL_OPENGL_ES_API;
            break;
        case EGL_CONTEXT_MAJOR_VERSION:
            static_assert(EGL_CONTEXT_MAJOR_VERSION == EGL_CONTEXT_CLIENT_VERSION);
            *value = context->getClientVersion().getMajor();
            break;
        case EGL_CONTEXT_MINOR_VERSION:
            *value = context->getClientVersion().getMinor();
            break;
        case EGL_RENDER_BUFFER:
            *value = context->getRenderBuffer();
            break;
        case EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
            *value = context->isRobustResourceInitEnabled();
            break;
        case EGL_CONTEXT_PRIORITY_LEVEL_IMG:
            *value = static_cast<EGLint>(context->getContextPriority());
            break;
        case EGL_PROTECTED_CONTENT_EXT:
            *value = context->getState().hasProtectedContent();
            break;
        case EGL_CONTEXT_MEMORY_USAGE_ANGLE:
        {
            uint64_t memory = context->getMemoryUsage();
            value[0]        = static_cast<GLint>(memory & 0xffffffff);
            value[1]        = static_cast<GLint>(memory >> 32);
        }
        break;
        default:
            UNREACHABLE();
            break;
    }
}

egl::Error QuerySurfaceAttrib(const Display *display,
                              const gl::Context *context,
                              Surface *surface,
                              EGLint attribute,
                              EGLint *value)
{
    switch (attribute)
    {
        case EGL_GL_COLORSPACE:
            *value = surface->getGLColorspace();
            break;
        case EGL_VG_ALPHA_FORMAT:
            *value = surface->getVGAlphaFormat();
            break;
        case EGL_VG_COLORSPACE:
            *value = surface->getVGColorspace();
            break;
        case EGL_CONFIG_ID:
            *value = surface->getConfig()->configID;
            break;
        case EGL_HEIGHT:
            ANGLE_TRY(surface->getUserSize(display, nullptr, value));
            break;
        case EGL_HORIZONTAL_RESOLUTION:
            *value = surface->getHorizontalResolution();
            break;
        case EGL_LARGEST_PBUFFER:
            // The EGL spec states that value is not written if the surface is not a pbuffer
            if (surface->getType() == EGL_PBUFFER_BIT)
            {
                *value = surface->getLargestPbuffer();
            }
            break;
        case EGL_MIPMAP_TEXTURE:
            // The EGL spec states that value is not written if the surface is not a pbuffer
            if (surface->getType() == EGL_PBUFFER_BIT)
            {
                *value = surface->getMipmapTexture();
            }
            break;
        case EGL_MIPMAP_LEVEL:
            // The EGL spec states that value is not written if the surface is not a pbuffer
            if (surface->getType() == EGL_PBUFFER_BIT)
            {
                *value = surface->getMipmapLevel();
            }
            break;
        case EGL_MULTISAMPLE_RESOLVE:
            *value = surface->getMultisampleResolve();
            break;
        case EGL_PIXEL_ASPECT_RATIO:
            *value = surface->getPixelAspectRatio();
            break;
        case EGL_RENDER_BUFFER:
            if (surface->getType() == EGL_WINDOW_BIT)
            {
                *value = surface->getRequestedRenderBuffer();
            }
            else
            {
                *value = surface->getRenderBuffer();
            }
            break;
        case EGL_SWAP_BEHAVIOR:
            *value = surface->getRequestedSwapBehavior();
            break;
        case EGL_TEXTURE_FORMAT:
            // The EGL spec states that value is not written if the surface is not a pbuffer
            if (surface->getType() == EGL_PBUFFER_BIT)
            {
                *value = ToEGLenum(surface->getTextureFormat());
            }
            break;
        case EGL_TEXTURE_TARGET:
            // The EGL spec states that value is not written if the surface is not a pbuffer
            if (surface->getType() == EGL_PBUFFER_BIT)
            {
                *value = surface->getTextureTarget();
            }
            break;
        case EGL_VERTICAL_RESOLUTION:
            *value = surface->getVerticalResolution();
            break;
        case EGL_WIDTH:
            ANGLE_TRY(surface->getUserSize(display, value, nullptr));
            break;
        case EGL_POST_SUB_BUFFER_SUPPORTED_NV:
            *value = surface->isPostSubBufferSupported();
            break;
        case EGL_FIXED_SIZE_ANGLE:
            *value = surface->isFixedSize();
            break;
        case EGL_SURFACE_ORIENTATION_ANGLE:
            *value = surface->getOrientation();
            break;
        case EGL_DIRECT_COMPOSITION_ANGLE:
            *value = surface->directComposition();
            break;
        case EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
            *value = surface->isRobustResourceInitEnabled();
            break;
        case EGL_TIMESTAMPS_ANDROID:
            *value = surface->isTimestampsEnabled();
            break;
        case EGL_BUFFER_AGE_EXT:
            ANGLE_TRY(surface->getBufferAge(context, value));
            break;
        case EGL_BITMAP_PITCH_KHR:
            *value = surface->getBitmapPitch();
            break;
        case EGL_BITMAP_ORIGIN_KHR:
            *value = surface->getBitmapOrigin();
            break;
        case EGL_BITMAP_PIXEL_RED_OFFSET_KHR:
            *value = surface->getRedOffset();
            break;
        case EGL_BITMAP_PIXEL_GREEN_OFFSET_KHR:
            *value = surface->getGreenOffset();
            break;
        case EGL_BITMAP_PIXEL_BLUE_OFFSET_KHR:
            *value = surface->getBlueOffset();
            break;
        case EGL_BITMAP_PIXEL_ALPHA_OFFSET_KHR:
            *value = surface->getAlphaOffset();
            break;
        case EGL_BITMAP_PIXEL_LUMINANCE_OFFSET_KHR:
            *value = surface->getLuminanceOffset();
            break;
        case EGL_BITMAP_PIXEL_SIZE_KHR:
            *value = surface->getBitmapPixelSize();
            break;
        case EGL_PROTECTED_CONTENT_EXT:
            *value = surface->hasProtectedContent();
            break;
        case EGL_SURFACE_COMPRESSION_EXT:
            ANGLE_TRY(surface->getCompressionRate(display, context, value));
            break;
        default:
            UNREACHABLE();
            break;
    }
    return NoError();
}

egl::Error QuerySurfaceAttrib64KHR(const Display *display,
                                   const gl::Context *context,
                                   Surface *surface,
                                   EGLint attribute,
                                   EGLAttribKHR *value)
{
    switch (attribute)
    {
        case EGL_BITMAP_PITCH_KHR:
            *value = static_cast<EGLAttribKHR>(surface->getBitmapPitch());
            break;
        case EGL_BITMAP_ORIGIN_KHR:
            *value = static_cast<EGLAttribKHR>(surface->getBitmapOrigin());
            break;
        case EGL_BITMAP_PIXEL_RED_OFFSET_KHR:
            *value = static_cast<EGLAttribKHR>(surface->getRedOffset());
            break;
        case EGL_BITMAP_PIXEL_GREEN_OFFSET_KHR:
            *value = static_cast<EGLAttribKHR>(surface->getGreenOffset());
            break;
        case EGL_BITMAP_PIXEL_BLUE_OFFSET_KHR:
            *value = static_cast<EGLAttribKHR>(surface->getBlueOffset());
            break;
        case EGL_BITMAP_PIXEL_ALPHA_OFFSET_KHR:
            *value = static_cast<EGLAttribKHR>(surface->getAlphaOffset());
            break;
        case EGL_BITMAP_PIXEL_LUMINANCE_OFFSET_KHR:
            *value = static_cast<EGLAttribKHR>(surface->getLuminanceOffset());
            break;
        case EGL_BITMAP_PIXEL_SIZE_KHR:
            *value = static_cast<EGLAttribKHR>(surface->getBitmapPixelSize());
            break;
        case EGL_BITMAP_POINTER_KHR:
            *value = surface->getBitmapPointer();
            break;
        default:
        {
            EGLint intValue = 0;
            ANGLE_TRY(QuerySurfaceAttrib(display, context, surface, attribute, &intValue));
            *value = static_cast<EGLAttribKHR>(intValue);
        }
        break;
    }
    return NoError();
}

egl::Error SetSurfaceAttrib(Surface *surface, EGLint attribute, EGLint value)
{
    switch (attribute)
    {
        case EGL_MIPMAP_LEVEL:
            surface->setMipmapLevel(value);
            break;
        case EGL_MULTISAMPLE_RESOLVE:
            surface->setMultisampleResolve(value);
            break;
        case EGL_SWAP_BEHAVIOR:
            surface->setRequestedSwapBehavior(value);
            break;
        case EGL_WIDTH:
            surface->setFixedWidth(value);
            break;
        case EGL_HEIGHT:
            surface->setFixedHeight(value);
            break;
        case EGL_TIMESTAMPS_ANDROID:
            surface->setTimestampsEnabled(value != EGL_FALSE);
            break;
        case EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID:
            return surface->setAutoRefreshEnabled(value != EGL_FALSE);
        case EGL_RENDER_BUFFER:
            surface->setRequestedRenderBuffer(value);
            break;
        default:
            UNREACHABLE();
            break;
    }
    return NoError();
}

Error GetSyncAttrib(Display *display, SyncID sync, EGLint attribute, EGLint *value)
{
    const egl::Sync *syncObj = display->getSync(sync);
    switch (attribute)
    {
        case EGL_SYNC_TYPE_KHR:
            *value = syncObj->getType();
            return NoError();

        case EGL_SYNC_STATUS_KHR:
            return syncObj->getStatus(display, value);

        case EGL_SYNC_CONDITION_KHR:
            *value = syncObj->getCondition();
            return NoError();

        default:
            break;
    }

    UNREACHABLE();
    return NoError();
}
}  // namespace egl
