dom/canvas/WebGLFormats.cpp
author Terrence Cole <terrence@mozilla.com>
Tue, 15 Dec 2015 07:45:22 -0800
changeset 331272 e673dbc9848f2ae4cd11911e7b9fae8e4757cd0e
parent 320517 c496c7c24113aa692b34e0b61b7be8f1ea9874ef
child 341593 17276a2bbd6acf9980efb6674d77f64c0b47e1ca
permissions -rw-r--r--
Bug 1232686 - Use decltype to infer Variant::match return type; r=fitzgen

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "WebGLFormats.h"

#include "gfxPrefs.h"
#include "GLDefs.h"
#include "mozilla/StaticMutex.h"

#ifdef FOO
#error FOO is already defined! We use FOO() macros to keep things succinct in this file.
#endif

namespace mozilla {
namespace webgl {

template<typename K, typename V, typename K2, typename V2>
static inline void
AlwaysInsert(std::map<K,V>& dest, const K2& key, const V2& val)
{
    auto res = dest.insert({ key, val });
    bool didInsert = res.second;
    MOZ_ALWAYS_TRUE(didInsert);
}

template<typename K, typename V, typename K2>
static inline V*
FindOrNull(const std::map<K,V*>& dest, const K2& key)
{
    auto itr = dest.find(key);
    if (itr == dest.end())
        return nullptr;

    return itr->second;
}

// Returns a pointer to the in-place value for `key`.
template<typename K, typename V, typename K2>
static inline V*
FindPtrOrNull(const std::map<K,V>& dest, const K2& key)
{
    auto itr = dest.find(key);
    if (itr == dest.end())
        return nullptr;

    return &(itr->second);
}

//////////////////////////////////////////////////////////////////////////////////////////

std::map<EffectiveFormat, const CompressedFormatInfo> gCompressedFormatInfoMap;
std::map<EffectiveFormat, const FormatInfo> gFormatInfoMap;

static inline const CompressedFormatInfo*
GetCompressedFormatInfo(EffectiveFormat format)
{
    MOZ_ASSERT(!gCompressedFormatInfoMap.empty());
    return FindPtrOrNull(gCompressedFormatInfoMap, format);
}

static inline const FormatInfo*
GetFormatInfo_NoLock(EffectiveFormat format)
{
    MOZ_ASSERT(!gFormatInfoMap.empty());
    return FindPtrOrNull(gFormatInfoMap, format);
}

//////////////////////////////////////////////////////////////////////////////////////////

static void
AddCompressedFormatInfo(EffectiveFormat format, uint16_t bitsPerBlock, uint8_t blockWidth,
                        uint8_t blockHeight, CompressionFamily family)
{
    MOZ_ASSERT(bitsPerBlock % 8 == 0);
    uint16_t bytesPerBlock = bitsPerBlock / 8; // The specs always state these in bits,
                                               // but it's only ever useful to us as
                                               // bytes.
    MOZ_ASSERT(bytesPerBlock <= 255);

    const CompressedFormatInfo info = { format, uint8_t(bytesPerBlock), blockWidth,
                                        blockHeight, family };
    AlwaysInsert(gCompressedFormatInfoMap, format, info);
}

static void
InitCompressedFormatInfo()
{
    // GLES 3.0.4, p147, table 3.19
    // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB8_ETC2                     ,  64, 4, 4, CompressionFamily::ES3);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_ETC2                    ,  64, 4, 4, CompressionFamily::ES3);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA8_ETC2_EAC                , 128, 4, 4, CompressionFamily::ES3);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         , 128, 4, 4, CompressionFamily::ES3);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_R11_EAC                       ,  64, 4, 4, CompressionFamily::ES3);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RG11_EAC                      , 128, 4, 4, CompressionFamily::ES3);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SIGNED_R11_EAC                ,  64, 4, 4, CompressionFamily::ES3);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SIGNED_RG11_EAC               , 128, 4, 4, CompressionFamily::ES3);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ,  64, 4, 4, CompressionFamily::ES3);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2,  64, 4, 4, CompressionFamily::ES3);

    // AMD_compressed_ATC_texture
    AddCompressedFormatInfo(EffectiveFormat::ATC_RGB_AMD                    ,  64, 4, 4, CompressionFamily::ATC);
    AddCompressedFormatInfo(EffectiveFormat::ATC_RGBA_EXPLICIT_ALPHA_AMD    , 128, 4, 4, CompressionFamily::ATC);
    AddCompressedFormatInfo(EffectiveFormat::ATC_RGBA_INTERPOLATED_ALPHA_AMD, 128, 4, 4, CompressionFamily::ATC);

    // EXT_texture_compression_s3tc
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_S3TC_DXT1_EXT ,  64, 4, 4, CompressionFamily::S3TC);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT1_EXT,  64, 4, 4, CompressionFamily::S3TC);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT3_EXT, 128, 4, 4, CompressionFamily::S3TC);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT5_EXT, 128, 4, 4, CompressionFamily::S3TC);

    // IMG_texture_compression_pvrtc
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_PVRTC_4BPPV1 , 256,  8, 8, CompressionFamily::PVRTC);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_PVRTC_4BPPV1, 256,  8, 8, CompressionFamily::PVRTC);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_PVRTC_2BPPV1 , 256, 16, 8, CompressionFamily::PVRTC);
    AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_PVRTC_2BPPV1, 256, 16, 8, CompressionFamily::PVRTC);

    // OES_compressed_ETC1_RGB8_texture
    AddCompressedFormatInfo(EffectiveFormat::ETC1_RGB8_OES, 64, 4, 4, CompressionFamily::ETC1);
}

//////////////////////////////////////////////////////////////////////////////////////////

