Implement swizzling for L/A/LA. draft
authorJeff Gilbert <jdashg@gmail.com>
Thu, 17 Dec 2015 16:16:52 -0800
changeset 316082 9e2b96f2efddeb8b29cba56f9ae7d86bf40b583b
parent 316081 ff9277c9ed1e95fbc5bfbd4cc7d59291a7f76d59
child 316083 04cc2f3be0b7c720c273b0ada76a868a087b2051
push id8514
push userjgilbert@mozilla.com
push dateFri, 18 Dec 2015 00:24:33 +0000
milestone45.0a1
Implement swizzling for L/A/LA.
dom/canvas/TexUnpackBlob.cpp
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextDraw.cpp
dom/canvas/WebGLContextGL.cpp
dom/canvas/WebGLExtensionTextureFloat.cpp
dom/canvas/WebGLExtensionTextureHalfFloat.cpp
dom/canvas/WebGLFormats.cpp
dom/canvas/WebGLFormats.h
dom/canvas/WebGLTexture.cpp
dom/canvas/WebGLTexture.h
dom/canvas/WebGLTextureUpload.cpp
dom/canvas/WebGLTypes.h
gfx/gl/GLBlitHelper.cpp
gfx/gl/GLBlitHelper.h
gfx/gl/ScopedGLHelpers.cpp
gfx/gl/ScopedGLHelpers.h
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -653,17 +653,17 @@ TexUnpackSurface::ConvertSurface(WebGLCo
     checkedDstSize *= height;
     if (!checkedDstSize.isValid()) {
         *out_outOfMemory = true;
         return false;
     }
 
     const size_t dstSize = checkedDstSize.value();
 
-    UniqueBuffer dstBuffer(malloc(dstSize));
+    UniqueBuffer dstBuffer = malloc(dstSize);
     if (!dstBuffer) {
         *out_outOfMemory = true;
         return false;
     }
     void* const dstBegin = dstBuffer.get();
 
     gl::OriginPos srcOrigin, dstOrigin;
     OriginsForDOM(webgl, &srcOrigin, &dstOrigin);
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -119,18 +119,16 @@ WebGLContext::WebGLContext()
     mFakeVertexAttrib0BufferObjectVector[0] = 0;
     mFakeVertexAttrib0BufferObjectVector[1] = 0;
     mFakeVertexAttrib0BufferObjectVector[2] = 0;
     mFakeVertexAttrib0BufferObjectVector[3] = 1;
     mFakeVertexAttrib0BufferObjectSize = 0;
     mFakeVertexAttrib0BufferObject = 0;
     mFakeVertexAttrib0BufferStatus = WebGLVertexAttrib0Status::Default;
 
-    mCanSkipFakeBlack = false;
-
     mViewportX = 0;
     mViewportY = 0;
     mViewportWidth = 0;
     mViewportHeight = 0;
 
     mDitherEnabled = 1;
     mRasterizerDiscardEnabled = 0; // OpenGL ES 3.0 spec p244
     mScissorTestEnabled = 0;
@@ -235,20 +233,24 @@ WebGLContext::DestroyResourcesAndContext
         mPrograms.getLast()->DeleteOnce();
     while (!mQueries.isEmpty())
         mQueries.getLast()->DeleteOnce();
     while (!mSamplers.isEmpty())
         mSamplers.getLast()->DeleteOnce();
     while (!mTransformFeedbacks.isEmpty())
         mTransformFeedbacks.getLast()->DeleteOnce();
 
-    mFakeBlack_2D_Opaque      = nullptr;
-    mFakeBlack_2D_Alpha       = nullptr;
-    mFakeBlack_CubeMap_Opaque = nullptr;
-    mFakeBlack_CubeMap_Alpha  = nullptr;
+    mFakeBlack_2D_0000       = nullptr;
+    mFakeBlack_2D_0001       = nullptr;
+    mFakeBlack_CubeMap_0000  = nullptr;
+    mFakeBlack_CubeMap_0001  = nullptr;
+    mFakeBlack_3D_0000       = nullptr;
+    mFakeBlack_3D_0001       = nullptr;
+    mFakeBlack_2D_Array_0000 = nullptr;
+    mFakeBlack_2D_Array_0001 = nullptr;
 
     if (mFakeVertexAttrib0BufferObject)
         gl->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject);
 
     // disable all extensions except "WEBGL_lose_context". see bug #927969
     // spec: http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
     for (size_t i = 0; i < size_t(WebGLExtensionID::Max); ++i) {
         WebGLExtensionID extension = WebGLExtensionID(i);
@@ -1975,17 +1977,17 @@ ZeroTextureData(WebGLContext* webgl, con
         checkedByteCount *= heightBlocks;
         checkedByteCount *= depth;
 
         if (!checkedByteCount.isValid())
             return false;
 
         const size_t byteCount = checkedByteCount.value();
 
-        UniqueBuffer zeros(calloc(1, byteCount));
+        UniqueBuffer zeros = calloc(1, byteCount);
         if (!zeros)
             return false;
 
         GLenum error = DoCompressedTexSubImage(gl, target.get(), level, xOffset, yOffset,
                                                zOffset, width, height, depth, sizedFormat,
                                                byteCount, zeros.get());
         if (error)
             return false;
@@ -2007,17 +2009,17 @@ ZeroTextureData(WebGLContext* webgl, con
     checkedByteCount *= height;
     checkedByteCount *= depth;
 
     if (!checkedByteCount.isValid())
         return false;
 
     const size_t byteCount = checkedByteCount.value();
 
-    UniqueBuffer zeros(calloc(1, byteCount));
+    UniqueBuffer zeros = calloc(1, byteCount);
     if (!zeros)
         return false;
 
     GLenum error;
     if (respecifyTexture) {
         MOZ_RELEASE_ASSERT(!xOffset && !yOffset && !zOffset);
 
         const auto internalFormat = driverUnpackInfo->internalFormat;
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -82,17 +82,18 @@ class nsIDocShell;
 #define LOCAL_GL_BROWSER_DEFAULT_WEBGL                       0x9244
 #define LOCAL_GL_CONTEXT_LOST_WEBGL                          0x9242
 #define LOCAL_GL_MAX_CLIENT_WAIT_TIMEOUT_WEBGL               0x9247
 #define LOCAL_GL_UNPACK_COLORSPACE_CONVERSION_WEBGL          0x9243
 #define LOCAL_GL_UNPACK_FLIP_Y_WEBGL                         0x9240
 #define LOCAL_GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL              0x9241
 
 namespace mozilla {
-
+class ScopedCopyTexImageSource;
+class ScopedResolveTexturesForDraw;
 class ScopedUnpackReset;
 class WebGLActiveInfo;
 class WebGLContextLossHandler;
 class WebGLBuffer;
 class WebGLExtensionBase;
 class WebGLFramebuffer;
 class WebGLProgram;
 class WebGLQuery;
@@ -1038,17 +1039,17 @@ private:
     uint32_t mMaxFetchedInstances;
 
     bool DrawArrays_check(GLint first, GLsizei count, GLsizei primcount,
                           const char* info);
     bool DrawElements_check(GLsizei count, GLenum type, WebGLintptr byteOffset,
                             GLsizei primcount, const char* info,
                             GLuint* out_upperBound);
     bool DrawInstanced_check(const char* info);
-    void Draw_cleanup();
+    void Draw_cleanup(const char* funcName);
 
     void VertexAttrib1fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
     void VertexAttrib2fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
     void VertexAttrib3fv_base(GLuint index, uint32_t arrayLength,
                               const GLfloat* ptr);
     void VertexAttrib4fv_base(GLuint index, uint32_t arrayLength,
@@ -1418,40 +1419,38 @@ protected:
     CheckedUint32 GetPackSize(uint32_t width, uint32_t height, uint8_t bytesPerPixel,
                               CheckedUint32* const out_startOffset,
                               CheckedUint32* const out_rowStride);
 
     GLenum mPixelStore_ColorspaceConversion;
     bool mPixelStore_FlipY;
     bool mPixelStore_PremultiplyAlpha;
 
-public:
-    // Fake-black
+    ////////////////////////////////////
     class FakeBlackTexture {
+    public:
         gl::GLContext* const mGL;
-        GLuint mGLName;
+        const GLuint mGLName;
 
-    public:
-        FakeBlackTexture(gl::GLContext* gl, TexTarget target, GLenum format);
+        FakeBlackTexture(gl::GLContext* gl, TexTarget target, FakeBlackType type);
         ~FakeBlackTexture();
-        GLuint GLName() const { return mGLName; }
     };
 
-    void InvalidateFakeBlackCache() { mCanSkipFakeBlack = false; }
-
-protected:
-    bool mCanSkipFakeBlack;
+    UniquePtr<FakeBlackTexture> mFakeBlack_2D_0000;
+    UniquePtr<FakeBlackTexture> mFakeBlack_2D_0001;
+    UniquePtr<FakeBlackTexture> mFakeBlack_CubeMap_0000;
+    UniquePtr<FakeBlackTexture> mFakeBlack_CubeMap_0001;
+    UniquePtr<FakeBlackTexture> mFakeBlack_3D_0000;
+    UniquePtr<FakeBlackTexture> mFakeBlack_3D_0001;
+    UniquePtr<FakeBlackTexture> mFakeBlack_2D_Array_0000;
+    UniquePtr<FakeBlackTexture> mFakeBlack_2D_Array_0001;
 
-    UniquePtr<FakeBlackTexture> mFakeBlack_2D_Opaque;
-    UniquePtr<FakeBlackTexture> mFakeBlack_2D_Alpha;
-    UniquePtr<FakeBlackTexture> mFakeBlack_CubeMap_Opaque;
-    UniquePtr<FakeBlackTexture> mFakeBlack_CubeMap_Alpha;
+    void BindFakeBlack(uint32_t texUnit, TexTarget target, FakeBlackType fakeBlack);
 
-    bool BindFakeBlackTextures(const char* funcName);
-    void UnbindFakeBlackTextures();
+    ////////////////////////////////////
 
     // Generic Vertex Attributes
     UniquePtr<GLenum[]> mVertexAttribType;
     GLfloat mVertexAttrib0Vector[4];
     GLfloat mFakeVertexAttrib0BufferObjectVector[4];
     size_t mFakeVertexAttrib0BufferObjectSize;
     GLuint mFakeVertexAttrib0BufferObject;
     WebGLVertexAttrib0Status mFakeVertexAttrib0BufferStatus;
@@ -1562,16 +1561,18 @@ public:
 
 public:
     UniquePtr<webgl::FormatUsageAuthority> mFormatUsage;
 
     virtual UniquePtr<webgl::FormatUsageAuthority>
     CreateFormatUsage(gl::GLContext* gl) const = 0;
 
     // Friend list
+    friend class ScopedCopyTexImageSource;
+    friend class ScopedResolveTexturesForDraw;
     friend class ScopedUnpackReset;
     friend class webgl::TexUnpackBytes;
     friend class webgl::TexUnpackSurface;
     friend class WebGLTexture;
     friend class WebGLFramebuffer;
     friend class WebGLRenderbuffer;
     friend class WebGLProgram;
     friend class WebGLQuery;
@@ -1686,17 +1687,17 @@ class UniqueBuffer
     // Like UniquePtr<>, but for void* and malloc/calloc/free.
     void* mBuffer;
 
 public:
     UniqueBuffer()
         : mBuffer(nullptr)
     { }
 
-    explicit UniqueBuffer(void* buffer)
+    UniqueBuffer(void* buffer)
         : mBuffer(buffer)
     { }
 
     ~UniqueBuffer() {
         free(mBuffer);
     }
 
     UniqueBuffer(UniqueBuffer&& other) {
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -18,16 +18,138 @@
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 namespace mozilla {
 
 // For a Tegra workaround.
 static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100;
 
+////////////////////////////////////////
+
+class ScopedResolveTexturesForDraw
+{
+    struct TexRebindRequest
+    {
+        const uint32_t texUnit;
+        WebGLTexture* const tex;
+    };
+
+    WebGLContext* const mWebGL;
+    std::vector<TexRebindRequest> mRebindRequests;
+
+public:
+    ScopedResolveTexturesForDraw(WebGLContext* webgl, const char* funcName,
+                                 bool* const out_error);
+    ~ScopedResolveTexturesForDraw();
+};
+
+ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw(WebGLContext* webgl,
+                                                           const char* funcName,
+                                                           bool* const out_error)
+    : mWebGL(webgl)
+{
+    //typedef nsTArray<WebGLRefPtr<WebGLTexture>> TexturesT;
+    typedef decltype(WebGLContext::mBound2DTextures) TexturesT;
+
+    const auto fnResolveAll = [this, funcName, out_error](const TexturesT& textures)
+    {
+        const auto len = textures.Length();
+        for (uint32_t texUnit = 0; texUnit < len; ++texUnit) {
+            WebGLTexture* tex = textures[texUnit];
+            if (!tex)
+                continue;
+
+            FakeBlackType fakeBlack;
+            *out_error |= !tex->ResolveForDraw(funcName, texUnit, &fakeBlack);
+
+            if (fakeBlack == FakeBlackType::None)
+                continue;
+
+            mWebGL->BindFakeBlack(texUnit, tex->Target(), fakeBlack);
+            mRebindRequests.push_back({texUnit, tex});
+        }
+    };
+
+    *out_error = false;
+
+    fnResolveAll(mWebGL->mBound2DTextures);
+    fnResolveAll(mWebGL->mBoundCubeMapTextures);
+    fnResolveAll(mWebGL->mBound3DTextures);
+    fnResolveAll(mWebGL->mBound2DArrayTextures);
+
+    if (*out_error) {
+        mWebGL->ErrorOutOfMemory("%s: Failed to resolve textures for draw.", funcName);
+    }
+}
+
+ScopedResolveTexturesForDraw::~ScopedResolveTexturesForDraw()
+{
+    if (!mRebindRequests.size())
+        return;
+
+    gl::GLContext* gl = mWebGL->gl;
+
+    for (const auto& itr : mRebindRequests) {
+        gl->fActiveTexture(LOCAL_GL_TEXTURE0 + itr.texUnit);
+        gl->fBindTexture(itr.tex->Target().get(), itr.tex->mGLName);
+    }
+
+    gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mWebGL->mActiveTexture);
+}
+
+void
+WebGLContext::BindFakeBlack(uint32_t texUnit, TexTarget target, FakeBlackType fakeBlack)
+{
+    MOZ_ASSERT(fakeBlack == FakeBlackType::RGBA0000 ||
+               fakeBlack == FakeBlackType::RGBA0001);
+
+    const auto fnGetSlot = [this, target, fakeBlack]() -> UniquePtr<FakeBlackTexture>*
+    {
+        switch (fakeBlack) {
+        case FakeBlackType::RGBA0000:
+            switch (target.get()) {
+            case LOCAL_GL_TEXTURE_2D      : return &mFakeBlack_2D_0000;
+            case LOCAL_GL_TEXTURE_CUBE_MAP: return &mFakeBlack_CubeMap_0000;
+            case LOCAL_GL_TEXTURE_3D      : return &mFakeBlack_3D_0000;
+            case LOCAL_GL_TEXTURE_2D_ARRAY: return &mFakeBlack_2D_Array_0000;
+            default: return nullptr;
+            }
+
+        case FakeBlackType::RGBA0001:
+            switch (target.get()) {
+            case LOCAL_GL_TEXTURE_2D      : return &mFakeBlack_2D_0001;
+            case LOCAL_GL_TEXTURE_CUBE_MAP: return &mFakeBlack_CubeMap_0001;
+            case LOCAL_GL_TEXTURE_3D      : return &mFakeBlack_3D_0001;
+            case LOCAL_GL_TEXTURE_2D_ARRAY: return &mFakeBlack_2D_Array_0001;
+            default: return nullptr;
+            }
+
+        default:
+            return nullptr;
+        }
+    };
+
+    UniquePtr<FakeBlackTexture>* slot = fnGetSlot();
+    if (!slot) {
+        MOZ_CRASH("fnGetSlot failed.");
+    }
+    UniquePtr<FakeBlackTexture>& fakeBlackTex = *slot;
+
+    if (!fakeBlackTex) {
+        fakeBlackTex.reset(new FakeBlackTexture(gl, target, fakeBlack));
+    }
+
+    gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit);
+    gl->fBindTexture(target.get(), fakeBlackTex->mGLName);
+    gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
+}
+
+////////////////////////////////////////
+
 bool
 WebGLContext::DrawInstanced_check(const char* info)
 {
     if ((IsWebGL2() ||
          IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays)) &&
         !mBufferFetchingHasPerVertex)
     {
         /* http://www.khronos.org/registry/gles/extensions/ANGLE/ANGLE_instanced_arrays.txt
@@ -110,64 +232,73 @@ WebGLContext::DrawArrays_check(GLint fir
     if (!DoFakeVertexAttrib0(checked_firstPlusCount.value())) {
         return false;
     }
 
     if (!DrawInstanced_check(info)) {
         return false;
     }
 
-    if (!BindFakeBlackTextures(info))
-        return false;
-
     return true;
 }
 
 void
 WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei count)
 {
+    const char funcName[] = "drawArrays";
     if (IsContextLost())
         return;
 
-    if (!ValidateDrawModeEnum(mode, "drawArrays: mode"))
+    if (!ValidateDrawModeEnum(mode, funcName))
         return;
 
-    if (!DrawArrays_check(first, count, 1, "drawArrays"))
+    bool error;
+    ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
+    if (error)
+        return;
+
+    if (!DrawArrays_check(first, count, 1, funcName))
         return;
 
     RunContextLossTimer();
 
     {
         ScopedMaskWorkaround autoMask(*this);
         gl->fDrawArrays(mode, first, count);
     }
 
-    Draw_cleanup();
+    Draw_cleanup(funcName);
 }
 
 void
 WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
 {
+    const char funcName[] = "drawArraysInstanced";
     if (IsContextLost())
         return;
 
-    if (!ValidateDrawModeEnum(mode, "drawArraysInstanced: mode"))
+    if (!ValidateDrawModeEnum(mode, funcName))
         return;
 
-    if (!DrawArrays_check(first, count, primcount, "drawArraysInstanced"))
+    bool error;
+    ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
+    if (error)
+        return;
+
+    if (!DrawArrays_check(first, count, primcount, funcName))
         return;
 
     RunContextLossTimer();
 
     {
         ScopedMaskWorkaround autoMask(*this);
         gl->fDrawArraysInstanced(mode, first, count, primcount);
     }
 
-    Draw_cleanup();
+    Draw_cleanup(funcName);
 }
 
 bool
 WebGLContext::DrawElements_check(GLsizei count, GLenum type,
                                  WebGLintptr byteOffset, GLsizei primcount,
                                  const char* info, GLuint* out_upperBound)
 {
     if (count < 0 || byteOffset < 0) {
@@ -292,89 +423,91 @@ WebGLContext::DrawElements_check(GLsizei
     if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) {
         return false;
     }
 
     if (!DrawInstanced_check(info)) {
         return false;
     }
 
-    if (!BindFakeBlackTextures(info))
-        return false;
-
     return true;
 }
 
 void
 WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type,
                            WebGLintptr byteOffset)
 {
+    const char funcName[] = "drawElements";
     if (IsContextLost())
         return;
 
-    if (!ValidateDrawModeEnum(mode, "drawElements: mode"))
+    if (!ValidateDrawModeEnum(mode, funcName))
+        return;
+
+    bool error;
+    ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
+    if (error)
         return;
 
     GLuint upperBound = 0;
-    if (!DrawElements_check(count, type, byteOffset, 1, "drawElements",
-                            &upperBound))
-    {
+    if (!DrawElements_check(count, type, byteOffset, 1, funcName, &upperBound))
         return;
-    }
 
     RunContextLossTimer();
 
     {
         ScopedMaskWorkaround autoMask(*this);
 
         if (gl->IsSupported(gl::GLFeature::draw_range_elements)) {
             gl->fDrawRangeElements(mode, 0, upperBound, count, type,
                                    reinterpret_cast<GLvoid*>(byteOffset));
         } else {
             gl->fDrawElements(mode, count, type,
                               reinterpret_cast<GLvoid*>(byteOffset));
         }
     }
 
-    Draw_cleanup();
+    Draw_cleanup(funcName);
 }
 
 void
 WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
                                     WebGLintptr byteOffset, GLsizei primcount)
 {
+    const char funcName[] = "drawElementsInstanced";
     if (IsContextLost())
         return;
 
-    if (!ValidateDrawModeEnum(mode, "drawElementsInstanced: mode"))
+    if (!ValidateDrawModeEnum(mode, funcName))
+        return;
+
+    bool error;
+    ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
+    if (error)
         return;
 
     GLuint upperBound = 0;
-    if (!DrawElements_check(count, type, byteOffset, primcount,
-                            "drawElementsInstanced", &upperBound))
-    {
+    if (!DrawElements_check(count, type, byteOffset, primcount, funcName, &upperBound))
         return;
-    }
 
     RunContextLossTimer();
 
     {
         ScopedMaskWorkaround autoMask(*this);
         gl->fDrawElementsInstanced(mode, count, type,
                                    reinterpret_cast<GLvoid*>(byteOffset),
                                    primcount);
     }
 
-    Draw_cleanup();
+    Draw_cleanup(funcName);
 }
 
-void WebGLContext::Draw_cleanup()
+void WebGLContext::Draw_cleanup(const char* funcName)
 {
     UndoFakeVertexAttrib0();
-    UnbindFakeBlackTextures();
 
     if (!mBoundDrawFramebuffer) {
         Invalidate();
         mShouldPresent = true;
         MOZ_ASSERT(!mBackbufferNeedsClear);
     }
 
     if (gl->WorkAroundDriverBugs()) {
@@ -402,18 +535,19 @@ void WebGLContext::Draw_cleanup()
         destWidth = mWidth;
         destHeight = mHeight;
     }
 
     if (mViewportWidth > int32_t(destWidth) ||
         mViewportHeight > int32_t(destHeight))
     {
         if (!mAlreadyWarnedAboutViewportLargerThanDest) {
-            GenerateWarning("Drawing to a destination rect smaller than the viewport rect. "
-                            "(This warning will only be given once)");
+            GenerateWarning("%s: Drawing to a destination rect smaller than the viewport"
+                            " rect. (This warning will only be given once)",
+                            funcName);
             mAlreadyWarnedAboutViewportLargerThanDest = true;
         }
     }
 }
 
 /*
  * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount)
  * that will be legal to be read from bound VBOs.
@@ -654,184 +788,64 @@ WebGLContext::UndoFakeVertexAttrib0()
         }
     } else {
         gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
     }
 
     gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0);
 }
 
-static bool
-BindFakeBlackHelper(const char* funcName, gl::GLContext* gl, TexTarget target,
-                    const nsTArray<WebGLRefPtr<WebGLTexture>>& texArray,
-                    WebGLContext::FakeBlackTexture* opaqueTex,
-                    WebGLContext::FakeBlackTexture* alphaTex, bool* const out_wasNeeded)
+static GLuint
+CreateGLTexture(gl::GLContext* gl)
 {
-    MOZ_ASSERT(opaqueTex && alphaTex);
-
-    *out_wasNeeded = false;
-
-    int32_t len = texArray.Length();
-    for (int32_t i = 0; i < len; i++) {
-        WebGLTexture* tex = texArray[i];
-        if (!tex)
-            continue;
-
-        WebGLTextureFakeBlackStatus status;
-        if (!tex->ResolveFakeBlackStatus(funcName, &status))
-            return false; // World is currently exploding...
-
-        MOZ_ASSERT(status != WebGLTextureFakeBlackStatus::Unknown);
-
-        if (MOZ_LIKELY(status == WebGLTextureFakeBlackStatus::NotNeeded))
-            continue;
-
-        // Ok, needs fake-black.
-        *out_wasNeeded = true;
-
-        // Default to (0, 0, 0, 1) for incomplete textures, as well as uninit'd alpha-less
-        // formats.
-        WebGLContext::FakeBlackTexture* fakeTex = opaqueTex;
-
-        if (status == WebGLTextureFakeBlackStatus::UninitializedImageData) {
-            const auto& imageInfo = tex->BaseImageInfo();
-            if (imageInfo.mFormat->format->hasAlpha) {
-                fakeTex = alphaTex;
-            }
-        }
-
-        gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
-        gl->fBindTexture(target.get(), fakeTex->GLName());
-    }
-
-    return true;
-}
-
-static bool
-UnbindFakeBlackHelper(gl::GLContext* gl, TexTarget target,
-                      const nsTArray<WebGLRefPtr<WebGLTexture>>& texArray)
-{
-    bool changedActiveTex = false;
-
-    const int32_t len = texArray.Length();
-    for (int32_t i = 0; i < len; i++) {
-        WebGLTexture* tex = texArray[i];
-        if (!tex)
-            continue;
-
-        auto status = tex->FakeBlackStatus();
-        MOZ_ASSERT(status != WebGLTextureFakeBlackStatus::Unknown);
-        if (MOZ_LIKELY(status == WebGLTextureFakeBlackStatus::NotNeeded))
-            continue;
-
-        gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
-        changedActiveTex = true;
-
-        gl->fBindTexture(target.get(), tex->mGLName);
-    }
-
-    return changedActiveTex;
+    MOZ_ASSERT(gl->IsCurrent());
+    GLuint ret = 0;
+    gl->fGenTextures(1, &ret);
+    return ret;
 }
 
-bool
-WebGLContext::BindFakeBlackTextures(const char* funcName)
+WebGLContext::FakeBlackTexture::FakeBlackTexture(gl::GLContext* gl, TexTarget target,
+                                                 FakeBlackType type)
+    : mGL(gl)
+    , mGLName(CreateGLTexture(gl))
 {
-    if (mCanSkipFakeBlack)
-        return true;
+    GLenum texFormat;
+    switch (type) {
+    case FakeBlackType::RGBA0000:
+        texFormat = LOCAL_GL_RGBA;
+        break;
 
-    if (!mFakeBlack_2D_Opaque) {
-        typedef FakeBlackTexture T;
+    case FakeBlackType::RGBA0001:
+        texFormat = LOCAL_GL_RGB;
+        break;
 
-        mFakeBlack_2D_Opaque = MakeUnique<T>(gl, LOCAL_GL_TEXTURE_2D, LOCAL_GL_RGB);
-        mFakeBlack_2D_Alpha = MakeUnique<T>(gl, LOCAL_GL_TEXTURE_2D, LOCAL_GL_RGBA);
-
-        mFakeBlack_CubeMap_Opaque = MakeUnique<T>(gl, LOCAL_GL_TEXTURE_CUBE_MAP,
-                                                  LOCAL_GL_RGB);
-        mFakeBlack_CubeMap_Alpha = MakeUnique<T>(gl, LOCAL_GL_TEXTURE_CUBE_MAP,
-                                                 LOCAL_GL_RGBA);
+    default:
+        MOZ_CRASH("bad type");
     }
 
-    bool wasEverNeeded = false;
-    bool wasNeeded;
-
-    if (!BindFakeBlackHelper(funcName, gl, LOCAL_GL_TEXTURE_2D, mBound2DTextures,
-                             mFakeBlack_2D_Opaque.get(), mFakeBlack_2D_Alpha.get(),
-                             &wasNeeded))
-    {
-        goto FAIL;
-    }
-    wasEverNeeded |= wasNeeded;
+    gl::ScopedBindTexture scopedBind(mGL, mGLName, target.get());
 
-    if (!BindFakeBlackHelper(funcName, gl, LOCAL_GL_TEXTURE_CUBE_MAP,
-                             mBoundCubeMapTextures, mFakeBlack_CubeMap_Opaque.get(),
-                             mFakeBlack_CubeMap_Alpha.get(), &wasNeeded))
-    {
-        goto FAIL;
-    }
-    wasEverNeeded |= wasNeeded;
-
-    mCanSkipFakeBlack = !wasEverNeeded;
-    return true;
-
-FAIL:
-    ErrorOutOfMemory("%s: Failed to bind fake-black textures.");
-    return false;
-}
-
-void
-WebGLContext::UnbindFakeBlackTextures()
-{
-    if (mCanSkipFakeBlack)
-        return;
-
-    bool changedActiveTex = false;
+    mGL->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
+    mGL->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
 
-    changedActiveTex |= UnbindFakeBlackHelper(gl, LOCAL_GL_TEXTURE_2D, mBound2DTextures);
-    changedActiveTex |= UnbindFakeBlackHelper(gl, LOCAL_GL_TEXTURE_CUBE_MAP,
-                                              mBoundCubeMapTextures);
-
-    if (changedActiveTex) {
-        gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
+    // We allocate our zeros on the heap, and we overallocate (16 bytes instead of 4) to
+    // minimize the risk of running into a driver bug in texImage2D, as it is a bit
+    // unusual maybe to create 1x1 textures, and the stack may not have the alignment that
+    // TexImage2D expects.
+    UniqueBuffer zeros = moz_xcalloc(1, 16);
+    if (target == LOCAL_GL_TEXTURE_CUBE_MAP) {
+        for (int i = 0; i < 6; ++i) {
+            DoTexImage(mGL, LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, texFormat, 1, 1,
+                       1, texFormat, LOCAL_GL_UNSIGNED_BYTE, zeros.get());
+        }
+    } else {
+        DoTexImage(mGL, target.get(), 0, texFormat, 1, 1, 1, texFormat,
+                   LOCAL_GL_UNSIGNED_BYTE, zeros.get());
     }
 }
 
-WebGLContext::FakeBlackTexture::FakeBlackTexture(gl::GLContext* gl, TexTarget target, GLenum format)
-    : mGL(gl)
-    , mGLName(0)
-{
-  MOZ_ASSERT(format == LOCAL_GL_RGB || format == LOCAL_GL_RGBA);
-
-  mGL->MakeCurrent();
-  GLuint formerBinding = 0;
-  gl->GetUIntegerv(target == LOCAL_GL_TEXTURE_2D
-                   ? LOCAL_GL_TEXTURE_BINDING_2D
-                   : LOCAL_GL_TEXTURE_BINDING_CUBE_MAP,
-                   &formerBinding);
-  gl->fGenTextures(1, &mGLName);
-  gl->fBindTexture(target.get(), mGLName);
-
-  // we allocate our zeros on the heap, and we overallocate (16 bytes instead of 4)
-  // to minimize the risk of running into a driver bug in texImage2D, as it is
-  // a bit unusual maybe to create 1x1 textures, and the stack may not have the alignment
-  // that texImage2D expects.
-  UniquePtr<uint8_t> zeros((uint8_t*)moz_xcalloc(1, 16));
-  if (target == LOCAL_GL_TEXTURE_2D) {
-      gl->fTexImage2D(target.get(), 0, format, 1, 1,
-                      0, format, LOCAL_GL_UNSIGNED_BYTE, zeros.get());
-  } else {
-      for (GLuint i = 0; i < 6; ++i) {
-          gl->fTexImage2D(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, 1, 1,
-                          0, format, LOCAL_GL_UNSIGNED_BYTE, zeros.get());
-      }
-  }
-
-  gl->fBindTexture(target.get(), formerBinding);
-}
-
 WebGLContext::FakeBlackTexture::~FakeBlackTexture()
 {
-  if (mGL) {
-      mGL->MakeCurrent();
-      mGL->fDeleteTextures(1, &mGLName);
-  }
+    mGL->MakeCurrent();
+    mGL->fDeleteTextures(1, &mGLName);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1335,17 +1335,17 @@ WebGLContext::DoReadPixelsAndConvert(GLi
         CheckedUint32 destStride;
         const CheckedUint32 destSize = GetPackSize(width, height, destBytesPerPixel,
                                                    &destOffset, &destStride);
         if (!readSize.isValid() || !destSize.isValid()) {
             ErrorOutOfMemory("readPixels: Overflow calculating sizes for conversion.");
             return false;
         }
 
-        UniqueBuffer readBuffer(malloc(readSize.value()));
+        UniqueBuffer readBuffer = malloc(readSize.value());
         if (!readBuffer) {
             ErrorOutOfMemory("readPixels: Failed to alloc temp buffer for conversion.");
             return false;
         }
 
         gl::GLContext::LocalErrorScope errorScope(*gl);
 
         gl->fReadPixels(x, y, width, height, readFormat, readType, readBuffer.get());
--- a/dom/canvas/WebGLExtensionTextureFloat.cpp
+++ b/dom/canvas/WebGLExtensionTextureFloat.cpp
@@ -10,49 +10,73 @@
 #include "WebGLFormats.h"
 
 namespace mozilla {
 
 WebGLExtensionTextureFloat::WebGLExtensionTextureFloat(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
     auto& fua = webgl->mFormatUsage;
+    gl::GLContext* gl = webgl->GL();
 
     webgl::PackingInfo pi;
     webgl::DriverUnpackInfo dui;
+    const GLint* swizzle = nullptr;
 
-    const auto fnAdd = [&fua, &pi, &dui](webgl::EffectiveFormat effFormat) {
+    const auto fnAdd = [&fua, &pi, &dui, &swizzle](webgl::EffectiveFormat effFormat) {
+        MOZ_ASSERT(!pi.type && !dui.unpackType);
+        pi.type = LOCAL_GL_FLOAT;
+        dui.unpackType = LOCAL_GL_FLOAT;
+
         auto usage = fua->EditUsage(effFormat);
         fua->AddUnsizedTexFormat(pi, usage);
         usage->AddUnpack(pi, dui);
+
+        usage->textureSwizzleRGBA = swizzle;
     };
 
-    const bool isCore = webgl->GL()->IsCoreProfile();
+    const bool isCore = gl->IsCoreProfile();
 
-    pi = {LOCAL_GL_RGBA, LOCAL_GL_FLOAT};
-    dui = {LOCAL_GL_RGBA, LOCAL_GL_RGBA, LOCAL_GL_FLOAT};
+    pi = {LOCAL_GL_RGBA, 0};
+    dui = {pi.format, pi.format, pi.type};
     fnAdd(webgl::EffectiveFormat::RGBA32F);
 
-    pi = {LOCAL_GL_RGB, LOCAL_GL_FLOAT};
-    dui = {LOCAL_GL_RGB, LOCAL_GL_RGB, LOCAL_GL_FLOAT};
+    pi = {LOCAL_GL_RGB, 0};
+    dui = {pi.format, pi.format, pi.type};
     fnAdd(webgl::EffectiveFormat::RGB32F);
 
-    pi = {LOCAL_GL_LUMINANCE, LOCAL_GL_FLOAT};
-    if (isCore) dui = {LOCAL_GL_R32F, LOCAL_GL_RED, LOCAL_GL_FLOAT};
-    else        dui = {LOCAL_GL_LUMINANCE, LOCAL_GL_LUMINANCE, LOCAL_GL_FLOAT};
+    pi = {LOCAL_GL_LUMINANCE, 0};
+    if (isCore) {
+        dui = {LOCAL_GL_R32F, LOCAL_GL_RED, 0};
+        swizzle = webgl::FormatUsageInfo::kLuminanceSwizzleRGBA;
+    } else {
+        dui = {pi.format, pi.format, pi.type};
+        swizzle = nullptr;
+    }
     fnAdd(webgl::EffectiveFormat::Luminance32F);
 
-    pi = {LOCAL_GL_ALPHA, LOCAL_GL_FLOAT};
-    if (isCore) dui = {LOCAL_GL_R32F, LOCAL_GL_RED, LOCAL_GL_FLOAT};
-    else        dui = {LOCAL_GL_ALPHA, LOCAL_GL_ALPHA, LOCAL_GL_FLOAT};
+    pi = {LOCAL_GL_ALPHA, 0};
+    if (isCore) {
+        dui = {LOCAL_GL_R32F, LOCAL_GL_RED, 0};
+        swizzle = webgl::FormatUsageInfo::kAlphaSwizzleRGBA;
+    } else {
+        dui = {pi.format, pi.format, pi.type};
+        swizzle = nullptr;
+    }
     fnAdd(webgl::EffectiveFormat::Alpha32F);
 
-    pi = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_FLOAT};
-    if (isCore) dui = {LOCAL_GL_RG32F, LOCAL_GL_RG, LOCAL_GL_FLOAT};
-    else        dui = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_FLOAT};
+    pi = {LOCAL_GL_LUMINANCE_ALPHA, 0};
+    dui = {pi.format, pi.format, pi.type};
+    if (isCore) {
+        dui = {LOCAL_GL_RG32F, LOCAL_GL_RG, 0};
+        swizzle = webgl::FormatUsageInfo::kLumAlphaSwizzleRGBA;
+    } else {
+        dui = {pi.format, pi.format, pi.type};
+        swizzle = nullptr;
+    }
     fnAdd(webgl::EffectiveFormat::Luminance32FAlpha32F);
 }
 
 WebGLExtensionTextureFloat::~WebGLExtensionTextureFloat()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionTextureFloat, OES_texture_float)
--- a/dom/canvas/WebGLExtensionTextureHalfFloat.cpp
+++ b/dom/canvas/WebGLExtensionTextureHalfFloat.cpp
@@ -10,55 +10,81 @@
 #include "WebGLFormats.h"
 
 namespace mozilla {
 
 WebGLExtensionTextureHalfFloat::WebGLExtensionTextureHalfFloat(WebGLContext* webgl)
     : WebGLExtensionBase(webgl)
 {
     auto& fua = webgl->mFormatUsage;
+    gl::GLContext* gl = webgl->GL();
 
     webgl::PackingInfo pi;
     webgl::DriverUnpackInfo dui;
-
-    const auto fnAdd = [&fua, &pi, &dui](webgl::EffectiveFormat effFormat) {
-        auto usage = fua->EditUsage(effFormat);
-        fua->AddUnsizedTexFormat(pi, usage);
-        usage->AddUnpack(pi, dui);
-    };
+    const GLint* swizzle = nullptr;
 
     GLenum driverUnpackType = LOCAL_GL_HALF_FLOAT;
-    if (!webgl->GL()->IsSupported(gl::GLFeature::texture_half_float)) {
-        MOZ_ASSERT(webgl->GL()->IsExtensionSupported(gl::GLContext::OES_texture_half_float));
+    if (!gl->IsSupported(gl::GLFeature::texture_half_float)) {
+        MOZ_ASSERT(gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float));
         driverUnpackType = LOCAL_GL_HALF_FLOAT_OES;
     }
 
-    const bool isCore = webgl->GL()->IsCoreProfile();
+    const auto fnAdd = [&fua, &pi, &dui, &swizzle,
+                        driverUnpackType](webgl::EffectiveFormat effFormat)
+    {
+        MOZ_ASSERT(!pi.type && !dui.unpackType);
+        pi.type = LOCAL_GL_HALF_FLOAT_OES;
+        dui.unpackType = driverUnpackType;
 
-    pi = {LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT_OES};
-    dui = {LOCAL_GL_RGBA, LOCAL_GL_RGBA, driverUnpackType};
+        auto usage = fua->EditUsage(effFormat);
+        fua->AddUnsizedTexFormat(pi, usage);
+        usage->AddUnpack(pi, dui);
+
+        usage->textureSwizzleRGBA = swizzle;
+    };
+
+    const bool isCore = gl->IsCoreProfile();
+
+    pi = {LOCAL_GL_RGBA, 0};
+    dui = {pi.format, pi.format, pi.type};
     fnAdd(webgl::EffectiveFormat::RGBA16F);
 
-    pi = {LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT_OES};
-    dui = {LOCAL_GL_RGB, LOCAL_GL_RGB, driverUnpackType};
+    pi = {LOCAL_GL_RGB, 0};
+    dui = {pi.format, pi.format, pi.type};
     fnAdd(webgl::EffectiveFormat::RGB16F);
 
-    pi = {LOCAL_GL_LUMINANCE, LOCAL_GL_HALF_FLOAT_OES};
-    if (isCore) dui = {LOCAL_GL_R16F, LOCAL_GL_RED, driverUnpackType};
-    else        dui = {LOCAL_GL_LUMINANCE, LOCAL_GL_LUMINANCE, driverUnpackType};
+    pi = {LOCAL_GL_LUMINANCE, 0};
+    if (isCore) {
+        dui = {LOCAL_GL_R16F, LOCAL_GL_RED, 0};
+        swizzle = webgl::FormatUsageInfo::kLuminanceSwizzleRGBA;
+    } else {
+        dui = {pi.format, pi.format, pi.type};
+        swizzle = nullptr;
+    }
     fnAdd(webgl::EffectiveFormat::Luminance16F);
 
-    pi = {LOCAL_GL_ALPHA, LOCAL_GL_HALF_FLOAT_OES};
-    if (isCore) dui = {LOCAL_GL_R16F, LOCAL_GL_RED, driverUnpackType};
-    else        dui = {LOCAL_GL_ALPHA, LOCAL_GL_ALPHA, driverUnpackType};
+    pi = {LOCAL_GL_ALPHA, 0};
+    if (isCore) {
+        dui = {LOCAL_GL_R16F, LOCAL_GL_RED, 0};
+        swizzle = webgl::FormatUsageInfo::kAlphaSwizzleRGBA;
+    } else {
+        dui = {pi.format, pi.format, pi.type};
+        swizzle = nullptr;
+    }
     fnAdd(webgl::EffectiveFormat::Alpha16F);
 
-    pi = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_HALF_FLOAT_OES};
-    if (isCore) dui = {LOCAL_GL_RG16F, LOCAL_GL_RG, driverUnpackType};
-    else        dui = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_LUMINANCE_ALPHA, driverUnpackType};
+    pi = {LOCAL_GL_LUMINANCE_ALPHA, 0};
+    dui = {pi.format, pi.format, pi.type};
+    if (isCore) {
+        dui = {LOCAL_GL_RG16F, LOCAL_GL_RG, 0};
+        swizzle = webgl::FormatUsageInfo::kLumAlphaSwizzleRGBA;
+    } else {
+        dui = {pi.format, pi.format, pi.type};
+        swizzle = nullptr;
+    }
     fnAdd(webgl::EffectiveFormat::Luminance16FAlpha16F);
 }
 
 WebGLExtensionTextureHalfFloat::~WebGLExtensionTextureHalfFloat()
 {
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionTextureHalfFloat, OES_texture_half_float)
--- a/dom/canvas/WebGLFormats.cpp
+++ b/dom/canvas/WebGLFormats.cpp
@@ -448,67 +448,85 @@ SetUsage(FormatUsageAuthority* fua, Effe
     MOZ_ASSERT(!fua->GetUsage(effFormat));
 
     auto usage = fua->EditUsage(effFormat);
     usage->isRenderable = isRenderable;
     usage->isFilterable = isFilterable;
 }
 
 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};
+    usage->AddUnpack(pi, dui);
+};
+
+
+/*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 void
 AddLegacyFormats_LA8(FormatUsageAuthority* fua, gl::GLContext* gl)
 {
-    PackingInfo pi;
-    DriverUnpackInfo dui;
+    if (gl->IsCoreProfile()) {
+        PackingInfo pi;
+        DriverUnpackInfo dui;
 
-    const auto fnAdd = [fua, &pi, &dui](EffectiveFormat effFormat) {
-        auto usage = fua->EditUsage(effFormat);
-        fua->AddUnsizedTexFormat(pi, usage);
-        usage->AddUnpack(pi, dui);
-    };
-
-    const bool isCore = gl->IsCoreProfile();
+        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;
+        };
 
-    pi = {LOCAL_GL_LUMINANCE, LOCAL_GL_UNSIGNED_BYTE};
-    if (isCore) dui = {LOCAL_GL_R8, LOCAL_GL_RED, LOCAL_GL_UNSIGNED_BYTE};
-    else        dui = {LOCAL_GL_LUMINANCE, LOCAL_GL_LUMINANCE, LOCAL_GL_UNSIGNED_BYTE};
-    fnAdd(EffectiveFormat::Luminance8);
+        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::Luminance8, FormatUsageInfo::kAlphaSwizzleRGBA);
 
-    pi = {LOCAL_GL_ALPHA, LOCAL_GL_UNSIGNED_BYTE};
-    if (isCore) dui = {LOCAL_GL_R8, LOCAL_GL_RED, LOCAL_GL_UNSIGNED_BYTE};
-    else        dui = {LOCAL_GL_ALPHA, LOCAL_GL_ALPHA, LOCAL_GL_UNSIGNED_BYTE};
-    fnAdd(EffectiveFormat::Alpha8);
-
-    pi = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE};
-    if (isCore) dui = {LOCAL_GL_RG8, LOCAL_GL_RG, LOCAL_GL_UNSIGNED_BYTE};
-    else        dui = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE};
-    fnAdd(EffectiveFormat::Luminance8Alpha8);
+        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);
+    }
 }
 
 static void
 AddBasicUnsizedFormats(FormatUsageAuthority* fua, gl::GLContext* gl)
 {
-    const auto fnAddSimpleUnsized = [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};
-        usage->AddUnpack(pi, dui);
-    };
-
     // GLES 2.0.25, p63, Table 3.4
-
-    fnAddSimpleUnsized(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE         , EffectiveFormat::RGBA8  );
-    fnAddSimpleUnsized(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4, EffectiveFormat::RGBA4  );
-    fnAddSimpleUnsized(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1, EffectiveFormat::RGB5_A1);
-    fnAddSimpleUnsized(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_BYTE         , EffectiveFormat::RGB8   );
-    fnAddSimpleUnsized(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_SHORT_5_6_5  , EffectiveFormat::RGB565 );
+    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
     AddLegacyFormats_LA8(fua, gl);
 }
 
 UniquePtr<FormatUsageAuthority>
 FormatUsageAuthority::CreateForWebGL1(gl::GLContext* gl)
 {
--- a/dom/canvas/WebGLFormats.h
+++ b/dom/canvas/WebGLFormats.h
@@ -240,23 +240,28 @@ GLenum ComponentType(const FormatInfo* f
 
 struct FormatUsageInfo
 {
     const FormatInfo* const format;
     bool isRenderable;
     bool isFilterable;
     std::map<PackingInfo, DriverUnpackInfo> validUnpacks;
     const DriverUnpackInfo* idealUnpack;
-    //const GLint* textureSwizzleRGBA;
+    const GLint* textureSwizzleRGBA;
+
+    static const GLint kLuminanceSwizzleRGBA[4];
+    static const GLint kAlphaSwizzleRGBA[4];
+    static const GLint kLumAlphaSwizzleRGBA[4];
 
     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
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -110,41 +110,42 @@ WebGLTexture::ImageInfo::MemoryUsage() c
 void
 WebGLTexture::ImageInfo::SetIsDataInitialized(bool isDataInitialized, WebGLTexture* tex)
 {
     MOZ_ASSERT(tex);
     MOZ_ASSERT(this >= &tex->mImageInfoArr[0]);
     MOZ_ASSERT(this < &tex->mImageInfoArr[kMaxLevelCount * kMaxFaceCount]);
 
     mIsDataInitialized = isDataInitialized;
-    tex->InvalidateFakeBlackCache();
+    tex->InvalidateResolveCache();
 }
 
 ////////////////////////////////////////
 
 JSObject*
 WebGLTexture::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) {
     return dom::WebGLTextureBinding::Wrap(cx, this, givenProto);
 }
 
 WebGLTexture::WebGLTexture(WebGLContext* webgl, GLuint tex)
     : WebGLContextBoundObject(webgl)
     , mGLName(tex)
     , mTarget(LOCAL_GL_NONE)
+    , mFaceCount(0)
     , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR)
     , mMagFilter(LOCAL_GL_LINEAR)
     , mWrapS(LOCAL_GL_REPEAT)
     , mWrapT(LOCAL_GL_REPEAT)
-    , mFaceCount(0)
     , mImmutable(false)
     , mImmutableLevelCount(0)
     , mBaseMipmapLevel(0)
     , mMaxMipmapLevel(1000)
-    , mFakeBlackStatus(WebGLTextureFakeBlackStatus::IncompleteTexture)
     , mTexCompareMode(LOCAL_GL_NONE)
+    , mIsResolved(false)
+    , mResolved_Swizzle(nullptr)
 {
     mContext->mTextures.insertBack(this);
 }
 
 void
 WebGLTexture::Delete()
 {
     for (auto& cur : mImageInfoArr) {
@@ -168,27 +169,27 @@ WebGLTexture::MemoryUsage() const
     return result;
 }
 
 void
 WebGLTexture::SetImageInfo(ImageInfo* target, const ImageInfo& newInfo)
 {
     *target = newInfo;
 
-    InvalidateFakeBlackCache();
+    InvalidateResolveCache();
 }
 
 void
 WebGLTexture::SetImageInfosAtLevel(uint32_t level, const ImageInfo& newInfo)
 {
     for (uint8_t i = 0; i < mFaceCount; i++) {
         ImageInfoAtFace(i, level) = newInfo;
     }
 
-    InvalidateFakeBlackCache();
+    InvalidateResolveCache();
 }
 
 static inline uint32_t
 MaxMipmapLevelsForSize(const WebGLTexture::ImageInfo& info)
 {
     auto size = std::max(std::max(info.mWidth, info.mHeight), info.mDepth);
 
     // Find floor(log2(maxsize)) + 1. (ES 3.0.4, 3.8 - Mipmapping).
@@ -422,58 +423,48 @@ WebGLTexture::IsComplete(const char** co
 
     return true;
 }
 
 
 uint32_t
 WebGLTexture::MaxEffectiveMipmapLevel() const
 {
-    const bool requiresMipmap = (mMinFilter != LOCAL_GL_NEAREST &&
-                                 mMinFilter != LOCAL_GL_LINEAR);
-    if (!requiresMipmap)
+    if (mMinFilter == LOCAL_GL_NEAREST ||
+        mMinFilter == LOCAL_GL_LINEAR)
+    {
+        // No mips used.
         return mBaseMipmapLevel;
+    }
 
     const auto& imageInfo = BaseImageInfo();
     MOZ_ASSERT(imageInfo.IsDefined());
 
     uint32_t maxLevelBySize = mBaseMipmapLevel + imageInfo.MaxMipmapLevels() - 1;
     return std::min<uint32_t>(maxLevelBySize, mMaxMipmapLevel);
 }
 
 bool
-WebGLTexture::ResolveFakeBlackStatus(const char* funcName,
-                                     WebGLTextureFakeBlackStatus* const out)
+WebGLTexture::GetFakeBlackType(const char* funcName, uint32_t texUnit,
+                               FakeBlackType* const out_fakeBlack)
 {
-    if (!ResolveFakeBlackStatus(funcName))
-        return false;
-
-    *out = mFakeBlackStatus;
-    return true;
-}
-
-bool
-WebGLTexture::ResolveFakeBlackStatus(const char* funcName)
-{
-    if (MOZ_LIKELY(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown))
-        return true;
-
     const char* incompleteReason;
     if (!IsComplete(&incompleteReason)) {
         if (incompleteReason) {
-            mContext->GenerateWarning("An active texture is going to be rendered as if it"
-                                      " were black, as per the GLES 2.0.24 $3.8.2: %s",
+            mContext->GenerateWarning("%s: Active texture %u for target 0x%04x is"
+                                      " 'incomplete', and will be rendered as"
+                                      " RGBA(0,0,0,1), as per the GLES 2.0.24 $3.8.2: %s",
+                                      funcName, texUnit, mTarget.get(),
                                       incompleteReason);
         }
-        mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture;
+        *out_fakeBlack = FakeBlackType::RGBA0001;
         return true;
     }
 
-    // We have exhausted all cases of incomplete textures, where we would need opaque black.
-    // We may still need transparent black in case of uninitialized image data.
+    // We may still want FakeBlack as an optimization for uninitialized image data.
     bool hasUninitializedData = false;
     bool hasInitializedData = false;
 
     const auto maxLevel = MaxEffectiveMipmapLevel();
     MOZ_ASSERT(mBaseMipmapLevel <= maxLevel);
     for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
         for (uint8_t face = 0; face < mFaceCount; face++) {
             const auto& cur = ImageInfoAtFace(face, level);
@@ -481,57 +472,116 @@ WebGLTexture::ResolveFakeBlackStatus(con
                 hasInitializedData = true;
             else
                 hasUninitializedData = true;
         }
     }
     MOZ_ASSERT(hasUninitializedData || hasInitializedData);
 
     if (!hasUninitializedData) {
-        mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
+        *out_fakeBlack = FakeBlackType::None;
         return true;
     }
 
     if (!hasInitializedData) {
-        mFakeBlackStatus = WebGLTextureFakeBlackStatus::UninitializedImageData;
-        return true;
+        const auto format = ImageInfoAtFace(0, mBaseMipmapLevel).mFormat->format;
+        if (format->isColorFormat) {
+            *out_fakeBlack = (format->hasAlpha ? FakeBlackType::RGBA0000
+                                               : FakeBlackType::RGBA0001);
+            return true;
+        }
+
+        mContext->GenerateWarning("%s: Active texture %u for target 0x%04x is"
+                                  " uninitialized, and will be (perhaps slowly) cleared"
+                                  " by the implementation.",
+                                  funcName, texUnit, mTarget.get());
+    } else {
+        mContext->GenerateWarning("%s: Active texture %u for target 0x%04x contains"
+                                  " TexImages with uninitialized data along with"
+                                  " TexImages with initialized data, forcing the"
+                                  " implementation to (slowly) initialize the"
+                                  " uninitialized TexImages.",
+                                  funcName, texUnit, mTarget.get());
     }
 
-    // Alright, we have both initialized and uninitialized data, so we have to initialize
-    // the uninitialized images. Feel free to be slow.
-    mContext->GenerateWarning("An active texture contains TexImages with uninitialized"
-                              " data along with TexImages with initialized data, forcing"
-                              " the implementation to (slowly) initialize the"
-                              " uninitialized TexImages.");
-
-    GLenum baseTexImageTarget = mTarget.get();
-    if (baseTexImageTarget == LOCAL_GL_TEXTURE_CUBE_MAP)
-        baseTexImageTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
+    GLenum baseImageTarget = mTarget.get();
+    if (baseImageTarget == LOCAL_GL_TEXTURE_CUBE_MAP)
+        baseImageTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
 
     for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
         for (uint8_t face = 0; face < mFaceCount; face++) {
-            TexImageTarget target = baseTexImageTarget + face;
-            if (!EnsureImageDataInitialized(funcName, target, level))
+            TexImageTarget imageTarget = baseImageTarget + face;
+            if (!EnsureImageDataInitialized(funcName, imageTarget, level))
                 return false; // The world just exploded.
         }
     }
 
-    mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
+    *out_fakeBlack = FakeBlackType::None;
+    return true;
+}
+
+static void
+SetSwizzle(gl::GLContext* gl, TexTarget target, const GLint* swizzle)
+{
+    static const GLint kNoSwizzle[4] = { LOCAL_GL_RED, LOCAL_GL_GREEN, LOCAL_GL_BLUE,
+                                         LOCAL_GL_ALPHA };
+    if (!swizzle) {
+        swizzle = kNoSwizzle;
+    }
+
+    gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_R, swizzle[0]);
+    gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_G, swizzle[1]);
+    gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_B, swizzle[2]);
+    gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_A, swizzle[3]);
+}
+
+bool
+WebGLTexture::ResolveForDraw(const char* funcName, uint32_t texUnit,
+                             FakeBlackType* const out_fakeBlack)
+{
+    if (!mIsResolved) {
+        if (!GetFakeBlackType(funcName, texUnit, &mResolved_FakeBlack))
+            return false;
+
+        // Check which swizzle we should use. Since the texture must be complete at this
+        // point, just grab the format off any valid image.
+        const GLint* newSwizzle = nullptr;
+        if (mResolved_FakeBlack == FakeBlackType::None) {
+            const auto& cur = ImageInfoAtFace(0, mBaseMipmapLevel);
+            newSwizzle = cur.mFormat->textureSwizzleRGBA;
+        }
+
+        // Only set the swizzle if it changed since last time we did it.
+        if (newSwizzle != mResolved_Swizzle) {
+            mResolved_Swizzle = newSwizzle;
+
+            // Set the new swizzle!
+            mContext->gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit);
+            SetSwizzle(mContext->gl, mTarget, mResolved_Swizzle);
+            mContext->gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mContext->mActiveTexture);
+        }
+
+        mIsResolved = true;
+    }
+
+    *out_fakeBlack = mResolved_FakeBlack;
     return true;
 }
 
 bool
 WebGLTexture::EnsureImageDataInitialized(const char* funcName, TexImageTarget target,
                                          uint32_t level)
 {
     auto& imageInfo = ImageInfoAt(target, level);
+    MOZ_ASSERT(imageInfo.IsDefined());
+    /*
     if (!imageInfo.IsDefined())
         return true; // The driver should handle this for us?
                      // (happens because ResolveFakeBlack plus incomplete mipchains)
-
+    */
     if (imageInfo.IsDataInitialized())
         return true;
 
     return InitializeImageData(funcName, target, level);
 }
 
 bool
 WebGLTexture::InitializeImageData(const char* funcName, TexImageTarget target,
@@ -603,23 +653,16 @@ WebGLTexture::PopulateMipChain(uint32_t 
         refWidth = std::max(uint32_t(1), refWidth / 2);
         refHeight = std::max(uint32_t(1), refHeight / 2);
         if (mTarget == LOCAL_GL_TEXTURE_3D) { // But not TEXTURE_2D_ARRAY!
             refDepth = std::max(uint32_t(1), refDepth / 2);
         }
     }
 }
 
-void
-WebGLTexture::InvalidateFakeBlackCache()
-{
-    mContext->InvalidateFakeBlackCache();
-    mFakeBlackStatus = WebGLTextureFakeBlackStatus::Unknown;
-}
-
 //////////////////////////////////////////////////////////////////////////////////////////
 // GL calls
 
 bool
 WebGLTexture::BindTexture(TexTarget texTarget)
 {
     // silently ignore a deleted texture
     if (IsDeleted())
@@ -648,20 +691,16 @@ WebGLTexture::BindTexture(TexTarget texT
         // If we are WebGL 2 though, we'll want to leave it as REPEAT.
         const bool hasWrapR = gl->IsSupported(gl::GLFeature::texture_3D);
         if (IsCubeMap() && hasWrapR && !mContext->IsWebGL2()) {
             gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_WRAP_R,
                                LOCAL_GL_CLAMP_TO_EDGE);
         }
     }
 
-    if (mFakeBlackStatus != WebGLTextureFakeBlackStatus::NotNeeded) {
-        mContext->InvalidateFakeBlackCache();
-    }
-
     return true;
 }
 
 
 void
 WebGLTexture::GenerateMipmap(TexTarget texTarget)
 {
     if (mBaseMipmapLevel > mMaxMipmapLevel) {
@@ -954,20 +993,25 @@ WebGLTexture::TexParameter(TexTarget tex
     case LOCAL_GL_TEXTURE_WRAP_T:
         mWrapT = intParam;
         break;
 
     // We don't actually need to store the WRAP_R, since it doesn't change texture
     // completeness rules.
     }
 
-    // The only pname that doesn't actually need fake-black invalidation is
-    // LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT.
-    if (pname != LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT) {
-        InvalidateFakeBlackCache();
+    // Only a couple of pnames don't need to invalidate our resolve status cache.
+    switch (pname) {
+    case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT:
+    case LOCAL_GL_TEXTURE_WRAP_R:
+        break;
+
+    default:
+        InvalidateResolveCache();
+        break;
     }
 
     ////////////////
 
     mContext->MakeContextCurrent();
     if (maybeIntParam)
         mContext->gl->fTexParameteri(texTarget.get(), pname, intParam);
     else
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -54,32 +54,35 @@ class WebGLTexture final
     ////////////////////////////////////
     // Members
 public:
     const GLuint mGLName;
 
 protected:
     TexTarget mTarget;
 
+    static const uint8_t kMaxFaceCount = 6;
+    uint8_t mFaceCount; // 6 for cube maps, 1 otherwise.
+
     TexMinFilter mMinFilter;
     TexMagFilter mMagFilter;
     TexWrap mWrapS, mWrapT;
 
-    static const uint8_t kMaxFaceCount = 6;
-    uint8_t mFaceCount; // 6 for cube maps, 1 otherwise.
-
     bool mImmutable; // Set by texStorage*
     uint8_t mImmutableLevelCount;
 
     uint32_t mBaseMipmapLevel; // Set by texParameter (defaults to 0)
     uint32_t mMaxMipmapLevel;  // Set by texParameter (defaults to 1000)
 
-    WebGLTextureFakeBlackStatus mFakeBlackStatus;
+    GLenum mTexCompareMode;
 
-    GLenum mTexCompareMode;
+    // Resolvable optimizations:
+    bool mIsResolved;
+    FakeBlackType mResolved_FakeBlack;
+    const GLint* mResolved_Swizzle; // nullptr means 'default swizzle'.
 
 public:
     class ImageInfo;
 
     // numLevels = log2(size) + 1
     // numLevels(16k) = log2(16k) + 1 = 14 + 1 = 15
     // numLevels(1M) = log2(1M) + 1 = 19.9 + 1 ~= 21
     // Or we can just max this out to 31, which is the number of unsigned bits in GLsizei.
@@ -369,26 +372,25 @@ public:
     bool IsCubeComplete() const;
 
     bool IsComplete(const char** const out_reason) const;
 
     bool IsMipmapCubeComplete() const;
 
     bool IsCubeMap() const { return (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP); }
 
-    // Fake black status
+    // Resolve cache optimizations
 protected:
-    bool ResolveFakeBlackStatus(const char* funcName);
+    bool GetFakeBlackType(const char* funcName, uint32_t texUnit,
+                          FakeBlackType* const out_fakeBlack);
 public:
-    bool ResolveFakeBlackStatus(const char* funcName,
-                                WebGLTextureFakeBlackStatus* const out);
+    bool ResolveForDraw(const char* funcName, uint32_t texUnit,
+                        FakeBlackType* const out_fakeBlack);
 
-    WebGLTextureFakeBlackStatus FakeBlackStatus() const { return mFakeBlackStatus; }
-
-    void InvalidateFakeBlackCache();
+    void InvalidateResolveCache() { mIsResolved = false; }
 };
 
 inline TexImageTarget
 TexImageTargetForTargetAndFace(TexTarget target, uint8_t face)
 {
     switch (target.get()) {
     case LOCAL_GL_TEXTURE_2D:
     case LOCAL_GL_TEXTURE_3D:
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -303,64 +303,16 @@ WebGLTexture::TexOrSubImage(bool isSubIm
         TexImage(funcName, target, level, internalFormat, border, unpackFormat,
                  unpackType, blob);
     }
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 
-
-/* This needs to be done (but cached) per texture per draw call.
-
-// Map R to A
-static const GLenum kLegacyAlphaSwizzle[4] = {
-    LOCAL_GL_ZERO, LOCAL_GL_ZERO, LOCAL_GL_ZERO, LOCAL_GL_RED
-};
-// Map R to RGB
-static const GLenum kLegacyLuminanceSwizzle[4] = {
-    LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_ONE
-};
-// Map R to RGB, G to A
-static const GLenum kLegacyLuminanceAlphaSwizzle[4] = {
-    LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_GREEN
-};
-
-static void
-SetLegacyTextureSwizzle(gl::GLContext* gl, GLenum target, GLenum internalformat)
-{
-    if (!gl->IsCoreProfile())
-        return;
-
-    // Only support swizzling on core profiles.
-    // Bug 1159117: Fix this.
-    // MOZ_RELEASE_ASSERT(gl->IsSupported(gl::GLFeature::texture_swizzle));
-
-    switch (internalformat) {
-    case LOCAL_GL_ALPHA:
-        gl->fTexParameteriv(target, LOCAL_GL_TEXTURE_SWIZZLE_RGBA,
-                            (GLint*) kLegacyAlphaSwizzle);
-        break;
-
-    case LOCAL_GL_LUMINANCE:
-        gl->fTexParameteriv(target, LOCAL_GL_TEXTURE_SWIZZLE_RGBA,
-                            (GLint*) kLegacyLuminanceSwizzle);
-        break;
-
-    case LOCAL_GL_LUMINANCE_ALPHA:
-        gl->fTexParameteriv(target, LOCAL_GL_TEXTURE_SWIZZLE_RGBA,
-                            (GLint*) kLegacyLuminanceAlphaSwizzle);
-        break;
-    }
-}
-*/
-
-//////////////////////////////////////////////////////////////////////////////////////////
-// Utils
-
 static bool
 ValidateTexImage(WebGLContext* webgl, WebGLTexture* texture, const char* funcName,
                  TexImageTarget target, GLint level,
                  WebGLTexture::ImageInfo** const out_imageInfo)
 {
     // Check level
     if (level < 0) {
         webgl->ErrorInvalidValue("%s: `level` must be >= 0.", funcName);
@@ -1568,16 +1520,175 @@ ValidateCopyTexImageFormats(WebGLContext
                                      " source channels. (GLES 3.0.4 p140 Table 3.16)",
                                      funcName);
         return false;
     }
 
     return true;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
+class ScopedCopyTexImageSource
+{
+    WebGLContext* const mWebGL;
+    GLuint mRB;
+    GLuint mFB;
+
+public:
+    ScopedCopyTexImageSource(WebGLContext* webgl, const char* funcName, uint32_t srcWidth,
+                             uint32_t srcHeight, const webgl::FormatInfo* srcFormat,
+                             const webgl::FormatUsageInfo* dstUsage);
+    ~ScopedCopyTexImageSource();
+};
+
+ScopedCopyTexImageSource::ScopedCopyTexImageSource(WebGLContext* webgl,
+                                                   const char* funcName,
+                                                   uint32_t srcWidth, uint32_t srcHeight,
+                                                   const webgl::FormatInfo* srcFormat,
+                                                   const webgl::FormatUsageInfo* dstUsage)
+    : mWebGL(webgl)
+    , mRB(0)
+    , mFB(0)
+{
+    switch (dstUsage->format->unsizedFormat) {
+    case webgl::UnsizedFormat::L:
+    case webgl::UnsizedFormat::A:
+    case webgl::UnsizedFormat::LA:
+        webgl->GenerateWarning("%s: Copying to a LUMINANCE, ALPHA, or LUMINANCE_ALPHA"
+                               " is deprecated, and has severely reduced performance"
+                               " on some platforms.",
+                               funcName);
+        break;
+
+    default:
+        MOZ_ASSERT(!dstUsage->textureSwizzleRGBA);
+        return;
+    }
+
+    if (!dstUsage->textureSwizzleRGBA)
+        return;
+
+    gl::GLContext* gl = webgl->gl;
+
+    GLenum sizedFormat;
+
+    switch (srcFormat->componentType) {
+    case webgl::ComponentType::NormUInt:
+        sizedFormat = LOCAL_GL_RGBA8;
+        break;
+
+    case webgl::ComponentType::Float:
+        if (webgl->IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float)) {
+            sizedFormat = LOCAL_GL_RGBA32F;
+            break;
+        }
+
+        if (webgl->IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float)) {
+            sizedFormat = LOCAL_GL_RGBA16F;
+            break;
+        }
+        MOZ_CRASH("Should be able to request CopyTexImage from Float.");
+
+    default:
+        MOZ_CRASH("Should be able to request CopyTexImage from this type.");
+    }
+
+    gl::ScopedTexture scopedTex(gl);
+    gl::ScopedBindTexture scopedBindTex(gl, scopedTex.Texture(), LOCAL_GL_TEXTURE_2D);
+
+    gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
+    gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
+
+    GLint blitSwizzle[4] = {LOCAL_GL_ZERO};
+    switch (dstUsage->format->unsizedFormat) {
+    case webgl::UnsizedFormat::L:
+        blitSwizzle[0] = LOCAL_GL_RED;
+        break;
+
+    case webgl::UnsizedFormat::A:
+        blitSwizzle[0] = LOCAL_GL_ALPHA;
+        break;
+
+    case webgl::UnsizedFormat::LA:
+        blitSwizzle[0] = LOCAL_GL_RED;
+        blitSwizzle[1] = LOCAL_GL_ALPHA;
+        break;
+
+    default:
+        MOZ_CRASH("Unhandled unsizedFormat.");
+    }
+
+    gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_R, blitSwizzle[0]);
+    gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_G, blitSwizzle[1]);
+    gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_B, blitSwizzle[2]);
+    gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_A, blitSwizzle[3]);
+
+    gl->fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, sizedFormat, 0, 0, srcWidth,
+                        srcHeight, 0);
+
+    // Now create the swizzled FB we'll be exposing.
+
+    GLuint rgbaRB = 0;
+    gl->fGenRenderbuffers(1, &rgbaRB);
+    gl::ScopedBindRenderbuffer scopedRB(gl, rgbaRB);
+    gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, sizedFormat, srcWidth, srcHeight);
+
+    GLuint rgbaFB = 0;
+    gl->fGenFramebuffers(1, &rgbaFB);
+    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, rgbaFB);
+    gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
+                                 LOCAL_GL_RENDERBUFFER, rgbaRB);
+
+    const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+    if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+        MOZ_CRASH("Temp framebuffer is not Complete.");
+    }
+
+    // Restore RB binding.
+    scopedRB.Unwrap(); // This function should really have a better name.
+
+    // Draw-blit rgbaTex into rgbaFB.
+    const gfx::IntSize srcSize(srcWidth, srcHeight);
+    gl->BlitHelper()->DrawBlitTextureToFramebuffer(scopedTex.Texture(), rgbaFB,
+                                                   srcSize, srcSize);
+
+    // Restore Tex2D binding and destroy the temp tex.
+    scopedBindTex.Unwrap();
+    scopedTex.Unwrap();
+
+    // Leave RB and FB alive, and FB bound.
+    mRB = rgbaRB;
+    mFB = rgbaFB;
+}
+
+template<typename T>
+static inline GLenum
+ToGLHandle(const T& obj)
+{
+    return (obj ? obj->mGLName : 0);
+}
+
+ScopedCopyTexImageSource::~ScopedCopyTexImageSource()
+{
+    gl::GLContext* gl = mWebGL->gl;
+
+    // If we're swizzling, it's because we're on a GL core (3.2+) profile, which has
+    // split framebuffer support.
+    gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
+                         ToGLHandle(mWebGL->mBoundDrawFramebuffer));
+    gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
+                         ToGLHandle(mWebGL->mBoundReadFramebuffer));
+
+    gl->fDeleteFramebuffers(1, &mFB);
+    gl->fDeleteRenderbuffers(1, &mRB);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 // There is no CopyTexImage3D.
 void
 WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internalFormat,
                              GLint x, GLint y, GLsizei width, GLsizei height,
                              GLint border)
 {
     const char funcName[] = "CopyTexImage2D";
 
@@ -1650,16 +1761,19 @@ WebGLTexture::CopyTexImage2D(TexImageTar
         return;
 
     ////////////////////////////////////
     // Do the thing!
 
     gl::GLContext* gl = mContext->gl;
     gl->MakeCurrent();
 
+    ScopedCopyTexImageSource maybeSwizzle(mContext, funcName, srcWidth, srcHeight,
+                                          srcFormat, dstUsage);
+
     uint32_t readX, readY;
     uint32_t writeX, writeY;
     uint32_t rwWidth, rwHeight;
     Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth);
     Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight);
 
     GLenum error;
     if (rwWidth == uint32_t(width) && rwHeight == uint32_t(height)) {
@@ -1756,16 +1870,19 @@ WebGLTexture::CopyTexSubImage(const char
     if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
         return;
 
     ////////////////////////////////////
     // Do the thing!
 
     mContext->gl->MakeCurrent();
 
+    ScopedCopyTexImageSource maybeSwizzle(mContext, funcName, srcWidth, srcHeight,
+                                          srcFormat, dstUsage);
+
     uint32_t readX, readY;
     uint32_t writeX, writeY;
     uint32_t rwWidth, rwHeight;
     Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth);
     Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight);
 
     if (!rwWidth || !rwHeight) {
         // There aren't any, so we're 'done'.
--- a/dom/canvas/WebGLTypes.h
+++ b/dom/canvas/WebGLTypes.h
@@ -33,21 +33,20 @@ namespace mozilla {
  *       uninitialized image data. See below the comment about WebGLImageDataStatus.
  *       WebGL must never have access to uninitialized image data. The WebGL 1 spec,
  *       section 4.1 'Resource Restrictions', specifies that in any such case, the
  *       uninitialized image data must be exposed to WebGL as if it were filled
  *       with zero bytes, which means it's either opaque or transparent black
  *       depending on whether the image format has alpha.
  */
 
-enum class WebGLTextureFakeBlackStatus : uint8_t {
-  Unknown,
-  NotNeeded,
-  IncompleteTexture,
-  UninitializedImageData
+enum class FakeBlackType : uint8_t {
+    None,
+    RGBA0001, // Incomplete textures and uninitialized no-alpha color textures.
+    RGBA0000, // Uninitialized with-alpha color textures.
 };
 
 /*
  * Implementing WebGL (or OpenGL ES 2.0) on top of desktop OpenGL requires
  * emulating the vertex attrib 0 array when it's not enabled. Indeed,
  * OpenGL ES 2.0 allows drawing without vertex attrib 0 array enabled, but
  * desktop OpenGL does not allow that.
  */
--- a/gfx/gl/GLBlitHelper.cpp
+++ b/gfx/gl/GLBlitHelper.cpp
@@ -929,16 +929,28 @@ GLBlitHelper::BlitTextureToFramebuffer(G
         MOZ_DIAGNOSTIC_ASSERT(srcWrapper.IsComplete());
 
         BlitFramebufferToFramebuffer(srcWrapper.FB(), destFB,
                                      srcSize, destSize,
                                      internalFBs);
         return;
     }
 
+    DrawBlitTextureToFramebuffer(srcTex, destFB, srcSize, destSize, srcTarget,
+                                 internalFBs);
+}
+
+
+void
+GLBlitHelper::DrawBlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
+                                           const gfx::IntSize& srcSize,
+                                           const gfx::IntSize& destSize,
+                                           GLenum srcTarget,
+                                           bool internalFBs)
+{
     BlitType type;
     switch (srcTarget) {
     case LOCAL_GL_TEXTURE_2D:
         type = BlitTex2D;
         break;
     case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
         type = BlitTexRect;
         break;
--- a/gfx/gl/GLBlitHelper.h
+++ b/gfx/gl/GLBlitHelper.h
@@ -138,16 +138,21 @@ public:
                                       const gfx::IntSize& destSize,
                                       const GLFormats& srcFormats,
                                       bool internalFBs = false);
     void BlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
                                   const gfx::IntSize& srcSize,
                                   const gfx::IntSize& destSize,
                                   GLenum srcTarget = LOCAL_GL_TEXTURE_2D,
                                   bool internalFBs = false);
+    void DrawBlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
+                                      const gfx::IntSize& srcSize,
+                                      const gfx::IntSize& destSize,
+                                      GLenum srcTarget = LOCAL_GL_TEXTURE_2D,
+                                      bool internalFBs = false);
     void BlitFramebufferToTexture(GLuint srcFB, GLuint destTex,
                                   const gfx::IntSize& srcSize,
                                   const gfx::IntSize& destSize,
                                   GLenum destTarget = LOCAL_GL_TEXTURE_2D,
                                   bool internalFBs = false);
     void BlitTextureToTexture(GLuint srcTex, GLuint destTex,
                               const gfx::IntSize& srcSize,
                               const gfx::IntSize& destSize,
--- a/gfx/gl/ScopedGLHelpers.cpp
+++ b/gfx/gl/ScopedGLHelpers.cpp
@@ -169,44 +169,66 @@ ScopedRenderbuffer::UnwrapImpl()
 {
     // Check that we're not falling out of scope after
     // the current context changed.
     MOZ_ASSERT(mGL->IsCurrent());
     mGL->fDeleteRenderbuffers(1, &mRB);
 }
 
 /* ScopedBindTexture **********************************************************/
-void
-ScopedBindTexture::Init(GLenum aTarget)
+
+static GLuint
+GetBoundTexture(GLContext* gl, GLenum texTarget)
 {
-    mTarget = aTarget;
-    mOldTex = 0;
-    GLenum bindingTarget = (aTarget == LOCAL_GL_TEXTURE_2D) ? LOCAL_GL_TEXTURE_BINDING_2D
-                         : (aTarget == LOCAL_GL_TEXTURE_3D) ? LOCAL_GL_TEXTURE_BINDING_3D
-                         : (aTarget == LOCAL_GL_TEXTURE_RECTANGLE_ARB) ? LOCAL_GL_TEXTURE_BINDING_RECTANGLE_ARB
-                         : (aTarget == LOCAL_GL_TEXTURE_CUBE_MAP) ? LOCAL_GL_TEXTURE_BINDING_CUBE_MAP
-                         : (aTarget == LOCAL_GL_TEXTURE_EXTERNAL) ? LOCAL_GL_TEXTURE_BINDING_EXTERNAL
-                         : LOCAL_GL_NONE;
-    MOZ_ASSERT(bindingTarget != LOCAL_GL_NONE);
-    mGL->GetUIntegerv(bindingTarget, &mOldTex);
+    GLenum bindingTarget;
+    switch (texTarget) {
+    case LOCAL_GL_TEXTURE_2D:
+        bindingTarget = LOCAL_GL_TEXTURE_BINDING_2D;
+        break;
+
+    case LOCAL_GL_TEXTURE_CUBE_MAP:
+        bindingTarget = LOCAL_GL_TEXTURE_BINDING_CUBE_MAP;
+        break;
+
+    case LOCAL_GL_TEXTURE_3D:
+        bindingTarget = LOCAL_GL_TEXTURE_BINDING_3D;
+        break;
+
+    case LOCAL_GL_TEXTURE_2D_ARRAY:
+        bindingTarget = LOCAL_GL_TEXTURE_BINDING_2D_ARRAY;
+        break;
+
+    case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
+        bindingTarget = LOCAL_GL_TEXTURE_BINDING_RECTANGLE_ARB;
+        break;
+
+    case LOCAL_GL_TEXTURE_EXTERNAL:
+        bindingTarget = LOCAL_GL_TEXTURE_BINDING_EXTERNAL;
+        break;
+
+    default:
+        MOZ_CRASH("bad texTarget");
+    }
+
+    GLuint ret = 0;
+    gl->GetUIntegerv(bindingTarget, &ret);
+    return ret;
 }
 
 ScopedBindTexture::ScopedBindTexture(GLContext* aGL, GLuint aNewTex, GLenum aTarget)
         : ScopedGLWrapper<ScopedBindTexture>(aGL)
-    {
-        Init(aTarget);
-        mGL->fBindTexture(aTarget, aNewTex);
-    }
+        , mTarget(aTarget)
+        , mOldTex(GetBoundTexture(aGL, aTarget))
+{
+    mGL->fBindTexture(mTarget, aNewTex);
+}
 
 void
 ScopedBindTexture::UnwrapImpl()
 {
-    // Check that we're not falling out of scope after the current context changed.
-    MOZ_ASSERT(mGL->IsCurrent());
-
     mGL->fBindTexture(mTarget, mOldTex);
 }
 
 
 /* ScopedBindRenderbuffer *****************************************************/
 
 void
 ScopedBindRenderbuffer::Init()
--- a/gfx/gl/ScopedGLHelpers.h
+++ b/gfx/gl/ScopedGLHelpers.h
@@ -164,21 +164,18 @@ protected:
 
 
 struct ScopedBindTexture
     : public ScopedGLWrapper<ScopedBindTexture>
 {
     friend struct ScopedGLWrapper<ScopedBindTexture>;
 
 protected:
-    GLuint mOldTex;
-    GLenum mTarget;
-
-private:
-    void Init(GLenum aTarget);
+    const GLenum mTarget;
+    const GLuint mOldTex;
 
 public:
     ScopedBindTexture(GLContext* aGL, GLuint aNewTex,
                       GLenum aTarget = LOCAL_GL_TEXTURE_2D);
 
 protected:
     void UnwrapImpl();
 };