Clarify some WebGLFormat funcs and support INVALID_ENUM checking. draft
authorJeff Gilbert <jdashg@gmail.com>
Thu, 17 Dec 2015 16:16:55 -0800
changeset 316117 db15243eb79b06712e7f988e62ba3c6f97f4850b
parent 316116 899f06dc6313532447c55e0b71402ecacb753883
child 316118 9cf63352ddc4baae5b5dadd670832d1806ed0b0e
push id8514
push userjgilbert@mozilla.com
push dateFri, 18 Dec 2015 00:24:33 +0000
milestone45.0a1
Clarify some WebGLFormat funcs and support INVALID_ENUM checking. For Tex(Sub)Image, at least.
dom/canvas/WebGLExtensionColorBufferFloat.cpp
dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp
dom/canvas/WebGLExtensionCompressedTextureATC.cpp
dom/canvas/WebGLExtensionCompressedTextureETC1.cpp
dom/canvas/WebGLExtensionCompressedTexturePVRTC.cpp
dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp
dom/canvas/WebGLExtensionDepthTexture.cpp
dom/canvas/WebGLExtensionSRGB.cpp
dom/canvas/WebGLExtensionTextureFloat.cpp
dom/canvas/WebGLExtensionTextureHalfFloat.cpp
dom/canvas/WebGLFormats.cpp
dom/canvas/WebGLFormats.h
dom/canvas/WebGLTextureUpload.cpp
--- a/dom/canvas/WebGLExtensionColorBufferFloat.cpp
+++ b/dom/canvas/WebGLExtensionColorBufferFloat.cpp
@@ -20,18 +20,17 @@ WebGLExtensionColorBufferFloat::WebGLExt
 {
     MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
 
     auto& fua = webgl->mFormatUsage;
 
     auto fnUpdateUsage = [&fua](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
         auto usage = fua->EditUsage(effFormat);
         usage->isRenderable = true;
-
-        fua->AddRBFormat(sizedFormat, usage);
+        fua->AllowRBFormat(sizedFormat, usage);
     };
 
 #define FOO(x) fnUpdateUsage(LOCAL_GL_ ## x, webgl::EffectiveFormat::x)
 
     // The extension doesn't actually add RGB32F; only RGBA32F.
     FOO(RGBA32F);
 
 #undef FOO
--- a/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp
+++ b/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp
@@ -20,18 +20,17 @@ WebGLExtensionColorBufferHalfFloat::WebG
 {
     MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
 
     auto& fua = webgl->mFormatUsage;
 
     auto fnUpdateUsage = [&fua](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
         auto usage = fua->EditUsage(effFormat);
         usage->isRenderable = true;
-
-        fua->AddRBFormat(sizedFormat, usage);
+        fua->AllowRBFormat(sizedFormat, usage);
     };
 
 #define FOO(x) fnUpdateUsage(LOCAL_GL_ ## x, webgl::EffectiveFormat::x)
 
     FOO(RGBA16F);
     FOO(RGB16F);
 
 #undef FOO
--- a/dom/canvas/WebGLExtensionCompressedTextureATC.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureATC.cpp
@@ -17,17 +17,17 @@ WebGLExtensionCompressedTextureATC::WebG
     : WebGLExtensionBase(webgl)
 {
     RefPtr<WebGLContext> webgl_ = webgl; // Bug 1201275
     const auto fnAdd = [&webgl_](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
         auto& fua = webgl_->mFormatUsage;
 
         auto usage = fua->EditUsage(effFormat);
         usage->isFilterable = true;
-        fua->AddSizedTexFormat(sizedFormat, usage);
+        fua->AllowSizedTexFormat(sizedFormat, usage);
 
         webgl_->mCompressedTextureFormats.AppendElement(sizedFormat);
     };
 
 #define FOO(x) LOCAL_GL_ ## x, webgl::EffectiveFormat::x
 
     fnAdd(FOO(ATC_RGB_AMD));
     fnAdd(FOO(ATC_RGBA_EXPLICIT_ALPHA_AMD));
--- a/dom/canvas/WebGLExtensionCompressedTextureETC1.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureETC1.cpp
@@ -17,17 +17,17 @@ WebGLExtensionCompressedTextureETC1::Web
     : WebGLExtensionBase(webgl)
 {
     RefPtr<WebGLContext> webgl_ = webgl; // Bug 1201275
     const auto fnAdd = [&webgl_](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
         auto& fua = webgl_->mFormatUsage;
 
         auto usage = fua->EditUsage(effFormat);
         usage->isFilterable = true;
-        fua->AddSizedTexFormat(sizedFormat, usage);
+        fua->AllowSizedTexFormat(sizedFormat, usage);
 
         webgl_->mCompressedTextureFormats.AppendElement(sizedFormat);
     };
 
 #define FOO(x) LOCAL_GL_ ## x, webgl::EffectiveFormat::x
 
     fnAdd(FOO(ETC1_RGB8_OES));
 
--- a/dom/canvas/WebGLExtensionCompressedTexturePVRTC.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTexturePVRTC.cpp
@@ -17,17 +17,17 @@ WebGLExtensionCompressedTexturePVRTC::We
     : WebGLExtensionBase(webgl)
 {
     RefPtr<WebGLContext> webgl_ = webgl; // Bug 1201275
     const auto fnAdd = [&webgl_](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
         auto& fua = webgl_->mFormatUsage;
 
         auto usage = fua->EditUsage(effFormat);
         usage->isFilterable = true;
-        fua->AddSizedTexFormat(sizedFormat, usage);
+        fua->AllowSizedTexFormat(sizedFormat, usage);
 
         webgl_->mCompressedTextureFormats.AppendElement(sizedFormat);
     };
 
 #define FOO(x) LOCAL_GL_ ## x, webgl::EffectiveFormat::x
 
     fnAdd(FOO(COMPRESSED_RGB_PVRTC_4BPPV1));
     fnAdd(FOO(COMPRESSED_RGB_PVRTC_2BPPV1));
--- a/dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp
+++ b/dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp
@@ -17,17 +17,17 @@ WebGLExtensionCompressedTextureS3TC::Web
     : WebGLExtensionBase(webgl)
 {
     RefPtr<WebGLContext> webgl_ = webgl; // Bug 1201275
     const auto fnAdd = [&webgl_](GLenum sizedFormat, webgl::EffectiveFormat effFormat) {
         auto& fua = webgl_->mFormatUsage;
 
         auto usage = fua->EditUsage(effFormat);
         usage->isFilterable = true;
-        fua->AddSizedTexFormat(sizedFormat, usage);
+        fua->AllowSizedTexFormat(sizedFormat, usage);
 
         webgl_->mCompressedTextureFormats.AppendElement(sizedFormat);
     };
 
 #define FOO(x) LOCAL_GL_ ## x, webgl::EffectiveFormat::x
 
     fnAdd(FOO(COMPRESSED_RGB_S3TC_DXT1_EXT));
     fnAdd(FOO(COMPRESSED_RGBA_S3TC_DXT1_EXT));
--- a/dom/canvas/WebGLExtensionDepthTexture.cpp
+++ b/dom/canvas/WebGLExtensionDepthTexture.cpp
@@ -18,19 +18,19 @@ WebGLExtensionDepthTexture::WebGLExtensi
     const auto fnAdd = [&fua](webgl::EffectiveFormat effFormat, GLenum unpackFormat,
                               GLenum unpackType)
     {
         auto usage = fua->EditUsage(effFormat);
         usage->isRenderable = true;
 
         const webgl::PackingInfo pi = {unpackFormat, unpackType};
         const webgl::DriverUnpackInfo dui = {unpackFormat, unpackFormat, unpackType};
+        fua->AddTexUnpack(usage, pi, dui);
 
-        fua->AddUnsizedTexFormat(pi, usage);
-        usage->AddUnpack(pi, dui);
+        fua->AllowUnsizedTexFormat(pi, usage);
     };
 
     fnAdd(webgl::EffectiveFormat::DEPTH_COMPONENT16, LOCAL_GL_DEPTH_COMPONENT,
           LOCAL_GL_UNSIGNED_SHORT);
 
     fnAdd(webgl::EffectiveFormat::DEPTH_COMPONENT24, LOCAL_GL_DEPTH_COMPONENT,
           LOCAL_GL_UNSIGNED_INT);
 
--- a/dom/canvas/WebGLExtensionSRGB.cpp
+++ b/dom/canvas/WebGLExtensionSRGB.cpp
@@ -35,26 +35,27 @@ WebGLExtensionSRGB::WebGLExtensionSRGB(W
         usage->isFilterable = true;
 
         webgl::DriverUnpackInfo dui = {format, format, LOCAL_GL_UNSIGNED_BYTE};
         const auto pi = dui.ToPacking();
 
         if (!gl_->IsGLES())
             dui.unpackFormat = desktopUnpackFormat;
 
-        fua->AddUnsizedTexFormat(pi, usage);
-        usage->AddUnpack(pi, dui);
+        fua->AddTexUnpack(usage, pi, dui);
+
+        fua->AllowUnsizedTexFormat(pi, usage);
     };
 
     fnAdd(webgl::EffectiveFormat::SRGB8, LOCAL_GL_SRGB, LOCAL_GL_RGB);
     fnAdd(webgl::EffectiveFormat::SRGB8_ALPHA8, LOCAL_GL_SRGB_ALPHA, LOCAL_GL_RGBA);
 
     auto usage = fua->EditUsage(webgl::EffectiveFormat::SRGB8_ALPHA8);
     usage->isRenderable = true;
-    fua->AddRBFormat(LOCAL_GL_SRGB8_ALPHA8, usage);
+    fua->AllowRBFormat(LOCAL_GL_SRGB8_ALPHA8, usage);
 }
 
 WebGLExtensionSRGB::~WebGLExtensionSRGB()
 {
 }
 
 bool
 WebGLExtensionSRGB::IsSupported(const WebGLContext* webgl)
--- a/dom/canvas/WebGLExtensionTextureFloat.cpp
+++ b/dom/canvas/WebGLExtensionTextureFloat.cpp
@@ -21,20 +21,20 @@ WebGLExtensionTextureFloat::WebGLExtensi
 
     webgl::PackingInfo pi;
     webgl::DriverUnpackInfo dui;
     const GLint* swizzle = nullptr;
 
     const auto fnAdd = [&fua, &pi, &dui, &swizzle](webgl::EffectiveFormat effFormat)
     {
         auto usage = fua->EditUsage(effFormat);
-        fua->AddUnsizedTexFormat(pi, usage);
-        usage->AddUnpack(pi, dui);
+        usage->textureSwizzleRGBA = swizzle;
+        fua->AddTexUnpack(usage, pi, dui);
 
-        usage->textureSwizzleRGBA = swizzle;
+        fua->AllowUnsizedTexFormat(pi, usage);
     };
 
     const bool needSizedInternal = !gl->IsGLES();
     MOZ_ASSERT_IF(needSizedInternal, gl->IsSupported(gl::GLFeature::texture_swizzle));
 
     ////////////////
 
     pi = {LOCAL_GL_RGBA, LOCAL_GL_FLOAT};
--- a/dom/canvas/WebGLExtensionTextureHalfFloat.cpp
+++ b/dom/canvas/WebGLExtensionTextureHalfFloat.cpp
@@ -19,20 +19,20 @@ WebGLExtensionTextureHalfFloat::WebGLExt
 
     webgl::PackingInfo pi;
     webgl::DriverUnpackInfo dui;
     const GLint* swizzle = nullptr;
 
     const auto fnAdd = [&fua, &pi, &dui, &swizzle](webgl::EffectiveFormat effFormat)
     {
         auto usage = fua->EditUsage(effFormat);
-        fua->AddUnsizedTexFormat(pi, usage);
-        usage->AddUnpack(pi, dui);
+        usage->textureSwizzleRGBA = swizzle;
+        fua->AddTexUnpack(usage, pi, dui);
 
-        usage->textureSwizzleRGBA = swizzle;
+        fua->AllowUnsizedTexFormat(pi, usage);
     };
 
     const bool needSizedInternal = !gl->IsGLES();
     MOZ_ASSERT_IF(needSizedInternal, gl->IsSupported(gl::GLFeature::texture_swizzle));
 
     GLenum driverUnpackType = LOCAL_GL_HALF_FLOAT;
     if (!gl->IsSupported(gl::GLFeature::texture_half_float)) {
         MOZ_ASSERT(gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float));
--- a/dom/canvas/WebGLFormats.cpp
+++ b/dom/canvas/WebGLFormats.cpp
@@ -400,38 +400,28 @@ BytesPerPixel(const PackingInfo& packing
         channels = 1;
         break;
     }
 
     return bytesPerChannel * channels;
 }
 
 
+
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 // FormatUsageAuthority
 
-void
-FormatUsageInfo::AddUnpack(const PackingInfo& key, const DriverUnpackInfo& value)
-{
-    // Don't AlwaysInsert here, since we'll see duplicates from sized and unsized formats.
-    auto res = validUnpacks.insert({ key, value });
-    auto itr = res.first;
 
-    if (!idealUnpack) {
-        // First one!
-        idealUnpack = &(itr->second);
-    }
-}
 
 bool
 FormatUsageInfo::IsUnpackValid(const PackingInfo& key,
                                const DriverUnpackInfo** const out_value) const
 {
     auto itr = validUnpacks.find(key);
     if (itr == validUnpacks.end())
         return false;
@@ -455,20 +445,20 @@ SetUsage(FormatUsageAuthority* fua, Effe
 
 static void
 AddSimpleUnsized(FormatUsageAuthority* fua, GLenum unpackFormat, GLenum unpackType,
                  EffectiveFormat effFormat)
 {
     auto usage = fua->EditUsage(effFormat);
 
     const PackingInfo pi = {unpackFormat, unpackType};
-    fua->AddUnsizedTexFormat(pi, usage);
+    const DriverUnpackInfo dui = {unpackFormat, unpackFormat, unpackType};
+    fua->AddTexUnpack(usage, pi, dui);
 
-    const DriverUnpackInfo dui = {unpackFormat, unpackFormat, unpackType};
-    usage->AddUnpack(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,
@@ -486,19 +476,21 @@ AddLegacyFormats_LA8(FormatUsageAuthorit
     if (gl->IsCoreProfile()) {
         PackingInfo pi;
         DriverUnpackInfo dui;
 
         const auto fnAdd = [fua, &pi, &dui](EffectiveFormat effFormat,
                                             const GLint* swizzle)
         {
             auto usage = fua->EditUsage(effFormat);
-            fua->AddUnsizedTexFormat(pi, usage);
-            usage->AddUnpack(pi, dui);
             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};
@@ -556,175 +548,58 @@ FormatUsageAuthority::CreateForWebGL1(gl
     SetUsage(ptr, EffectiveFormat::STENCIL_INDEX8   , true, false);
 
     // Added in WebGL 1.0 spec:
     SetUsage(ptr, EffectiveFormat::DEPTH24_STENCIL8, true, false);
 
     ////////////////////////////////////
     // RB formats
 
-#define FOO(x) ptr->AddRBFormat(LOCAL_GL_ ## x, ptr->GetUsage(EffectiveFormat::x))
+#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->AddRBFormat(LOCAL_GL_DEPTH_STENCIL,
-                     ptr->GetUsage(EffectiveFormat::DEPTH24_STENCIL8));
+    ptr->AllowRBFormat(LOCAL_GL_DEPTH_STENCIL,
+                       ptr->GetUsage(EffectiveFormat::DEPTH24_STENCIL8));
 
     ////////////////////////////////////////////////////////////////////////////
 
     AddBasicUnsizedFormats(ptr, gl);
 
     return Move(ret);
 }
 
 UniquePtr<FormatUsageAuthority>
 FormatUsageAuthority::CreateForWebGL2(gl::GLContext* gl)
 {
     UniquePtr<FormatUsageAuthority> ret(new FormatUsageAuthority);
-    FormatUsageAuthority* const ptr = ret.get();
-
-    const auto fnAddES3TexFormat = [ptr](GLenum sizedFormat, EffectiveFormat effFormat,
-                                         bool isRenderable, bool isFilterable)
-    {
-        SetUsage(ptr, effFormat, isRenderable, isFilterable);
-        auto usage = ptr->GetUsage(effFormat);
-        ptr->AddSizedTexFormat(sizedFormat, usage);
-
-        if (isRenderable) {
-            ptr->AddRBFormat(sizedFormat, usage);
-        }
-    };
+    const auto ptr = ret.get();
 
     ////////////////////////////////////////////////////////////////////////////
-
-    // For renderable, see GLES 3.0.4, p212 "Framebuffer Completeness"
-    // For filterable, see GLES 3.0.4, p161 "...a texture is complete unless..."
-
-    // GLES 3.0.4, p128-129 "Required Texture Formats"
-    // GLES 3.0.4, p130-132, table 3.13
-
-#define FOO(x) LOCAL_GL_ ## x, EffectiveFormat::x
-
-    //                                 render filter
-    //                                  able   able
-    fnAddES3TexFormat(FOO(R8         ), true , true );
-    fnAddES3TexFormat(FOO(R8_SNORM   ), false, true );
-    fnAddES3TexFormat(FOO(RG8        ), true , true );
-    fnAddES3TexFormat(FOO(RG8_SNORM  ), false, true );
-    fnAddES3TexFormat(FOO(RGB8       ), true , true );
-    fnAddES3TexFormat(FOO(RGB8_SNORM ), false, true );
-    fnAddES3TexFormat(FOO(RGB565     ), true , true );
-    fnAddES3TexFormat(FOO(RGBA4      ), true , true );
-    fnAddES3TexFormat(FOO(RGB5_A1    ), true , true );
-    fnAddES3TexFormat(FOO(RGBA8      ), true , true );
-    fnAddES3TexFormat(FOO(RGBA8_SNORM), false, true );
-    fnAddES3TexFormat(FOO(RGB10_A2   ), true , true );
-    fnAddES3TexFormat(FOO(RGB10_A2UI ), true , false);
-
-    fnAddES3TexFormat(FOO(SRGB8       ), false, true);
-    fnAddES3TexFormat(FOO(SRGB8_ALPHA8), true , true);
-
-    fnAddES3TexFormat(FOO(R16F   ), false, true);
-    fnAddES3TexFormat(FOO(RG16F  ), false, true);
-    fnAddES3TexFormat(FOO(RGB16F ), false, true);
-    fnAddES3TexFormat(FOO(RGBA16F), false, true);
-
-    fnAddES3TexFormat(FOO(R32F   ), false, false);
-    fnAddES3TexFormat(FOO(RG32F  ), false, false);
-    fnAddES3TexFormat(FOO(RGB32F ), false, false);
-    fnAddES3TexFormat(FOO(RGBA32F), false, false);
-
-    fnAddES3TexFormat(FOO(R11F_G11F_B10F), false, true);
-    fnAddES3TexFormat(FOO(RGB9_E5       ), false, true);
-
-    fnAddES3TexFormat(FOO(R8I  ), true, false);
-    fnAddES3TexFormat(FOO(R8UI ), true, false);
-    fnAddES3TexFormat(FOO(R16I ), true, false);
-    fnAddES3TexFormat(FOO(R16UI), true, false);
-    fnAddES3TexFormat(FOO(R32I ), true, false);
-    fnAddES3TexFormat(FOO(R32UI), true, false);
-
-    fnAddES3TexFormat(FOO(RG8I  ), true, false);
-    fnAddES3TexFormat(FOO(RG8UI ), true, false);
-    fnAddES3TexFormat(FOO(RG16I ), true, false);
-    fnAddES3TexFormat(FOO(RG16UI), true, false);
-    fnAddES3TexFormat(FOO(RG32I ), true, false);
-    fnAddES3TexFormat(FOO(RG32UI), true, false);
-
-    fnAddES3TexFormat(FOO(RGB8I  ), false, false);
-    fnAddES3TexFormat(FOO(RGB8UI ), false, false);
-    fnAddES3TexFormat(FOO(RGB16I ), false, false);
-    fnAddES3TexFormat(FOO(RGB16UI), false, false);
-    fnAddES3TexFormat(FOO(RGB32I ), false, false);
-    fnAddES3TexFormat(FOO(RGB32UI), false, false);
-
-    fnAddES3TexFormat(FOO(RGBA8I  ), true, false);
-    fnAddES3TexFormat(FOO(RGBA8UI ), true, false);
-    fnAddES3TexFormat(FOO(RGBA16I ), true, false);
-    fnAddES3TexFormat(FOO(RGBA16UI), true, false);
-    fnAddES3TexFormat(FOO(RGBA32I ), true, false);
-    fnAddES3TexFormat(FOO(RGBA32UI), true, false);
-
-    // GLES 3.0.4, p133, table 3.14
-    // GLES 3.0.4, p161 "...a texture is complete unless..."
-    fnAddES3TexFormat(FOO(DEPTH_COMPONENT16 ), true, false);
-    fnAddES3TexFormat(FOO(DEPTH_COMPONENT24 ), true, false);
-    fnAddES3TexFormat(FOO(DEPTH_COMPONENT32F), true, false);
-    fnAddES3TexFormat(FOO(DEPTH24_STENCIL8  ), true, false);
-    fnAddES3TexFormat(FOO(DEPTH32F_STENCIL8 ), true, false);
-
-    // GLES 3.0.4, p205-206, "Required Renderbuffer Formats"
-    fnAddES3TexFormat(FOO(STENCIL_INDEX8), true, false);
-
-    // GLES 3.0.4, p147, table 3.19
-    // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image 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.
-    fnAddES3TexFormat(FOO(COMPRESSED_RGB8_ETC2                     ), false, true);
-    fnAddES3TexFormat(FOO(COMPRESSED_SRGB8_ETC2                    ), false, true);
-    fnAddES3TexFormat(FOO(COMPRESSED_RGBA8_ETC2_EAC                ), false, true);
-    fnAddES3TexFormat(FOO(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC         ), false, true);
-    fnAddES3TexFormat(FOO(COMPRESSED_R11_EAC                       ), false, true);
-    fnAddES3TexFormat(FOO(COMPRESSED_RG11_EAC                      ), false, true);
-    fnAddES3TexFormat(FOO(COMPRESSED_SIGNED_R11_EAC                ), false, true);
-    fnAddES3TexFormat(FOO(COMPRESSED_SIGNED_RG11_EAC               ), false, true);
-    fnAddES3TexFormat(FOO(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ), false, true);
-    fnAddES3TexFormat(FOO(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2), false, true);
-
-#undef FOO
+    // 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};
-        usage->AddUnpack(pi, dui);
+        ptr->AddTexUnpack(usage, pi, dui);
     };
 
 #define FOO(x) EffectiveFormat::x, LOCAL_GL_ ## x
 
-    ////////////////////////////////////////////////////////////////////////////
-    // GLES 3.0.4 p111-113
     // 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              );
@@ -802,53 +677,232 @@ FormatUsageAuthority::CreateForWebGL2(gl
     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);
 
-    if (gfxPrefs::WebGL2CompatMode()) {
-        AddSimpleUnsized(ptr, LOCAL_GL_RGBA, LOCAL_GL_FLOAT, EffectiveFormat::RGBA32F);
-        AddSimpleUnsized(ptr, LOCAL_GL_RGB , LOCAL_GL_FLOAT, EffectiveFormat::RGB32F );
+#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)
+    {
+        SetUsage(ptr, effFormat, isRenderable, isFilterable);
+        auto usage = ptr->GetUsage(effFormat);
+        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);
 
-        AddSimpleUnsized(ptr, LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGBA16F);
-        AddSimpleUnsized(ptr, LOCAL_GL_RGB , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGB16F );
-    }
+    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, p205-206, "Required Renderbuffer Formats"
+    fnAllowES3TexFormat(FOO(STENCIL_INDEX8), true, false);
+
+    // GLES 3.0.4, p147, table 3.19
+    // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image 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);
+
 #undef FOO
 
+    ////////////////
+    // Legacy formats
+
     SetUsage(ptr, EffectiveFormat::Luminance8Alpha8, false, true);
     SetUsage(ptr, EffectiveFormat::Luminance8      , false, true);
     SetUsage(ptr, EffectiveFormat::Alpha8          , false, true);
 
     AddBasicUnsizedFormats(ptr, gl);
 
+    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::AddRBFormat(GLenum sizedFormat, const FormatUsageInfo* usage)
+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::AddSizedTexFormat(GLenum sizedFormat, const FormatUsageInfo* usage)
+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::AddUnsizedTexFormat(const PackingInfo& pi,
-                                          const FormatUsageInfo* usage)
+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);
 }
 
--- a/dom/canvas/WebGLFormats.h
+++ b/dom/canvas/WebGLFormats.h
@@ -259,54 +259,54 @@ struct FormatUsageInfo
     explicit FormatUsageInfo(const FormatInfo* _format)
         : format(_format)
         , isRenderable(false)
         , isFilterable(false)
         , idealUnpack(nullptr)
         , textureSwizzleRGBA(nullptr)
     { }
 
-    void AddUnpack(const PackingInfo& key, const DriverUnpackInfo& value);
     bool IsUnpackValid(const PackingInfo& key,
                        const DriverUnpackInfo** const out_value) const;
 };
 
 class FormatUsageAuthority
 {
     std::map<EffectiveFormat, FormatUsageInfo> mUsageMap;
 
     std::map<GLenum, const FormatUsageInfo*> mRBFormatMap;
     std::map<GLenum, const FormatUsageInfo*> mSizedTexFormatMap;
     std::map<PackingInfo, const FormatUsageInfo*> mUnsizedTexFormatMap;
 
+    std::set<GLenum> mValidTexInternalFormats;
+    std::set<GLenum> mValidTexUnpackFormats;
+    std::set<GLenum> mValidTexUnpackTypes;
+
 public:
     static UniquePtr<FormatUsageAuthority> CreateForWebGL1(gl::GLContext* gl);
     static UniquePtr<FormatUsageAuthority> CreateForWebGL2(gl::GLContext* gl);
 
 private:
     FormatUsageAuthority() { }
 
 public:
-    void AddRBFormat(GLenum sizedFormat, const FormatUsageInfo* usage);
-    void AddSizedTexFormat(GLenum sizedFormat, const FormatUsageInfo* usage);
-    void AddUnsizedTexFormat(const PackingInfo& pi, const FormatUsageInfo* usage);
+    FormatUsageInfo* EditUsage(EffectiveFormat format);
+    const FormatUsageInfo* GetUsage(EffectiveFormat format) const;
+
+    void AddTexUnpack(FormatUsageInfo* usage, const PackingInfo& pi,
+                      const DriverUnpackInfo& dui);
+
+    bool IsInternalFormatEnumValid(GLenum internalFormat) const;
+    bool AreUnpackEnumsValid(GLenum unpackFormat, GLenum unpackType) const;
+
+    void AllowRBFormat(GLenum sizedFormat, const FormatUsageInfo* usage);
+    void AllowSizedTexFormat(GLenum sizedFormat, const FormatUsageInfo* usage);
+    void AllowUnsizedTexFormat(const PackingInfo& pi, const FormatUsageInfo* usage);
 
     const FormatUsageInfo* GetRBUsage(GLenum sizedFormat) const;
     const FormatUsageInfo* GetSizedTexUsage(GLenum sizedFormat) const;
     const FormatUsageInfo* GetUnsizedTexUsage(const PackingInfo& pi) const;
-
-    FormatUsageInfo* EditUsage(EffectiveFormat format);
-
-    const FormatUsageInfo* GetUsage(EffectiveFormat format) const;
-
-    const FormatUsageInfo* GetUsage(const FormatInfo* format) const
-    {
-        if (!format)
-            return nullptr;
-
-        return GetUsage(format->effectiveFormat);
-    }
 };
 
 } // namespace webgl
 } // namespace mozilla
 
 #endif // WEBGL_FORMATS_H_
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -99,16 +99,23 @@ DoesJSTypeMatchUnpackType(GLenum unpackT
 
 static bool
 ValidateUnpackArrayType(WebGLContext* webgl, const char* funcName, GLenum unpackType,
                         js::Scalar::Type jsType)
 {
     if (DoesJSTypeMatchUnpackType(unpackType, jsType))
         return true;
 
+    const auto& fua = webgl->mFormatUsage;
+    const GLenum fakeUnpackFormat = LOCAL_GL_RGBA;
+    if (!fua->AreUnpackEnumsValid(fakeUnpackFormat, unpackType)) {
+        webgl->ErrorInvalidEnum("%s: Invalid unpack type: 0x%04x", funcName, unpackType);
+        return false;
+    }
+
     webgl->ErrorInvalidOperation("%s: `pixels` be compatible with unpack `type`.",
                                  funcName);
     return false;
 }
 
 static UniquePtr<webgl::TexUnpackBlob>
 UnpackBlobFromMaybeView(WebGLContext* webgl, const char* funcName, GLsizei width,
                         GLsizei height, GLsizei depth, GLenum unpackType,
@@ -989,77 +996,16 @@ WebGLTexture::TexStorage(const char* fun
     PopulateMipChain(0, levels-1);
 
     mImmutable = true;
 }
 
 ////////////////////////////////////////
 // Tex(Sub)Image
 
-static bool
-ValidateUnpackEnums(const webgl::PackingInfo& pi, WebGLContext* webgl,
-                    const char* funcName)
-{
-    switch (pi.format) {
-    case LOCAL_GL_RED:
-    case LOCAL_GL_RED_INTEGER:
-    case LOCAL_GL_DEPTH_COMPONENT:
-    case LOCAL_GL_LUMINANCE:
-    case LOCAL_GL_ALPHA:
-
-    case LOCAL_GL_RG:
-    case LOCAL_GL_RG_INTEGER:
-    case LOCAL_GL_LUMINANCE_ALPHA:
-    case LOCAL_GL_DEPTH_STENCIL:
-
-    case LOCAL_GL_RGB:
-    case LOCAL_GL_RGB_INTEGER:
-
-    case LOCAL_GL_RGBA:
-    case LOCAL_GL_RGBA_INTEGER:
-        break;
-
-    default:
-        webgl->ErrorInvalidEnum("%s: Invalid format enum: 0x%04x",
-                                funcName, pi.format);
-        return false;
-    }
-
-    switch (pi.type) {
-    case LOCAL_GL_UNSIGNED_BYTE:
-    case LOCAL_GL_UNSIGNED_SHORT:
-    case LOCAL_GL_UNSIGNED_INT:
-    case LOCAL_GL_BYTE:
-    case LOCAL_GL_SHORT:
-    case LOCAL_GL_INT:
-    case LOCAL_GL_FLOAT:
-    case LOCAL_GL_HALF_FLOAT:
-    case LOCAL_GL_HALF_FLOAT_OES:
-
-    case LOCAL_GL_UNSIGNED_INT_24_8:
-    case LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
-
-    case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
-    case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
-    case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV:
-
-    case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
-    case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
-    case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV:
-        break;
-
-    default:
-        webgl->ErrorInvalidEnum("%s: Invalid type enum: 0x%04x",
-                                funcName, pi.type);
-        return false;
-    }
-
-    return true;
-}
-
 void
 WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level,
                        GLenum internalFormat, GLint border, GLenum unpackFormat,
                        GLenum unpackType, webgl::TexUnpackBlob* blob)
 {
     ////////////////////////////////////
     // Get dest info
 
@@ -1068,29 +1014,45 @@ WebGLTexture::TexImage(const char* funcN
                                        blob->mHeight, blob->mDepth, border, &imageInfo))
     {
         return;
     }
     MOZ_ASSERT(imageInfo);
 
     const webgl::PackingInfo srcPacking = { unpackFormat, unpackType };
 
-    auto dstUsage = mContext->mFormatUsage->GetSizedTexUsage(internalFormat);
+    const auto& fua = mContext->mFormatUsage;
+    auto dstUsage = fua->GetSizedTexUsage(internalFormat);
     if (!dstUsage) {
         if (internalFormat != unpackFormat) {
             mContext->ErrorInvalidOperation("%s: Unsized internalFormat must match"
                                             " unpack format.",
                                             funcName);
             return;
         }
 
-        dstUsage = mContext->mFormatUsage->GetUnsizedTexUsage(srcPacking);
+        dstUsage = fua->GetUnsizedTexUsage(srcPacking);
     }
 
     if (!dstUsage) {
+        if (!mContext->IsWebGL2()) {
+            if (!fua->IsInternalFormatEnumValid(internalFormat)) {
+                mContext->ErrorInvalidValue("%s: Invalid internalformat: 0x%04x",
+                                            funcName, internalFormat);
+                return;
+            }
+
+            if (!fua->AreUnpackEnumsValid(unpackFormat, unpackType)) {
+                mContext->ErrorInvalidEnum("%s: Invalid unpack format/type:"
+                                           " 0x%04x/0x%04x",
+                                           funcName, unpackFormat, unpackType);
+                return;
+            }
+        }
+
         mContext->ErrorInvalidOperation("%s: Invalid internalformat/format/type:"
                                         " 0x%04x/0x%04x/0x%04x",
                                         funcName, internalFormat, unpackFormat,
                                         unpackType);
         return;
     }
 
     const webgl::DriverUnpackInfo* driverUnpackInfo;
@@ -1206,21 +1168,28 @@ WebGLTexture::TexSubImage(const char* fu
                                         funcName, dstFormat->name);
         return;
     }
 
     ////////////////////////////////////
     // Get source info
 
     const webgl::PackingInfo srcPacking = { unpackFormat, unpackType };
-    if (!ValidateUnpackEnums(srcPacking, mContext, funcName))
-        return;
-
     const webgl::DriverUnpackInfo* driverUnpackInfo;
     if (!dstUsage->IsUnpackValid(srcPacking, &driverUnpackInfo)) {
+        if (!mContext->IsWebGL2()) {
+            const auto& fua = mContext->mFormatUsage;
+            if (!fua->AreUnpackEnumsValid(unpackFormat, unpackType)) {
+                mContext->ErrorInvalidEnum("%s: Invalid unpack format/type:"
+                                           " 0x%04x/0x%04x",
+                                           funcName, unpackFormat, unpackType);
+                return;
+            }
+        }
+
         mContext->ErrorInvalidOperation("%s: Mismatched internalFormat and format/type:"
                                         " %s and 0x%04x/0x%04x",
                                         funcName, dstFormat->name, unpackFormat,
                                         unpackType);
         return;
     }
 
     const bool isFunc3D = Is3D(target);