static void
AddFormatInfo(EffectiveFormat format, const char* name, GLenum sizedFormat,
              uint8_t bytesPerPixel, UnsizedFormat unsizedFormat, bool isSRGB,
              ComponentType componentType)
{
    bool isColorFormat = false;
    bool hasAlpha = false;
    bool hasDepth = false;
    bool hasStencil = false;

    switch (unsizedFormat) {
    case UnsizedFormat::L:
    case UnsizedFormat::R:
    case UnsizedFormat::RG:
    case UnsizedFormat::RGB:
        isColorFormat = true;
        break;

    case UnsizedFormat::A: // Alpha is a 'color format' since it's 'color-attachable'.
    case UnsizedFormat::LA:
    case UnsizedFormat::RGBA:
        isColorFormat = true;
        hasAlpha = true;
        break;

    case UnsizedFormat::D:
        hasDepth = true;
        break;

    case UnsizedFormat::S:
        hasStencil = true;
        break;

    case UnsizedFormat::DS:
        hasDepth = true;
        hasStencil = true;
        break;

    default:
        MOZ_CRASH("Missing UnsizedFormat case.");
    }

    const CompressedFormatInfo* compressedFormatInfo = GetCompressedFormatInfo(format);
    MOZ_ASSERT(!bytesPerPixel == bool(compressedFormatInfo));

    const FormatInfo info = { format, name, sizedFormat, unsizedFormat, componentType,
                              bytesPerPixel, isColorFormat, isSRGB, hasAlpha, hasDepth,
                              hasStencil, compressedFormatInfo };
    AlwaysInsert(gFormatInfoMap, format, info);
}

