// GENERATED FILE - DO NOT EDIT.
// Generated by gen_packed_gl_enums.py using data from packed_gl_enums.json.
//
// Copyright 2017 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.
//
// PackedGLEnums_autogen.cpp:
//   Implements ANGLE-specific enums classes for GLenums and functions operating
//   on them.

#include "common/PackedGLEnums_autogen.h"
#include "common/debug.h"

namespace gl
{

template <>
AlphaTestFunc FromGLenum<AlphaTestFunc>(GLenum from)
{
    switch (from)
    {
        case GL_ALWAYS:
            return AlphaTestFunc::AlwaysPass;
        case GL_EQUAL:
            return AlphaTestFunc::Equal;
        case GL_GEQUAL:
            return AlphaTestFunc::Gequal;
        case GL_GREATER:
            return AlphaTestFunc::Greater;
        case GL_LEQUAL:
            return AlphaTestFunc::Lequal;
        case GL_LESS:
            return AlphaTestFunc::Less;
        case GL_NEVER:
            return AlphaTestFunc::Never;
        case GL_NOTEQUAL:
            return AlphaTestFunc::NotEqual;
        default:
            return AlphaTestFunc::InvalidEnum;
    }
}

GLenum ToGLenum(AlphaTestFunc from)
{
    switch (from)
    {
        case AlphaTestFunc::AlwaysPass:
            return GL_ALWAYS;
        case AlphaTestFunc::Equal:
            return GL_EQUAL;
        case AlphaTestFunc::Gequal:
            return GL_GEQUAL;
        case AlphaTestFunc::Greater:
            return GL_GREATER;
        case AlphaTestFunc::Lequal:
            return GL_LEQUAL;
        case AlphaTestFunc::Less:
            return GL_LESS;
        case AlphaTestFunc::Never:
            return GL_NEVER;
        case AlphaTestFunc::NotEqual:
            return GL_NOTEQUAL;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, AlphaTestFunc value)
{
    switch (value)
    {
        case AlphaTestFunc::AlwaysPass:
            os << "GL_ALWAYS";
            break;
        case AlphaTestFunc::Equal:
            os << "GL_EQUAL";
            break;
        case AlphaTestFunc::Gequal:
            os << "GL_GEQUAL";
            break;
        case AlphaTestFunc::Greater:
            os << "GL_GREATER";
            break;
        case AlphaTestFunc::Lequal:
            os << "GL_LEQUAL";
            break;
        case AlphaTestFunc::Less:
            os << "GL_LESS";
            break;
        case AlphaTestFunc::Never:
            os << "GL_NEVER";
            break;
        case AlphaTestFunc::NotEqual:
            os << "GL_NOTEQUAL";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
BufferBinding FromGLenum<BufferBinding>(GLenum from)
{
    switch (from)
    {
        case GL_ARRAY_BUFFER:
            return BufferBinding::Array;
        case GL_ATOMIC_COUNTER_BUFFER:
            return BufferBinding::AtomicCounter;
        case GL_COPY_READ_BUFFER:
            return BufferBinding::CopyRead;
        case GL_COPY_WRITE_BUFFER:
            return BufferBinding::CopyWrite;
        case GL_DISPATCH_INDIRECT_BUFFER:
            return BufferBinding::DispatchIndirect;
        case GL_DRAW_INDIRECT_BUFFER:
            return BufferBinding::DrawIndirect;
        case GL_ELEMENT_ARRAY_BUFFER:
            return BufferBinding::ElementArray;
        case GL_PIXEL_PACK_BUFFER:
            return BufferBinding::PixelPack;
        case GL_PIXEL_UNPACK_BUFFER:
            return BufferBinding::PixelUnpack;
        case GL_SHADER_STORAGE_BUFFER:
            return BufferBinding::ShaderStorage;
        case GL_TEXTURE_BUFFER:
            return BufferBinding::Texture;
        case GL_TRANSFORM_FEEDBACK_BUFFER:
            return BufferBinding::TransformFeedback;
        case GL_UNIFORM_BUFFER:
            return BufferBinding::Uniform;
        default:
            return BufferBinding::InvalidEnum;
    }
}

GLenum ToGLenum(BufferBinding from)
{
    switch (from)
    {
        case BufferBinding::Array:
            return GL_ARRAY_BUFFER;
        case BufferBinding::AtomicCounter:
            return GL_ATOMIC_COUNTER_BUFFER;
        case BufferBinding::CopyRead:
            return GL_COPY_READ_BUFFER;
        case BufferBinding::CopyWrite:
            return GL_COPY_WRITE_BUFFER;
        case BufferBinding::DispatchIndirect:
            return GL_DISPATCH_INDIRECT_BUFFER;
        case BufferBinding::DrawIndirect:
            return GL_DRAW_INDIRECT_BUFFER;
        case BufferBinding::ElementArray:
            return GL_ELEMENT_ARRAY_BUFFER;
        case BufferBinding::PixelPack:
            return GL_PIXEL_PACK_BUFFER;
        case BufferBinding::PixelUnpack:
            return GL_PIXEL_UNPACK_BUFFER;
        case BufferBinding::ShaderStorage:
            return GL_SHADER_STORAGE_BUFFER;
        case BufferBinding::Texture:
            return GL_TEXTURE_BUFFER;
        case BufferBinding::TransformFeedback:
            return GL_TRANSFORM_FEEDBACK_BUFFER;
        case BufferBinding::Uniform:
            return GL_UNIFORM_BUFFER;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, BufferBinding value)
{
    switch (value)
    {
        case BufferBinding::Array:
            os << "GL_ARRAY_BUFFER";
            break;
        case BufferBinding::AtomicCounter:
            os << "GL_ATOMIC_COUNTER_BUFFER";
            break;
        case BufferBinding::CopyRead:
            os << "GL_COPY_READ_BUFFER";
            break;
        case BufferBinding::CopyWrite:
            os << "GL_COPY_WRITE_BUFFER";
            break;
        case BufferBinding::DispatchIndirect:
            os << "GL_DISPATCH_INDIRECT_BUFFER";
            break;
        case BufferBinding::DrawIndirect:
            os << "GL_DRAW_INDIRECT_BUFFER";
            break;
        case BufferBinding::ElementArray:
            os << "GL_ELEMENT_ARRAY_BUFFER";
            break;
        case BufferBinding::PixelPack:
            os << "GL_PIXEL_PACK_BUFFER";
            break;
        case BufferBinding::PixelUnpack:
            os << "GL_PIXEL_UNPACK_BUFFER";
            break;
        case BufferBinding::ShaderStorage:
            os << "GL_SHADER_STORAGE_BUFFER";
            break;
        case BufferBinding::Texture:
            os << "GL_TEXTURE_BUFFER";
            break;
        case BufferBinding::TransformFeedback:
            os << "GL_TRANSFORM_FEEDBACK_BUFFER";
            break;
        case BufferBinding::Uniform:
            os << "GL_UNIFORM_BUFFER";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
BufferParam FromGLenum<BufferParam>(GLenum from)
{
    switch (from)
    {
        case GL_BUFFER_IMMUTABLE_STORAGE_EXT:
            return BufferParam::ImmutableStorage;
        case GL_BUFFER_STORAGE_FLAGS_EXT:
            return BufferParam::StorageFlags;
        case GL_BUFFER_SIZE:
            return BufferParam::BufferSize;
        case GL_BUFFER_USAGE:
            return BufferParam::BufferUsage;
        case GL_BUFFER_ACCESS_OES:
            return BufferParam::BufferAccess;
        case GL_BUFFER_MAPPED:
            return BufferParam::BufferMapped;
        case GL_BUFFER_ACCESS_FLAGS:
            return BufferParam::BufferAccessFlags;
        case GL_BUFFER_MAP_LENGTH:
            return BufferParam::BufferMapLength;
        case GL_BUFFER_MAP_OFFSET:
            return BufferParam::BufferMapOffset;
        case GL_MEMORY_SIZE_ANGLE:
            return BufferParam::MemorySize;
        case GL_RESOURCE_INITIALIZED_ANGLE:
            return BufferParam::ResourceInitialized;
        default:
            return BufferParam::InvalidEnum;
    }
}

GLenum ToGLenum(BufferParam from)
{
    switch (from)
    {
        case BufferParam::ImmutableStorage:
            return GL_BUFFER_IMMUTABLE_STORAGE_EXT;
        case BufferParam::StorageFlags:
            return GL_BUFFER_STORAGE_FLAGS_EXT;
        case BufferParam::BufferSize:
            return GL_BUFFER_SIZE;
        case BufferParam::BufferUsage:
            return GL_BUFFER_USAGE;
        case BufferParam::BufferAccess:
            return GL_BUFFER_ACCESS_OES;
        case BufferParam::BufferMapped:
            return GL_BUFFER_MAPPED;
        case BufferParam::BufferAccessFlags:
            return GL_BUFFER_ACCESS_FLAGS;
        case BufferParam::BufferMapLength:
            return GL_BUFFER_MAP_LENGTH;
        case BufferParam::BufferMapOffset:
            return GL_BUFFER_MAP_OFFSET;
        case BufferParam::MemorySize:
            return GL_MEMORY_SIZE_ANGLE;
        case BufferParam::ResourceInitialized:
            return GL_RESOURCE_INITIALIZED_ANGLE;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, BufferParam value)
{
    switch (value)
    {
        case BufferParam::ImmutableStorage:
            os << "GL_BUFFER_IMMUTABLE_STORAGE_EXT";
            break;
        case BufferParam::StorageFlags:
            os << "GL_BUFFER_STORAGE_FLAGS_EXT";
            break;
        case BufferParam::BufferSize:
            os << "GL_BUFFER_SIZE";
            break;
        case BufferParam::BufferUsage:
            os << "GL_BUFFER_USAGE";
            break;
        case BufferParam::BufferAccess:
            os << "GL_BUFFER_ACCESS_OES";
            break;
        case BufferParam::BufferMapped:
            os << "GL_BUFFER_MAPPED";
            break;
        case BufferParam::BufferAccessFlags:
            os << "GL_BUFFER_ACCESS_FLAGS";
            break;
        case BufferParam::BufferMapLength:
            os << "GL_BUFFER_MAP_LENGTH";
            break;
        case BufferParam::BufferMapOffset:
            os << "GL_BUFFER_MAP_OFFSET";
            break;
        case BufferParam::MemorySize:
            os << "GL_MEMORY_SIZE_ANGLE";
            break;
        case BufferParam::ResourceInitialized:
            os << "GL_RESOURCE_INITIALIZED_ANGLE";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
BufferUsage FromGLenum<BufferUsage>(GLenum from)
{
    switch (from)
    {
        case GL_DYNAMIC_COPY:
            return BufferUsage::DynamicCopy;
        case GL_DYNAMIC_DRAW:
            return BufferUsage::DynamicDraw;
        case GL_DYNAMIC_READ:
            return BufferUsage::DynamicRead;
        case GL_STATIC_COPY:
            return BufferUsage::StaticCopy;
        case GL_STATIC_DRAW:
            return BufferUsage::StaticDraw;
        case GL_STATIC_READ:
            return BufferUsage::StaticRead;
        case GL_STREAM_COPY:
            return BufferUsage::StreamCopy;
        case GL_STREAM_DRAW:
            return BufferUsage::StreamDraw;
        case GL_STREAM_READ:
            return BufferUsage::StreamRead;
        default:
            return BufferUsage::InvalidEnum;
    }
}

GLenum ToGLenum(BufferUsage from)
{
    switch (from)
    {
        case BufferUsage::DynamicCopy:
            return GL_DYNAMIC_COPY;
        case BufferUsage::DynamicDraw:
            return GL_DYNAMIC_DRAW;
        case BufferUsage::DynamicRead:
            return GL_DYNAMIC_READ;
        case BufferUsage::StaticCopy:
            return GL_STATIC_COPY;
        case BufferUsage::StaticDraw:
            return GL_STATIC_DRAW;
        case BufferUsage::StaticRead:
            return GL_STATIC_READ;
        case BufferUsage::StreamCopy:
            return GL_STREAM_COPY;
        case BufferUsage::StreamDraw:
            return GL_STREAM_DRAW;
        case BufferUsage::StreamRead:
            return GL_STREAM_READ;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, BufferUsage value)
{
    switch (value)
    {
        case BufferUsage::DynamicCopy:
            os << "GL_DYNAMIC_COPY";
            break;
        case BufferUsage::DynamicDraw:
            os << "GL_DYNAMIC_DRAW";
            break;
        case BufferUsage::DynamicRead:
            os << "GL_DYNAMIC_READ";
            break;
        case BufferUsage::StaticCopy:
            os << "GL_STATIC_COPY";
            break;
        case BufferUsage::StaticDraw:
            os << "GL_STATIC_DRAW";
            break;
        case BufferUsage::StaticRead:
            os << "GL_STATIC_READ";
            break;
        case BufferUsage::StreamCopy:
            os << "GL_STREAM_COPY";
            break;
        case BufferUsage::StreamDraw:
            os << "GL_STREAM_DRAW";
            break;
        case BufferUsage::StreamRead:
            os << "GL_STREAM_READ";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
ClientVertexArrayType FromGLenum<ClientVertexArrayType>(GLenum from)
{
    switch (from)
    {
        case GL_COLOR_ARRAY:
            return ClientVertexArrayType::Color;
        case GL_NORMAL_ARRAY:
            return ClientVertexArrayType::Normal;
        case GL_POINT_SIZE_ARRAY_OES:
            return ClientVertexArrayType::PointSize;
        case GL_TEXTURE_COORD_ARRAY:
            return ClientVertexArrayType::TextureCoord;
        case GL_VERTEX_ARRAY:
            return ClientVertexArrayType::Vertex;
        default:
            return ClientVertexArrayType::InvalidEnum;
    }
}

GLenum ToGLenum(ClientVertexArrayType from)
{
    switch (from)
    {
        case ClientVertexArrayType::Color:
            return GL_COLOR_ARRAY;
        case ClientVertexArrayType::Normal:
            return GL_NORMAL_ARRAY;
        case ClientVertexArrayType::PointSize:
            return GL_POINT_SIZE_ARRAY_OES;
        case ClientVertexArrayType::TextureCoord:
            return GL_TEXTURE_COORD_ARRAY;
        case ClientVertexArrayType::Vertex:
            return GL_VERTEX_ARRAY;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, ClientVertexArrayType value)
{
    switch (value)
    {
        case ClientVertexArrayType::Color:
            os << "GL_COLOR_ARRAY";
            break;
        case ClientVertexArrayType::Normal:
            os << "GL_NORMAL_ARRAY";
            break;
        case ClientVertexArrayType::PointSize:
            os << "GL_POINT_SIZE_ARRAY_OES";
            break;
        case ClientVertexArrayType::TextureCoord:
            os << "GL_TEXTURE_COORD_ARRAY";
            break;
        case ClientVertexArrayType::Vertex:
            os << "GL_VERTEX_ARRAY";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
ClipDepthMode FromGLenum<ClipDepthMode>(GLenum from)
{
    switch (from)
    {
        case GL_NEGATIVE_ONE_TO_ONE_EXT:
            return ClipDepthMode::NegativeOneToOne;
        case GL_ZERO_TO_ONE_EXT:
            return ClipDepthMode::ZeroToOne;
        default:
            return ClipDepthMode::InvalidEnum;
    }
}

GLenum ToGLenum(ClipDepthMode from)
{
    switch (from)
    {
        case ClipDepthMode::NegativeOneToOne:
            return GL_NEGATIVE_ONE_TO_ONE_EXT;
        case ClipDepthMode::ZeroToOne:
            return GL_ZERO_TO_ONE_EXT;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, ClipDepthMode value)
{
    switch (value)
    {
        case ClipDepthMode::NegativeOneToOne:
            os << "GL_NEGATIVE_ONE_TO_ONE_EXT";
            break;
        case ClipDepthMode::ZeroToOne:
            os << "GL_ZERO_TO_ONE_EXT";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
ClipOrigin FromGLenum<ClipOrigin>(GLenum from)
{
    switch (from)
    {
        case GL_LOWER_LEFT_EXT:
            return ClipOrigin::LowerLeft;
        case GL_UPPER_LEFT_EXT:
            return ClipOrigin::UpperLeft;
        default:
            return ClipOrigin::InvalidEnum;
    }
}

GLenum ToGLenum(ClipOrigin from)
{
    switch (from)
    {
        case ClipOrigin::LowerLeft:
            return GL_LOWER_LEFT_EXT;
        case ClipOrigin::UpperLeft:
            return GL_UPPER_LEFT_EXT;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, ClipOrigin value)
{
    switch (value)
    {
        case ClipOrigin::LowerLeft:
            os << "GL_LOWER_LEFT_EXT";
            break;
        case ClipOrigin::UpperLeft:
            os << "GL_UPPER_LEFT_EXT";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
CombinerOp FromGLenum<CombinerOp>(GLenum from)
{
    switch (from)
    {
        case GL_NONE:
            return CombinerOp::Undefined;
        case GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT:
            return CombinerOp::Keep;
        case GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT:
            return CombinerOp::Replace;
        case GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MIN_EXT:
            return CombinerOp::Min;
        case GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_EXT:
            return CombinerOp::Max;
        case GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_EXT:
            return CombinerOp::Mul;
        default:
            return CombinerOp::InvalidEnum;
    }
}

GLenum ToGLenum(CombinerOp from)
{
    switch (from)
    {
        case CombinerOp::Undefined:
            return GL_NONE;
        case CombinerOp::Keep:
            return GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT;
        case CombinerOp::Replace:
            return GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT;
        case CombinerOp::Min:
            return GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MIN_EXT;
        case CombinerOp::Max:
            return GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_EXT;
        case CombinerOp::Mul:
            return GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_EXT;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, CombinerOp value)
{
    switch (value)
    {
        case CombinerOp::Undefined:
            os << "GL_NONE";
            break;
        case CombinerOp::Keep:
            os << "GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT";
            break;
        case CombinerOp::Replace:
            os << "GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT";
            break;
        case CombinerOp::Min:
            os << "GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MIN_EXT";
            break;
        case CombinerOp::Max:
            os << "GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_EXT";
            break;
        case CombinerOp::Mul:
            os << "GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_EXT";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
CullFaceMode FromGLenum<CullFaceMode>(GLenum from)
{
    switch (from)
    {
        case GL_BACK:
            return CullFaceMode::Back;
        case GL_FRONT:
            return CullFaceMode::Front;
        case GL_FRONT_AND_BACK:
            return CullFaceMode::FrontAndBack;
        default:
            return CullFaceMode::InvalidEnum;
    }
}

GLenum ToGLenum(CullFaceMode from)
{
    switch (from)
    {
        case CullFaceMode::Back:
            return GL_BACK;
        case CullFaceMode::Front:
            return GL_FRONT;
        case CullFaceMode::FrontAndBack:
            return GL_FRONT_AND_BACK;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, CullFaceMode value)
{
    switch (value)
    {
        case CullFaceMode::Back:
            os << "GL_BACK";
            break;
        case CullFaceMode::Front:
            os << "GL_FRONT";
            break;
        case CullFaceMode::FrontAndBack:
            os << "GL_FRONT_AND_BACK";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
FilterMode FromGLenum<FilterMode>(GLenum from)
{
    switch (from)
    {
        case GL_NEAREST:
            return FilterMode::Nearest;
        case GL_LINEAR:
            return FilterMode::Linear;
        case GL_NEAREST_MIPMAP_NEAREST:
            return FilterMode::NearestMipmapNearest;
        case GL_NEAREST_MIPMAP_LINEAR:
            return FilterMode::NearestMipmapLinear;
        case GL_LINEAR_MIPMAP_LINEAR:
            return FilterMode::LinearMipmapLinear;
        default:
            return FilterMode::InvalidEnum;
    }
}

GLenum ToGLenum(FilterMode from)
{
    switch (from)
    {
        case FilterMode::Nearest:
            return GL_NEAREST;
        case FilterMode::Linear:
            return GL_LINEAR;
        case FilterMode::NearestMipmapNearest:
            return GL_NEAREST_MIPMAP_NEAREST;
        case FilterMode::NearestMipmapLinear:
            return GL_NEAREST_MIPMAP_LINEAR;
        case FilterMode::LinearMipmapLinear:
            return GL_LINEAR_MIPMAP_LINEAR;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, FilterMode value)
{
    switch (value)
    {
        case FilterMode::Nearest:
            os << "GL_NEAREST";
            break;
        case FilterMode::Linear:
            os << "GL_LINEAR";
            break;
        case FilterMode::NearestMipmapNearest:
            os << "GL_NEAREST_MIPMAP_NEAREST";
            break;
        case FilterMode::NearestMipmapLinear:
            os << "GL_NEAREST_MIPMAP_LINEAR";
            break;
        case FilterMode::LinearMipmapLinear:
            os << "GL_LINEAR_MIPMAP_LINEAR";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
FogMode FromGLenum<FogMode>(GLenum from)
{
    switch (from)
    {
        case GL_EXP:
            return FogMode::Exp;
        case GL_EXP2:
            return FogMode::Exp2;
        case GL_LINEAR:
            return FogMode::Linear;
        default:
            return FogMode::InvalidEnum;
    }
}

GLenum ToGLenum(FogMode from)
{
    switch (from)
    {
        case FogMode::Exp:
            return GL_EXP;
        case FogMode::Exp2:
            return GL_EXP2;
        case FogMode::Linear:
            return GL_LINEAR;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, FogMode value)
{
    switch (value)
    {
        case FogMode::Exp:
            os << "GL_EXP";
            break;
        case FogMode::Exp2:
            os << "GL_EXP2";
            break;
        case FogMode::Linear:
            os << "GL_LINEAR";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
FramebufferParameter FromGLenum<FramebufferParameter>(GLenum from)
{
    switch (from)
    {
        case GL_FRAMEBUFFER_DEFAULT_WIDTH:
            return FramebufferParameter::DefaultWidth;
        case GL_FRAMEBUFFER_DEFAULT_HEIGHT:
            return FramebufferParameter::DefaultHeight;
        case GL_FRAMEBUFFER_DEFAULT_LAYERS:
            return FramebufferParameter::DefaultLayers;
        case GL_FRAMEBUFFER_DEFAULT_SAMPLES:
            return FramebufferParameter::DefaultSamples;
        case GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS:
            return FramebufferParameter::DefaultFixedSampleLocations;
        case GL_FRAMEBUFFER_FLIP_Y_MESA:
            return FramebufferParameter::FlipY;
        default:
            return FramebufferParameter::InvalidEnum;
    }
}

GLenum ToGLenum(FramebufferParameter from)
{
    switch (from)
    {
        case FramebufferParameter::DefaultWidth:
            return GL_FRAMEBUFFER_DEFAULT_WIDTH;
        case FramebufferParameter::DefaultHeight:
            return GL_FRAMEBUFFER_DEFAULT_HEIGHT;
        case FramebufferParameter::DefaultLayers:
            return GL_FRAMEBUFFER_DEFAULT_LAYERS;
        case FramebufferParameter::DefaultSamples:
            return GL_FRAMEBUFFER_DEFAULT_SAMPLES;
        case FramebufferParameter::DefaultFixedSampleLocations:
            return GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS;
        case FramebufferParameter::FlipY:
            return GL_FRAMEBUFFER_FLIP_Y_MESA;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, FramebufferParameter value)
{
    switch (value)
    {
        case FramebufferParameter::DefaultWidth:
            os << "GL_FRAMEBUFFER_DEFAULT_WIDTH";
            break;
        case FramebufferParameter::DefaultHeight:
            os << "GL_FRAMEBUFFER_DEFAULT_HEIGHT";
            break;
        case FramebufferParameter::DefaultLayers:
            os << "GL_FRAMEBUFFER_DEFAULT_LAYERS";
            break;
        case FramebufferParameter::DefaultSamples:
            os << "GL_FRAMEBUFFER_DEFAULT_SAMPLES";
            break;
        case FramebufferParameter::DefaultFixedSampleLocations:
            os << "GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS";
            break;
        case FramebufferParameter::FlipY:
            os << "GL_FRAMEBUFFER_FLIP_Y_MESA";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
GraphicsResetStatus FromGLenum<GraphicsResetStatus>(GLenum from)
{
    switch (from)
    {
        case GL_NO_ERROR:
            return GraphicsResetStatus::NoError;
        case GL_GUILTY_CONTEXT_RESET:
            return GraphicsResetStatus::GuiltyContextReset;
        case GL_INNOCENT_CONTEXT_RESET:
            return GraphicsResetStatus::InnocentContextReset;
        case GL_UNKNOWN_CONTEXT_RESET:
            return GraphicsResetStatus::UnknownContextReset;
        case GL_PURGED_CONTEXT_RESET_NV:
            return GraphicsResetStatus::PurgedContextResetNV;
        default:
            return GraphicsResetStatus::InvalidEnum;
    }
}

GLenum ToGLenum(GraphicsResetStatus from)
{
    switch (from)
    {
        case GraphicsResetStatus::NoError:
            return GL_NO_ERROR;
        case GraphicsResetStatus::GuiltyContextReset:
            return GL_GUILTY_CONTEXT_RESET;
        case GraphicsResetStatus::InnocentContextReset:
            return GL_INNOCENT_CONTEXT_RESET;
        case GraphicsResetStatus::UnknownContextReset:
            return GL_UNKNOWN_CONTEXT_RESET;
        case GraphicsResetStatus::PurgedContextResetNV:
            return GL_PURGED_CONTEXT_RESET_NV;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, GraphicsResetStatus value)
{
    switch (value)
    {
        case GraphicsResetStatus::NoError:
            os << "GL_NO_ERROR";
            break;
        case GraphicsResetStatus::GuiltyContextReset:
            os << "GL_GUILTY_CONTEXT_RESET";
            break;
        case GraphicsResetStatus::InnocentContextReset:
            os << "GL_INNOCENT_CONTEXT_RESET";
            break;
        case GraphicsResetStatus::UnknownContextReset:
            os << "GL_UNKNOWN_CONTEXT_RESET";
            break;
        case GraphicsResetStatus::PurgedContextResetNV:
            os << "GL_PURGED_CONTEXT_RESET_NV";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
HandleType FromGLenum<HandleType>(GLenum from)
{
    switch (from)
    {
        case GL_HANDLE_TYPE_OPAQUE_FD_EXT:
            return HandleType::OpaqueFd;
        case GL_HANDLE_TYPE_ZIRCON_VMO_ANGLE:
            return HandleType::ZirconVmo;
        case GL_HANDLE_TYPE_ZIRCON_EVENT_ANGLE:
            return HandleType::ZirconEvent;
        default:
            return HandleType::InvalidEnum;
    }
}

GLenum ToGLenum(HandleType from)
{
    switch (from)
    {
        case HandleType::OpaqueFd:
            return GL_HANDLE_TYPE_OPAQUE_FD_EXT;
        case HandleType::ZirconVmo:
            return GL_HANDLE_TYPE_ZIRCON_VMO_ANGLE;
        case HandleType::ZirconEvent:
            return GL_HANDLE_TYPE_ZIRCON_EVENT_ANGLE;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, HandleType value)
{
    switch (value)
    {
        case HandleType::OpaqueFd:
            os << "GL_HANDLE_TYPE_OPAQUE_FD_EXT";
            break;
        case HandleType::ZirconVmo:
            os << "GL_HANDLE_TYPE_ZIRCON_VMO_ANGLE";
            break;
        case HandleType::ZirconEvent:
            os << "GL_HANDLE_TYPE_ZIRCON_EVENT_ANGLE";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
HintSetting FromGLenum<HintSetting>(GLenum from)
{
    switch (from)
    {
        case GL_DONT_CARE:
            return HintSetting::DontCare;
        case GL_FASTEST:
            return HintSetting::Fastest;
        case GL_NICEST:
            return HintSetting::Nicest;
        default:
            return HintSetting::InvalidEnum;
    }
}

GLenum ToGLenum(HintSetting from)
{
    switch (from)
    {
        case HintSetting::DontCare:
            return GL_DONT_CARE;
        case HintSetting::Fastest:
            return GL_FASTEST;
        case HintSetting::Nicest:
            return GL_NICEST;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, HintSetting value)
{
    switch (value)
    {
        case HintSetting::DontCare:
            os << "GL_DONT_CARE";
            break;
        case HintSetting::Fastest:
            os << "GL_FASTEST";
            break;
        case HintSetting::Nicest:
            os << "GL_NICEST";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
ImageLayout FromGLenum<ImageLayout>(GLenum from)
{
    switch (from)
    {
        case GL_NONE:
            return ImageLayout::Undefined;
        case GL_LAYOUT_GENERAL_EXT:
            return ImageLayout::General;
        case GL_LAYOUT_COLOR_ATTACHMENT_EXT:
            return ImageLayout::ColorAttachment;
        case GL_LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT:
            return ImageLayout::DepthStencilAttachment;
        case GL_LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT:
            return ImageLayout::DepthStencilReadOnlyAttachment;
        case GL_LAYOUT_SHADER_READ_ONLY_EXT:
            return ImageLayout::ShaderReadOnly;
        case GL_LAYOUT_TRANSFER_SRC_EXT:
            return ImageLayout::TransferSrc;
        case GL_LAYOUT_TRANSFER_DST_EXT:
            return ImageLayout::TransferDst;
        case GL_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_EXT:
            return ImageLayout::DepthReadOnlyStencilAttachment;
        case GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT:
            return ImageLayout::DepthAttachmentStencilReadOnly;
        default:
            return ImageLayout::InvalidEnum;
    }
}

GLenum ToGLenum(ImageLayout from)
{
    switch (from)
    {
        case ImageLayout::Undefined:
            return GL_NONE;
        case ImageLayout::General:
            return GL_LAYOUT_GENERAL_EXT;
        case ImageLayout::ColorAttachment:
            return GL_LAYOUT_COLOR_ATTACHMENT_EXT;
        case ImageLayout::DepthStencilAttachment:
            return GL_LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT;
        case ImageLayout::DepthStencilReadOnlyAttachment:
            return GL_LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT;
        case ImageLayout::ShaderReadOnly:
            return GL_LAYOUT_SHADER_READ_ONLY_EXT;
        case ImageLayout::TransferSrc:
            return GL_LAYOUT_TRANSFER_SRC_EXT;
        case ImageLayout::TransferDst:
            return GL_LAYOUT_TRANSFER_DST_EXT;
        case ImageLayout::DepthReadOnlyStencilAttachment:
            return GL_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_EXT;
        case ImageLayout::DepthAttachmentStencilReadOnly:
            return GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, ImageLayout value)
{
    switch (value)
    {
        case ImageLayout::Undefined:
            os << "GL_NONE";
            break;
        case ImageLayout::General:
            os << "GL_LAYOUT_GENERAL_EXT";
            break;
        case ImageLayout::ColorAttachment:
            os << "GL_LAYOUT_COLOR_ATTACHMENT_EXT";
            break;
        case ImageLayout::DepthStencilAttachment:
            os << "GL_LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT";
            break;
        case ImageLayout::DepthStencilReadOnlyAttachment:
            os << "GL_LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT";
            break;
        case ImageLayout::ShaderReadOnly:
            os << "GL_LAYOUT_SHADER_READ_ONLY_EXT";
            break;
        case ImageLayout::TransferSrc:
            os << "GL_LAYOUT_TRANSFER_SRC_EXT";
            break;
        case ImageLayout::TransferDst:
            os << "GL_LAYOUT_TRANSFER_DST_EXT";
            break;
        case ImageLayout::DepthReadOnlyStencilAttachment:
            os << "GL_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_EXT";
            break;
        case ImageLayout::DepthAttachmentStencilReadOnly:
            os << "GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
LightParameter FromGLenum<LightParameter>(GLenum from)
{
    switch (from)
    {
        case GL_AMBIENT:
            return LightParameter::Ambient;
        case GL_AMBIENT_AND_DIFFUSE:
            return LightParameter::AmbientAndDiffuse;
        case GL_CONSTANT_ATTENUATION:
            return LightParameter::ConstantAttenuation;
        case GL_DIFFUSE:
            return LightParameter::Diffuse;
        case GL_LINEAR_ATTENUATION:
            return LightParameter::LinearAttenuation;
        case GL_POSITION:
            return LightParameter::Position;
        case GL_QUADRATIC_ATTENUATION:
            return LightParameter::QuadraticAttenuation;
        case GL_SPECULAR:
            return LightParameter::Specular;
        case GL_SPOT_CUTOFF:
            return LightParameter::SpotCutoff;
        case GL_SPOT_DIRECTION:
            return LightParameter::SpotDirection;
        case GL_SPOT_EXPONENT:
            return LightParameter::SpotExponent;
        default:
            return LightParameter::InvalidEnum;
    }
}

GLenum ToGLenum(LightParameter from)
{
    switch (from)
    {
        case LightParameter::Ambient:
            return GL_AMBIENT;
        case LightParameter::AmbientAndDiffuse:
            return GL_AMBIENT_AND_DIFFUSE;
        case LightParameter::ConstantAttenuation:
            return GL_CONSTANT_ATTENUATION;
        case LightParameter::Diffuse:
            return GL_DIFFUSE;
        case LightParameter::LinearAttenuation:
            return GL_LINEAR_ATTENUATION;
        case LightParameter::Position:
            return GL_POSITION;
        case LightParameter::QuadraticAttenuation:
            return GL_QUADRATIC_ATTENUATION;
        case LightParameter::Specular:
            return GL_SPECULAR;
        case LightParameter::SpotCutoff:
            return GL_SPOT_CUTOFF;
        case LightParameter::SpotDirection:
            return GL_SPOT_DIRECTION;
        case LightParameter::SpotExponent:
            return GL_SPOT_EXPONENT;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, LightParameter value)
{
    switch (value)
    {
        case LightParameter::Ambient:
            os << "GL_AMBIENT";
            break;
        case LightParameter::AmbientAndDiffuse:
            os << "GL_AMBIENT_AND_DIFFUSE";
            break;
        case LightParameter::ConstantAttenuation:
            os << "GL_CONSTANT_ATTENUATION";
            break;
        case LightParameter::Diffuse:
            os << "GL_DIFFUSE";
            break;
        case LightParameter::LinearAttenuation:
            os << "GL_LINEAR_ATTENUATION";
            break;
        case LightParameter::Position:
            os << "GL_POSITION";
            break;
        case LightParameter::QuadraticAttenuation:
            os << "GL_QUADRATIC_ATTENUATION";
            break;
        case LightParameter::Specular:
            os << "GL_SPECULAR";
            break;
        case LightParameter::SpotCutoff:
            os << "GL_SPOT_CUTOFF";
            break;
        case LightParameter::SpotDirection:
            os << "GL_SPOT_DIRECTION";
            break;
        case LightParameter::SpotExponent:
            os << "GL_SPOT_EXPONENT";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
LogicalOperation FromGLenum<LogicalOperation>(GLenum from)
{
    switch (from)
    {
        case GL_AND:
            return LogicalOperation::And;
        case GL_AND_INVERTED:
            return LogicalOperation::AndInverted;
        case GL_AND_REVERSE:
            return LogicalOperation::AndReverse;
        case GL_CLEAR:
            return LogicalOperation::Clear;
        case GL_COPY:
            return LogicalOperation::Copy;
        case GL_COPY_INVERTED:
            return LogicalOperation::CopyInverted;
        case GL_EQUIV:
            return LogicalOperation::Equiv;
        case GL_INVERT:
            return LogicalOperation::Invert;
        case GL_NAND:
            return LogicalOperation::Nand;
        case GL_NOOP:
            return LogicalOperation::Noop;
        case GL_NOR:
            return LogicalOperation::Nor;
        case GL_OR:
            return LogicalOperation::Or;
        case GL_OR_INVERTED:
            return LogicalOperation::OrInverted;
        case GL_OR_REVERSE:
            return LogicalOperation::OrReverse;
        case GL_SET:
            return LogicalOperation::Set;
        case GL_XOR:
            return LogicalOperation::Xor;
        default:
            return LogicalOperation::InvalidEnum;
    }
}

GLenum ToGLenum(LogicalOperation from)
{
    switch (from)
    {
        case LogicalOperation::And:
            return GL_AND;
        case LogicalOperation::AndInverted:
            return GL_AND_INVERTED;
        case LogicalOperation::AndReverse:
            return GL_AND_REVERSE;
        case LogicalOperation::Clear:
            return GL_CLEAR;
        case LogicalOperation::Copy:
            return GL_COPY;
        case LogicalOperation::CopyInverted:
            return GL_COPY_INVERTED;
        case LogicalOperation::Equiv:
            return GL_EQUIV;
        case LogicalOperation::Invert:
            return GL_INVERT;
        case LogicalOperation::Nand:
            return GL_NAND;
        case LogicalOperation::Noop:
            return GL_NOOP;
        case LogicalOperation::Nor:
            return GL_NOR;
        case LogicalOperation::Or:
            return GL_OR;
        case LogicalOperation::OrInverted:
            return GL_OR_INVERTED;
        case LogicalOperation::OrReverse:
            return GL_OR_REVERSE;
        case LogicalOperation::Set:
            return GL_SET;
        case LogicalOperation::Xor:
            return GL_XOR;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, LogicalOperation value)
{
    switch (value)
    {
        case LogicalOperation::And:
            os << "GL_AND";
            break;
        case LogicalOperation::AndInverted:
            os << "GL_AND_INVERTED";
            break;
        case LogicalOperation::AndReverse:
            os << "GL_AND_REVERSE";
            break;
        case LogicalOperation::Clear:
            os << "GL_CLEAR";
            break;
        case LogicalOperation::Copy:
            os << "GL_COPY";
            break;
        case LogicalOperation::CopyInverted:
            os << "GL_COPY_INVERTED";
            break;
        case LogicalOperation::Equiv:
            os << "GL_EQUIV";
            break;
        case LogicalOperation::Invert:
            os << "GL_INVERT";
            break;
        case LogicalOperation::Nand:
            os << "GL_NAND";
            break;
        case LogicalOperation::Noop:
            os << "GL_NOOP";
            break;
        case LogicalOperation::Nor:
            os << "GL_NOR";
            break;
        case LogicalOperation::Or:
            os << "GL_OR";
            break;
        case LogicalOperation::OrInverted:
            os << "GL_OR_INVERTED";
            break;
        case LogicalOperation::OrReverse:
            os << "GL_OR_REVERSE";
            break;
        case LogicalOperation::Set:
            os << "GL_SET";
            break;
        case LogicalOperation::Xor:
            os << "GL_XOR";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
MaterialParameter FromGLenum<MaterialParameter>(GLenum from)
{
    switch (from)
    {
        case GL_AMBIENT:
            return MaterialParameter::Ambient;
        case GL_AMBIENT_AND_DIFFUSE:
            return MaterialParameter::AmbientAndDiffuse;
        case GL_DIFFUSE:
            return MaterialParameter::Diffuse;
        case GL_EMISSION:
            return MaterialParameter::Emission;
        case GL_SHININESS:
            return MaterialParameter::Shininess;
        case GL_SPECULAR:
            return MaterialParameter::Specular;
        default:
            return MaterialParameter::InvalidEnum;
    }
}

GLenum ToGLenum(MaterialParameter from)
{
    switch (from)
    {
        case MaterialParameter::Ambient:
            return GL_AMBIENT;
        case MaterialParameter::AmbientAndDiffuse:
            return GL_AMBIENT_AND_DIFFUSE;
        case MaterialParameter::Diffuse:
            return GL_DIFFUSE;
        case MaterialParameter::Emission:
            return GL_EMISSION;
        case MaterialParameter::Shininess:
            return GL_SHININESS;
        case MaterialParameter::Specular:
            return GL_SPECULAR;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, MaterialParameter value)
{
    switch (value)
    {
        case MaterialParameter::Ambient:
            os << "GL_AMBIENT";
            break;
        case MaterialParameter::AmbientAndDiffuse:
            os << "GL_AMBIENT_AND_DIFFUSE";
            break;
        case MaterialParameter::Diffuse:
            os << "GL_DIFFUSE";
            break;
        case MaterialParameter::Emission:
            os << "GL_EMISSION";
            break;
        case MaterialParameter::Shininess:
            os << "GL_SHININESS";
            break;
        case MaterialParameter::Specular:
            os << "GL_SPECULAR";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
MatrixType FromGLenum<MatrixType>(GLenum from)
{
    switch (from)
    {
        case GL_MODELVIEW:
            return MatrixType::Modelview;
        case GL_PROJECTION:
            return MatrixType::Projection;
        case GL_TEXTURE:
            return MatrixType::Texture;
        default:
            return MatrixType::InvalidEnum;
    }
}

GLenum ToGLenum(MatrixType from)
{
    switch (from)
    {
        case MatrixType::Modelview:
            return GL_MODELVIEW;
        case MatrixType::Projection:
            return GL_PROJECTION;
        case MatrixType::Texture:
            return GL_TEXTURE;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, MatrixType value)
{
    switch (value)
    {
        case MatrixType::Modelview:
            os << "GL_MODELVIEW";
            break;
        case MatrixType::Projection:
            os << "GL_PROJECTION";
            break;
        case MatrixType::Texture:
            os << "GL_TEXTURE";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
PlaneParameter FromGLenum<PlaneParameter>(GLenum from)
{
    switch (from)
    {
        case GL_PIXEL_LOCAL_INTERNAL_FORMAT_ANGLE:
            return PlaneParameter::InternalFormat;
        case GL_PIXEL_LOCAL_TEXTURE_NAME_ANGLE:
            return PlaneParameter::TextureName;
        case GL_PIXEL_LOCAL_TEXTURE_LEVEL_ANGLE:
            return PlaneParameter::TextureLevel;
        case GL_PIXEL_LOCAL_TEXTURE_LAYER_ANGLE:
            return PlaneParameter::TextureLayer;
        case GL_PIXEL_LOCAL_USAGE_ANGLE:
            return PlaneParameter::Usage;
        case GL_PIXEL_LOCAL_CLEAR_VALUE_FLOAT_ANGLE:
            return PlaneParameter::ClearValueFloat;
        case GL_PIXEL_LOCAL_CLEAR_VALUE_INT_ANGLE:
            return PlaneParameter::ClearValueInt;
        case GL_PIXEL_LOCAL_CLEAR_VALUE_UNSIGNED_INT_ANGLE:
            return PlaneParameter::ClearValueUnsignedInt;
        default:
            return PlaneParameter::InvalidEnum;
    }
}

GLenum ToGLenum(PlaneParameter from)
{
    switch (from)
    {
        case PlaneParameter::InternalFormat:
            return GL_PIXEL_LOCAL_INTERNAL_FORMAT_ANGLE;
        case PlaneParameter::TextureName:
            return GL_PIXEL_LOCAL_TEXTURE_NAME_ANGLE;
        case PlaneParameter::TextureLevel:
            return GL_PIXEL_LOCAL_TEXTURE_LEVEL_ANGLE;
        case PlaneParameter::TextureLayer:
            return GL_PIXEL_LOCAL_TEXTURE_LAYER_ANGLE;
        case PlaneParameter::Usage:
            return GL_PIXEL_LOCAL_USAGE_ANGLE;
        case PlaneParameter::ClearValueFloat:
            return GL_PIXEL_LOCAL_CLEAR_VALUE_FLOAT_ANGLE;
        case PlaneParameter::ClearValueInt:
            return GL_PIXEL_LOCAL_CLEAR_VALUE_INT_ANGLE;
        case PlaneParameter::ClearValueUnsignedInt:
            return GL_PIXEL_LOCAL_CLEAR_VALUE_UNSIGNED_INT_ANGLE;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, PlaneParameter value)
{
    switch (value)
    {
        case PlaneParameter::InternalFormat:
            os << "GL_PIXEL_LOCAL_INTERNAL_FORMAT_ANGLE";
            break;
        case PlaneParameter::TextureName:
            os << "GL_PIXEL_LOCAL_TEXTURE_NAME_ANGLE";
            break;
        case PlaneParameter::TextureLevel:
            os << "GL_PIXEL_LOCAL_TEXTURE_LEVEL_ANGLE";
            break;
        case PlaneParameter::TextureLayer:
            os << "GL_PIXEL_LOCAL_TEXTURE_LAYER_ANGLE";
            break;
        case PlaneParameter::Usage:
            os << "GL_PIXEL_LOCAL_USAGE_ANGLE";
            break;
        case PlaneParameter::ClearValueFloat:
            os << "GL_PIXEL_LOCAL_CLEAR_VALUE_FLOAT_ANGLE";
            break;
        case PlaneParameter::ClearValueInt:
            os << "GL_PIXEL_LOCAL_CLEAR_VALUE_INT_ANGLE";
            break;
        case PlaneParameter::ClearValueUnsignedInt:
            os << "GL_PIXEL_LOCAL_CLEAR_VALUE_UNSIGNED_INT_ANGLE";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
PointParameter FromGLenum<PointParameter>(GLenum from)
{
    switch (from)
    {
        case GL_POINT_SIZE_MIN:
            return PointParameter::PointSizeMin;
        case GL_POINT_SIZE_MAX:
            return PointParameter::PointSizeMax;
        case GL_POINT_FADE_THRESHOLD_SIZE:
            return PointParameter::PointFadeThresholdSize;
        case GL_POINT_DISTANCE_ATTENUATION:
            return PointParameter::PointDistanceAttenuation;
        default:
            return PointParameter::InvalidEnum;
    }
}

GLenum ToGLenum(PointParameter from)
{
    switch (from)
    {
        case PointParameter::PointSizeMin:
            return GL_POINT_SIZE_MIN;
        case PointParameter::PointSizeMax:
            return GL_POINT_SIZE_MAX;
        case PointParameter::PointFadeThresholdSize:
            return GL_POINT_FADE_THRESHOLD_SIZE;
        case PointParameter::PointDistanceAttenuation:
            return GL_POINT_DISTANCE_ATTENUATION;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, PointParameter value)
{
    switch (value)
    {
        case PointParameter::PointSizeMin:
            os << "GL_POINT_SIZE_MIN";
            break;
        case PointParameter::PointSizeMax:
            os << "GL_POINT_SIZE_MAX";
            break;
        case PointParameter::PointFadeThresholdSize:
            os << "GL_POINT_FADE_THRESHOLD_SIZE";
            break;
        case PointParameter::PointDistanceAttenuation:
            os << "GL_POINT_DISTANCE_ATTENUATION";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
PolygonMode FromGLenum<PolygonMode>(GLenum from)
{
    switch (from)
    {
        case GL_POINT_NV:
            return PolygonMode::Point;
        case GL_LINE_NV:
            return PolygonMode::Line;
        case GL_FILL_NV:
            return PolygonMode::Fill;
        default:
            return PolygonMode::InvalidEnum;
    }
}

GLenum ToGLenum(PolygonMode from)
{
    switch (from)
    {
        case PolygonMode::Point:
            return GL_POINT_NV;
        case PolygonMode::Line:
            return GL_LINE_NV;
        case PolygonMode::Fill:
            return GL_FILL_NV;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, PolygonMode value)
{
    switch (value)
    {
        case PolygonMode::Point:
            os << "GL_POINT_NV";
            break;
        case PolygonMode::Line:
            os << "GL_LINE_NV";
            break;
        case PolygonMode::Fill:
            os << "GL_FILL_NV";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
ProvokingVertexConvention FromGLenum<ProvokingVertexConvention>(GLenum from)
{
    switch (from)
    {
        case GL_FIRST_VERTEX_CONVENTION_ANGLE:
            return ProvokingVertexConvention::FirstVertexConvention;
        case GL_LAST_VERTEX_CONVENTION_ANGLE:
            return ProvokingVertexConvention::LastVertexConvention;
        default:
            return ProvokingVertexConvention::InvalidEnum;
    }
}

GLenum ToGLenum(ProvokingVertexConvention from)
{
    switch (from)
    {
        case ProvokingVertexConvention::FirstVertexConvention:
            return GL_FIRST_VERTEX_CONVENTION_ANGLE;
        case ProvokingVertexConvention::LastVertexConvention:
            return GL_LAST_VERTEX_CONVENTION_ANGLE;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, ProvokingVertexConvention value)
{
    switch (value)
    {
        case ProvokingVertexConvention::FirstVertexConvention:
            os << "GL_FIRST_VERTEX_CONVENTION_ANGLE";
            break;
        case ProvokingVertexConvention::LastVertexConvention:
            os << "GL_LAST_VERTEX_CONVENTION_ANGLE";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
QueryObjectParameter FromGLenum<QueryObjectParameter>(GLenum from)
{
    switch (from)
    {
        case GL_QUERY_RESULT:
            return QueryObjectParameter::QueryResult;
        case GL_QUERY_RESULT_AVAILABLE:
            return QueryObjectParameter::QueryResultAvailable;
        default:
            return QueryObjectParameter::InvalidEnum;
    }
}

GLenum ToGLenum(QueryObjectParameter from)
{
    switch (from)
    {
        case QueryObjectParameter::QueryResult:
            return GL_QUERY_RESULT;
        case QueryObjectParameter::QueryResultAvailable:
            return GL_QUERY_RESULT_AVAILABLE;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, QueryObjectParameter value)
{
    switch (value)
    {
        case QueryObjectParameter::QueryResult:
            os << "GL_QUERY_RESULT";
            break;
        case QueryObjectParameter::QueryResultAvailable:
            os << "GL_QUERY_RESULT_AVAILABLE";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
QueryParameter FromGLenum<QueryParameter>(GLenum from)
{
    switch (from)
    {
        case GL_QUERY_COUNTER_BITS_EXT:
            return QueryParameter::QueryCounterBits;
        case GL_CURRENT_QUERY:
            return QueryParameter::CurrentQuery;
        default:
            return QueryParameter::InvalidEnum;
    }
}

GLenum ToGLenum(QueryParameter from)
{
    switch (from)
    {
        case QueryParameter::QueryCounterBits:
            return GL_QUERY_COUNTER_BITS_EXT;
        case QueryParameter::CurrentQuery:
            return GL_CURRENT_QUERY;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, QueryParameter value)
{
    switch (value)
    {
        case QueryParameter::QueryCounterBits:
            os << "GL_QUERY_COUNTER_BITS_EXT";
            break;
        case QueryParameter::CurrentQuery:
            os << "GL_CURRENT_QUERY";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
QueryType FromGLenum<QueryType>(GLenum from)
{
    switch (from)
    {
        case GL_ANY_SAMPLES_PASSED:
            return QueryType::AnySamples;
        case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
            return QueryType::AnySamplesConservative;
        case GL_PRIMITIVES_GENERATED_EXT:
            return QueryType::PrimitivesGenerated;
        case GL_TIME_ELAPSED_EXT:
            return QueryType::TimeElapsed;
        case GL_TIMESTAMP_EXT:
            return QueryType::Timestamp;
        case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
            return QueryType::TransformFeedbackPrimitivesWritten;
        default:
            return QueryType::InvalidEnum;
    }
}

GLenum ToGLenum(QueryType from)
{
    switch (from)
    {
        case QueryType::AnySamples:
            return GL_ANY_SAMPLES_PASSED;
        case QueryType::AnySamplesConservative:
            return GL_ANY_SAMPLES_PASSED_CONSERVATIVE;
        case QueryType::PrimitivesGenerated:
            return GL_PRIMITIVES_GENERATED_EXT;
        case QueryType::TimeElapsed:
            return GL_TIME_ELAPSED_EXT;
        case QueryType::Timestamp:
            return GL_TIMESTAMP_EXT;
        case QueryType::TransformFeedbackPrimitivesWritten:
            return GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, QueryType value)
{
    switch (value)
    {
        case QueryType::AnySamples:
            os << "GL_ANY_SAMPLES_PASSED";
            break;
        case QueryType::AnySamplesConservative:
            os << "GL_ANY_SAMPLES_PASSED_CONSERVATIVE";
            break;
        case QueryType::PrimitivesGenerated:
            os << "GL_PRIMITIVES_GENERATED_EXT";
            break;
        case QueryType::TimeElapsed:
            os << "GL_TIME_ELAPSED_EXT";
            break;
        case QueryType::Timestamp:
            os << "GL_TIMESTAMP_EXT";
            break;
        case QueryType::TransformFeedbackPrimitivesWritten:
            os << "GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
SamplerParameter FromGLenum<SamplerParameter>(GLenum from)
{
    switch (from)
    {
        case GL_TEXTURE_MAG_FILTER:
            return SamplerParameter::MagFilter;
        case GL_TEXTURE_MIN_FILTER:
            return SamplerParameter::MinFilter;
        case GL_TEXTURE_WRAP_S:
            return SamplerParameter::WrapS;
        case GL_TEXTURE_WRAP_T:
            return SamplerParameter::WrapT;
        case GL_TEXTURE_WRAP_R:
            return SamplerParameter::WrapR;
        case GL_TEXTURE_MIN_LOD:
            return SamplerParameter::MinLod;
        case GL_TEXTURE_MAX_LOD:
            return SamplerParameter::MaxLod;
        case GL_TEXTURE_COMPARE_MODE:
            return SamplerParameter::CompareMode;
        case GL_TEXTURE_COMPARE_FUNC:
            return SamplerParameter::CompareFunc;
        case GL_TEXTURE_BORDER_COLOR:
            return SamplerParameter::BorderColor;
        case GL_TEXTURE_MAX_ANISOTROPY_EXT:
            return SamplerParameter::MaxAnisotropy;
        case GL_TEXTURE_SRGB_DECODE_EXT:
            return SamplerParameter::SrgbDecode;
        case GL_TEXTURE_LOD_BIAS_QCOM:
            return SamplerParameter::LodBiasQCOM;
        default:
            return SamplerParameter::InvalidEnum;
    }
}

GLenum ToGLenum(SamplerParameter from)
{
    switch (from)
    {
        case SamplerParameter::MagFilter:
            return GL_TEXTURE_MAG_FILTER;
        case SamplerParameter::MinFilter:
            return GL_TEXTURE_MIN_FILTER;
        case SamplerParameter::WrapS:
            return GL_TEXTURE_WRAP_S;
        case SamplerParameter::WrapT:
            return GL_TEXTURE_WRAP_T;
        case SamplerParameter::WrapR:
            return GL_TEXTURE_WRAP_R;
        case SamplerParameter::MinLod:
            return GL_TEXTURE_MIN_LOD;
        case SamplerParameter::MaxLod:
            return GL_TEXTURE_MAX_LOD;
        case SamplerParameter::CompareMode:
            return GL_TEXTURE_COMPARE_MODE;
        case SamplerParameter::CompareFunc:
            return GL_TEXTURE_COMPARE_FUNC;
        case SamplerParameter::BorderColor:
            return GL_TEXTURE_BORDER_COLOR;
        case SamplerParameter::MaxAnisotropy:
            return GL_TEXTURE_MAX_ANISOTROPY_EXT;
        case SamplerParameter::SrgbDecode:
            return GL_TEXTURE_SRGB_DECODE_EXT;
        case SamplerParameter::LodBiasQCOM:
            return GL_TEXTURE_LOD_BIAS_QCOM;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, SamplerParameter value)
{
    switch (value)
    {
        case SamplerParameter::MagFilter:
            os << "GL_TEXTURE_MAG_FILTER";
            break;
        case SamplerParameter::MinFilter:
            os << "GL_TEXTURE_MIN_FILTER";
            break;
        case SamplerParameter::WrapS:
            os << "GL_TEXTURE_WRAP_S";
            break;
        case SamplerParameter::WrapT:
            os << "GL_TEXTURE_WRAP_T";
            break;
        case SamplerParameter::WrapR:
            os << "GL_TEXTURE_WRAP_R";
            break;
        case SamplerParameter::MinLod:
            os << "GL_TEXTURE_MIN_LOD";
            break;
        case SamplerParameter::MaxLod:
            os << "GL_TEXTURE_MAX_LOD";
            break;
        case SamplerParameter::CompareMode:
            os << "GL_TEXTURE_COMPARE_MODE";
            break;
        case SamplerParameter::CompareFunc:
            os << "GL_TEXTURE_COMPARE_FUNC";
            break;
        case SamplerParameter::BorderColor:
            os << "GL_TEXTURE_BORDER_COLOR";
            break;
        case SamplerParameter::MaxAnisotropy:
            os << "GL_TEXTURE_MAX_ANISOTROPY_EXT";
            break;
        case SamplerParameter::SrgbDecode:
            os << "GL_TEXTURE_SRGB_DECODE_EXT";
            break;
        case SamplerParameter::LodBiasQCOM:
            os << "GL_TEXTURE_LOD_BIAS_QCOM";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
ShaderParameter FromGLenum<ShaderParameter>(GLenum from)
{
    switch (from)
    {
        case GL_SHADER_TYPE:
            return ShaderParameter::ShaderType;
        case GL_DELETE_STATUS:
            return ShaderParameter::DeleteStatus;
        case GL_COMPILE_STATUS:
            return ShaderParameter::CompileStatus;
        case GL_INFO_LOG_LENGTH:
            return ShaderParameter::InfoLogLength;
        case GL_SHADER_SOURCE_LENGTH:
            return ShaderParameter::ShaderSourceLength;
        case GL_COMPLETION_STATUS_KHR:
            return ShaderParameter::CompletionStatus;
        case GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE:
            return ShaderParameter::TranslatedShaderSourceLength;
        default:
            return ShaderParameter::InvalidEnum;
    }
}

GLenum ToGLenum(ShaderParameter from)
{
    switch (from)
    {
        case ShaderParameter::ShaderType:
            return GL_SHADER_TYPE;
        case ShaderParameter::DeleteStatus:
            return GL_DELETE_STATUS;
        case ShaderParameter::CompileStatus:
            return GL_COMPILE_STATUS;
        case ShaderParameter::InfoLogLength:
            return GL_INFO_LOG_LENGTH;
        case ShaderParameter::ShaderSourceLength:
            return GL_SHADER_SOURCE_LENGTH;
        case ShaderParameter::CompletionStatus:
            return GL_COMPLETION_STATUS_KHR;
        case ShaderParameter::TranslatedShaderSourceLength:
            return GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, ShaderParameter value)
{
    switch (value)
    {
        case ShaderParameter::ShaderType:
            os << "GL_SHADER_TYPE";
            break;
        case ShaderParameter::DeleteStatus:
            os << "GL_DELETE_STATUS";
            break;
        case ShaderParameter::CompileStatus:
            os << "GL_COMPILE_STATUS";
            break;
        case ShaderParameter::InfoLogLength:
            os << "GL_INFO_LOG_LENGTH";
            break;
        case ShaderParameter::ShaderSourceLength:
            os << "GL_SHADER_SOURCE_LENGTH";
            break;
        case ShaderParameter::CompletionStatus:
            os << "GL_COMPLETION_STATUS_KHR";
            break;
        case ShaderParameter::TranslatedShaderSourceLength:
            os << "GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
ShaderType FromGLenum<ShaderType>(GLenum from)
{
    switch (from)
    {
        case GL_VERTEX_SHADER:
            return ShaderType::Vertex;
        case GL_TESS_CONTROL_SHADER_EXT:
            return ShaderType::TessControl;
        case GL_TESS_EVALUATION_SHADER_EXT:
            return ShaderType::TessEvaluation;
        case GL_GEOMETRY_SHADER_EXT:
            return ShaderType::Geometry;
        case GL_FRAGMENT_SHADER:
            return ShaderType::Fragment;
        case GL_COMPUTE_SHADER:
            return ShaderType::Compute;
        default:
            return ShaderType::InvalidEnum;
    }
}

GLenum ToGLenum(ShaderType from)
{
    switch (from)
    {
        case ShaderType::Vertex:
            return GL_VERTEX_SHADER;
        case ShaderType::TessControl:
            return GL_TESS_CONTROL_SHADER_EXT;
        case ShaderType::TessEvaluation:
            return GL_TESS_EVALUATION_SHADER_EXT;
        case ShaderType::Geometry:
            return GL_GEOMETRY_SHADER_EXT;
        case ShaderType::Fragment:
            return GL_FRAGMENT_SHADER;
        case ShaderType::Compute:
            return GL_COMPUTE_SHADER;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, ShaderType value)
{
    switch (value)
    {
        case ShaderType::Vertex:
            os << "GL_VERTEX_SHADER";
            break;
        case ShaderType::TessControl:
            os << "GL_TESS_CONTROL_SHADER_EXT";
            break;
        case ShaderType::TessEvaluation:
            os << "GL_TESS_EVALUATION_SHADER_EXT";
            break;
        case ShaderType::Geometry:
            os << "GL_GEOMETRY_SHADER_EXT";
            break;
        case ShaderType::Fragment:
            os << "GL_FRAGMENT_SHADER";
            break;
        case ShaderType::Compute:
            os << "GL_COMPUTE_SHADER";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
ShadingModel FromGLenum<ShadingModel>(GLenum from)
{
    switch (from)
    {
        case GL_FLAT:
            return ShadingModel::Flat;
        case GL_SMOOTH:
            return ShadingModel::Smooth;
        default:
            return ShadingModel::InvalidEnum;
    }
}

GLenum ToGLenum(ShadingModel from)
{
    switch (from)
    {
        case ShadingModel::Flat:
            return GL_FLAT;
        case ShadingModel::Smooth:
            return GL_SMOOTH;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, ShadingModel value)
{
    switch (value)
    {
        case ShadingModel::Flat:
            os << "GL_FLAT";
            break;
        case ShadingModel::Smooth:
            os << "GL_SMOOTH";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
ShadingRate FromGLenum<ShadingRate>(GLenum from)
{
    switch (from)
    {
        case GL_NONE:
            return ShadingRate::Undefined;
        case GL_SHADING_RATE_1X1_PIXELS_EXT:
            return ShadingRate::_1x1;
        case GL_SHADING_RATE_1X2_PIXELS_EXT:
            return ShadingRate::_1x2;
        case GL_SHADING_RATE_1X4_PIXELS_EXT:
            return ShadingRate::_1x4;
        case GL_SHADING_RATE_2X1_PIXELS_EXT:
            return ShadingRate::_2x1;
        case GL_SHADING_RATE_2X2_PIXELS_EXT:
            return ShadingRate::_2x2;
        case GL_SHADING_RATE_2X4_PIXELS_EXT:
            return ShadingRate::_2x4;
        case GL_SHADING_RATE_4X1_PIXELS_EXT:
            return ShadingRate::_4x1;
        case GL_SHADING_RATE_4X2_PIXELS_EXT:
            return ShadingRate::_4x2;
        case GL_SHADING_RATE_4X4_PIXELS_EXT:
            return ShadingRate::_4x4;
        default:
            return ShadingRate::InvalidEnum;
    }
}

GLenum ToGLenum(ShadingRate from)
{
    switch (from)
    {
        case ShadingRate::Undefined:
            return GL_NONE;
        case ShadingRate::_1x1:
            return GL_SHADING_RATE_1X1_PIXELS_EXT;
        case ShadingRate::_1x2:
            return GL_SHADING_RATE_1X2_PIXELS_EXT;
        case ShadingRate::_1x4:
            return GL_SHADING_RATE_1X4_PIXELS_EXT;
        case ShadingRate::_2x1:
            return GL_SHADING_RATE_2X1_PIXELS_EXT;
        case ShadingRate::_2x2:
            return GL_SHADING_RATE_2X2_PIXELS_EXT;
        case ShadingRate::_2x4:
            return GL_SHADING_RATE_2X4_PIXELS_EXT;
        case ShadingRate::_4x1:
            return GL_SHADING_RATE_4X1_PIXELS_EXT;
        case ShadingRate::_4x2:
            return GL_SHADING_RATE_4X2_PIXELS_EXT;
        case ShadingRate::_4x4:
            return GL_SHADING_RATE_4X4_PIXELS_EXT;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, ShadingRate value)
{
    switch (value)
    {
        case ShadingRate::Undefined:
            os << "GL_NONE";
            break;
        case ShadingRate::_1x1:
            os << "GL_SHADING_RATE_1X1_PIXELS_EXT";
            break;
        case ShadingRate::_1x2:
            os << "GL_SHADING_RATE_1X2_PIXELS_EXT";
            break;
        case ShadingRate::_1x4:
            os << "GL_SHADING_RATE_1X4_PIXELS_EXT";
            break;
        case ShadingRate::_2x1:
            os << "GL_SHADING_RATE_2X1_PIXELS_EXT";
            break;
        case ShadingRate::_2x2:
            os << "GL_SHADING_RATE_2X2_PIXELS_EXT";
            break;
        case ShadingRate::_2x4:
            os << "GL_SHADING_RATE_2X4_PIXELS_EXT";
            break;
        case ShadingRate::_4x1:
            os << "GL_SHADING_RATE_4X1_PIXELS_EXT";
            break;
        case ShadingRate::_4x2:
            os << "GL_SHADING_RATE_4X2_PIXELS_EXT";
            break;
        case ShadingRate::_4x4:
            os << "GL_SHADING_RATE_4X4_PIXELS_EXT";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
TextureCombine FromGLenum<TextureCombine>(GLenum from)
{
    switch (from)
    {
        case GL_ADD:
            return TextureCombine::Add;
        case GL_ADD_SIGNED:
            return TextureCombine::AddSigned;
        case GL_DOT3_RGB:
            return TextureCombine::Dot3Rgb;
        case GL_DOT3_RGBA:
            return TextureCombine::Dot3Rgba;
        case GL_INTERPOLATE:
            return TextureCombine::Interpolate;
        case GL_MODULATE:
            return TextureCombine::Modulate;
        case GL_REPLACE:
            return TextureCombine::Replace;
        case GL_SUBTRACT:
            return TextureCombine::Subtract;
        default:
            return TextureCombine::InvalidEnum;
    }
}

GLenum ToGLenum(TextureCombine from)
{
    switch (from)
    {
        case TextureCombine::Add:
            return GL_ADD;
        case TextureCombine::AddSigned:
            return GL_ADD_SIGNED;
        case TextureCombine::Dot3Rgb:
            return GL_DOT3_RGB;
        case TextureCombine::Dot3Rgba:
            return GL_DOT3_RGBA;
        case TextureCombine::Interpolate:
            return GL_INTERPOLATE;
        case TextureCombine::Modulate:
            return GL_MODULATE;
        case TextureCombine::Replace:
            return GL_REPLACE;
        case TextureCombine::Subtract:
            return GL_SUBTRACT;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, TextureCombine value)
{
    switch (value)
    {
        case TextureCombine::Add:
            os << "GL_ADD";
            break;
        case TextureCombine::AddSigned:
            os << "GL_ADD_SIGNED";
            break;
        case TextureCombine::Dot3Rgb:
            os << "GL_DOT3_RGB";
            break;
        case TextureCombine::Dot3Rgba:
            os << "GL_DOT3_RGBA";
            break;
        case TextureCombine::Interpolate:
            os << "GL_INTERPOLATE";
            break;
        case TextureCombine::Modulate:
            os << "GL_MODULATE";
            break;
        case TextureCombine::Replace:
            os << "GL_REPLACE";
            break;
        case TextureCombine::Subtract:
            os << "GL_SUBTRACT";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
TextureEnvMode FromGLenum<TextureEnvMode>(GLenum from)
{
    switch (from)
    {
        case GL_ADD:
            return TextureEnvMode::Add;
        case GL_BLEND:
            return TextureEnvMode::Blend;
        case GL_COMBINE:
            return TextureEnvMode::Combine;
        case GL_DECAL:
            return TextureEnvMode::Decal;
        case GL_MODULATE:
            return TextureEnvMode::Modulate;
        case GL_REPLACE:
            return TextureEnvMode::Replace;
        default:
            return TextureEnvMode::InvalidEnum;
    }
}

GLenum ToGLenum(TextureEnvMode from)
{
    switch (from)
    {
        case TextureEnvMode::Add:
            return GL_ADD;
        case TextureEnvMode::Blend:
            return GL_BLEND;
        case TextureEnvMode::Combine:
            return GL_COMBINE;
        case TextureEnvMode::Decal:
            return GL_DECAL;
        case TextureEnvMode::Modulate:
            return GL_MODULATE;
        case TextureEnvMode::Replace:
            return GL_REPLACE;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, TextureEnvMode value)
{
    switch (value)
    {
        case TextureEnvMode::Add:
            os << "GL_ADD";
            break;
        case TextureEnvMode::Blend:
            os << "GL_BLEND";
            break;
        case TextureEnvMode::Combine:
            os << "GL_COMBINE";
            break;
        case TextureEnvMode::Decal:
            os << "GL_DECAL";
            break;
        case TextureEnvMode::Modulate:
            os << "GL_MODULATE";
            break;
        case TextureEnvMode::Replace:
            os << "GL_REPLACE";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
TextureEnvParameter FromGLenum<TextureEnvParameter>(GLenum from)
{
    switch (from)
    {
        case GL_TEXTURE_ENV_MODE:
            return TextureEnvParameter::Mode;
        case GL_TEXTURE_ENV_COLOR:
            return TextureEnvParameter::Color;
        case GL_COMBINE_RGB:
            return TextureEnvParameter::CombineRgb;
        case GL_COMBINE_ALPHA:
            return TextureEnvParameter::CombineAlpha;
        case GL_RGB_SCALE:
            return TextureEnvParameter::RgbScale;
        case GL_ALPHA_SCALE:
            return TextureEnvParameter::AlphaScale;
        case GL_SRC0_RGB:
            return TextureEnvParameter::Src0Rgb;
        case GL_SRC1_RGB:
            return TextureEnvParameter::Src1Rgb;
        case GL_SRC2_RGB:
            return TextureEnvParameter::Src2Rgb;
        case GL_SRC0_ALPHA:
            return TextureEnvParameter::Src0Alpha;
        case GL_SRC1_ALPHA:
            return TextureEnvParameter::Src1Alpha;
        case GL_SRC2_ALPHA:
            return TextureEnvParameter::Src2Alpha;
        case GL_OPERAND0_RGB:
            return TextureEnvParameter::Op0Rgb;
        case GL_OPERAND1_RGB:
            return TextureEnvParameter::Op1Rgb;
        case GL_OPERAND2_RGB:
            return TextureEnvParameter::Op2Rgb;
        case GL_OPERAND0_ALPHA:
            return TextureEnvParameter::Op0Alpha;
        case GL_OPERAND1_ALPHA:
            return TextureEnvParameter::Op1Alpha;
        case GL_OPERAND2_ALPHA:
            return TextureEnvParameter::Op2Alpha;
        case GL_COORD_REPLACE_OES:
            return TextureEnvParameter::PointCoordReplace;
        case GL_TEXTURE_LOD_BIAS_EXT:
            return TextureEnvParameter::LodBias;
        default:
            return TextureEnvParameter::InvalidEnum;
    }
}

GLenum ToGLenum(TextureEnvParameter from)
{
    switch (from)
    {
        case TextureEnvParameter::Mode:
            return GL_TEXTURE_ENV_MODE;
        case TextureEnvParameter::Color:
            return GL_TEXTURE_ENV_COLOR;
        case TextureEnvParameter::CombineRgb:
            return GL_COMBINE_RGB;
        case TextureEnvParameter::CombineAlpha:
            return GL_COMBINE_ALPHA;
        case TextureEnvParameter::RgbScale:
            return GL_RGB_SCALE;
        case TextureEnvParameter::AlphaScale:
            return GL_ALPHA_SCALE;
        case TextureEnvParameter::Src0Rgb:
            return GL_SRC0_RGB;
        case TextureEnvParameter::Src1Rgb:
            return GL_SRC1_RGB;
        case TextureEnvParameter::Src2Rgb:
            return GL_SRC2_RGB;
        case TextureEnvParameter::Src0Alpha:
            return GL_SRC0_ALPHA;
        case TextureEnvParameter::Src1Alpha:
            return GL_SRC1_ALPHA;
        case TextureEnvParameter::Src2Alpha:
            return GL_SRC2_ALPHA;
        case TextureEnvParameter::Op0Rgb:
            return GL_OPERAND0_RGB;
        case TextureEnvParameter::Op1Rgb:
            return GL_OPERAND1_RGB;
        case TextureEnvParameter::Op2Rgb:
            return GL_OPERAND2_RGB;
        case TextureEnvParameter::Op0Alpha:
            return GL_OPERAND0_ALPHA;
        case TextureEnvParameter::Op1Alpha:
            return GL_OPERAND1_ALPHA;
        case TextureEnvParameter::Op2Alpha:
            return GL_OPERAND2_ALPHA;
        case TextureEnvParameter::PointCoordReplace:
            return GL_COORD_REPLACE_OES;
        case TextureEnvParameter::LodBias:
            return GL_TEXTURE_LOD_BIAS_EXT;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, TextureEnvParameter value)
{
    switch (value)
    {
        case TextureEnvParameter::Mode:
            os << "GL_TEXTURE_ENV_MODE";
            break;
        case TextureEnvParameter::Color:
            os << "GL_TEXTURE_ENV_COLOR";
            break;
        case TextureEnvParameter::CombineRgb:
            os << "GL_COMBINE_RGB";
            break;
        case TextureEnvParameter::CombineAlpha:
            os << "GL_COMBINE_ALPHA";
            break;
        case TextureEnvParameter::RgbScale:
            os << "GL_RGB_SCALE";
            break;
        case TextureEnvParameter::AlphaScale:
            os << "GL_ALPHA_SCALE";
            break;
        case TextureEnvParameter::Src0Rgb:
            os << "GL_SRC0_RGB";
            break;
        case TextureEnvParameter::Src1Rgb:
            os << "GL_SRC1_RGB";
            break;
        case TextureEnvParameter::Src2Rgb:
            os << "GL_SRC2_RGB";
            break;
        case TextureEnvParameter::Src0Alpha:
            os << "GL_SRC0_ALPHA";
            break;
        case TextureEnvParameter::Src1Alpha:
            os << "GL_SRC1_ALPHA";
            break;
        case TextureEnvParameter::Src2Alpha:
            os << "GL_SRC2_ALPHA";
            break;
        case TextureEnvParameter::Op0Rgb:
            os << "GL_OPERAND0_RGB";
            break;
        case TextureEnvParameter::Op1Rgb:
            os << "GL_OPERAND1_RGB";
            break;
        case TextureEnvParameter::Op2Rgb:
            os << "GL_OPERAND2_RGB";
            break;
        case TextureEnvParameter::Op0Alpha:
            os << "GL_OPERAND0_ALPHA";
            break;
        case TextureEnvParameter::Op1Alpha:
            os << "GL_OPERAND1_ALPHA";
            break;
        case TextureEnvParameter::Op2Alpha:
            os << "GL_OPERAND2_ALPHA";
            break;
        case TextureEnvParameter::PointCoordReplace:
            os << "GL_COORD_REPLACE_OES";
            break;
        case TextureEnvParameter::LodBias:
            os << "GL_TEXTURE_LOD_BIAS_EXT";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
TextureEnvTarget FromGLenum<TextureEnvTarget>(GLenum from)
{
    switch (from)
    {
        case GL_TEXTURE_ENV:
            return TextureEnvTarget::Env;
        case GL_POINT_SPRITE_OES:
            return TextureEnvTarget::PointSprite;
        case GL_TEXTURE_FILTER_CONTROL_EXT:
            return TextureEnvTarget::TextureFilterControl;
        default:
            return TextureEnvTarget::InvalidEnum;
    }
}

GLenum ToGLenum(TextureEnvTarget from)
{
    switch (from)
    {
        case TextureEnvTarget::Env:
            return GL_TEXTURE_ENV;
        case TextureEnvTarget::PointSprite:
            return GL_POINT_SPRITE_OES;
        case TextureEnvTarget::TextureFilterControl:
            return GL_TEXTURE_FILTER_CONTROL_EXT;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, TextureEnvTarget value)
{
    switch (value)
    {
        case TextureEnvTarget::Env:
            os << "GL_TEXTURE_ENV";
            break;
        case TextureEnvTarget::PointSprite:
            os << "GL_POINT_SPRITE_OES";
            break;
        case TextureEnvTarget::TextureFilterControl:
            os << "GL_TEXTURE_FILTER_CONTROL_EXT";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
TextureImageParameter FromGLenum<TextureImageParameter>(GLenum from)
{
    switch (from)
    {
        case GL_TEXTURE_WIDTH:
            return TextureImageParameter::Width;
        case GL_TEXTURE_HEIGHT:
            return TextureImageParameter::Height;
        case GL_TEXTURE_DEPTH:
            return TextureImageParameter::Depth;
        case GL_TEXTURE_INTERNAL_FORMAT:
            return TextureImageParameter::InternalFormat;
        case GL_TEXTURE_RED_SIZE:
            return TextureImageParameter::RedSize;
        case GL_TEXTURE_GREEN_SIZE:
            return TextureImageParameter::GreenSize;
        case GL_TEXTURE_BLUE_SIZE:
            return TextureImageParameter::BlueSize;
        case GL_TEXTURE_ALPHA_SIZE:
            return TextureImageParameter::AlphaSize;
        case GL_TEXTURE_DEPTH_SIZE:
            return TextureImageParameter::DepthSize;
        case GL_TEXTURE_STENCIL_SIZE:
            return TextureImageParameter::StencilSize;
        case GL_TEXTURE_SHARED_SIZE:
            return TextureImageParameter::SharedSize;
        case GL_TEXTURE_RED_TYPE:
            return TextureImageParameter::RedType;
        case GL_TEXTURE_GREEN_TYPE:
            return TextureImageParameter::GreenType;
        case GL_TEXTURE_BLUE_TYPE:
            return TextureImageParameter::BlueType;
        case GL_TEXTURE_ALPHA_TYPE:
            return TextureImageParameter::AlphaType;
        case GL_TEXTURE_DEPTH_TYPE:
            return TextureImageParameter::DepthType;
        case GL_TEXTURE_COMPRESSED:
            return TextureImageParameter::Compressed;
        case GL_TEXTURE_SAMPLES:
            return TextureImageParameter::Samples;
        case GL_TEXTURE_FIXED_SAMPLE_LOCATIONS:
            return TextureImageParameter::FixedSampleLocations;
        case GL_TEXTURE_BUFFER_DATA_STORE_BINDING:
            return TextureImageParameter::BufferDataStoreBinding;
        case GL_TEXTURE_BUFFER_OFFSET:
            return TextureImageParameter::BufferOffset;
        case GL_TEXTURE_BUFFER_SIZE:
            return TextureImageParameter::BufferSize;
        case GL_MEMORY_SIZE_ANGLE:
            return TextureImageParameter::MemorySize;
        case GL_RESOURCE_INITIALIZED_ANGLE:
            return TextureImageParameter::ResourceInitialized;
        default:
            return TextureImageParameter::InvalidEnum;
    }
}

GLenum ToGLenum(TextureImageParameter from)
{
    switch (from)
    {
        case TextureImageParameter::Width:
            return GL_TEXTURE_WIDTH;
        case TextureImageParameter::Height:
            return GL_TEXTURE_HEIGHT;
        case TextureImageParameter::Depth:
            return GL_TEXTURE_DEPTH;
        case TextureImageParameter::InternalFormat:
            return GL_TEXTURE_INTERNAL_FORMAT;
        case TextureImageParameter::RedSize:
            return GL_TEXTURE_RED_SIZE;
        case TextureImageParameter::GreenSize:
            return GL_TEXTURE_GREEN_SIZE;
        case TextureImageParameter::BlueSize:
            return GL_TEXTURE_BLUE_SIZE;
        case TextureImageParameter::AlphaSize:
            return GL_TEXTURE_ALPHA_SIZE;
        case TextureImageParameter::DepthSize:
            return GL_TEXTURE_DEPTH_SIZE;
        case TextureImageParameter::StencilSize:
            return GL_TEXTURE_STENCIL_SIZE;
        case TextureImageParameter::SharedSize:
            return GL_TEXTURE_SHARED_SIZE;
        case TextureImageParameter::RedType:
            return GL_TEXTURE_RED_TYPE;
        case TextureImageParameter::GreenType:
            return GL_TEXTURE_GREEN_TYPE;
        case TextureImageParameter::BlueType:
            return GL_TEXTURE_BLUE_TYPE;
        case TextureImageParameter::AlphaType:
            return GL_TEXTURE_ALPHA_TYPE;
        case TextureImageParameter::DepthType:
            return GL_TEXTURE_DEPTH_TYPE;
        case TextureImageParameter::Compressed:
            return GL_TEXTURE_COMPRESSED;
        case TextureImageParameter::Samples:
            return GL_TEXTURE_SAMPLES;
        case TextureImageParameter::FixedSampleLocations:
            return GL_TEXTURE_FIXED_SAMPLE_LOCATIONS;
        case TextureImageParameter::BufferDataStoreBinding:
            return GL_TEXTURE_BUFFER_DATA_STORE_BINDING;
        case TextureImageParameter::BufferOffset:
            return GL_TEXTURE_BUFFER_OFFSET;
        case TextureImageParameter::BufferSize:
            return GL_TEXTURE_BUFFER_SIZE;
        case TextureImageParameter::MemorySize:
            return GL_MEMORY_SIZE_ANGLE;
        case TextureImageParameter::ResourceInitialized:
            return GL_RESOURCE_INITIALIZED_ANGLE;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, TextureImageParameter value)
{
    switch (value)
    {
        case TextureImageParameter::Width:
            os << "GL_TEXTURE_WIDTH";
            break;
        case TextureImageParameter::Height:
            os << "GL_TEXTURE_HEIGHT";
            break;
        case TextureImageParameter::Depth:
            os << "GL_TEXTURE_DEPTH";
            break;
        case TextureImageParameter::InternalFormat:
            os << "GL_TEXTURE_INTERNAL_FORMAT";
            break;
        case TextureImageParameter::RedSize:
            os << "GL_TEXTURE_RED_SIZE";
            break;
        case TextureImageParameter::GreenSize:
            os << "GL_TEXTURE_GREEN_SIZE";
            break;
        case TextureImageParameter::BlueSize:
            os << "GL_TEXTURE_BLUE_SIZE";
            break;
        case TextureImageParameter::AlphaSize:
            os << "GL_TEXTURE_ALPHA_SIZE";
            break;
        case TextureImageParameter::DepthSize:
            os << "GL_TEXTURE_DEPTH_SIZE";
            break;
        case TextureImageParameter::StencilSize:
            os << "GL_TEXTURE_STENCIL_SIZE";
            break;
        case TextureImageParameter::SharedSize:
            os << "GL_TEXTURE_SHARED_SIZE";
            break;
        case TextureImageParameter::RedType:
            os << "GL_TEXTURE_RED_TYPE";
            break;
        case TextureImageParameter::GreenType:
            os << "GL_TEXTURE_GREEN_TYPE";
            break;
        case TextureImageParameter::BlueType:
            os << "GL_TEXTURE_BLUE_TYPE";
            break;
        case TextureImageParameter::AlphaType:
            os << "GL_TEXTURE_ALPHA_TYPE";
            break;
        case TextureImageParameter::DepthType:
            os << "GL_TEXTURE_DEPTH_TYPE";
            break;
        case TextureImageParameter::Compressed:
            os << "GL_TEXTURE_COMPRESSED";
            break;
        case TextureImageParameter::Samples:
            os << "GL_TEXTURE_SAMPLES";
            break;
        case TextureImageParameter::FixedSampleLocations:
            os << "GL_TEXTURE_FIXED_SAMPLE_LOCATIONS";
            break;
        case TextureImageParameter::BufferDataStoreBinding:
            os << "GL_TEXTURE_BUFFER_DATA_STORE_BINDING";
            break;
        case TextureImageParameter::BufferOffset:
            os << "GL_TEXTURE_BUFFER_OFFSET";
            break;
        case TextureImageParameter::BufferSize:
            os << "GL_TEXTURE_BUFFER_SIZE";
            break;
        case TextureImageParameter::MemorySize:
            os << "GL_MEMORY_SIZE_ANGLE";
            break;
        case TextureImageParameter::ResourceInitialized:
            os << "GL_RESOURCE_INITIALIZED_ANGLE";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
TextureOp FromGLenum<TextureOp>(GLenum from)
{
    switch (from)
    {
        case GL_ONE_MINUS_SRC_ALPHA:
            return TextureOp::OneMinusSrcAlpha;
        case GL_ONE_MINUS_SRC_COLOR:
            return TextureOp::OneMinusSrcColor;
        case GL_SRC_ALPHA:
            return TextureOp::SrcAlpha;
        case GL_SRC_COLOR:
            return TextureOp::SrcColor;
        default:
            return TextureOp::InvalidEnum;
    }
}

GLenum ToGLenum(TextureOp from)
{
    switch (from)
    {
        case TextureOp::OneMinusSrcAlpha:
            return GL_ONE_MINUS_SRC_ALPHA;
        case TextureOp::OneMinusSrcColor:
            return GL_ONE_MINUS_SRC_COLOR;
        case TextureOp::SrcAlpha:
            return GL_SRC_ALPHA;
        case TextureOp::SrcColor:
            return GL_SRC_COLOR;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, TextureOp value)
{
    switch (value)
    {
        case TextureOp::OneMinusSrcAlpha:
            os << "GL_ONE_MINUS_SRC_ALPHA";
            break;
        case TextureOp::OneMinusSrcColor:
            os << "GL_ONE_MINUS_SRC_COLOR";
            break;
        case TextureOp::SrcAlpha:
            os << "GL_SRC_ALPHA";
            break;
        case TextureOp::SrcColor:
            os << "GL_SRC_COLOR";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
TextureSrc FromGLenum<TextureSrc>(GLenum from)
{
    switch (from)
    {
        case GL_CONSTANT:
            return TextureSrc::Constant;
        case GL_PREVIOUS:
            return TextureSrc::Previous;
        case GL_PRIMARY_COLOR:
            return TextureSrc::PrimaryColor;
        case GL_TEXTURE:
            return TextureSrc::Texture;
        default:
            return TextureSrc::InvalidEnum;
    }
}

GLenum ToGLenum(TextureSrc from)
{
    switch (from)
    {
        case TextureSrc::Constant:
            return GL_CONSTANT;
        case TextureSrc::Previous:
            return GL_PREVIOUS;
        case TextureSrc::PrimaryColor:
            return GL_PRIMARY_COLOR;
        case TextureSrc::Texture:
            return GL_TEXTURE;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, TextureSrc value)
{
    switch (value)
    {
        case TextureSrc::Constant:
            os << "GL_CONSTANT";
            break;
        case TextureSrc::Previous:
            os << "GL_PREVIOUS";
            break;
        case TextureSrc::PrimaryColor:
            os << "GL_PRIMARY_COLOR";
            break;
        case TextureSrc::Texture:
            os << "GL_TEXTURE";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
TextureTarget FromGLenum<TextureTarget>(GLenum from)
{
    switch (from)
    {
        case GL_TEXTURE_2D:
            return TextureTarget::_2D;
        case GL_TEXTURE_2D_ARRAY:
            return TextureTarget::_2DArray;
        case GL_TEXTURE_2D_MULTISAMPLE:
            return TextureTarget::_2DMultisample;
        case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
            return TextureTarget::_2DMultisampleArray;
        case GL_TEXTURE_3D:
            return TextureTarget::_3D;
        case GL_TEXTURE_EXTERNAL_OES:
            return TextureTarget::External;
        case GL_TEXTURE_RECTANGLE_ANGLE:
            return TextureTarget::Rectangle;
        case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
            return TextureTarget::CubeMapPositiveX;
        case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
            return TextureTarget::CubeMapNegativeX;
        case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
            return TextureTarget::CubeMapPositiveY;
        case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
            return TextureTarget::CubeMapNegativeY;
        case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
            return TextureTarget::CubeMapPositiveZ;
        case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
            return TextureTarget::CubeMapNegativeZ;
        case GL_TEXTURE_CUBE_MAP_ARRAY:
            return TextureTarget::CubeMapArray;
        case GL_TEXTURE_BUFFER:
            return TextureTarget::Buffer;
        default:
            return TextureTarget::InvalidEnum;
    }
}

GLenum ToGLenum(TextureTarget from)
{
    switch (from)
    {
        case TextureTarget::_2D:
            return GL_TEXTURE_2D;
        case TextureTarget::_2DArray:
            return GL_TEXTURE_2D_ARRAY;
        case TextureTarget::_2DMultisample:
            return GL_TEXTURE_2D_MULTISAMPLE;
        case TextureTarget::_2DMultisampleArray:
            return GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
        case TextureTarget::_3D:
            return GL_TEXTURE_3D;
        case TextureTarget::External:
            return GL_TEXTURE_EXTERNAL_OES;
        case TextureTarget::Rectangle:
            return GL_TEXTURE_RECTANGLE_ANGLE;
        case TextureTarget::CubeMapPositiveX:
            return GL_TEXTURE_CUBE_MAP_POSITIVE_X;
        case TextureTarget::CubeMapNegativeX:
            return GL_TEXTURE_CUBE_MAP_NEGATIVE_X;
        case TextureTarget::CubeMapPositiveY:
            return GL_TEXTURE_CUBE_MAP_POSITIVE_Y;
        case TextureTarget::CubeMapNegativeY:
            return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y;
        case TextureTarget::CubeMapPositiveZ:
            return GL_TEXTURE_CUBE_MAP_POSITIVE_Z;
        case TextureTarget::CubeMapNegativeZ:
            return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
        case TextureTarget::CubeMapArray:
            return GL_TEXTURE_CUBE_MAP_ARRAY;
        case TextureTarget::Buffer:
            return GL_TEXTURE_BUFFER;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, TextureTarget value)
{
    switch (value)
    {
        case TextureTarget::_2D:
            os << "GL_TEXTURE_2D";
            break;
        case TextureTarget::_2DArray:
            os << "GL_TEXTURE_2D_ARRAY";
            break;
        case TextureTarget::_2DMultisample:
            os << "GL_TEXTURE_2D_MULTISAMPLE";
            break;
        case TextureTarget::_2DMultisampleArray:
            os << "GL_TEXTURE_2D_MULTISAMPLE_ARRAY";
            break;
        case TextureTarget::_3D:
            os << "GL_TEXTURE_3D";
            break;
        case TextureTarget::External:
            os << "GL_TEXTURE_EXTERNAL_OES";
            break;
        case TextureTarget::Rectangle:
            os << "GL_TEXTURE_RECTANGLE_ANGLE";
            break;
        case TextureTarget::CubeMapPositiveX:
            os << "GL_TEXTURE_CUBE_MAP_POSITIVE_X";
            break;
        case TextureTarget::CubeMapNegativeX:
            os << "GL_TEXTURE_CUBE_MAP_NEGATIVE_X";
            break;
        case TextureTarget::CubeMapPositiveY:
            os << "GL_TEXTURE_CUBE_MAP_POSITIVE_Y";
            break;
        case TextureTarget::CubeMapNegativeY:
            os << "GL_TEXTURE_CUBE_MAP_NEGATIVE_Y";
            break;
        case TextureTarget::CubeMapPositiveZ:
            os << "GL_TEXTURE_CUBE_MAP_POSITIVE_Z";
            break;
        case TextureTarget::CubeMapNegativeZ:
            os << "GL_TEXTURE_CUBE_MAP_NEGATIVE_Z";
            break;
        case TextureTarget::CubeMapArray:
            os << "GL_TEXTURE_CUBE_MAP_ARRAY";
            break;
        case TextureTarget::Buffer:
            os << "GL_TEXTURE_BUFFER";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
TextureType FromGLenum<TextureType>(GLenum from)
{
    switch (from)
    {
        case GL_TEXTURE_2D:
            return TextureType::_2D;
        case GL_TEXTURE_2D_ARRAY:
            return TextureType::_2DArray;
        case GL_TEXTURE_2D_MULTISAMPLE:
            return TextureType::_2DMultisample;
        case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
            return TextureType::_2DMultisampleArray;
        case GL_TEXTURE_3D:
            return TextureType::_3D;
        case GL_TEXTURE_EXTERNAL_OES:
            return TextureType::External;
        case GL_TEXTURE_RECTANGLE_ANGLE:
            return TextureType::Rectangle;
        case GL_TEXTURE_CUBE_MAP:
            return TextureType::CubeMap;
        case GL_TEXTURE_CUBE_MAP_ARRAY:
            return TextureType::CubeMapArray;
        case GL_TEXTURE_BUFFER:
            return TextureType::Buffer;
        default:
            return TextureType::InvalidEnum;
    }
}

GLenum ToGLenum(TextureType from)
{
    switch (from)
    {
        case TextureType::_2D:
            return GL_TEXTURE_2D;
        case TextureType::_2DArray:
            return GL_TEXTURE_2D_ARRAY;
        case TextureType::_2DMultisample:
            return GL_TEXTURE_2D_MULTISAMPLE;
        case TextureType::_2DMultisampleArray:
            return GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
        case TextureType::_3D:
            return GL_TEXTURE_3D;
        case TextureType::External:
            return GL_TEXTURE_EXTERNAL_OES;
        case TextureType::Rectangle:
            return GL_TEXTURE_RECTANGLE_ANGLE;
        case TextureType::CubeMap:
            return GL_TEXTURE_CUBE_MAP;
        case TextureType::CubeMapArray:
            return GL_TEXTURE_CUBE_MAP_ARRAY;
        case TextureType::Buffer:
            return GL_TEXTURE_BUFFER;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, TextureType value)
{
    switch (value)
    {
        case TextureType::_2D:
            os << "GL_TEXTURE_2D";
            break;
        case TextureType::_2DArray:
            os << "GL_TEXTURE_2D_ARRAY";
            break;
        case TextureType::_2DMultisample:
            os << "GL_TEXTURE_2D_MULTISAMPLE";
            break;
        case TextureType::_2DMultisampleArray:
            os << "GL_TEXTURE_2D_MULTISAMPLE_ARRAY";
            break;
        case TextureType::_3D:
            os << "GL_TEXTURE_3D";
            break;
        case TextureType::External:
            os << "GL_TEXTURE_EXTERNAL_OES";
            break;
        case TextureType::Rectangle:
            os << "GL_TEXTURE_RECTANGLE_ANGLE";
            break;
        case TextureType::CubeMap:
            os << "GL_TEXTURE_CUBE_MAP";
            break;
        case TextureType::CubeMapArray:
            os << "GL_TEXTURE_CUBE_MAP_ARRAY";
            break;
        case TextureType::Buffer:
            os << "GL_TEXTURE_BUFFER";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
TilingMode FromGLenum<TilingMode>(GLenum from)
{
    switch (from)
    {
        case GL_OPTIMAL_TILING_EXT:
            return TilingMode::Optimal;
        case GL_LINEAR_TILING_EXT:
            return TilingMode::Linear;
        default:
            return TilingMode::InvalidEnum;
    }
}

GLenum ToGLenum(TilingMode from)
{
    switch (from)
    {
        case TilingMode::Optimal:
            return GL_OPTIMAL_TILING_EXT;
        case TilingMode::Linear:
            return GL_LINEAR_TILING_EXT;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, TilingMode value)
{
    switch (value)
    {
        case TilingMode::Optimal:
            os << "GL_OPTIMAL_TILING_EXT";
            break;
        case TilingMode::Linear:
            os << "GL_LINEAR_TILING_EXT";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
UniformBlockParameter FromGLenum<UniformBlockParameter>(GLenum from)
{
    switch (from)
    {
        case GL_UNIFORM_BLOCK_BINDING:
            return UniformBlockParameter::Binding;
        case GL_UNIFORM_BLOCK_DATA_SIZE:
            return UniformBlockParameter::DataSize;
        case GL_UNIFORM_BLOCK_NAME_LENGTH:
            return UniformBlockParameter::NameLength;
        case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
            return UniformBlockParameter::ActiveUniforms;
        case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES:
            return UniformBlockParameter::ActiveUniformIndices;
        case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
            return UniformBlockParameter::ReferencedByVertexShader;
        case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
            return UniformBlockParameter::ReferencedByFragmentShader;
        default:
            return UniformBlockParameter::InvalidEnum;
    }
}

GLenum ToGLenum(UniformBlockParameter from)
{
    switch (from)
    {
        case UniformBlockParameter::Binding:
            return GL_UNIFORM_BLOCK_BINDING;
        case UniformBlockParameter::DataSize:
            return GL_UNIFORM_BLOCK_DATA_SIZE;
        case UniformBlockParameter::NameLength:
            return GL_UNIFORM_BLOCK_NAME_LENGTH;
        case UniformBlockParameter::ActiveUniforms:
            return GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS;
        case UniformBlockParameter::ActiveUniformIndices:
            return GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES;
        case UniformBlockParameter::ReferencedByVertexShader:
            return GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER;
        case UniformBlockParameter::ReferencedByFragmentShader:
            return GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, UniformBlockParameter value)
{
    switch (value)
    {
        case UniformBlockParameter::Binding:
            os << "GL_UNIFORM_BLOCK_BINDING";
            break;
        case UniformBlockParameter::DataSize:
            os << "GL_UNIFORM_BLOCK_DATA_SIZE";
            break;
        case UniformBlockParameter::NameLength:
            os << "GL_UNIFORM_BLOCK_NAME_LENGTH";
            break;
        case UniformBlockParameter::ActiveUniforms:
            os << "GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS";
            break;
        case UniformBlockParameter::ActiveUniformIndices:
            os << "GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES";
            break;
        case UniformBlockParameter::ReferencedByVertexShader:
            os << "GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER";
            break;
        case UniformBlockParameter::ReferencedByFragmentShader:
            os << "GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
VertexArrayType FromGLenum<VertexArrayType>(GLenum from)
{
    switch (from)
    {
        case GL_COLOR_ARRAY:
            return VertexArrayType::Color;
        case GL_NORMAL_ARRAY:
            return VertexArrayType::Normal;
        case GL_POINT_SIZE_ARRAY_OES:
            return VertexArrayType::PointSize;
        case GL_TEXTURE_COORD_ARRAY:
            return VertexArrayType::TextureCoord;
        case GL_VERTEX_ARRAY:
            return VertexArrayType::Vertex;
        default:
            return VertexArrayType::InvalidEnum;
    }
}

GLenum ToGLenum(VertexArrayType from)
{
    switch (from)
    {
        case VertexArrayType::Color:
            return GL_COLOR_ARRAY;
        case VertexArrayType::Normal:
            return GL_NORMAL_ARRAY;
        case VertexArrayType::PointSize:
            return GL_POINT_SIZE_ARRAY_OES;
        case VertexArrayType::TextureCoord:
            return GL_TEXTURE_COORD_ARRAY;
        case VertexArrayType::Vertex:
            return GL_VERTEX_ARRAY;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, VertexArrayType value)
{
    switch (value)
    {
        case VertexArrayType::Color:
            os << "GL_COLOR_ARRAY";
            break;
        case VertexArrayType::Normal:
            os << "GL_NORMAL_ARRAY";
            break;
        case VertexArrayType::PointSize:
            os << "GL_POINT_SIZE_ARRAY_OES";
            break;
        case VertexArrayType::TextureCoord:
            os << "GL_TEXTURE_COORD_ARRAY";
            break;
        case VertexArrayType::Vertex:
            os << "GL_VERTEX_ARRAY";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

template <>
WrapMode FromGLenum<WrapMode>(GLenum from)
{
    switch (from)
    {
        case GL_CLAMP_TO_EDGE:
            return WrapMode::ClampToEdge;
        case GL_CLAMP_TO_BORDER:
            return WrapMode::ClampToBorder;
        case GL_MIRRORED_REPEAT:
            return WrapMode::MirroredRepeat;
        case GL_REPEAT:
            return WrapMode::Repeat;
        default:
            return WrapMode::InvalidEnum;
    }
}

GLenum ToGLenum(WrapMode from)
{
    switch (from)
    {
        case WrapMode::ClampToEdge:
            return GL_CLAMP_TO_EDGE;
        case WrapMode::ClampToBorder:
            return GL_CLAMP_TO_BORDER;
        case WrapMode::MirroredRepeat:
            return GL_MIRRORED_REPEAT;
        case WrapMode::Repeat:
            return GL_REPEAT;
        default:
            UNREACHABLE();
            return 0;
    }
}

std::ostream &operator<<(std::ostream &os, WrapMode value)
{
    switch (value)
    {
        case WrapMode::ClampToEdge:
            os << "GL_CLAMP_TO_EDGE";
            break;
        case WrapMode::ClampToBorder:
            os << "GL_CLAMP_TO_BORDER";
            break;
        case WrapMode::MirroredRepeat:
            os << "GL_MIRRORED_REPEAT";
            break;
        case WrapMode::Repeat:
            os << "GL_REPEAT";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

}  // namespace gl