static void
InitFormatInfo()
{
#define FOO(x) EffectiveFormat::x, #x, LOCAL_GL_ ## x

    // GLES 3.0.4, p130-132, table 3.13
    AddFormatInfo(FOO(R8            ),  1, UnsizedFormat::R   , false, ComponentType::NormUInt);
    AddFormatInfo(FOO(R8_SNORM      ),  1, UnsizedFormat::R   , false, ComponentType::NormInt );
    AddFormatInfo(FOO(RG8           ),  2, UnsizedFormat::RG  , false, ComponentType::NormUInt);
    AddFormatInfo(FOO(RG8_SNORM     ),  2, UnsizedFormat::RG  , false, ComponentType::NormInt );
    AddFormatInfo(FOO(RGB8          ),  3, UnsizedFormat::RGB , false, ComponentType::NormUInt);
    AddFormatInfo(FOO(RGB8_SNORM    ),  3, UnsizedFormat::RGB , false, ComponentType::NormInt );
    AddFormatInfo(FOO(RGB565        ),  2, UnsizedFormat::RGB , false, ComponentType::NormUInt);
    AddFormatInfo(FOO(RGBA4         ),  2, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
    AddFormatInfo(FOO(RGB5_A1       ),  2, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
    AddFormatInfo(FOO(RGBA8         ),  4, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
    AddFormatInfo(FOO(RGBA8_SNORM   ),  4, UnsizedFormat::RGBA, false, ComponentType::NormInt );
    AddFormatInfo(FOO(RGB10_A2      ),  4, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
    AddFormatInfo(FOO(RGB10_A2UI    ),  4, UnsizedFormat::RGBA, false, ComponentType::UInt    );

    AddFormatInfo(FOO(SRGB8         ),  3, UnsizedFormat::RGB , true , ComponentType::NormUInt);
    AddFormatInfo(FOO(SRGB8_ALPHA8  ),  4, UnsizedFormat::RGBA, true , ComponentType::NormUInt);

    AddFormatInfo(FOO(R16F          ),  2, UnsizedFormat::R   , false, ComponentType::Float   );
    AddFormatInfo(FOO(RG16F         ),  4, UnsizedFormat::RG  , false, ComponentType::Float   );
    AddFormatInfo(FOO(RGB16F        ),  6, UnsizedFormat::RGB , false, ComponentType::Float   );
    AddFormatInfo(FOO(RGBA16F       ),  8, UnsizedFormat::RGBA, false, ComponentType::Float   );
    AddFormatInfo(FOO(R32F          ),  4, UnsizedFormat::R   , false, ComponentType::Float   );
    AddFormatInfo(FOO(RG32F         ),  8, UnsizedFormat::RG  , false, ComponentType::Float   );
    AddFormatInfo(FOO(RGB32F        ), 12, UnsizedFormat::RGB , false, ComponentType::Float   );
    AddFormatInfo(FOO(RGBA32F       ), 16, UnsizedFormat::RGBA, false, ComponentType::Float   );

    AddFormatInfo(FOO(R11F_G11F_B10F),  4, UnsizedFormat::RGB , false, ComponentType::Float   );
    AddFormatInfo(FOO(RGB9_E5       ),  4, UnsizedFormat::RGB , false, ComponentType::Float   );

    AddFormatInfo(FOO(R8I           ),  1, UnsizedFormat::R   , false, ComponentType::Int     );
    AddFormatInfo(FOO(R8UI          ),  1, UnsizedFormat::R   , false, ComponentType::UInt    );
    AddFormatInfo(FOO(R16I          ),  2, UnsizedFormat::R   , false, ComponentType::Int     );
    AddFormatInfo(FOO(R16UI         ),  2, UnsizedFormat::R   , false, ComponentType::UInt    );
    AddFormatInfo(FOO(R32I          ),  4, UnsizedFormat::R   , false, ComponentType::Int     );
    AddFormatInfo(FOO(R32UI         ),  4, UnsizedFormat::R   , false, ComponentType::UInt    );

    AddFormatInfo(FOO(RG8I          ),  2, UnsizedFormat::RG  , false, ComponentType::Int     );
    AddFormatInfo(FOO(RG8UI         ),  2, UnsizedFormat::RG  , false, ComponentType::UInt    );
    AddFormatInfo(FOO(RG16I         ),  4, UnsizedFormat::RG  , false, ComponentType::Int     );
    AddFormatInfo(FOO(RG16UI        ),  4, UnsizedFormat::RG  , false, ComponentType::UInt    );
    AddFormatInfo(FOO(RG32I         ),  8, UnsizedFormat::RG  , false, ComponentType::Int     );
    AddFormatInfo(FOO(RG32UI        ),  8, UnsizedFormat::RG  , false, ComponentType::UInt    );

    AddFormatInfo(FOO(RGB8I         ),  3, UnsizedFormat::RGB , false, ComponentType::Int     );
    AddFormatInfo(FOO(RGB8UI        ),  3, UnsizedFormat::RGB , false, ComponentType::UInt    );
    AddFormatInfo(FOO(RGB16I        ),  6, UnsizedFormat::RGB , false, ComponentType::Int     );
    AddFormatInfo(FOO(RGB16UI       ),  6, UnsizedFormat::RGB , false, ComponentType::UInt    );
    AddFormatInfo(FOO(RGB32I        ), 12, UnsizedFormat::RGB , false, ComponentType::Int     );
    AddFormatInfo(FOO(RGB32UI       ), 12, UnsizedFormat::RGB , false, ComponentType::UInt    );

    AddFormatInfo(FOO(RGBA8I        ),  4, UnsizedFormat::RGBA, false, ComponentType::Int     );
    AddFormatInfo(FOO(RGBA8UI       ),  4, UnsizedFormat::RGBA, false, ComponentType::UInt    );
    AddFormatInfo(FOO(RGBA16I       ),  8, UnsizedFormat::RGBA, false, ComponentType::Int     );
    AddFormatInfo(FOO(RGBA16UI      ),  8, UnsizedFormat::RGBA, false, ComponentType::UInt    );
    AddFormatInfo(FOO(RGBA32I       ), 16, UnsizedFormat::RGBA, false, ComponentType::Int     );
    AddFormatInfo(FOO(RGBA32UI      ), 16, UnsizedFormat::RGBA, false, ComponentType::UInt    );

    // GLES 3.0.4, p133, table 3.14
    AddFormatInfo(FOO(DEPTH_COMPONENT16 ), 2, UnsizedFormat::D , false, ComponentType::NormUInt);
    AddFormatInfo(FOO(DEPTH_COMPONENT24 ), 3, UnsizedFormat::D , false, ComponentType::NormUInt);
    AddFormatInfo(FOO(DEPTH_COMPONENT32F), 4, UnsizedFormat::D , false, ComponentType::Float);
    AddFormatInfo(FOO(DEPTH24_STENCIL8  ), 4, UnsizedFormat::DS, false, ComponentType::Special);
    AddFormatInfo(FOO(DEPTH32F_STENCIL8 ), 5, UnsizedFormat::DS, false, ComponentType::Special);

    // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
    AddFormatInfo(FOO(STENCIL_INDEX8), 1, UnsizedFormat::S, false, ComponentType::UInt);

    // GLES 3.0.4, p147, table 3.19
    // GLES 3.0.4  p286+  $C.1 "ETC Compressed Texture Image Formats"
    AddFormatInfo(FOO(COMPRESSED_RGB8_ETC2                     ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
    AddFormatInfo(FOO(COMPRESSED_SRGB8_ETC2                    ), 0, UnsizedFormat::RGB , true , ComponentType::NormUInt);
    AddFormatInfo(FOO(COMPRESSED_RGBA8_ETC2_EAC                ), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
    AddFormatInfo(FOO(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         ), 0, UnsizedFormat::RGBA, true , ComponentType::NormUInt);
    AddFormatInfo(FOO(COMPRESSED_R11_EAC                       ), 0, UnsizedFormat::R   , false, ComponentType::NormUInt);
    AddFormatInfo(FOO(COMPRESSED_RG11_EAC                      ), 0, UnsizedFormat::RG  , false, ComponentType::NormUInt);
    AddFormatInfo(FOO(COMPRESSED_SIGNED_R11_EAC                ), 0, UnsizedFormat::R   , false, ComponentType::NormInt );
    AddFormatInfo(FOO(COMPRESSED_SIGNED_RG11_EAC               ), 0, UnsizedFormat::RG  , false, ComponentType::NormInt );
    AddFormatInfo(FOO(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
    AddFormatInfo(FOO(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2), 0, UnsizedFormat::RGBA, true , ComponentType::NormUInt);

    // AMD_compressed_ATC_texture
    AddFormatInfo(FOO(ATC_RGB_AMD                    ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
    AddFormatInfo(FOO(ATC_RGBA_EXPLICIT_ALPHA_AMD    ), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
    AddFormatInfo(FOO(ATC_RGBA_INTERPOLATED_ALPHA_AMD), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);

    // EXT_texture_compression_s3tc
    AddFormatInfo(FOO(COMPRESSED_RGB_S3TC_DXT1_EXT ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT1_EXT), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT3_EXT), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
    AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT5_EXT), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);

    // IMG_texture_compression_pvrtc
    AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_4BPPV1 ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
    AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_4BPPV1), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);
    AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_2BPPV1 ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt);
    AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_2BPPV1), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt);

    // OES_compressed_ETC1_RGB8_texture
    AddFormatInfo(FOO(ETC1_RGB8_OES), 0, UnsizedFormat::RGB, false, ComponentType::NormUInt);

#undef FOO

    // 'Virtual' effective formats have no sizedFormat.
#define FOO(x) EffectiveFormat::x, #x, 0

    // GLES 3.0.4, p128, table 3.12.
    AddFormatInfo(FOO(Luminance8Alpha8), 2, UnsizedFormat::LA, false, ComponentType::NormUInt);
    AddFormatInfo(FOO(Luminance8      ), 1, UnsizedFormat::L , false, ComponentType::NormUInt);
    AddFormatInfo(FOO(Alpha8          ), 1, UnsizedFormat::A , false, ComponentType::NormUInt);

    // OES_texture_float
    AddFormatInfo(FOO(Luminance32FAlpha32F), 8, UnsizedFormat::LA, false, ComponentType::Float);
    AddFormatInfo(FOO(Luminance32F        ), 4, UnsizedFormat::L , false, ComponentType::Float);
    AddFormatInfo(FOO(Alpha32F            ), 4, UnsizedFormat::A , false, ComponentType::Float);

    // OES_texture_half_float
    AddFormatInfo(FOO(Luminance16FAlpha16F), 4, UnsizedFormat::LA, false, ComponentType::Float);
    AddFormatInfo(FOO(Luminance16F        ), 2, UnsizedFormat::L , false, ComponentType::Float);
    AddFormatInfo(FOO(Alpha16F            ), 2, UnsizedFormat::A , false, ComponentType::Float);

#undef FOO
}

//////////////////////////////////////////////////////////////////////////////////////////

bool gAreFormatTablesInitialized = false;

static void
EnsureInitFormatTables(const StaticMutexAutoLock&) // Prove that you locked it!
{
    if (MOZ_LIKELY(gAreFormatTablesInitialized))
        return;

    gAreFormatTablesInitialized = true;

    InitCompressedFormatInfo();
    InitFormatInfo();
}

//////////////////////////////////////////////////////////////////////////////////////////
// Public funcs

StaticMutex gFormatMapMutex;

const FormatInfo*
GetFormat(EffectiveFormat format)
{
    StaticMutexAutoLock lock(gFormatMapMutex);
    EnsureInitFormatTables(lock);

    return GetFormatInfo_NoLock(format);
}

//////////////////////////////////////////////////////////////////////////////////////////

uint8_t
BytesPerPixel(const PackingInfo& packing)
{
    uint8_t bytesPerChannel;
    switch (packing.type) {
    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
        return 2;

    case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
    case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
    case LOCAL_GL_UNSIGNED_INT_24_8:
    case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
        return 4;

    case LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
        return 8;

    // Alright, that's all the fixed-size unpackTypes.

    case LOCAL_GL_BYTE:
    case LOCAL_GL_UNSIGNED_BYTE:
        bytesPerChannel = 1;
        break;

    case LOCAL_GL_SHORT:
    case LOCAL_GL_UNSIGNED_SHORT:
    case LOCAL_GL_HALF_FLOAT:
    case LOCAL_GL_HALF_FLOAT_OES:
        bytesPerChannel = 2;
        break;

    case LOCAL_GL_INT:
    case LOCAL_GL_UNSIGNED_INT:
    case LOCAL_GL_FLOAT:
        bytesPerChannel = 4;
        break;

    default:
        MOZ_CRASH("invalid PackingInfo");
    }

    uint8_t channels;
    switch (packing.format) {
    case LOCAL_GL_RG:
    case LOCAL_GL_RG_INTEGER:
    case LOCAL_GL_LUMINANCE_ALPHA:
        channels = 2;
        break;

    case LOCAL_GL_RGB:
    case LOCAL_GL_RGB_INTEGER:
        channels = 3;
        break;

    case LOCAL_GL_RGBA:
    case LOCAL_GL_RGBA_INTEGER:
        channels = 4;
        break;

    default:
        channels = 1;
        break;
    }

    return bytesPerChannel * channels;
}



//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// FormatUsageAuthority

bool
FormatUsageInfo::IsUnpackValid(const PackingInfo& key,
                               const DriverUnpackInfo** const out_value) const
{
    auto itr = validUnpacks.find(key);
    if (itr == validUnpacks.end())
        return false;

    *out_value = &(itr->second);
    return true;
}

void
FormatUsageInfo::ResolveMaxSamples(gl::GLContext* gl)
{
    MOZ_ASSERT(!this->maxSamplesKnown);
    MOZ_ASSERT(this->maxSamples == 0);
    MOZ_ASSERT(gl->IsCurrent());

    this->maxSamplesKnown = true;

    const GLenum internalFormat = this->format->sizedFormat;
    if (!internalFormat)
        return;

    if (!gl->IsSupported(gl::GLFeature::internalformat_query))
        return; // Leave it at 0.

    GLint maxSamplesGL = 0;
    gl->fGetInternalformativ(LOCAL_GL_RENDERBUFFER, internalFormat, LOCAL_GL_SAMPLES, 1,
                             &maxSamplesGL);

    this->maxSamples = maxSamplesGL;
}

////////////////////////////////////////

static void
AddSimpleUnsized(FormatUsageAuthority* fua, GLenum unpackFormat, GLenum unpackType,
                 EffectiveFormat effFormat)
{
    auto usage = fua->EditUsage(effFormat);
    usage->isFilterable = true;

    const PackingInfo pi = {unpackFormat, unpackType};
    const DriverUnpackInfo dui = {unpackFormat, unpackFormat, unpackType};
    fua->AddTexUnpack(usage, pi, dui);

    fua->AllowUnsizedTexFormat(pi, usage);
};


/*static*/ const GLint FormatUsageInfo::kLuminanceSwizzleRGBA[4] = { LOCAL_GL_RED,
                                                                     LOCAL_GL_RED,
                                                                     LOCAL_GL_RED,
                                                                     LOCAL_GL_ONE };
/*static*/ const GLint FormatUsageInfo::kAlphaSwizzleRGBA[4] = { LOCAL_GL_ZERO,
                                                                 LOCAL_GL_ZERO,
                                                                 LOCAL_GL_ZERO,
                                                                 LOCAL_GL_RED };
/*static*/ const GLint FormatUsageInfo::kLumAlphaSwizzleRGBA[4] = { LOCAL_GL_RED,
                                                                    LOCAL_GL_RED,
                                                                    LOCAL_GL_RED,
                                                                    LOCAL_GL_GREEN };

static bool
AddLegacyFormats_LA8(FormatUsageAuthority* fua, gl::GLContext* gl)
{
    if (gl->IsCoreProfile()) {
        if (!gl->IsSupported(gl::GLFeature::texture_swizzle))
            return false;

        PackingInfo pi;
        DriverUnpackInfo dui;

        const auto fnAdd = [fua, &pi, &dui](EffectiveFormat effFormat,
                                            const GLint* swizzle)
        {
            auto usage = fua->EditUsage(effFormat);
            usage->isFilterable = true;
            usage->textureSwizzleRGBA = swizzle;

            fua->AddTexUnpack(usage, pi, dui);

            fua->AllowUnsizedTexFormat(pi, usage);
        };

        pi = {LOCAL_GL_LUMINANCE, LOCAL_GL_UNSIGNED_BYTE};
        dui = {LOCAL_GL_R8, LOCAL_GL_RED, LOCAL_GL_UNSIGNED_BYTE};
        fnAdd(EffectiveFormat::Luminance8, FormatUsageInfo::kLuminanceSwizzleRGBA);

        pi = {LOCAL_GL_ALPHA, LOCAL_GL_UNSIGNED_BYTE};
        dui = {LOCAL_GL_R8, LOCAL_GL_RED, LOCAL_GL_UNSIGNED_BYTE};
        fnAdd(EffectiveFormat::Alpha8, FormatUsageInfo::kAlphaSwizzleRGBA);

        pi = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE};
        dui = {LOCAL_GL_RG8, LOCAL_GL_RG, LOCAL_GL_UNSIGNED_BYTE};
        fnAdd(EffectiveFormat::Luminance8Alpha8, FormatUsageInfo::kLumAlphaSwizzleRGBA);
    } else {
        AddSimpleUnsized(fua, LOCAL_GL_LUMINANCE      , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8      );
        AddSimpleUnsized(fua, LOCAL_GL_ALPHA          , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Alpha8          );
        AddSimpleUnsized(fua, LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8Alpha8);
    }

    return true;
}

static bool
AddUnsizedFormats(FormatUsageAuthority* fua, gl::GLContext* gl)
{
    // GLES 2.0.25, p63, Table 3.4
    AddSimpleUnsized(fua, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE         , EffectiveFormat::RGBA8  );
    AddSimpleUnsized(fua, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4, EffectiveFormat::RGBA4  );
    AddSimpleUnsized(fua, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1, EffectiveFormat::RGB5_A1);
    AddSimpleUnsized(fua, LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_BYTE         , EffectiveFormat::RGB8   );
    AddSimpleUnsized(fua, LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_SHORT_5_6_5  , EffectiveFormat::RGB565 );

    // L, A, LA
    return AddLegacyFormats_LA8(fua, gl);
}

UniquePtr<FormatUsageAuthority>
FormatUsageAuthority::CreateForWebGL1(gl::GLContext* gl)
{
    UniquePtr<FormatUsageAuthority> ret(new FormatUsageAuthority);
    const auto ptr = ret.get();

    ////////////////////////////////////////////////////////////////////////////
    // Usages

    const auto fnSet = [ptr](EffectiveFormat effFormat, bool isRenderable,
                             bool isFilterable)
    {
        MOZ_ASSERT(!ptr->GetUsage(effFormat));

        auto usage = ptr->EditUsage(effFormat);
        usage->isRenderable = isRenderable;
        usage->isFilterable = isFilterable;
    };

    // GLES 2.0.25, p117, Table 4.5
    // RGBA8 is made renderable in WebGL 1.0, "Framebuffer Object Attachments"
    //                              render filter
    //                              able   able
    fnSet(EffectiveFormat::RGBA8  , true, true);
    fnSet(EffectiveFormat::RGBA4  , true, true);
    fnSet(EffectiveFormat::RGB5_A1, true, true);
    fnSet(EffectiveFormat::RGB565 , true, true);

    // RGB8 is not guaranteed to be renderable, but we should allow it for web-compat.
    // Min-capability mode should mark this as non-renderable.
    fnSet(EffectiveFormat::RGB8, true, true);

    fnSet(EffectiveFormat::Luminance8Alpha8, false, true);
    fnSet(EffectiveFormat::Luminance8      , false, true);
    fnSet(EffectiveFormat::Alpha8          , false, true);

    fnSet(EffectiveFormat::DEPTH_COMPONENT16, true, false);
    fnSet(EffectiveFormat::STENCIL_INDEX8   , true, false);

    // Added in WebGL 1.0 spec:
    fnSet(EffectiveFormat::DEPTH24_STENCIL8, true, false);

    ////////////////////////////////////
    // RB formats

#define FOO(x) ptr->AllowRBFormat(LOCAL_GL_ ## x, ptr->GetUsage(EffectiveFormat::x))

    FOO(RGBA4            );
    FOO(RGB5_A1          );
    FOO(RGB565           );
    FOO(DEPTH_COMPONENT16);
    FOO(STENCIL_INDEX8   );
    //FOO(DEPTH24_STENCIL8 ); // WebGL 1 uses DEPTH_STENCIL instead of DEPTH24_STENCIL8.

#undef FOO

    ptr->AllowRBFormat(LOCAL_GL_DEPTH_STENCIL,
                       ptr->GetUsage(EffectiveFormat::DEPTH24_STENCIL8));

    ////////////////////////////////////////////////////////////////////////////

    if (!AddUnsizedFormats(ptr, gl))
        return nullptr;

    return Move(ret);
}

UniquePtr<FormatUsageAuthority>
FormatUsageAuthority::CreateForWebGL2(gl::GLContext* gl)
{
    UniquePtr<FormatUsageAuthority> ret(new FormatUsageAuthority);
    const auto ptr = ret.get();

    ////////////////////////////////////////////////////////////////////////////
    // GLES 3.0.4 p111-113

    const auto fnAddSizedUnpack = [ptr](EffectiveFormat effFormat, GLenum internalFormat,
                                        GLenum unpackFormat, GLenum unpackType)
    {
        auto usage = ptr->EditUsage(effFormat);

        const PackingInfo pi = {unpackFormat, unpackType};
        const DriverUnpackInfo dui = {internalFormat, unpackFormat, unpackType};
        ptr->AddTexUnpack(usage, pi, dui);
    };

#define FOO(x) EffectiveFormat::x, LOCAL_GL_ ## x

    // RGBA
    fnAddSizedUnpack(FOO(RGBA8       ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              );
    fnAddSizedUnpack(FOO(RGBA4       ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4     );
    fnAddSizedUnpack(FOO(RGBA4       ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              );
    fnAddSizedUnpack(FOO(RGB5_A1     ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1     );
    fnAddSizedUnpack(FOO(RGB5_A1     ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              );
    fnAddSizedUnpack(FOO(RGB5_A1     ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV);
    fnAddSizedUnpack(FOO(SRGB8_ALPHA8), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE              );
    fnAddSizedUnpack(FOO(RGBA8_SNORM ), LOCAL_GL_RGBA, LOCAL_GL_BYTE                       );
    fnAddSizedUnpack(FOO(RGB10_A2    ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV);
    fnAddSizedUnpack(FOO(RGBA16F     ), LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT                 );
    fnAddSizedUnpack(FOO(RGBA16F     ), LOCAL_GL_RGBA, LOCAL_GL_FLOAT                      );
    fnAddSizedUnpack(FOO(RGBA32F     ), LOCAL_GL_RGBA, LOCAL_GL_FLOAT                      );

    // RGBA_INTEGER
    fnAddSizedUnpack(FOO(RGBA8UI   ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_BYTE              );
    fnAddSizedUnpack(FOO(RGBA8I    ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_BYTE                       );
    fnAddSizedUnpack(FOO(RGBA16UI  ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_SHORT             );
    fnAddSizedUnpack(FOO(RGBA16I   ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_SHORT                      );
    fnAddSizedUnpack(FOO(RGBA32UI  ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT               );
    fnAddSizedUnpack(FOO(RGBA32I   ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_INT                        );
    fnAddSizedUnpack(FOO(RGB10_A2UI), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV);

    // RGB
    fnAddSizedUnpack(FOO(RGB8          ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE               );
    fnAddSizedUnpack(FOO(SRGB8         ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE               );
    fnAddSizedUnpack(FOO(RGB565        ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_SHORT_5_6_5        );
    fnAddSizedUnpack(FOO(RGB565        ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE               );
    fnAddSizedUnpack(FOO(RGB8_SNORM    ), LOCAL_GL_RGB, LOCAL_GL_BYTE                        );
    fnAddSizedUnpack(FOO(R11F_G11F_B10F), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV);
    fnAddSizedUnpack(FOO(R11F_G11F_B10F), LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT                  );
    fnAddSizedUnpack(FOO(R11F_G11F_B10F), LOCAL_GL_RGB, LOCAL_GL_FLOAT                       );
    fnAddSizedUnpack(FOO(RGB16F        ), LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT                  );
    fnAddSizedUnpack(FOO(RGB16F        ), LOCAL_GL_RGB, LOCAL_GL_FLOAT                       );
    fnAddSizedUnpack(FOO(RGB9_E5       ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV    );
    fnAddSizedUnpack(FOO(RGB9_E5       ), LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT                  );
    fnAddSizedUnpack(FOO(RGB9_E5       ), LOCAL_GL_RGB, LOCAL_GL_FLOAT                       );
    fnAddSizedUnpack(FOO(RGB32F        ), LOCAL_GL_RGB, LOCAL_GL_FLOAT                       );

    // RGB_INTEGER
    fnAddSizedUnpack(FOO(RGB8UI ), LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_BYTE );
    fnAddSizedUnpack(FOO(RGB8I  ), LOCAL_GL_RGB_INTEGER, LOCAL_GL_BYTE          );
    fnAddSizedUnpack(FOO(RGB16UI), LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_SHORT);
    fnAddSizedUnpack(FOO(RGB16I ), LOCAL_GL_RGB_INTEGER, LOCAL_GL_SHORT         );
    fnAddSizedUnpack(FOO(RGB32UI), LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_INT  );
    fnAddSizedUnpack(FOO(RGB32I ), LOCAL_GL_RGB_INTEGER, LOCAL_GL_INT           );

    // RG
    fnAddSizedUnpack(FOO(RG8      ), LOCAL_GL_RG, LOCAL_GL_UNSIGNED_BYTE);
    fnAddSizedUnpack(FOO(RG8_SNORM), LOCAL_GL_RG, LOCAL_GL_BYTE         );
    fnAddSizedUnpack(FOO(RG16F    ), LOCAL_GL_RG, LOCAL_GL_HALF_FLOAT   );
    fnAddSizedUnpack(FOO(RG16F    ), LOCAL_GL_RG, LOCAL_GL_FLOAT        );
    fnAddSizedUnpack(FOO(RG32F    ), LOCAL_GL_RG, LOCAL_GL_FLOAT        );

    // RG_INTEGER
    fnAddSizedUnpack(FOO(RG8UI ), LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_BYTE );
    fnAddSizedUnpack(FOO(RG8I  ), LOCAL_GL_RG_INTEGER, LOCAL_GL_BYTE          );
    fnAddSizedUnpack(FOO(RG16UI), LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_SHORT);
    fnAddSizedUnpack(FOO(RG16I ), LOCAL_GL_RG_INTEGER, LOCAL_GL_SHORT         );
    fnAddSizedUnpack(FOO(RG32UI), LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_INT  );
    fnAddSizedUnpack(FOO(RG32I ), LOCAL_GL_RG_INTEGER, LOCAL_GL_INT           );

    // RED
    fnAddSizedUnpack(FOO(R8      ), LOCAL_GL_RED, LOCAL_GL_UNSIGNED_BYTE);
    fnAddSizedUnpack(FOO(R8_SNORM), LOCAL_GL_RED, LOCAL_GL_BYTE         );
    fnAddSizedUnpack(FOO(R16F    ), LOCAL_GL_RED, LOCAL_GL_HALF_FLOAT   );
    fnAddSizedUnpack(FOO(R16F    ), LOCAL_GL_RED, LOCAL_GL_FLOAT        );
    fnAddSizedUnpack(FOO(R32F    ), LOCAL_GL_RED, LOCAL_GL_FLOAT        );

    // RED_INTEGER
    fnAddSizedUnpack(FOO(R8UI ), LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_BYTE );
    fnAddSizedUnpack(FOO(R8I  ), LOCAL_GL_RED_INTEGER, LOCAL_GL_BYTE          );
    fnAddSizedUnpack(FOO(R16UI), LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_SHORT);
    fnAddSizedUnpack(FOO(R16I ), LOCAL_GL_RED_INTEGER, LOCAL_GL_SHORT         );
    fnAddSizedUnpack(FOO(R32UI), LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_INT  );
    fnAddSizedUnpack(FOO(R32I ), LOCAL_GL_RED_INTEGER, LOCAL_GL_INT           );

    // DEPTH_COMPONENT
    fnAddSizedUnpack(FOO(DEPTH_COMPONENT16 ), LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_SHORT);
    fnAddSizedUnpack(FOO(DEPTH_COMPONENT16 ), LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_INT  );
    fnAddSizedUnpack(FOO(DEPTH_COMPONENT24 ), LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_INT  );
    fnAddSizedUnpack(FOO(DEPTH_COMPONENT32F), LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_FLOAT         );

    // DEPTH_STENCIL
    fnAddSizedUnpack(FOO(DEPTH24_STENCIL8 ), LOCAL_GL_DEPTH_STENCIL, LOCAL_GL_UNSIGNED_INT_24_8             );
    fnAddSizedUnpack(FOO(DEPTH32F_STENCIL8), LOCAL_GL_DEPTH_STENCIL, LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV);

#undef FOO

    ////////////////////////////////////////////////////////////////////////////

    // For renderable, see GLES 3.0.4, p212 "Framebuffer Completeness"
    // For filterable, see GLES 3.0.4, p161 "...a texture is complete unless..."

    const auto fnAllowES3TexFormat = [ptr](GLenum sizedFormat, EffectiveFormat effFormat,
                                           bool isRenderable, bool isFilterable)
    {
        auto usage = ptr->EditUsage(effFormat);
        usage->isRenderable = isRenderable;
        usage->isFilterable = isFilterable;

        ptr->AllowSizedTexFormat(sizedFormat, usage);

        if (isRenderable) {
            ptr->AllowRBFormat(sizedFormat, usage);
        }
    };

#define FOO(x) LOCAL_GL_ ## x, EffectiveFormat::x

    // GLES 3.0.4, p128-129 "Required Texture Formats"
    // GLES 3.0.4, p130-132, table 3.13
    //                                   render filter
    //                                    able   able
    fnAllowES3TexFormat(FOO(R8         ), true , true );
    fnAllowES3TexFormat(FOO(R8_SNORM   ), false, true );
    fnAllowES3TexFormat(FOO(RG8        ), true , true );
    fnAllowES3TexFormat(FOO(RG8_SNORM  ), false, true );
    fnAllowES3TexFormat(FOO(RGB8       ), true , true );
    fnAllowES3TexFormat(FOO(RGB8_SNORM ), false, true );
    fnAllowES3TexFormat(FOO(RGB565     ), true , true );
    fnAllowES3TexFormat(FOO(RGBA4      ), true , true );
    fnAllowES3TexFormat(FOO(RGB5_A1    ), true , true );
    fnAllowES3TexFormat(FOO(RGBA8      ), true , true );
    fnAllowES3TexFormat(FOO(RGBA8_SNORM), false, true );
    fnAllowES3TexFormat(FOO(RGB10_A2   ), true , true );
    fnAllowES3TexFormat(FOO(RGB10_A2UI ), true , false);

    fnAllowES3TexFormat(FOO(SRGB8       ), false, true);
    fnAllowES3TexFormat(FOO(SRGB8_ALPHA8), true , true);

    fnAllowES3TexFormat(FOO(R16F   ), false, true);
    fnAllowES3TexFormat(FOO(RG16F  ), false, true);
    fnAllowES3TexFormat(FOO(RGB16F ), false, true);
    fnAllowES3TexFormat(FOO(RGBA16F), false, true);

    fnAllowES3TexFormat(FOO(R32F   ), false, false);
    fnAllowES3TexFormat(FOO(RG32F  ), false, false);
    fnAllowES3TexFormat(FOO(RGB32F ), false, false);
    fnAllowES3TexFormat(FOO(RGBA32F), false, false);

    fnAllowES3TexFormat(FOO(R11F_G11F_B10F), false, true);
    fnAllowES3TexFormat(FOO(RGB9_E5       ), false, true);

    fnAllowES3TexFormat(FOO(R8I  ), true, false);
    fnAllowES3TexFormat(FOO(R8UI ), true, false);
    fnAllowES3TexFormat(FOO(R16I ), true, false);
    fnAllowES3TexFormat(FOO(R16UI), true, false);
    fnAllowES3TexFormat(FOO(R32I ), true, false);
    fnAllowES3TexFormat(FOO(R32UI), true, false);

    fnAllowES3TexFormat(FOO(RG8I  ), true, false);
    fnAllowES3TexFormat(FOO(RG8UI ), true, false);
    fnAllowES3TexFormat(FOO(RG16I ), true, false);
    fnAllowES3TexFormat(FOO(RG16UI), true, false);
    fnAllowES3TexFormat(FOO(RG32I ), true, false);
    fnAllowES3TexFormat(FOO(RG32UI), true, false);

    fnAllowES3TexFormat(FOO(RGB8I  ), false, false);
    fnAllowES3TexFormat(FOO(RGB8UI ), false, false);
    fnAllowES3TexFormat(FOO(RGB16I ), false, false);
    fnAllowES3TexFormat(FOO(RGB16UI), false, false);
    fnAllowES3TexFormat(FOO(RGB32I ), false, false);
    fnAllowES3TexFormat(FOO(RGB32UI), false, false);

    fnAllowES3TexFormat(FOO(RGBA8I  ), true, false);
    fnAllowES3TexFormat(FOO(RGBA8UI ), true, false);
    fnAllowES3TexFormat(FOO(RGBA16I ), true, false);
    fnAllowES3TexFormat(FOO(RGBA16UI), true, false);
    fnAllowES3TexFormat(FOO(RGBA32I ), true, false);
    fnAllowES3TexFormat(FOO(RGBA32UI), true, false);

    // GLES 3.0.4, p133, table 3.14
    fnAllowES3TexFormat(FOO(DEPTH_COMPONENT16 ), true, false);
    fnAllowES3TexFormat(FOO(DEPTH_COMPONENT24 ), true, false);
    fnAllowES3TexFormat(FOO(DEPTH_COMPONENT32F), true, false);
    fnAllowES3TexFormat(FOO(DEPTH24_STENCIL8  ), true, false);
    fnAllowES3TexFormat(FOO(DEPTH32F_STENCIL8 ), true, false);

    // GLES 3.0.4, p147, table 3.19
    // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats"
#if ALLOW_ES3_FORMATS
    // Note that all compressed texture formats are filterable:
    // GLES 3.0.4 p161:
    // "[A] texture is complete unless any of the following conditions hold true:
    //  [...]
    //  * The effective internal format specified for the texture arrays is a sized
    //    internal color format that is not texture-filterable (see table 3.13) and [the
    //    mag filter requires filtering]."
    // Compressed formats are not sized internal color formats, and indeed they are not
    // listed in table 3.13.
    fnAllowES3TexFormat(FOO(COMPRESSED_RGB8_ETC2                     ), false, true);
    fnAllowES3TexFormat(FOO(COMPRESSED_SRGB8_ETC2                    ), false, true);
    fnAllowES3TexFormat(FOO(COMPRESSED_RGBA8_ETC2_EAC                ), false, true);
    fnAllowES3TexFormat(FOO(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         ), false, true);
    fnAllowES3TexFormat(FOO(COMPRESSED_R11_EAC                       ), false, true);
    fnAllowES3TexFormat(FOO(COMPRESSED_RG11_EAC                      ), false, true);
    fnAllowES3TexFormat(FOO(COMPRESSED_SIGNED_R11_EAC                ), false, true);
    fnAllowES3TexFormat(FOO(COMPRESSED_SIGNED_RG11_EAC               ), false, true);
    fnAllowES3TexFormat(FOO(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ), false, true);
    fnAllowES3TexFormat(FOO(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2), false, true);
#endif
#undef FOO

    // GLES 3.0.4, p206, "Required Renderbuffer Formats":
    // "Implementations are also required to support STENCIL_INDEX8. Requesting this
    //  internal format for a renderbuffer will allocate at least 8 stencil bit planes."

    auto usage = ptr->EditUsage(EffectiveFormat::STENCIL_INDEX8);
    usage->isRenderable = true;
    ptr->AllowRBFormat(LOCAL_GL_STENCIL_INDEX8, usage);

    ////////////////
    // Legacy formats

    if (!AddUnsizedFormats(ptr, gl))
        return nullptr;

    ptr->AllowRBFormat(LOCAL_GL_DEPTH_STENCIL,
                       ptr->GetUsage(EffectiveFormat::DEPTH24_STENCIL8));

    if (gfxPrefs::WebGL2CompatMode()) {
        AddSimpleUnsized(ptr, LOCAL_GL_RGBA, LOCAL_GL_FLOAT, EffectiveFormat::RGBA32F);
        AddSimpleUnsized(ptr, LOCAL_GL_RGB , LOCAL_GL_FLOAT, EffectiveFormat::RGB32F );

        AddSimpleUnsized(ptr, LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGBA16F);
        AddSimpleUnsized(ptr, LOCAL_GL_RGB , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGB16F );
    }

    return Move(ret);
}

//////////////////////////////////////////////////////////////////////////////////////////

void
FormatUsageAuthority::AddTexUnpack(FormatUsageInfo* usage, const PackingInfo& pi,
                                   const DriverUnpackInfo& dui)
{
    // Don't AlwaysInsert here, since we'll see duplicates from sized and unsized formats.
    auto res = usage->validUnpacks.insert({ pi, dui });
    auto itr = res.first;

    if (!usage->idealUnpack) {
        // First one!
        usage->idealUnpack = &(itr->second);
    }

    mValidTexUnpackFormats.insert(pi.format);
    mValidTexUnpackTypes.insert(pi.type);
}

static bool
Contains(const std::set<GLenum>& set, GLenum key)
{
    return set.find(key) != set.end();
}

bool
FormatUsageAuthority::IsInternalFormatEnumValid(GLenum internalFormat) const
{
    return Contains(mValidTexInternalFormats, internalFormat);
}

bool
FormatUsageAuthority::AreUnpackEnumsValid(GLenum unpackFormat, GLenum unpackType) const
{
    return (Contains(mValidTexUnpackFormats, unpackFormat) &&
            Contains(mValidTexUnpackTypes, unpackType));
}

////////////////////

void
FormatUsageAuthority::AllowRBFormat(GLenum sizedFormat, const FormatUsageInfo* usage)
{
    MOZ_ASSERT(!usage->format->compression);
    MOZ_ASSERT(usage->format->sizedFormat);
    MOZ_ASSERT(usage->isRenderable);

    AlwaysInsert(mRBFormatMap, sizedFormat, usage);
}

void
FormatUsageAuthority::AllowSizedTexFormat(GLenum sizedFormat,
                                          const FormatUsageInfo* usage)
{
    if (usage->format->compression) {
        MOZ_ASSERT(usage->isFilterable, "Compressed formats should be filterable.");
    } else {
        MOZ_ASSERT(usage->validUnpacks.size() && usage->idealUnpack,
                   "AddTexUnpack() first.");
    }

    AlwaysInsert(mSizedTexFormatMap, sizedFormat, usage);

    mValidTexInternalFormats.insert(sizedFormat);
}

void
FormatUsageAuthority::AllowUnsizedTexFormat(const PackingInfo& pi,
                                            const FormatUsageInfo* usage)
{
    MOZ_ASSERT(!usage->format->compression);
    MOZ_ASSERT(usage->validUnpacks.size() && usage->idealUnpack, "AddTexUnpack() first.");

    AlwaysInsert(mUnsizedTexFormatMap, pi, usage);

    mValidTexInternalFormats.insert(pi.format);
    mValidTexUnpackFormats.insert(pi.format);
    mValidTexUnpackTypes.insert(pi.type);
}

const FormatUsageInfo*
FormatUsageAuthority::GetRBUsage(GLenum sizedFormat) const
{
    return FindOrNull(mRBFormatMap, sizedFormat);
}

const FormatUsageInfo*
FormatUsageAuthority::GetSizedTexUsage(GLenum sizedFormat) const
{
    return FindOrNull(mSizedTexFormatMap, sizedFormat);
}

const FormatUsageInfo*
FormatUsageAuthority::GetUnsizedTexUsage(const PackingInfo& pi) const
{
    return FindOrNull(mUnsizedTexFormatMap, pi);
}

FormatUsageInfo*
FormatUsageAuthority::EditUsage(EffectiveFormat format)
{
    auto itr = mUsageMap.find(format);

    if (itr == mUsageMap.end()) {
        const FormatInfo* formatInfo = GetFormat(format);
        MOZ_RELEASE_ASSERT(formatInfo);

        FormatUsageInfo usage(formatInfo);

        auto res = mUsageMap.insert({ format, usage });
        DebugOnly<bool> didInsert = res.second;
        MOZ_ASSERT(didInsert);

        itr = res.first;
    }

    return &(itr->second);
}

const FormatUsageInfo*
FormatUsageAuthority::GetUsage(EffectiveFormat format) const
{
    auto itr = mUsageMap.find(format);
    if (itr == mUsageMap.end())
        return nullptr;

    return &(itr->second);
}

////////////////////////////////////////////////////////////////////////////////

} // namespace webgl
} // namespace mozilla