Bug 1427668 - Move the webgl backbuffer into webgl. - r=daoshengmu draft
authorJeff Gilbert <jgilbert@mozilla.com>
Mon, 18 Dec 2017 18:30:56 -0800
changeset 716054 41b45ac5be765ef2d57f486fe522e7f758fb9e21
parent 716053 44c13b0337ae588db25c34af7bc98174dc9ac634
child 716055 f7144948a0e50cc2c7287e0559b22c193d22a8ba
push id94316
push userbmo:jgilbert@mozilla.com
push dateFri, 05 Jan 2018 03:14:09 +0000
reviewersdaoshengmu
bugs1427668
milestone59.0a1
Bug 1427668 - Move the webgl backbuffer into webgl. - r=daoshengmu MozReview-Commit-ID: 2WpG7mfMRQ8
dom/canvas/OffscreenCanvas.cpp
dom/canvas/WebGL2ContextFramebuffers.cpp
dom/canvas/WebGL2ContextMRTs.cpp
dom/canvas/WebGL2ContextState.cpp
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextDraw.cpp
dom/canvas/WebGLContextFramebufferOperations.cpp
dom/canvas/WebGLContextGL.cpp
dom/canvas/WebGLContextState.cpp
dom/canvas/WebGLContextUtils.cpp
dom/canvas/WebGLContextValidate.cpp
dom/canvas/WebGLFramebuffer.cpp
dom/canvas/WebGLFramebuffer.h
dom/canvas/WebGLTextureUpload.cpp
dom/canvas/moz.build
gfx/gl/GLContext.cpp
gfx/layers/CopyableCanvasRenderer.cpp
gfx/layers/ShareableCanvasRenderer.cpp
gfx/thebes/gfxPrefs.h
--- a/dom/canvas/OffscreenCanvas.cpp
+++ b/dom/canvas/OffscreenCanvas.cpp
@@ -220,24 +220,17 @@ already_AddRefed<ImageBitmap>
 OffscreenCanvas::TransferToImageBitmap(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> globalObject = GetGlobalObject();
   RefPtr<ImageBitmap> result = ImageBitmap::CreateFromOffscreenCanvas(globalObject, *this, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  // Clear the content.
-  if ((mCurrentContextType == CanvasContextType::WebGL1 ||
-       mCurrentContextType == CanvasContextType::WebGL2))
-  {
-    WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
-    webGL->ClearScreen();
-  }
-
+  // TODO: Clear the content?
   return result.forget();
 }
 
 already_AddRefed<Promise>
 OffscreenCanvas::ToBlob(JSContext* aCx,
                         const nsAString& aType,
                         JS::Handle<JS::Value> aParams,
                         ErrorResult& aRv)
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -35,39 +35,28 @@ WebGL2Context::BlitFramebuffer(GLint src
         break;
     default:
         ErrorInvalidEnumInfo("blitFramebuffer: Bad `filter`:", filter);
         return;
     }
 
     ////
 
-    const auto& readFB = mBoundReadFramebuffer;
-    if (readFB &&
-        !readFB->ValidateAndInitAttachments("blitFramebuffer's READ_FRAMEBUFFER"))
+    if (!ValidateAndInitFB("blitFramebuffer: READ_FRAMEBUFFER", mBoundReadFramebuffer) ||
+        !ValidateAndInitFB("blitFramebuffer: DRAW_FRAMEBUFFER", mBoundDrawFramebuffer))
     {
         return;
     }
 
-    const auto& drawFB = mBoundDrawFramebuffer;
-    if (drawFB &&
-        !drawFB->ValidateAndInitAttachments("blitFramebuffer's DRAW_FRAMEBUFFER"))
-    {
-        return;
-    }
-
-    ////
-
-    if (!mBoundReadFramebuffer) {
-        ClearBackbufferIfNeeded();
-    }
+    DoBindFB(mBoundReadFramebuffer, LOCAL_GL_READ_FRAMEBUFFER);
+    DoBindFB(mBoundDrawFramebuffer, LOCAL_GL_DRAW_FRAMEBUFFER);
 
     WebGLFramebuffer::BlitFramebuffer(this,
-                                      readFB, srcX0, srcY0, srcX1, srcY1,
-                                      drawFB, dstX0, dstY0, dstX1, dstY1,
+                                      srcX0, srcY0, srcX1, srcY1,
+                                      dstX0, dstY0, dstX1, dstY1,
                                       mask, filter);
 }
 
 void
 WebGL2Context::FramebufferTextureLayer(GLenum target, GLenum attachment,
                                        WebGLTexture* texture, GLint level, GLint layer)
 {
     const char funcName[] = "framebufferTextureLayer";
@@ -165,33 +154,35 @@ WebGLContext::ValidateInvalidateFramebuf
 {
     if (IsContextLost())
         return false;
 
     if (!ValidateFramebufferTarget(target, funcName))
         return false;
 
     const WebGLFramebuffer* fb;
-    bool isDefaultFB;
+    bool isDefaultFB = false;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         fb = mBoundDrawFramebuffer;
-        isDefaultFB = gl->Screen()->IsDrawFramebufferDefault();
         break;
 
     case LOCAL_GL_READ_FRAMEBUFFER:
         fb = mBoundReadFramebuffer;
-        isDefaultFB = gl->Screen()->IsReadFramebufferDefault();
         break;
 
     default:
         MOZ_CRASH("GFX: Bad target.");
     }
 
+    if (!ValidateAndInitFB(funcName, fb))
+        return false;
+    DoBindFB(fb, target);
+
     *out_glNumAttachments = attachments.Length();
     *out_glAttachments = attachments.Elements();
 
     if (fb) {
         for (const auto& attachment : attachments) {
             if (!ValidateFramebufferAttachmentEnum(this, funcName, attachment))
                 return false;
         }
@@ -225,23 +216,19 @@ WebGLContext::ValidateInvalidateFramebuf
             *out_glNumAttachments = scopedVector->size();
             *out_glAttachments = scopedVector->data();
         }
     }
 
     ////
 
     if (!fb) {
-        ClearBackbufferIfNeeded();
-
-        // Don't do more validation after these.
-        Invalidate();
-        mShouldPresent = true;
+        mDefaultFB_IsInvalid = true;
+        mResolvedDefaultFB = nullptr;
     }
-
     return true;
 }
 
 void
 WebGL2Context::InvalidateFramebuffer(GLenum target,
                                      const dom::Sequence<GLenum>& attachments,
                                      ErrorResult& rv)
 {
@@ -326,12 +313,12 @@ WebGL2Context::ReadBuffer(GLenum mode)
         nsCString enumName;
         EnumName(mode, &enumName);
         ErrorInvalidOperation("%s: If READ_FRAMEBUFFER is null, `mode` must be BACK or"
                               " NONE. Was %s.",
                               funcName, enumName.BeginReading());
         return;
     }
 
-    gl->Screen()->SetReadBuffer(mode);
+    mDefaultFB_ReadBuffer = mode;
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextMRTs.cpp
+++ b/dom/canvas/WebGL2ContextMRTs.cpp
@@ -57,21 +57,21 @@ WebGL2Context::ValidateClearBuffer(const
     if (availElemCount < requiredElements) {
         ErrorInvalidValue("%s: Not enough elements. Require %zu. Given %zu.",
                           funcName, requiredElements, availElemCount);
         return false;
     }
 
     ////
 
+    if (!BindCurFBForDraw(funcName))
+        return false;
+
     const auto& fb = mBoundDrawFramebuffer;
     if (fb) {
-        if (!fb->ValidateAndInitAttachments(funcName))
-            return false;
-
         if (!fb->ValidateClearBufferType(funcName, buffer, drawBuffer, funcType))
             return false;
     } else if (buffer == LOCAL_GL_COLOR) {
         if (drawBuffer != 0)
             return true;
 
         if (mDefaultFB_DrawBuffer0 == LOCAL_GL_NONE)
             return true;
@@ -105,16 +105,23 @@ WebGL2Context::ClearBufferfv(GLenum buff
     }
 
     if (!ValidateClearBuffer(funcName, buffer, drawBuffer, src.elemCount, srcElemOffset,
                              LOCAL_GL_FLOAT))
     {
         return;
     }
 
+    if (!mBoundDrawFramebuffer &&
+        buffer == LOCAL_GL_DEPTH &&
+        mNeedsFakeNoDepth)
+    {
+        return;
+    }
+
     ScopedDrawCallWrapper wrapper(*this);
     const auto ptr = src.elemBytes + srcElemOffset;
     gl->fClearBufferfv(buffer, drawBuffer, ptr);
 }
 
 void
 WebGL2Context::ClearBufferiv(GLenum buffer, GLint drawBuffer, const Int32Arr& src,
                              GLuint srcElemOffset)
@@ -131,16 +138,23 @@ WebGL2Context::ClearBufferiv(GLenum buff
     }
 
     if (!ValidateClearBuffer(funcName, buffer, drawBuffer, src.elemCount, srcElemOffset,
                              LOCAL_GL_INT))
     {
         return;
     }
 
+    if (!mBoundDrawFramebuffer &&
+        buffer == LOCAL_GL_STENCIL &&
+        mNeedsFakeNoStencil)
+    {
+        return;
+    }
+
     ScopedDrawCallWrapper wrapper(*this);
     const auto ptr = src.elemBytes + srcElemOffset;
     gl->fClearBufferiv(buffer, drawBuffer, ptr);
 }
 
 void
 WebGL2Context::ClearBufferuiv(GLenum buffer, GLint drawBuffer, const Uint32Arr& src,
                               GLuint srcElemOffset)
@@ -174,13 +188,23 @@ WebGL2Context::ClearBufferfi(GLenum buff
         return;
 
     if (buffer != LOCAL_GL_DEPTH_STENCIL)
         return ErrorInvalidEnum("%s: buffer must be DEPTH_STENCIL.", funcName);
 
     if (!ValidateClearBuffer(funcName, buffer, drawBuffer, 2, 0, 0))
         return;
 
+    auto driverDepth = depth;
+    auto driverStencil = stencil;
+    if (!mBoundDrawFramebuffer) {
+        if (mNeedsFakeNoDepth) {
+            driverDepth = 1.0f;
+        } else if (mNeedsFakeNoStencil) {
+            driverStencil = 0;
+        }
+    }
+
     ScopedDrawCallWrapper wrapper(*this);
-    gl->fClearBufferfi(buffer, drawBuffer, depth, stencil);
+    gl->fClearBufferfi(buffer, drawBuffer, driverDepth, driverStencil);
 }
 
 } // namespace mozilla
--- a/dom/canvas/WebGL2ContextState.cpp
+++ b/dom/canvas/WebGL2ContextState.cpp
@@ -40,17 +40,17 @@ WebGL2Context::GetParameter(JSContext* c
     case LOCAL_GL_TRANSFORM_FEEDBACK_ACTIVE:
       return JS::BooleanValue(mBoundTransformFeedback->mIsActive);
     case LOCAL_GL_TRANSFORM_FEEDBACK_PAUSED:
       return JS::BooleanValue(mBoundTransformFeedback->mIsPaused);
 
     /* GLenum */
     case LOCAL_GL_READ_BUFFER: {
       if (!mBoundReadFramebuffer)
-        return JS::Int32Value(gl->Screen()->GetReadBufferMode());
+        return JS::Int32Value(mDefaultFB_ReadBuffer);
 
       if (!mBoundReadFramebuffer->ColorReadBuffer())
         return JS::Int32Value(LOCAL_GL_NONE);
 
       return JS::Int32Value(mBoundReadFramebuffer->ColorReadBuffer()->mAttachmentPoint);
     }
 
     case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -9,16 +9,17 @@
 #include <queue>
 
 #include "AccessCheck.h"
 #include "gfxContext.h"
 #include "gfxCrashReporterUtils.h"
 #include "gfxPattern.h"
 #include "gfxPrefs.h"
 #include "gfxUtils.h"
+#include "gfx/gl/MozFramebuffer.h"
 #include "GLBlitHelper.h"
 #include "GLContext.h"
 #include "GLContextProvider.h"
 #include "GLReadTexImageHelper.h"
 #include "GLScreenBuffer.h"
 #include "ImageContainer.h"
 #include "ImageEncoder.h"
 #include "Layers.h"
@@ -114,42 +115,31 @@ WebGLContext::WebGLContext()
     , mMaxAcceptableFBStatusInvals(gfxPrefs::WebGLMaxAcceptableFBStatusInvals())
     , mDataAllocGLCallCount(0)
     , mBypassShaderValidation(false)
     , mEmptyTFO(0)
     , mContextLossHandler(this)
     , mNeedsFakeNoAlpha(false)
     , mNeedsFakeNoDepth(false)
     , mNeedsFakeNoStencil(false)
-    , mNeedsEmulatedLoneDepthStencil(false)
     , mAllowFBInvalidation(gfxPrefs::WebGLFBInvalidation())
+    , mMsaaSamples(gfxPrefs::WebGLMsaaSamples())
 {
     mGeneration = 0;
     mInvalidated = false;
     mCapturedFrameInvalidated = false;
     mShouldPresent = true;
     mResetLayer = true;
     mOptionsFrozen = false;
     mDisableExtensions = false;
     mIsMesa = false;
     mEmitContextLostErrorOnce = false;
     mWebGLError = 0;
     mUnderlyingGLError = 0;
 
-    mActiveTexture = 0;
-
-    mStencilRefFront = 0;
-    mStencilRefBack = 0;
-    mStencilValueMaskFront = 0;
-    mStencilValueMaskBack = 0;
-    mStencilWriteMaskFront = 0;
-    mStencilWriteMaskBack = 0;
-    mDepthWriteMask = 0;
-    mStencilClearValue = 0;
-    mDepthClearValue = 0;
     mContextLostErrorSet = false;
 
     mViewportX = 0;
     mViewportY = 0;
     mViewportWidth = 0;
     mViewportHeight = 0;
 
     mDitherEnabled = 1;
@@ -207,16 +197,19 @@ ClearLinkedList(LinkedList<T>& list)
 }
 
 void
 WebGLContext::DestroyResourcesAndContext()
 {
     if (!gl)
         return;
 
+    mDefaultFB = nullptr;
+    mResolvedDefaultFB = nullptr;
+
     mBound2DTextures.Clear();
     mBoundCubeMapTextures.Clear();
     mBound3DTextures.Clear();
     mBound2DArrayTextures.Clear();
     mBoundSamplers.Clear();
     mBoundArrayBuffer = nullptr;
     mBoundCopyReadBuffer = nullptr;
     mBoundCopyWriteBuffer = nullptr;
@@ -290,16 +283,17 @@ WebGLContext::DestroyResourcesAndContext
 
     // We just got rid of everything, so the context had better
     // have been going away.
     if (GLContext::ShouldSpew()) {
         printf_stderr("--- WebGL context destroyed: %p\n", gl.get());
     }
 
     MOZ_ASSERT(gl);
+    gl->MarkDestroyed();
     mGL_OnlyClearInDestroyResourcesAndContext = nullptr;
     MOZ_ASSERT(!gl);
 }
 
 void
 WebGLContext::Invalidate()
 {
     if (!mCanvasElement)
@@ -393,28 +387,16 @@ WebGLContext::SetContextOptions(JSContex
         // aren't the same as what they were originally.
         return NS_ERROR_FAILURE;
     }
 
     mOptions = newOpts;
     return NS_OK;
 }
 
-int32_t
-WebGLContext::GetWidth() const
-{
-    return mWidth;
-}
-
-int32_t
-WebGLContext::GetHeight() const
-{
-    return mHeight;
-}
-
 /* So there are a number of points of failure here. We might fail based
  * on EGL vs. WGL, or we might fail to alloc a too-large size, or we
  * might not be able to create a context with a certain combo of context
  * creation attribs.
  *
  * We don't want to test the complete fallback matrix. (for now, at
  * least) Instead, attempt creation in this order:
  * 1. By platform API. (e.g. EGL vs. WGL)
@@ -481,64 +463,34 @@ HasAcceleratedLayers(const nsCOMPtr<nsIG
     return false;
 }
 
 static void
 PopulateCapFallbackQueue(const gl::SurfaceCaps& baseCaps,
                          std::queue<gl::SurfaceCaps>* out_fallbackCaps)
 {
     out_fallbackCaps->push(baseCaps);
-
-    // Dropping antialias drops our quality, but not our correctness.
-    // The user basically doesn't have to handle if this fails, they
-    // just get reduced quality.
-    if (baseCaps.antialias) {
-        gl::SurfaceCaps nextCaps(baseCaps);
-        nextCaps.antialias = false;
-        PopulateCapFallbackQueue(nextCaps, out_fallbackCaps);
-    }
-
-    // If we have to drop one of depth or stencil, we'd prefer to keep
-    // depth. However, the client app will need to handle if this
-    // doesn't work.
-    if (baseCaps.stencil) {
-        gl::SurfaceCaps nextCaps(baseCaps);
-        nextCaps.stencil = false;
-        PopulateCapFallbackQueue(nextCaps, out_fallbackCaps);
-    }
-
-    if (baseCaps.depth) {
-        gl::SurfaceCaps nextCaps(baseCaps);
-        nextCaps.depth = false;
-        PopulateCapFallbackQueue(nextCaps, out_fallbackCaps);
-    }
 }
 
 static gl::SurfaceCaps
 BaseCaps(const WebGLContextOptions& options, WebGLContext* webgl)
 {
     gl::SurfaceCaps baseCaps;
 
     baseCaps.color = true;
-    baseCaps.alpha = options.alpha;
-    baseCaps.antialias = options.antialias;
-    baseCaps.depth = options.depth;
+    baseCaps.alpha = true;
+    baseCaps.antialias = false;
+    baseCaps.depth = false;
+    baseCaps.stencil = false;
     baseCaps.premultAlpha = options.premultipliedAlpha;
     baseCaps.preserve = options.preserveDrawingBuffer;
-    baseCaps.stencil = options.stencil;
-
-    if (!baseCaps.alpha)
+
+    if (!baseCaps.alpha) {
         baseCaps.premultAlpha = true;
-
-    // we should really have this behind a
-    // |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
-    // for now it's just behind a pref for testing/evaluation.
-    baseCaps.bpp16 = gfxPrefs::WebGLPrefer16bpp();
-
-    // Done with baseCaps construction.
+    }
 
     if (!gfxPrefs::WebGLForceMSAA()) {
         const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
 
         nsCString blocklistId;
         if (IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_MSAA, &blocklistId)) {
             webgl->GenerateWarning("Disallowing antialiased backbuffers due"
                                    " to blacklisting.");
@@ -767,67 +719,78 @@ WebGLContext::CreateAndInitGL(bool force
 
         if (CreateAndInitGLWith(CreateGLWithDefault, baseCaps, flags, out_failReasons))
             return true;
     }
 
     //////
 
     if (tryANGLE) {
-        // Force enable alpha channel to make sure ANGLE use correct framebuffer formart
-        gl::SurfaceCaps& angleCaps = const_cast<gl::SurfaceCaps&>(baseCaps);
+        // Force enable alpha channel to make sure ANGLE use correct framebuffer format
+        auto angleCaps = baseCaps;
         angleCaps.alpha = true;
         return CreateAndInitGLWith(CreateGLWithANGLE, angleCaps, flags, out_failReasons);
     }
 
     //////
 
     out_failReasons->push_back(FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_DRIVERS",
                                              "Exhausted GL driver options."));
     return false;
 }
 
 // Fallback for resizes:
+
 bool
-WebGLContext::ResizeBackbuffer(uint32_t requestedWidth,
-                               uint32_t requestedHeight)
+WebGLContext::EnsureDefaultFB() const
 {
-    uint32_t width = requestedWidth;
-    uint32_t height = requestedHeight;
-
-    bool resized = false;
-    while (width || height) {
-      width = width ? width : 1;
-      height = height ? height : 1;
-
-      gfx::IntSize curSize(width, height);
-      if (gl->ResizeOffscreen(curSize)) {
-          resized = true;
-          break;
-      }
-
-      width /= 2;
-      height /= 2;
+    if (mDefaultFB) {
+        MOZ_ASSERT(mDefaultFB->mSize == mRequestedSize);
+        return true;
     }
 
-    if (!resized)
+    const bool depthStencil = mOptions.depth || mOptions.stencil;
+    auto attemptSize = mRequestedSize;
+
+    while (attemptSize.width || attemptSize.height) {
+        attemptSize.width = std::max(attemptSize.width, 1);
+        attemptSize.height = std::max(attemptSize.height, 1);
+
+        [&]() {
+            if (mOptions.antialias) {
+                MOZ_ASSERT(!mDefaultFB);
+                mDefaultFB = MozFramebuffer::Create(gl, attemptSize, mMsaaSamples,
+                                                    depthStencil);
+                if (mDefaultFB)
+                    return;
+                if (mOptionsFrozen)
+                    return;
+            }
+
+            MOZ_ASSERT(!mDefaultFB);
+            mDefaultFB = MozFramebuffer::Create(gl, attemptSize, 0, depthStencil);
+        }();
+
+        if (mDefaultFB)
+            break;
+
+        attemptSize.width /= 2;
+        attemptSize.height /= 2;
+    }
+
+    if (!mDefaultFB)
         return false;
 
-    mWidth = gl->OffscreenSize().width;
-    mHeight = gl->OffscreenSize().height;
-    MOZ_ASSERT((uint32_t)mWidth == width);
-    MOZ_ASSERT((uint32_t)mHeight == height);
-
-    if (width != requestedWidth ||
-        height != requestedHeight)
-    {
+    mDefaultFB_IsInvalid = true;
+
+    if (mDefaultFB->mSize != mRequestedSize) {
         GenerateWarning("Requested size %dx%d was too large, but resize"
                           " to %dx%d succeeded.",
-                        requestedWidth, requestedHeight,
-                        width, height);
+                        mRequestedSize.width, mRequestedSize.height,
+                        mDefaultFB->mSize.width, mDefaultFB->mSize.height);
     }
     return true;
 }
 
 void
 WebGLContext::ThrowEvent_WebGLContextCreationError(const nsACString& text)
 {
     RefPtr<EventTarget> target = mCanvasElement;
@@ -882,46 +845,38 @@ WebGLContext::SetDimensions(int32_t sign
     if (width == 0)
         width = 1;
 
     if (height == 0)
         height = 1;
 
     // If we already have a gl context, then we just need to resize it
     if (gl) {
-        if ((uint32_t)mWidth == width &&
-            (uint32_t)mHeight == height)
+        if (uint32_t(mRequestedSize.width) == width &&
+            uint32_t(mRequestedSize.height) == height)
         {
             return NS_OK;
         }
 
         if (IsContextLost())
             return NS_OK;
 
         // If we've already drawn, we should commit the current buffer.
         PresentScreenBuffer();
 
         if (IsContextLost()) {
             GenerateWarning("WebGL context was lost due to swap failure.");
             return NS_OK;
         }
 
-        // ResizeOffscreen scraps the current prod buffer before making a new one.
-        if (!ResizeBackbuffer(width, height)) {
-            GenerateWarning("WebGL context failed to resize.");
-            ForceLoseContext();
-            return NS_OK;
-        }
-
-        // everything's good, we're done here
+        // Kill our current default fb(s), for later lazy allocation.
+        mRequestedSize = {width, height};
+        mDefaultFB = nullptr;
+
         mResetLayer = true;
-        mBackbufferNeedsClear = true;
-
-        gl->ResetSyncCallCount("Existing WebGLContext resized.");
-
         return NS_OK;
     }
 
     nsCString failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_UNKOWN");
     auto autoTelemetry = mozilla::MakeScopeExit([&] {
         Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
                               failureId);
     });
@@ -1038,121 +993,83 @@ WebGLContext::SetDimensions(int32_t sign
             failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_DXGL_INTEROP2");
             const nsLiteralCString text("Caveat: WGL without DXGLInterop2.");
             ThrowEvent_WebGLContextCreationError(text);
             return NS_ERROR_FAILURE;
         }
 #endif
     }
 
-    if (!ResizeBackbuffer(width, height)) {
+    MOZ_ASSERT(!mDefaultFB);
+    mRequestedSize = {width, height};
+    if (!EnsureDefaultFB()) {
         failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_BACKBUFFER");
         const nsLiteralCString text("Initializing WebGL backbuffer failed.");
         ThrowEvent_WebGLContextCreationError(text);
         return NS_ERROR_FAILURE;
     }
 
     if (GLContext::ShouldSpew()) {
         printf_stderr("--- WebGL context created: %p\n", gl.get());
     }
 
+    // Update our internal stuff:
+    mOptions.antialias = bool(mDefaultFB->mSamples);
+
+    if (!mOptions.alpha) {
+        // We always have alpha.
+        mNeedsFakeNoAlpha = true;
+    }
+
+    if (mOptions.depth || mOptions.stencil) {
+        // We always have depth+stencil if we have either.
+        if (!mOptions.depth) {
+            mNeedsFakeNoDepth = true;
+        }
+        if (!mOptions.stencil) {
+            mNeedsFakeNoStencil = true;
+        }
+    }
+
     mResetLayer = true;
     mOptionsFrozen = true;
 
-    // Update our internal stuff:
-    if (gl->WorkAroundDriverBugs()) {
-        if (!mOptions.alpha && gl->Caps().alpha)
-            mNeedsFakeNoAlpha = true;
-
-        if (!mOptions.depth && gl->Caps().depth)
-            mNeedsFakeNoDepth = true;
-
-        if (!mOptions.stencil && gl->Caps().stencil)
-            mNeedsFakeNoStencil = true;
-
-#ifdef MOZ_WIDGET_COCOA
-        if (!nsCocoaFeatures::IsAtLeastVersion(10, 12) &&
-            gl->Vendor() == GLVendor::Intel)
-        {
-            mNeedsEmulatedLoneDepthStencil = true;
-        }
-#endif
-    }
-
-    // Update mOptions.
-    if (!gl->Caps().depth)
-        mOptions.depth = false;
-
-    if (!gl->Caps().stencil)
-        mOptions.stencil = false;
-
-    mOptions.antialias = gl->Caps().antialias;
-
     //////
     // Initial setup.
 
     gl->mImplicitMakeCurrent = true;
 
-    gl->fViewport(0, 0, mWidth, mHeight);
+    const auto& size = mDefaultFB->mSize;
+
     mViewportX = mViewportY = 0;
-    mViewportWidth = mWidth;
-    mViewportHeight = mHeight;
-
-    gl->fScissor(0, 0, mWidth, mHeight);
-    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+    mViewportWidth = size.width;
+    mViewportHeight = size.height;
+    gl->fViewport(mViewportX, mViewportY, mViewportWidth, mViewportHeight);
+
+    gl->fScissor(0, 0, size.width, size.height);
 
     //////
     // Check everything
 
     AssertCachedBindings();
     AssertCachedGlobalState();
 
-    MOZ_ASSERT(gl->Caps().color);
-
-    MOZ_ASSERT_IF(!mNeedsFakeNoAlpha, gl->Caps().alpha == mOptions.alpha);
-    MOZ_ASSERT_IF(mNeedsFakeNoAlpha, !mOptions.alpha && gl->Caps().alpha);
-
-    MOZ_ASSERT_IF(!mNeedsFakeNoDepth, gl->Caps().depth == mOptions.depth);
-    MOZ_ASSERT_IF(mNeedsFakeNoDepth, !mOptions.depth && gl->Caps().depth);
-
-    MOZ_ASSERT_IF(!mNeedsFakeNoStencil, gl->Caps().stencil == mOptions.stencil);
-    MOZ_ASSERT_IF(mNeedsFakeNoStencil, !mOptions.stencil && gl->Caps().stencil);
-
-    MOZ_ASSERT(gl->Caps().antialias == mOptions.antialias);
-    MOZ_ASSERT(gl->Caps().preserve == mOptions.preserveDrawingBuffer);
-
-    //////
-    // Clear immediately, because we need to present the cleared initial buffer
-    mBackbufferNeedsClear = true;
-    ClearBackbufferIfNeeded();
-
     mShouldPresent = true;
 
     //////
 
     reporter.SetSuccessful();
 
     failureId = NS_LITERAL_CSTRING("SUCCESS");
 
     gl->ResetSyncCallCount("WebGLContext Initialization");
     return NS_OK;
 }
 
 void
-WebGLContext::ClearBackbufferIfNeeded()
-{
-    if (!mBackbufferNeedsClear)
-        return;
-
-    ClearScreen();
-
-    mBackbufferNeedsClear = false;
-}
-
-void
 WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
 {
     const auto maxWebGLContexts = gfxPrefs::WebGLMaxContexts();
     auto maxWebGLContextsPerPrincipal = gfxPrefs::WebGLMaxContextsPerPrincipal();
 
     // maxWebGLContextsPerPrincipal must be less than maxWebGLContexts
     MOZ_ASSERT(maxWebGLContextsPerPrincipal <= maxWebGLContexts);
     maxWebGLContextsPerPrincipal = std::min(maxWebGLContextsPerPrincipal, maxWebGLContexts);
@@ -1398,18 +1315,18 @@ WebGLContext::InitializeCanvasRenderer(n
         // the invalidation state to indicate that the canvas is up to date.
         data.mPreTransCallback = WebGLContextUserData::PreTransactionCallback;
         data.mPreTransCallbackData = this;
         data.mDidTransCallback = WebGLContextUserData::DidTransactionCallback;
         data.mDidTransCallbackData = this;
     }
 
     data.mGLContext = gl;
-    data.mSize = nsIntSize(mWidth, mHeight);
-    data.mHasAlpha = gl->Caps().alpha;
+    data.mSize = DrawingBufferSize();
+    data.mHasAlpha = mOptions.alpha;
     data.mIsGLAlphaPremult = IsPremultAlpha() || !data.mHasAlpha;
 
     aRenderer->Initialize(data);
     aRenderer->SetDirty();
     return true;
 }
 
 layers::LayersBackend
@@ -1501,55 +1418,33 @@ WebGLContext::MozGetUnderlyingParamStrin
     default:
         return NS_ERROR_INVALID_ARG;
     }
 
     return NS_OK;
 }
 
 void
-WebGLContext::ClearScreen()
-{
-    ScopedBindFramebuffer autoFB(gl, 0);
-
-    const bool changeDrawBuffers = (mDefaultFB_DrawBuffer0 != LOCAL_GL_BACK);
-    if (changeDrawBuffers) {
-        gl->Screen()->SetDrawBuffer(LOCAL_GL_BACK);
-    }
-
-    GLbitfield bufferBits = LOCAL_GL_COLOR_BUFFER_BIT;
-    if (mOptions.depth)
-        bufferBits |= LOCAL_GL_DEPTH_BUFFER_BIT;
-    if (mOptions.stencil)
-        bufferBits |= LOCAL_GL_STENCIL_BUFFER_BIT;
-
-    ForceClearFramebufferWithDefaultValues(bufferBits, mNeedsFakeNoAlpha);
-
-    if (changeDrawBuffers) {
-        gl->Screen()->SetDrawBuffer(mDefaultFB_DrawBuffer0);
-    }
-}
-
-void
-WebGLContext::ForceClearFramebufferWithDefaultValues(GLbitfield clearBits,
-                                                     bool fakeNoAlpha)
+WebGLContext::ForceClearFramebufferWithDefaultValues(const GLbitfield clearBits,
+                                                     const bool fakeNoAlpha) const
 {
     const bool initializeColorBuffer = bool(clearBits & LOCAL_GL_COLOR_BUFFER_BIT);
     const bool initializeDepthBuffer = bool(clearBits & LOCAL_GL_DEPTH_BUFFER_BIT);
     const bool initializeStencilBuffer = bool(clearBits & LOCAL_GL_STENCIL_BUFFER_BIT);
 
     // Fun GL fact: No need to worry about the viewport here, glViewport is just
     // setting up a coordinates transformation, it doesn't affect glClear at all.
     AssertCachedGlobalState();
 
     // Prepare GL state for clearing.
     gl->fDisable(LOCAL_GL_SCISSOR_TEST);
 
     if (initializeColorBuffer) {
         gl->fColorMask(1, 1, 1, 1);
+        mDriverColorMask = 0x0f;
 
         if (fakeNoAlpha) {
             gl->fClearColor(0.0f, 0.0f, 0.0f, 1.0f);
         } else {
             gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
         }
     }
 
@@ -1578,20 +1473,16 @@ WebGLContext::ForceClearFramebufferWithD
         gl->fEnable(LOCAL_GL_SCISSOR_TEST);
 
     if (mRasterizerDiscardEnabled) {
         gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD);
     }
 
     // Restore GL state after clearing.
     if (initializeColorBuffer) {
-        gl->fColorMask(mColorWriteMask[0],
-                       mColorWriteMask[1],
-                       mColorWriteMask[2],
-                       mColorWriteMask[3]);
         gl->fClearColor(mColorClearValue[0],
                         mColorClearValue[1],
                         mColorClearValue[2],
                         mColorClearValue[3]);
     }
 
     if (initializeDepthBuffer) {
         gl->fDepthMask(mDepthWriteMask);
@@ -1611,41 +1502,91 @@ WebGLContext::OnEndOfFrame() const
    if (gfxPrefs::WebGLSpewFrameAllocs()) {
       GeneratePerfWarning("[webgl.perf.spew-frame-allocs] %" PRIu64 " data allocations this frame.",
                            mDataAllocGLCallCount);
    }
    mDataAllocGLCallCount = 0;
    gl->ResetSyncCallCount("WebGLContext PresentScreenBuffer");
 }
 
+void
+WebGLContext::BlitBackbufferToCurDriverFB() const
+{
+    if (mScissorTestEnabled) {
+        gl->fDisable(LOCAL_GL_SCISSOR_TEST);
+    }
+
+    [&]() {
+        const auto& size = mDefaultFB->mSize;
+
+        if (gl->IsSupported(GLFeature::framebuffer_blit)) {
+            gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, mDefaultFB->mFB);
+            gl->fBlitFramebuffer(0, 0, size.width, size.height,
+                                 0, 0, size.width, size.height,
+                                 LOCAL_GL_COLOR_BUFFER_BIT, LOCAL_GL_NEAREST);
+            return;
+        }
+        if (mDefaultFB->mSamples &&
+            gl->IsExtensionSupported(GLContext::APPLE_framebuffer_multisample))
+        {
+            gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, mDefaultFB->mFB);
+            gl->fResolveMultisampleFramebufferAPPLE();
+            return;
+        }
+
+        gl->BlitHelper()->DrawBlitTextureToFramebuffer(mDefaultFB->ColorTex(), size,
+                                                       size);
+    }();
+
+    if (mScissorTestEnabled) {
+        gl->fEnable(LOCAL_GL_SCISSOR_TEST);
+    }
+}
+
 // For an overview of how WebGL compositing works, see:
 // https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
 bool
 WebGLContext::PresentScreenBuffer()
 {
-    if (IsContextLost()) {
+    if (IsContextLost())
+        return false;
+
+    if (!mShouldPresent)
+        return false;
+
+    if (!ValidateAndInitFB("Present", nullptr))
         return false;
-    }
-
-    if (!mShouldPresent) {
+
+    const auto& screen = gl->Screen();
+    if (screen->Size() != mDefaultFB->mSize &&
+        !screen->Resize(mDefaultFB->mSize))
+    {
+        GenerateWarning("screen->Resize failed. Losing context.");
+        ForceLoseContext();
         return false;
     }
-    MOZ_ASSERT(!mBackbufferNeedsClear);
-
-    GLScreenBuffer* screen = gl->Screen();
-    MOZ_ASSERT(screen);
+
+    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+    BlitBackbufferToCurDriverFB();
 
     if (!screen->PublishFrame(screen->Size())) {
+        GenerateWarning("PublishFrame failed. Losing context.");
         ForceLoseContext();
         return false;
     }
 
     if (!mOptions.preserveDrawingBuffer) {
-        mBackbufferNeedsClear = true;
+        if (gl->IsSupported(gl::GLFeature::invalidate_framebuffer)) {
+            gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
+            const GLenum attachments[] = { LOCAL_GL_COLOR_ATTACHMENT0 };
+            gl->fInvalidateFramebuffer(LOCAL_GL_FRAMEBUFFER, 1, attachments);
+        }
+        mDefaultFB_IsInvalid = true;
     }
+    mResolvedDefaultFB = nullptr;
 
     mShouldPresent = false;
     OnEndOfFrame();
 
     return true;
 }
 
 // Prepare the context for capture before compositing
@@ -1742,17 +1683,17 @@ CheckContextLost(GLContext* gl, bool* co
 
     *out_isGuilty = isGuilty;
     return true;
 }
 
 bool
 WebGLContext::TryToRestoreContext()
 {
-    if (NS_FAILED(SetDimensions(mWidth, mHeight)))
+    if (NS_FAILED(SetDimensions(mRequestedSize.width, mRequestedSize.height)))
         return false;
 
     return true;
 }
 
 void
 WebGLContext::RunContextLossTimer()
 {
@@ -1954,41 +1895,28 @@ WebGLContext::ForceRestoreContext()
 }
 
 already_AddRefed<mozilla::gfx::SourceSurface>
 WebGLContext::GetSurfaceSnapshot(gfxAlphaType* const out_alphaType)
 {
     if (!gl)
         return nullptr;
 
+    if (!BindDefaultFBForRead("GetSurfaceSnapshot"))
+        return nullptr;
+
     const auto surfFormat = mOptions.alpha ? SurfaceFormat::B8G8R8A8
                                            : SurfaceFormat::B8G8R8X8;
+    const auto& size = mDefaultFB->mSize;
     RefPtr<DataSourceSurface> surf;
-    surf = Factory::CreateDataSourceSurfaceWithStride(IntSize(mWidth, mHeight),
-                                                      surfFormat,
-                                                      mWidth * 4);
+    surf = Factory::CreateDataSourceSurfaceWithStride(size, surfFormat, size.width * 4);
     if (NS_WARN_IF(!surf))
         return nullptr;
 
-    {
-        ScopedBindFramebuffer autoFB(gl, 0);
-        ClearBackbufferIfNeeded();
-
-        // Save, override, then restore glReadBuffer.
-        const GLenum readBufferMode = gl->Screen()->GetReadBufferMode();
-
-        if (readBufferMode != LOCAL_GL_BACK) {
-            gl->Screen()->SetReadBuffer(LOCAL_GL_BACK);
-        }
-        ReadPixelsIntoDataSurface(gl, surf);
-
-        if (readBufferMode != LOCAL_GL_BACK) {
-            gl->Screen()->SetReadBuffer(readBufferMode);
-        }
-    }
+    ReadPixelsIntoDataSurface(gl, surf);
 
     gfxAlphaType alphaType;
     if (!mOptions.alpha) {
         alphaType = gfxAlphaType::Opaque;
     } else if (mOptions.premultipliedAlpha) {
         alphaType = gfxAlphaType::Premult;
     } else {
         alphaType = gfxAlphaType::NonPremult;
@@ -2000,138 +1928,211 @@ WebGLContext::GetSurfaceSnapshot(gfxAlph
         // Expects Opaque or Premult
         if (alphaType == gfxAlphaType::NonPremult) {
             gfxUtils::PremultiplyDataSurface(surf, surf);
         }
     }
 
     RefPtr<DrawTarget> dt =
         Factory::CreateDrawTarget(gfxPlatform::GetPlatform()->GetSoftwareBackend(),
-                                  IntSize(mWidth, mHeight),
-                                  SurfaceFormat::B8G8R8A8);
+                                  size, SurfaceFormat::B8G8R8A8);
     if (!dt)
         return nullptr;
 
-    dt->SetTransform(Matrix::Translation(0.0, mHeight).PreScale(1.0, -1.0));
-
-    dt->DrawSurface(surf,
-                    Rect(0, 0, mWidth, mHeight),
-                    Rect(0, 0, mWidth, mHeight),
-                    DrawSurfaceOptions(),
+    dt->SetTransform(Matrix::Translation(0.0, size.height).PreScale(1.0, -1.0));
+
+    const gfx::Rect rect{0, 0, float(size.width), float(size.height)};
+    dt->DrawSurface(surf, rect, rect, DrawSurfaceOptions(),
                     DrawOptions(1.0f, CompositionOp::OP_SOURCE));
 
     return dt->Snapshot();
 }
 
 void
 WebGLContext::DidRefresh()
 {
     if (gl) {
         gl->FlushIfHeavyGLCallsSinceLastFlush();
     }
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
+gfx::IntSize
+WebGLContext::DrawingBufferSize() const
+{
+    const gfx::IntSize zeros{0, 0};
+    if (IsContextLost())
+        return zeros;
+
+    if (!EnsureDefaultFB())
+        return zeros;
+
+    return mDefaultFB->mSize;
+}
+
 bool
-WebGLContext::ValidateCurFBForRead(const char* funcName,
-                                   const webgl::FormatUsageInfo** const out_format,
-                                   uint32_t* const out_width, uint32_t* const out_height)
+WebGLContext::ValidateAndInitFB(const char* const funcName,
+                                const WebGLFramebuffer* const fb)
+{
+    if (fb)
+        return fb->ValidateAndInitAttachments(funcName);
+
+    if (!EnsureDefaultFB()) {
+        GenerateWarning("%s: Lazy resize failed. Losing context.", funcName);
+        ForceLoseContext();
+        return false;
+    }
+
+    if (mDefaultFB_IsInvalid) {
+        gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
+        const GLbitfield bits = LOCAL_GL_COLOR_BUFFER_BIT |
+                                LOCAL_GL_DEPTH_BUFFER_BIT |
+                                LOCAL_GL_STENCIL_BUFFER_BIT;
+        const bool fakeNoAlpha = !mOptions.alpha;
+        ForceClearFramebufferWithDefaultValues(bits, fakeNoAlpha);
+        mDefaultFB_IsInvalid = false;
+    }
+    return true;
+}
+
+void
+WebGLContext::DoBindFB(const WebGLFramebuffer* const fb, const GLenum target) const
+{
+    const GLenum driverFB = fb ? fb->mGLName : mDefaultFB->mFB;
+    gl->fBindFramebuffer(target, driverFB);
+}
+
+bool
+WebGLContext::BindCurFBForDraw(const char* const funcName)
 {
-    if (!mBoundReadFramebuffer) {
-        const GLenum readBufferMode = gl->Screen()->GetReadBufferMode();
-        if (readBufferMode == LOCAL_GL_NONE) {
-            ErrorInvalidOperation("%s: Can't read from backbuffer when readBuffer mode is"
-                                  " NONE.",
-                                  funcName);
+    const auto& fb = mBoundDrawFramebuffer;
+    if (!ValidateAndInitFB(funcName, fb))
+        return false;
+
+    DoBindFB(fb);
+    return true;
+}
+
+bool
+WebGLContext::BindCurFBForColorRead(const char* const funcName,
+                                    const webgl::FormatUsageInfo** const out_format,
+                                    uint32_t* const out_width,
+                                    uint32_t* const out_height)
+{
+    const auto& fb = mBoundReadFramebuffer;
+
+    if (fb) {
+        if (!ValidateAndInitFB(funcName, fb))
+            return false;
+        if (!fb->ValidateForColorRead(funcName, out_format, out_width, out_height))
+            return false;
+
+        gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb->mGLName);
+        return true;
+    }
+
+    if (!BindDefaultFBForRead(funcName))
+        return false;
+
+    if (mDefaultFB_ReadBuffer == LOCAL_GL_NONE) {
+        ErrorInvalidOperation("%s: Can't read from backbuffer when readBuffer mode is"
+                              " NONE.",
+                              funcName);
+        return false;
+    }
+
+    auto effFormat = mOptions.alpha ? webgl::EffectiveFormat::RGBA8
+                                    : webgl::EffectiveFormat::RGB8;
+
+    *out_format = mFormatUsage->GetUsage(effFormat);
+    MOZ_ASSERT(*out_format);
+
+    *out_width = mDefaultFB->mSize.width;
+    *out_height = mDefaultFB->mSize.height;
+    return true;
+}
+
+bool
+WebGLContext::BindDefaultFBForRead(const char* const funcName)
+{
+    if (!ValidateAndInitFB(funcName, nullptr))
+        return false;
+
+    if (!mDefaultFB->mSamples) {
+        gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
+        return true;
+    }
+
+    if (!mResolvedDefaultFB) {
+        mResolvedDefaultFB = MozFramebuffer::Create(gl, mDefaultFB->mSize, 0, false);
+        if (!mResolvedDefaultFB) {
+            gfxCriticalNote << funcName << ": Failed to create mResolvedDefaultFB.";
             return false;
         }
-
-        ClearBackbufferIfNeeded();
-
-        // FIXME - here we're assuming that the default framebuffer is backed by
-        // UNSIGNED_BYTE that might not always be true, say if we had a 16bpp default
-        // framebuffer.
-        auto effFormat = mOptions.alpha ? webgl::EffectiveFormat::RGBA8
-                                        : webgl::EffectiveFormat::RGB8;
-
-        *out_format = mFormatUsage->GetUsage(effFormat);
-        MOZ_ASSERT(*out_format);
-
-        *out_width = mWidth;
-        *out_height = mHeight;
-        return true;
     }
 
-    return mBoundReadFramebuffer->ValidateForRead(funcName, out_format, out_width,
-                                                  out_height);
+    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB);
+    BlitBackbufferToCurDriverFB();
+
+    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB);
+    return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
-WebGLContext::ScopedDrawCallWrapper::ScopedDrawCallWrapper(WebGLContext& webgl)
+ScopedDrawCallWrapper::ScopedDrawCallWrapper(WebGLContext& webgl)
     : mWebGL(webgl)
-    , mFakeNoAlpha(ShouldFakeNoAlpha(webgl))
-    , mFakeNoDepth(ShouldFakeNoDepth(webgl))
-    , mFakeNoStencil(ShouldFakeNoStencil(webgl))
 {
+    uint8_t driverColorMask = mWebGL.mColorWriteMask;
+    bool driverDepthTest    = mWebGL.mDepthTestEnabled;
+    bool driverStencilTest  = mWebGL.mStencilTestEnabled;
     if (!mWebGL.mBoundDrawFramebuffer) {
-        mWebGL.ClearBackbufferIfNeeded();
+        if (mWebGL.mDefaultFB_DrawBuffer0 == LOCAL_GL_NONE) {
+            driverColorMask = 0; // Is this well-optimized enough for depth-first
+                                 // rendering?
+        } else {
+            driverColorMask &= ~(uint8_t(mWebGL.mNeedsFakeNoAlpha) << 3);
+        }
+        driverDepthTest   &= !mWebGL.mNeedsFakeNoDepth;
+        driverStencilTest &= !mWebGL.mNeedsFakeNoStencil;
     }
 
-    if (mFakeNoAlpha) {
-        mWebGL.gl->fColorMask(mWebGL.mColorWriteMask[0],
-                              mWebGL.mColorWriteMask[1],
-                              mWebGL.mColorWriteMask[2],
-                              false);
+    const auto& gl = mWebGL.gl;
+    if (mWebGL.mDriverColorMask != driverColorMask) {
+        mWebGL.mDriverColorMask = driverColorMask;
+        gl->fColorMask(bool(mWebGL.mDriverColorMask & (1 << 0)),
+                       bool(mWebGL.mDriverColorMask & (1 << 1)),
+                       bool(mWebGL.mDriverColorMask & (1 << 2)),
+                       bool(mWebGL.mDriverColorMask & (1 << 3)));
     }
-    if (mFakeNoDepth) {
-        mWebGL.gl->fDisable(LOCAL_GL_DEPTH_TEST);
+    if (mWebGL.mDriverDepthTest != driverDepthTest) {
+        // "When disabled, the depth comparison and subsequent possible updates to the
+        //  depth buffer value are bypassed and the fragment is passed to the next
+        //  operation." [GLES 3.0.5, p177]
+        mWebGL.mDriverDepthTest = driverDepthTest;
+        gl->SetEnabled(LOCAL_GL_DEPTH_TEST, mWebGL.mDriverDepthTest);
     }
-    if (mFakeNoStencil) {
-        mWebGL.gl->fDisable(LOCAL_GL_STENCIL_TEST);
+    if (mWebGL.mDriverStencilTest != driverStencilTest) {
+        // "When disabled, the stencil test and associated modifications are not made, and
+        //  the fragment is always passed." [GLES 3.0.5, p175]
+        mWebGL.mDriverStencilTest = driverStencilTest;
+        gl->SetEnabled(LOCAL_GL_STENCIL_TEST, mWebGL.mDriverStencilTest);
     }
 }
 
-WebGLContext::ScopedDrawCallWrapper::~ScopedDrawCallWrapper()
+ScopedDrawCallWrapper::~ScopedDrawCallWrapper()
 {
-    if (mFakeNoAlpha) {
-        mWebGL.gl->fColorMask(mWebGL.mColorWriteMask[0],
-                              mWebGL.mColorWriteMask[1],
-                              mWebGL.mColorWriteMask[2],
-                              mWebGL.mColorWriteMask[3]);
-    }
-    if (mFakeNoDepth) {
-        mWebGL.gl->fEnable(LOCAL_GL_DEPTH_TEST);
-    }
-    if (mFakeNoStencil) {
-        MOZ_ASSERT(mWebGL.mStencilTestEnabled);
-        mWebGL.gl->fEnable(LOCAL_GL_STENCIL_TEST);
-    }
-
-    if (!mWebGL.mBoundDrawFramebuffer) {
-        mWebGL.Invalidate();
-        mWebGL.mShouldPresent = true;
-    }
-}
-
-/*static*/ bool
-WebGLContext::ScopedDrawCallWrapper::HasDepthButNoStencil(const WebGLFramebuffer* fb)
-{
-    const auto& depth = fb->DepthAttachment();
-    const auto& stencil = fb->StencilAttachment();
-    return depth.IsDefined() && !stencil.IsDefined();
-}
-
-////
-
-void
-WebGLContext::OnBeforeReadCall()
-{
-    if (!mBoundReadFramebuffer) {
-        ClearBackbufferIfNeeded();
-    }
+    if (mWebGL.mBoundDrawFramebuffer)
+        return;
+
+    mWebGL.mResolvedDefaultFB = nullptr;
+
+    mWebGL.Invalidate();
+    mWebGL.mShouldPresent = true;
 }
 
 ////////////////////////////////////////
 
 IndexedBufferBinding::IndexedBufferBinding()
     : mRangeStart(0)
     , mRangeSize(0)
 { }
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -62,16 +62,17 @@ class nsIDocShell;
 #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 ScopedDrawCallWrapper;
 class ScopedResolveTexturesForDraw;
 class ScopedUnpackReset;
 class WebGLActiveInfo;
 class WebGLBuffer;
 class WebGLExtensionBase;
 class WebGLFramebuffer;
 class WebGLProgram;
 class WebGLQuery;
@@ -93,16 +94,20 @@ struct WebGLContextAttributes;
 template<typename> struct Nullable;
 } // namespace dom
 
 namespace gfx {
 class SourceSurface;
 class VRLayerChild;
 } // namespace gfx
 
+namespace gl {
+class MozFramebuffer;
+} // namespace gl
+
 namespace webgl {
 struct LinkedProgramInfo;
 class ShaderValidator;
 class TexUnpackBlob;
 struct UniformInfo;
 struct UniformBlockInfo;
 } // namespace webgl
 
@@ -268,19 +273,19 @@ struct TexImageSourceAdapter final : pub
 
 ////////////////////////////////////////////////////////////////////////////////
 
 class WebGLContext
     : public nsIDOMWebGLRenderingContext
     , public nsICanvasRenderingContextInternal
     , public nsSupportsWeakReference
     , public WebGLContextUnchecked
-    , public WebGLRectangleObject
     , public nsWrapperCache
 {
+    friend class ScopedDrawCallWrapper;
     friend class ScopedDrawHelper;
     friend class ScopedDrawWithTransformFeedback;
     friend class ScopedFBRebinder;
     friend class WebGL2Context;
     friend class WebGLContextUserData;
     friend class WebGLExtensionCompressedTextureASTC;
     friend class WebGLExtensionCompressedTextureATC;
     friend class WebGLExtensionCompressedTextureES3;
@@ -332,18 +337,18 @@ public:
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override = 0;
 
     NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT
 
     virtual void OnVisibilityChange() override;
     virtual void OnMemoryPressure() override;
 
     // nsICanvasRenderingContextInternal
-    virtual int32_t GetWidth() const override;
-    virtual int32_t GetHeight() const override;
+    virtual int32_t GetWidth() const override { return DrawingBufferWidth(); }
+    virtual int32_t GetHeight() const override { return DrawingBufferHeight(); }
 
     NS_IMETHOD SetDimensions(int32_t width, int32_t height) override;
     NS_IMETHOD InitializeWithDrawTarget(nsIDocShell*,
                                         NotNull<gfx::DrawTarget*>) override
     {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
 
@@ -376,32 +381,32 @@ public:
      * before it is destroyed.
      */
     virtual void DidRefresh() override;
 
     NS_IMETHOD Redraw(const gfxRect&) override {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
 
-    void SynthesizeGLError(GLenum err);
-    void SynthesizeGLError(GLenum err, const char* fmt, ...) MOZ_FORMAT_PRINTF(3, 4);
+    void SynthesizeGLError(GLenum err) const;
+    void SynthesizeGLError(GLenum err, const char* fmt, ...) const MOZ_FORMAT_PRINTF(3, 4);
 
-    void ErrorInvalidEnum(const char* fmt = 0, ...) MOZ_FORMAT_PRINTF(2, 3);
-    void ErrorInvalidOperation(const char* fmt = 0, ...) MOZ_FORMAT_PRINTF(2, 3);
-    void ErrorInvalidValue(const char* fmt = 0, ...) MOZ_FORMAT_PRINTF(2, 3);
-    void ErrorInvalidFramebufferOperation(const char* fmt = 0, ...) MOZ_FORMAT_PRINTF(2, 3);
-    void ErrorInvalidEnumInfo(const char* info, GLenum enumValue);
+    void ErrorInvalidEnum(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
+    void ErrorInvalidOperation(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
+    void ErrorInvalidValue(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
+    void ErrorInvalidFramebufferOperation(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
+    void ErrorInvalidEnumInfo(const char* info, GLenum enumValue) const;
     void ErrorInvalidEnumInfo(const char* info, const char* funcName,
-                              GLenum enumValue);
-    void ErrorOutOfMemory(const char* fmt = 0, ...) MOZ_FORMAT_PRINTF(2, 3);
-    void ErrorImplementationBug(const char* fmt = 0, ...) MOZ_FORMAT_PRINTF(2, 3);
+                              GLenum enumValue) const;
+    void ErrorOutOfMemory(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
+    void ErrorImplementationBug(const char* fmt = 0, ...) const MOZ_FORMAT_PRINTF(2, 3);
 
-    void ErrorInvalidEnumArg(const char* funcName, const char* argName, GLenum val);
+    void ErrorInvalidEnumArg(const char* funcName, const char* argName, GLenum val) const;
 
-    const char* ErrorName(GLenum error);
+    static const char* ErrorName(GLenum error);
 
     /**
      * Return displayable name for GLenum.
      * This version is like gl::GLenumToStr but with out the GL_ prefix to
      * keep consistency with how errors are reported from WebGL.
      * Returns hex formatted version of glenum if glenum is unknown.
      */
     static void EnumName(GLenum val, nsCString* out_name);
@@ -472,41 +477,39 @@ public:
     // a number that increments every time we have an event that causes
     // all context resources to be lost.
     uint32_t Generation() const { return mGeneration.value(); }
 
     // This is similar to GLContext::ClearSafely, but tries to minimize the
     // amount of work it does.
     // It only clears the buffers we specify, and can reset its state without
     // first having to query anything, as WebGL knows its state at all times.
-    void ForceClearFramebufferWithDefaultValues(GLbitfield bufferBits, bool fakeNoAlpha);
-
-    // Calls ForceClearFramebufferWithDefaultValues() for the Context's 'screen'.
-    void ClearScreen();
-    void ClearBackbufferIfNeeded();
+    void ForceClearFramebufferWithDefaultValues(GLbitfield bufferBits,
+                                                bool fakeNoAlpha) const;
 
     void RunContextLossTimer();
     void UpdateContextLossStatus();
     void EnqueueUpdateContextLossStatus();
 
     bool TryToRestoreContext();
 
-    void AssertCachedBindings();
-    void AssertCachedGlobalState();
+    void AssertCachedBindings() const;
+    void AssertCachedGlobalState() const;
 
     dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
     nsIDocument* GetOwnerDoc() const;
 
     // WebIDL WebGLRenderingContext API
     void Commit();
     void GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval);
-    GLsizei DrawingBufferWidth() const { return IsContextLost() ? 0 : mWidth; }
-    GLsizei DrawingBufferHeight() const {
-        return IsContextLost() ? 0 : mHeight;
-    }
+private:
+    gfx::IntSize DrawingBufferSize() const;
+public:
+    GLsizei DrawingBufferWidth() const { return DrawingBufferSize().width; }
+    GLsizei DrawingBufferHeight() const { return DrawingBufferSize().height; }
 
     layers::LayersBackend GetCompositorBackendType() const;
 
     void
     GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval);
 
     bool IsContextLost() const { return mContextStatus != ContextNotLost; }
     void GetSupportedExtensions(dom::Nullable< nsTArray<nsString> >& retval,
@@ -981,17 +984,17 @@ public:
                            JS::MutableHandleValue retval, const char* funcName = nullptr);
 
 
 // -----------------------------------------------------------------------------
 // State and State Requests (WebGLContextState.cpp)
 public:
     void Disable(GLenum cap);
     void Enable(GLenum cap);
-    bool GetStencilBits(GLint* const out_stencilBits);
+    bool GetStencilBits(GLint* const out_stencilBits) const;
     bool GetChannelBits(const char* funcName, GLenum pname, GLint* const out_val);
     virtual JS::Value GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv);
 
     void GetParameter(JSContext* cx, GLenum pname,
                       JS::MutableHandle<JS::Value> retval, ErrorResult& rv)
     {
         retval.set(GetParameter(cx, pname, rv));
     }
@@ -1438,30 +1441,30 @@ protected:
     bool mResetLayer;
     bool mOptionsFrozen;
     bool mDisableExtensions;
     bool mIsMesa;
     bool mLoseContextOnMemoryPressure;
     bool mCanLoseContextInForeground;
     bool mRestoreWhenVisible;
     bool mShouldPresent;
-    bool mBackbufferNeedsClear;
     bool mDisableFragHighP;
 
     template<typename WebGLObjectType>
     void DeleteWebGLObjectsArray(nsTArray<WebGLObjectType>& array);
 
     GLuint mActiveTexture;
     GLenum mDefaultFB_DrawBuffer0;
+    GLenum mDefaultFB_ReadBuffer;
 
     // glGetError sources:
     bool mEmitContextLostErrorOnce;
-    GLenum mWebGLError;
-    GLenum mUnderlyingGLError;
-    GLenum GetAndFlushUnderlyingGLErrors();
+    mutable GLenum mWebGLError;
+    mutable GLenum mUnderlyingGLError;
+    GLenum GetAndFlushUnderlyingGLErrors() const;
 
     bool mBypassShaderValidation;
 
     webgl::ShaderValidator* CreateShaderValidator(GLenum shaderType) const;
 
     // some GL constants
     uint32_t mGLMaxTextureUnits;
 
@@ -1580,18 +1583,16 @@ public:
         { }
     };
 protected:
     bool InitWebGL2(FailureReason* const out_failReason);
 
     bool CreateAndInitGL(bool forceEnabled,
                          std::vector<FailureReason>* const out_failReasons);
 
-    bool ResizeBackbuffer(uint32_t width, uint32_t height);
-
     typedef already_AddRefed<gl::GLContext> FnCreateGL_T(const gl::SurfaceCaps& caps,
                                                          gl::CreateContextFlags flags,
                                                          WebGLContext* webgl,
                                                          std::vector<FailureReason>* const out_failReasons);
 
     bool CreateAndInitGLWith(FnCreateGL_T fnCreateGL, const gl::SurfaceCaps& baseCaps,
                              gl::CreateContextFlags flags,
                              std::vector<FailureReason>* const out_failReasons);
@@ -1657,20 +1658,16 @@ protected:
                                       uint32_t byteLength,
                                       WebGLTexImageFunc func,
                                       WebGLTexDimensions dims);
 
     bool ValidateUniformLocationForProgram(WebGLUniformLocation* location,
                                            WebGLProgram* program,
                                            const char* funcName);
 
-    bool ValidateCurFBForRead(const char* funcName,
-                              const webgl::FormatUsageInfo** const out_format,
-                              uint32_t* const out_width, uint32_t* const out_height);
-
     bool HasDrawBuffers() const {
         return IsWebGL2() ||
                IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers);
     }
 
     WebGLRefPtr<WebGLBuffer>* ValidateBufferSlot(const char* funcName, GLenum target);
 public:
     WebGLBuffer* ValidateBufferSelection(const char* funcName, GLenum target);
@@ -1933,17 +1930,17 @@ protected:
     JSObject* GetVertexAttribUint32Array(JSContext* cx, GLuint index);
 
     GLint mStencilRefFront;
     GLint mStencilRefBack;
     GLuint mStencilValueMaskFront;
     GLuint mStencilValueMaskBack;
     GLuint mStencilWriteMaskFront;
     GLuint mStencilWriteMaskBack;
-    realGLboolean mColorWriteMask[4];
+    uint8_t mColorWriteMask; // bitmask
     realGLboolean mDepthWriteMask;
     GLfloat mColorClearValue[4];
     GLint mStencilClearValue;
     GLfloat mDepthClearValue;
 
     GLint mViewportX;
     GLint mViewportY;
     GLsizei mViewportWidth;
@@ -1957,96 +1954,66 @@ protected:
     bool mLastLossWasSimulated;
     ContextStatus mContextStatus;
     bool mContextLostErrorSet;
 
     // Used for some hardware (particularly Tegra 2 and 4) that likes to
     // be Flushed while doing hundreds of draw calls.
     int mDrawCallsSinceLastFlush;
 
-    int mAlreadyGeneratedWarnings;
+    mutable int mAlreadyGeneratedWarnings;
     int mMaxWarnings;
     bool mAlreadyWarnedAboutFakeVertexAttrib0;
 
     bool ShouldGenerateWarnings() const;
 
     bool ShouldGeneratePerfWarnings() const {
         return mNumPerfWarnings < mMaxPerfWarnings;
     }
 
     uint64_t mLastUseIndex;
 
     bool mNeedsFakeNoAlpha;
     bool mNeedsFakeNoDepth;
     bool mNeedsFakeNoStencil;
-    bool mNeedsEmulatedLoneDepthStencil;
+
+    mutable uint8_t mDriverColorMask;
+    bool mDriverDepthTest;
+    bool mDriverStencilTest;
 
     bool mNeedsIndexValidation;
 
     const bool mAllowFBInvalidation;
 
     bool Has64BitTimestamps() const;
 
-    struct ScopedDrawCallWrapper final {
-        WebGLContext& mWebGL;
-        const bool mFakeNoAlpha;
-        const bool mFakeNoDepth;
-        const bool mFakeNoStencil;
+    // --
 
-        static bool ShouldFakeNoAlpha(WebGLContext& webgl) {
-            // We should only be doing this if we're about to draw to the backbuffer, but
-            // the backbuffer needs to have this fake-no-alpha workaround.
-            return !webgl.mBoundDrawFramebuffer &&
-                   webgl.mNeedsFakeNoAlpha &&
-                   webgl.mColorWriteMask[3] != false;
-        }
+    const uint8_t mMsaaSamples;
+    gfx::IntSize mRequestedSize;
+    mutable UniquePtr<gl::MozFramebuffer> mDefaultFB;
+    mutable bool mDefaultFB_IsInvalid;
+    mutable UniquePtr<gl::MozFramebuffer> mResolvedDefaultFB;
 
-        static bool ShouldFakeNoDepth(WebGLContext& webgl) {
-            // We should only be doing this if we're about to draw to the backbuffer.
-            return !webgl.mBoundDrawFramebuffer &&
-                   webgl.mNeedsFakeNoDepth &&
-                   webgl.mDepthTestEnabled;
-        }
-
-        static bool HasDepthButNoStencil(const WebGLFramebuffer* fb);
-
-        static bool ShouldFakeNoStencil(WebGLContext& webgl) {
-            if (!webgl.mStencilTestEnabled)
-                return false;
+    // --
 
-            if (!webgl.mBoundDrawFramebuffer) {
-                if (webgl.mNeedsFakeNoStencil)
-                    return true;
-
-                if (webgl.mNeedsEmulatedLoneDepthStencil &&
-                    webgl.mOptions.depth && !webgl.mOptions.stencil)
-                {
-                    return true;
-                }
-
-                return false;
-            }
+    bool EnsureDefaultFB() const;
+    bool ValidateAndInitFB(const char* funcName, const WebGLFramebuffer* fb);
+    void DoBindFB(const WebGLFramebuffer* fb, GLenum target = LOCAL_GL_FRAMEBUFFER) const;
 
-            if (webgl.mNeedsEmulatedLoneDepthStencil &&
-                HasDepthButNoStencil(webgl.mBoundDrawFramebuffer))
-            {
-                return true;
-            }
-
-            return false;
-        }
+    bool BindCurFBForDraw(const char* funcName);
+    bool BindCurFBForColorRead(const char* funcName,
+                               const webgl::FormatUsageInfo** out_format,
+                               uint32_t* out_width, uint32_t* out_height);
+    void BlitBackbufferToCurDriverFB() const;
+    bool BindDefaultFBForRead(const char* funcName);
 
-        ////
+    // --
 
-        explicit ScopedDrawCallWrapper(WebGLContext& webgl);
-        ~ScopedDrawCallWrapper();
-    };
-
-    void OnBeforeReadCall();
-
+public:
     void LoseOldestWebGLContextIfLimitExceeded();
     void UpdateLastUseIndex();
 
     template <typename WebGLObjectType>
     JS::Value WebGLObjectAsJSValue(JSContext* cx, const WebGLObjectType*,
                                    ErrorResult& rv) const;
     template <typename WebGLObjectType>
     JSObject* WebGLObjectAsJSObject(JSContext* cx, const WebGLObjectType*,
@@ -2058,18 +2025,18 @@ protected:
     // these objects at high frequency. Having WebGLContext's hold one such object seems fine,
     // because WebGLContext objects only go away during GC, which shouldn't happen too frequently.
     // If in the future GC becomes much more frequent, we may have to revisit then (maybe use a timer).
     ForceDiscreteGPUHelperCGL mForceDiscreteGPUHelper;
 #endif
 
 public:
     // console logging helpers
-    void GenerateWarning(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
-    void GenerateWarning(const char* fmt, va_list ap) MOZ_FORMAT_PRINTF(2, 0);
+    void GenerateWarning(const char* fmt, ...) const MOZ_FORMAT_PRINTF(2, 3);
+    void GenerateWarning(const char* fmt, va_list ap) const MOZ_FORMAT_PRINTF(2, 0);
 
     void GeneratePerfWarning(const char* fmt, ...) const MOZ_FORMAT_PRINTF(2, 3);
 
 public:
     UniquePtr<webgl::FormatUsageAuthority> mFormatUsage;
 
     virtual UniquePtr<webgl::FormatUsageAuthority>
     CreateFormatUsage(gl::GLContext* gl) const = 0;
@@ -2185,16 +2152,27 @@ Intersect(int32_t srcSize, int32_t read0
           int32_t* out_intWrite0, int32_t* out_intSize);
 
 uint64_t
 AvailGroups(uint64_t totalAvailItems, uint64_t firstItemOffset, uint32_t groupSize,
             uint32_t groupStride);
 
 ////
 
+class ScopedDrawCallWrapper final
+{
+public:
+    WebGLContext& mWebGL;
+
+    explicit ScopedDrawCallWrapper(WebGLContext& webgl);
+    ~ScopedDrawCallWrapper();
+};
+
+////
+
 void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
                             const std::vector<IndexedBufferBinding>& field,
                             const char* name, uint32_t flags = 0);
 
 void
 ImplCycleCollectionUnlink(std::vector<IndexedBufferBinding>& field);
 
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLContext.h"
 
 #include "GeckoProfiler.h"
+#include "gfx/gl/MozFramebuffer.h"
 #include "GLContext.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "nsPrintfCString.h"
 #include "WebGLBuffer.h"
 #include "WebGLContextUtils.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
@@ -94,33 +95,20 @@ WebGLTexture::IsFeedback(WebGLContext* w
 
 ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw(WebGLContext* webgl,
                                                            const char* funcName,
                                                            bool* const out_error)
     : mWebGL(webgl)
 {
     MOZ_ASSERT(mWebGL->gl->IsCurrent());
 
-    if (!mWebGL->mActiveProgramLinkInfo) {
-        mWebGL->ErrorInvalidOperation("%s: The current program is not linked.", funcName);
-        *out_error = true;
-        return;
-    }
-
     const std::vector<const WebGLFBAttachPoint*>* attachList = nullptr;
     const auto& fb = mWebGL->mBoundDrawFramebuffer;
     if (fb) {
-        if (!fb->ValidateAndInitAttachments(funcName)) {
-            *out_error = true;
-            return;
-        }
-
         attachList = &(fb->ResolvedCompleteData()->texDrawBuffers);
-    } else {
-        webgl->ClearBackbufferIfNeeded();
     }
 
     MOZ_ASSERT(mWebGL->mActiveProgramLinkInfo);
     const auto& uniformSamplers = mWebGL->mActiveProgramLinkInfo->uniformSamplers;
     for (const auto& uniform : uniformSamplers) {
         const auto& texList = *(uniform->mSamplerTexList);
 
         for (const auto& texUnit : uniform->mSamplerValues) {
@@ -250,42 +238,41 @@ public:
     ScopedDrawHelper(WebGLContext* const webgl, const char* const funcName,
                      const GLenum mode, const Maybe<uint32_t>& lastRequiredVertex,
                      const uint32_t instanceCount, bool* const out_error)
         : mWebGL(webgl)
         , mDidFake(false)
     {
         MOZ_ASSERT(mWebGL->gl->IsCurrent());
 
+        if (!mWebGL->BindCurFBForDraw(funcName)) {
+            *out_error = true;
+            return;
+        }
+
         if (!mWebGL->ValidateDrawModeEnum(mode, funcName)) {
             *out_error = true;
             return;
         }
 
         if (!mWebGL->ValidateStencilParamsForDrawCall()) {
             *out_error = true;
             return;
         }
 
-        ////
-
-        if (mWebGL->mBoundDrawFramebuffer) {
-            if (!mWebGL->mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName)) {
-                *out_error = true;
-                return;
-            }
-        } else {
-            mWebGL->ClearBackbufferIfNeeded();
+        if (!mWebGL->mActiveProgramLinkInfo) {
+            mWebGL->ErrorInvalidOperation("%s: The current program is not linked.", funcName);
+            *out_error = true;
+            return;
         }
+        const auto& linkInfo = mWebGL->mActiveProgramLinkInfo;
 
         ////
         // Check UBO sizes.
 
-        const auto& linkInfo = mWebGL->mActiveProgramLinkInfo;
-
         for (const auto& cur : linkInfo->uniformBlocks) {
             const auto& dataSize = cur->mDataSize;
             const auto& binding = cur->mBinding;
             if (!binding) {
                 mWebGL->ErrorInvalidOperation("%s: Buffer for uniform block is null.",
                                               funcName);
                 *out_error = true;
                 return;
@@ -520,30 +507,30 @@ WebGLContext::DrawArrays_check(const cha
 void
 WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei vertCount,
                                   GLsizei instanceCount, const char* const funcName)
 {
     AUTO_PROFILER_LABEL("WebGLContext::DrawArraysInstanced", GRAPHICS);
     if (IsContextLost())
         return;
 
-    bool error = false;
-    ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
-    if (error)
-        return;
-
     Maybe<uint32_t> lastVert;
     if (!DrawArrays_check(funcName, first, vertCount, instanceCount, &lastVert))
         return;
 
+    bool error = false;
     const ScopedDrawHelper scopedHelper(this, funcName, mode, lastVert, instanceCount,
                                         &error);
     if (error)
         return;
 
+    const ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
+    if (error)
+        return;
+
     const ScopedDrawWithTransformFeedback scopedTF(this, funcName, mode, vertCount,
                                                    instanceCount, &error);
     if (error)
         return;
 
     {
         ScopedDrawCallWrapper wrapper(*this);
         if (vertCount && instanceCount) {
@@ -676,33 +663,33 @@ void
 WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei indexCount, GLenum type,
                                     WebGLintptr byteOffset, GLsizei instanceCount,
                                     const char* const funcName)
 {
     AUTO_PROFILER_LABEL("WebGLContext::DrawElementsInstanced", GRAPHICS);
     if (IsContextLost())
         return;
 
-    bool error = false;
-    ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
-    if (error)
-        return;
-
     Maybe<uint32_t> lastVert;
     if (!DrawElements_check(funcName, indexCount, type, byteOffset, instanceCount,
                             &lastVert))
     {
         return;
     }
 
+    bool error = false;
     const ScopedDrawHelper scopedHelper(this, funcName, mode, lastVert, instanceCount,
                                         &error);
     if (error)
         return;
 
+    const ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error);
+    if (error)
+        return;
+
     {
         ScopedDrawCallWrapper wrapper(*this);
         {
             UniquePtr<gl::GLContext::LocalErrorScope> errorScope;
 
             if (gl->IsANGLE()) {
                 errorScope.reset(new gl::GLContext::LocalErrorScope(*gl));
             }
@@ -754,18 +741,18 @@ WebGLContext::Draw_cleanup(const char* f
         const auto& drawBuffers = mBoundDrawFramebuffer->ColorDrawBuffers();
         for (const auto& cur : drawBuffers) {
             if (!cur->IsDefined())
                 continue;
             cur->Size(&destWidth, &destHeight);
             break;
         }
     } else {
-        destWidth = mWidth;
-        destHeight = mHeight;
+        destWidth = mDefaultFB->mSize.width;
+        destHeight = mDefaultFB->mSize.height;
     }
 
     if (mViewportWidth > int32_t(destWidth) ||
         mViewportHeight > int32_t(destHeight))
     {
         if (!mAlreadyWarnedAboutViewportLargerThanDest) {
             GenerateWarning("%s: Drawing to a destination rect smaller than the viewport"
                             " rect. (This warning will only be given once)",
--- a/dom/canvas/WebGLContextFramebufferOperations.cpp
+++ b/dom/canvas/WebGLContextFramebufferOperations.cpp
@@ -25,20 +25,17 @@ WebGLContext::Clear(GLbitfield mask)
         return ErrorInvalidValue("%s: invalid mask bits", funcName);
 
     if (mask == 0) {
         GenerateWarning("Calling gl.clear(0) has no effect.");
     } else if (mRasterizerDiscardEnabled) {
         GenerateWarning("Calling gl.clear() with RASTERIZER_DISCARD enabled has no effects.");
     }
 
-    if (mBoundDrawFramebuffer) {
-        if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName))
-            return;
-
+    if (mask & LOCAL_GL_COLOR_BUFFER_BIT && mBoundDrawFramebuffer) {
         if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
             for (const auto& cur : mBoundDrawFramebuffer->ColorDrawBuffers()) {
                 if (!cur->IsDefined())
                     continue;
 
                 switch (cur->Format()->format->componentType) {
                 case webgl::ComponentType::Float:
                 case webgl::ComponentType::NormInt:
@@ -50,18 +47,31 @@ WebGLContext::Clear(GLbitfield mask)
                                           " or fixed-point. (normalized (u)ints)",
                                           funcName);
                     return;
                 }
             }
         }
     }
 
-    ScopedDrawCallWrapper wrapper(*this);
-    gl->fClear(mask);
+    if (!BindCurFBForDraw(funcName))
+        return;
+
+    auto driverMask = mask;
+    if (!mBoundDrawFramebuffer) {
+        if (mNeedsFakeNoDepth) {
+            driverMask &= ~LOCAL_GL_DEPTH_BUFFER_BIT;
+        }
+        if (mNeedsFakeNoStencil) {
+            driverMask &= ~LOCAL_GL_STENCIL_BUFFER_BIT;
+        }
+    }
+
+    const ScopedDrawCallWrapper wrapper(*this);
+    gl->fClear(driverMask);
 }
 
 static GLfloat
 GLClampFloat(GLfloat val)
 {
     if (val < 0.0)
         return 0.0;
 
@@ -116,21 +126,21 @@ WebGLContext::ClearStencil(GLint v)
 }
 
 void
 WebGLContext::ColorMask(WebGLboolean r, WebGLboolean g, WebGLboolean b, WebGLboolean a)
 {
     if (IsContextLost())
         return;
 
-    mColorWriteMask[0] = r;
-    mColorWriteMask[1] = g;
-    mColorWriteMask[2] = b;
-    mColorWriteMask[3] = a;
     gl->fColorMask(r, g, b, a);
+    mColorWriteMask = uint8_t(bool(r)) << 0 |
+                      uint8_t(bool(g)) << 1 |
+                      uint8_t(bool(b)) << 2 |
+                      uint8_t(bool(a)) << 3;
 }
 
 void
 WebGLContext::DepthMask(WebGLboolean b)
 {
     if (IsContextLost())
         return;
 
@@ -170,17 +180,17 @@ WebGLContext::DrawBuffers(const dom::Seq
     default:
         ErrorInvalidOperation("%s: For the default framebuffer, `buffers[0]` must be"
                               " BACK or NONE.",
                               funcName);
         return;
     }
 
     mDefaultFB_DrawBuffer0 = buffers[0];
-    gl->Screen()->SetDrawBuffer(buffers[0]);
+    // Don't actually set it.
 }
 
 void
 WebGLContext::StencilMask(GLuint mask)
 {
     if (IsContextLost())
         return;
 
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1556,17 +1556,17 @@ WebGLContext::ReadPixelsImpl(GLint x, GL
     const uint32_t width(rawWidth);
     const uint32_t height(rawHeight);
 
     //////
 
     const webgl::FormatUsageInfo* srcFormat;
     uint32_t srcWidth;
     uint32_t srcHeight;
-    if (!ValidateCurFBForRead("readPixels", &srcFormat, &srcWidth, &srcHeight))
+    if (!BindCurFBForColorRead("readPixels", &srcFormat, &srcWidth, &srcHeight))
         return;
 
     //////
 
     const webgl::PackingInfo pi = {packFormat, packType};
     if (!ValidateReadPixelsFormatAndType(srcFormat, pi, gl, this))
         return;
 
@@ -1601,18 +1601,16 @@ WebGLContext::ReadPixelsImpl(GLint x, GL
     {
         ErrorOutOfMemory("readPixels: Bad subrect selection.");
         return;
     }
 
     ////////////////
     // Now that the errors are out of the way, on to actually reading!
 
-    OnBeforeReadCall();
-
     if (!rwWidth || !rwHeight) {
         // Disjoint rects, so we're done already.
         DummyReadFramebufferOperation("readPixels");
         return;
     }
 
     if (uint32_t(rwWidth) == width &&
         uint32_t(rwHeight) == height)
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -55,17 +55,17 @@ WebGLContext::Enable(GLenum cap)
     {
         *trackingSlot = 1;
     }
 
     gl->fEnable(cap);
 }
 
 bool
-WebGLContext::GetStencilBits(GLint* const out_stencilBits)
+WebGLContext::GetStencilBits(GLint* const out_stencilBits) const
 {
     *out_stencilBits = 0;
     if (mBoundDrawFramebuffer) {
         if (mBoundDrawFramebuffer->StencilAttachment().IsDefined() &&
             mBoundDrawFramebuffer->DepthStencilAttachment().IsDefined())
         {
             // Error, we don't know which stencil buffer's bits to use
             ErrorInvalidFramebufferOperation("getParameter: framebuffer has two stencil buffers bound");
@@ -185,17 +185,17 @@ WebGLContext::GetParameter(JSContext* cx
             return JS::Int32Value(mGLMaxDrawBuffers);
 
         } else if (pname >= LOCAL_GL_DRAW_BUFFER0 &&
                    pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mGLMaxDrawBuffers))
         {
             GLint ret = LOCAL_GL_NONE;
             if (!mBoundDrawFramebuffer) {
                 if (pname == LOCAL_GL_DRAW_BUFFER0) {
-                    ret = gl->Screen()->GetDrawBufferMode();
+                    ret = mDefaultFB_DrawBuffer0;
                 }
             } else {
                 gl->fGetIntegerv(pname, &ret);
             }
             return JS::Int32Value(ret);
         }
     }
 
@@ -337,17 +337,17 @@ WebGLContext::GetParameter(JSContext* cx
 
         case LOCAL_GL_GENERATE_MIPMAP_HINT:
             return JS::NumberValue(mGenerateMipmapHint);
 
         case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT:
         case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: {
             const webgl::FormatUsageInfo* usage;
             uint32_t width, height;
-            if (!ValidateCurFBForRead(funcName, &usage, &width, &height))
+            if (!BindCurFBForColorRead(funcName, &usage, &width, &height))
                 return JS::NullValue();
 
             const auto implPI = ValidImplementationColorReadPI(usage);
 
             GLenum ret;
             if (pname == LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT) {
                 ret = implPI.format;
             } else {
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -51,28 +51,28 @@ StringValue(JSContext* cx, const char* c
         rv.Throw(NS_ERROR_OUT_OF_MEMORY);
         return JS::NullValue();
     }
 
     return JS::StringValue(str);
 }
 
 void
-WebGLContext::GenerateWarning(const char* fmt, ...)
+WebGLContext::GenerateWarning(const char* fmt, ...) const
 {
     va_list ap;
     va_start(ap, fmt);
 
     GenerateWarning(fmt, ap);
 
     va_end(ap);
 }
 
 void
-WebGLContext::GenerateWarning(const char* fmt, va_list ap)
+WebGLContext::GenerateWarning(const char* fmt, va_list ap) const
 {
     if (!ShouldGenerateWarnings())
         return;
 
     mAlreadyGeneratedWarnings++;
 
     char buf[1024];
     VsprintfLiteral(buf, fmt, ap);
@@ -141,131 +141,131 @@ WebGLContext::GeneratePerfWarning(const 
         JS_ReportWarningASCII(cx,
                               "WebGL: After reporting %u, no further perf warnings will"
                               " be reported for this WebGL context.",
                               uint32_t(mNumPerfWarnings));
     }
 }
 
 void
-WebGLContext::SynthesizeGLError(GLenum err)
+WebGLContext::SynthesizeGLError(GLenum err) const
 {
     /* ES2 section 2.5 "GL Errors" states that implementations can have
      * multiple 'flags', as errors might be caught in different parts of
      * a distributed implementation.
      * We're signing up as a distributed implementation here, with
      * separate flags for WebGL and the underlying GLContext.
      */
     if (!mWebGLError)
         mWebGLError = err;
 }
 
 void
-WebGLContext::SynthesizeGLError(GLenum err, const char* fmt, ...)
+WebGLContext::SynthesizeGLError(GLenum err, const char* fmt, ...) const
 {
     va_list va;
     va_start(va, fmt);
     GenerateWarning(fmt, va);
     va_end(va);
 
     return SynthesizeGLError(err);
 }
 
 void
-WebGLContext::ErrorInvalidEnum(const char* fmt, ...)
+WebGLContext::ErrorInvalidEnum(const char* fmt, ...) const
 {
     va_list va;
     va_start(va, fmt);
     GenerateWarning(fmt, va);
     va_end(va);
 
     return SynthesizeGLError(LOCAL_GL_INVALID_ENUM);
 }
 
 void
-WebGLContext::ErrorInvalidEnumInfo(const char* info, GLenum enumValue)
+WebGLContext::ErrorInvalidEnumInfo(const char* info, GLenum enumValue) const
 {
     nsCString name;
     EnumName(enumValue, &name);
 
     return ErrorInvalidEnum("%s: invalid enum value %s", info, name.BeginReading());
 }
 
 void
 WebGLContext::ErrorInvalidEnumInfo(const char* info, const char* funcName,
-                                   GLenum enumValue)
+                                   GLenum enumValue) const
 {
     nsCString name;
     EnumName(enumValue, &name);
 
     ErrorInvalidEnum("%s: %s: Invalid enum: 0x%04x (%s).", funcName, info,
                      enumValue, name.BeginReading());
 }
 
 void
-WebGLContext::ErrorInvalidOperation(const char* fmt, ...)
+WebGLContext::ErrorInvalidOperation(const char* fmt, ...) const
 {
     va_list va;
     va_start(va, fmt);
     GenerateWarning(fmt, va);
     va_end(va);
 
     return SynthesizeGLError(LOCAL_GL_INVALID_OPERATION);
 }
 
 void
-WebGLContext::ErrorInvalidValue(const char* fmt, ...)
+WebGLContext::ErrorInvalidValue(const char* fmt, ...) const
 {
     va_list va;
     va_start(va, fmt);
     GenerateWarning(fmt, va);
     va_end(va);
 
     return SynthesizeGLError(LOCAL_GL_INVALID_VALUE);
 }
 
 void
-WebGLContext::ErrorInvalidFramebufferOperation(const char* fmt, ...)
+WebGLContext::ErrorInvalidFramebufferOperation(const char* fmt, ...) const
 {
     va_list va;
     va_start(va, fmt);
     GenerateWarning(fmt, va);
     va_end(va);
 
     return SynthesizeGLError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION);
 }
 
 void
-WebGLContext::ErrorOutOfMemory(const char* fmt, ...)
+WebGLContext::ErrorOutOfMemory(const char* fmt, ...) const
 {
     va_list va;
     va_start(va, fmt);
     GenerateWarning(fmt, va);
     va_end(va);
 
     return SynthesizeGLError(LOCAL_GL_OUT_OF_MEMORY);
 }
 
 void
-WebGLContext::ErrorImplementationBug(const char* fmt, ...)
+WebGLContext::ErrorImplementationBug(const char* fmt, ...) const
 {
     const nsPrintfCString warning("Implementation bug, please file at %s! %s",
                                   "https://bugzilla.mozilla.org/", fmt);
 
     va_list va;
     va_start(va, fmt);
     GenerateWarning(warning.BeginReading(), va);
     va_end(va);
 
     MOZ_ASSERT(false, "WebGLContext::ErrorImplementationBug");
     NS_ERROR("WebGLContext::ErrorImplementationBug");
     return SynthesizeGLError(LOCAL_GL_OUT_OF_MEMORY);
 }
 
-const char*
+/*static*/ const char*
 WebGLContext::ErrorName(GLenum error)
 {
     switch(error) {
     case LOCAL_GL_INVALID_ENUM:
         return "INVALID_ENUM";
     case LOCAL_GL_INVALID_OPERATION:
         return "INVALID_OPERATION";
     case LOCAL_GL_INVALID_VALUE:
@@ -620,17 +620,18 @@ WebGLContext::EnumName(GLenum val, nsCSt
         *out_name = name;
         return;
     }
 
     *out_name = nsPrintfCString("<enum 0x%04x>", val);
 }
 
 void
-WebGLContext::ErrorInvalidEnumArg(const char* funcName, const char* argName, GLenum val)
+WebGLContext::ErrorInvalidEnumArg(const char* funcName, const char* argName,
+                                  GLenum val) const
 {
     nsCString enumName;
     EnumName(val, &enumName);
     ErrorInvalidEnum("%s: Bad `%s`: %s", funcName, argName, enumName.BeginReading());
 }
 
 bool
 IsCompressedTextureFormat(GLenum format)
@@ -671,17 +672,17 @@ IsCompressedTextureFormat(GLenum format)
 
 bool
 IsTextureFormatCompressed(TexInternalFormat format)
 {
     return IsCompressedTextureFormat(format.get());
 }
 
 GLenum
-WebGLContext::GetAndFlushUnderlyingGLErrors()
+WebGLContext::GetAndFlushUnderlyingGLErrors() const
 {
     // Get and clear GL error in ALL cases.
     GLenum error = gl->fGetError();
 
     // Only store in mUnderlyingGLError if is hasn't already recorded an
     // error.
     if (!mUnderlyingGLError)
         mUnderlyingGLError = error;
@@ -734,41 +735,26 @@ AssertMaskedUintParamCorrect(gl::GLConte
 #else
 void
 AssertUintParamCorrect(gl::GLContext*, GLenum, GLuint)
 {
 }
 #endif
 
 void
-WebGLContext::AssertCachedBindings()
+WebGLContext::AssertCachedBindings() const
 {
 #ifdef DEBUG
     GetAndFlushUnderlyingGLErrors();
 
     if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) {
         GLuint bound = mBoundVertexArray ? mBoundVertexArray->GLName() : 0;
         AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_BINDING, bound);
     }
 
-    // Framebuffers
-    if (IsWebGL2()) {
-        GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->mGLName
-                                             : 0;
-        AssertUintParamCorrect(gl, LOCAL_GL_DRAW_FRAMEBUFFER_BINDING, bound);
-
-        bound = mBoundReadFramebuffer ? mBoundReadFramebuffer->mGLName : 0;
-        AssertUintParamCorrect(gl, LOCAL_GL_READ_FRAMEBUFFER_BINDING, bound);
-    } else {
-        MOZ_ASSERT(mBoundDrawFramebuffer == mBoundReadFramebuffer);
-        GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->mGLName
-                                             : 0;
-        AssertUintParamCorrect(gl, LOCAL_GL_FRAMEBUFFER_BINDING, bound);
-    }
-
     GLint stencilBits = 0;
     if (GetStencilBits(&stencilBits)) { // Depends on current draw framebuffer.
         const GLuint stencilRefMask = (1 << stencilBits) - 1;
 
         AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_REF,      stencilRefMask, mStencilRefFront);
         AssertMaskedUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_REF, stencilRefMask, mStencilRefBack);
     }
 
@@ -799,37 +785,28 @@ WebGLContext::AssertCachedBindings()
 
     MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors());
 #endif
 
     // We do not check the renderbuffer binding, because we never rely on it matching.
 }
 
 void
-WebGLContext::AssertCachedGlobalState()
+WebGLContext::AssertCachedGlobalState() const
 {
 #ifdef DEBUG
     GetAndFlushUnderlyingGLErrors();
 
     ////////////////
 
     // Draw state
-    MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_DEPTH_TEST) == mDepthTestEnabled);
     MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_DITHER) == mDitherEnabled);
     MOZ_ASSERT_IF(IsWebGL2(),
                   gl->fIsEnabled(LOCAL_GL_RASTERIZER_DISCARD) == mRasterizerDiscardEnabled);
     MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST) == mScissorTestEnabled);
-    MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_STENCIL_TEST) == mStencilTestEnabled);
-
-    realGLboolean colorWriteMask[4] = {0, 0, 0, 0};
-    gl->fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorWriteMask);
-    MOZ_ASSERT(colorWriteMask[0] == mColorWriteMask[0] &&
-               colorWriteMask[1] == mColorWriteMask[1] &&
-               colorWriteMask[2] == mColorWriteMask[2] &&
-               colorWriteMask[3] == mColorWriteMask[3]);
 
     GLfloat colorClearValue[4] = {0.0f, 0.0f, 0.0f, 0.0f};
     gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, colorClearValue);
     MOZ_ASSERT(IsCacheCorrect(mColorClearValue[0], colorClearValue[0]) &&
                IsCacheCorrect(mColorClearValue[1], colorClearValue[1]) &&
                IsCacheCorrect(mColorClearValue[2], colorClearValue[2]) &&
                IsCacheCorrect(mColorClearValue[3], colorClearValue[3]));
 
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -415,25 +415,23 @@ WebGLContext::InitAndValidateGL(FailureR
 
     mDisableExtensions = gfxPrefs::WebGLDisableExtensions();
     mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure();
     mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground();
     mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible();
 
     // These are the default values, see 6.2 State tables in the
     // OpenGL ES 2.0.25 spec.
-    mColorWriteMask[0] = 1;
-    mColorWriteMask[1] = 1;
-    mColorWriteMask[2] = 1;
-    mColorWriteMask[3] = 1;
-    mDepthWriteMask = 1;
+    mColorWriteMask = 0x0f;
+    mDriverColorMask = mColorWriteMask;
     mColorClearValue[0] = 0.f;
     mColorClearValue[1] = 0.f;
     mColorClearValue[2] = 0.f;
     mColorClearValue[3] = 0.f;
+    mDepthWriteMask = true;
     mDepthClearValue = 1.f;
     mStencilClearValue = 0;
     mStencilRefFront = 0;
     mStencilRefBack = 0;
 
     mLineWidth = 1.0;
 
     /*
@@ -456,23 +454,28 @@ WebGLContext::InitAndValidateGL(FailureR
     AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_VALUE_MASK,      mStencilValueMaskFront);
     AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_VALUE_MASK, mStencilValueMaskBack);
     AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_WRITEMASK,       mStencilWriteMaskFront);
     AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_WRITEMASK,  mStencilWriteMaskBack);
 
     mDitherEnabled = true;
     mRasterizerDiscardEnabled = false;
     mScissorTestEnabled = false;
+
     mDepthTestEnabled = 0;
+    mDriverDepthTest = false;
     mStencilTestEnabled = 0;
+    mDriverStencilTest = false;
+
     mGenerateMipmapHint = LOCAL_GL_DONT_CARE;
 
     // Bindings, etc.
     mActiveTexture = 0;
     mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK;
+    mDefaultFB_ReadBuffer = LOCAL_GL_BACK;
 
     mEmitContextLostErrorOnce = true;
     mWebGLError = LOCAL_GL_NO_ERROR;
     mUnderlyingGLError = LOCAL_GL_NO_ERROR;
 
     mBound2DTextures.Clear();
     mBoundCubeMapTextures.Clear();
     mBound3DTextures.Clear();
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -884,17 +884,17 @@ WebGLFramebuffer::PrecheckFramebufferSta
 
     return LOCAL_GL_FRAMEBUFFER_COMPLETE;
 }
 
 ////////////////////////////////////////
 // Validation
 
 bool
-WebGLFramebuffer::ValidateAndInitAttachments(const char* funcName)
+WebGLFramebuffer::ValidateAndInitAttachments(const char* funcName) const
 {
     MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
                mContext->mBoundReadFramebuffer == this);
 
     const auto fbStatus = CheckFramebufferStatus(funcName);
     if (fbStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE)
         return true;
 
@@ -937,23 +937,21 @@ WebGLFramebuffer::ValidateClearBufferTyp
                                         funcName, attachType, funcType);
         return false;
     }
 
     return true;
 }
 
 bool
-WebGLFramebuffer::ValidateForRead(const char* funcName,
-                                  const webgl::FormatUsageInfo** const out_format,
-                                  uint32_t* const out_width, uint32_t* const out_height)
+WebGLFramebuffer::ValidateForColorRead(const char* funcName,
+                                       const webgl::FormatUsageInfo** const out_format,
+                                       uint32_t* const out_width,
+                                       uint32_t* const out_height) const
 {
-    if (!ValidateAndInitAttachments(funcName))
-        return false;
-
     if (!mColorReadBuffer) {
         mContext->ErrorInvalidOperation("%s: READ_BUFFER must not be NONE.", funcName);
         return false;
     }
 
     if (!mColorReadBuffer->IsDefined()) {
         mContext->ErrorInvalidOperation("%s: The READ_BUFFER attachment is not defined.",
                                         funcName);
@@ -1180,17 +1178,17 @@ WebGLFramebuffer::RefreshResolvedData()
         mResolvedCompleteData.reset(new ResolvedData(*this));
     }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Entrypoints
 
 FBStatus
-WebGLFramebuffer::CheckFramebufferStatus(const char* funcName)
+WebGLFramebuffer::CheckFramebufferStatus(const char* const funcName) const
 {
     if (IsResolvedComplete())
         return LOCAL_GL_FRAMEBUFFER_COMPLETE;
 
     // Ok, let's try to resolve it!
 
     nsCString statusInfo;
     FBStatus ret = PrecheckFramebufferStatus(&statusInfo);
@@ -1646,25 +1644,26 @@ GetBackbufferFormats(const WebGLContext*
         if (options.stencil) {
             *out_stencil = webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8);
         }
     }
 }
 
 /*static*/ void
 WebGLFramebuffer::BlitFramebuffer(WebGLContext* webgl,
-                                  const WebGLFramebuffer* srcFB, GLint srcX0, GLint srcY0,
-                                  GLint srcX1, GLint srcY1,
-                                  const WebGLFramebuffer* dstFB, GLint dstX0, GLint dstY0,
-                                  GLint dstX1, GLint dstY1,
+                                  GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+                                  GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                                   GLbitfield mask, GLenum filter)
 {
     const char funcName[] = "blitFramebuffer";
     const auto& gl = webgl->gl;
 
+    const auto& srcFB = webgl->mBoundReadFramebuffer;
+    const auto& dstFB = webgl->mBoundDrawFramebuffer;
+
     ////
     // Collect data
 
     const auto fnGetDepthAndStencilAttach = [](const WebGLFramebuffer* fb,
                                                const WebGLFBAttachPoint** const out_depth,
                                                const WebGLFBAttachPoint** const out_stencil)
     {
         *out_depth = nullptr;
@@ -1923,18 +1922,17 @@ WebGLFramebuffer::BlitFramebuffer(WebGLC
         }
     } else if (!srcFB && !dstFB) {
         webgl->ErrorInvalidOperation("%s: Feedback with default framebuffer.", funcName);
         return;
     }
 
     ////
 
-    webgl->OnBeforeReadCall();
-    WebGLContext::ScopedDrawCallWrapper wrapper(*webgl);
+    const ScopedDrawCallWrapper wrapper(*webgl);
     gl->fBlitFramebuffer(srcX0, srcY0, srcX1, srcY1,
                          dstX0, dstY0, dstX1, dstY1,
                          mask, filter);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Goop.
 
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -190,17 +190,17 @@ protected:
         // IsFeedback
         std::vector<const WebGLFBAttachPoint*> texDrawBuffers; // Non-null
         std::set<WebGLFBAttachPoint::Ordered> drawSet;
         std::set<WebGLFBAttachPoint::Ordered> readSet;
 
         explicit ResolvedData(const WebGLFramebuffer& parent);
     };
 
-    UniquePtr<const ResolvedData> mResolvedCompleteData;
+    mutable UniquePtr<const ResolvedData> mResolvedCompleteData;
 
     ////
 
 public:
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLFramebuffer)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLFramebuffer)
 
     WebGLFramebuffer(WebGLContext* webgl, GLuint fbo);
@@ -231,23 +231,23 @@ protected:
     void ResolveAttachments() const;
     void RefreshDrawBuffers() const;
     void RefreshReadBuffer() const;
     bool ResolveAttachmentData(const char* funcName) const;
 
 public:
     void DetachTexture(const char* funcName, const WebGLTexture* tex);
     void DetachRenderbuffer(const char* funcName, const WebGLRenderbuffer* rb);
-    bool ValidateAndInitAttachments(const char* funcName);
+    bool ValidateAndInitAttachments(const char* funcName) const;
     bool ValidateClearBufferType(const char* funcName, GLenum buffer, uint32_t drawBuffer,
                                  GLenum funcType) const;
 
-    bool ValidateForRead(const char* info,
-                         const webgl::FormatUsageInfo** const out_format,
-                         uint32_t* const out_width, uint32_t* const out_height);
+    bool ValidateForColorRead(const char* funcName,
+                              const webgl::FormatUsageInfo** out_format,
+                              uint32_t* out_width, uint32_t* out_height) const;
 
     ////////////////
     // Getters
 
 #define GETTER(X) const decltype(m##X)& X() const { return m##X; }
 
     GETTER(DepthAttachment)
     GETTER(StencilAttachment)
@@ -263,33 +263,31 @@ public:
 
     bool IsResolvedComplete() const { return bool(mResolvedCompleteData); }
     void InvalidateFramebufferStatus(const char* funcName);
     void RefreshResolvedData();
 
     ////////////////
     // WebGL funcs
 
-    FBStatus CheckFramebufferStatus(const char* funcName);
+    FBStatus CheckFramebufferStatus(const char* funcName) const;
     void FramebufferRenderbuffer(const char* funcName, GLenum attachment, GLenum rbtarget,
                                  WebGLRenderbuffer* rb);
     void FramebufferTexture2D(const char* funcName, GLenum attachment,
                               GLenum texImageTarget, WebGLTexture* tex, GLint level);
     void FramebufferTextureLayer(const char* funcName, GLenum attachment,
                                  WebGLTexture* tex, GLint level, GLint layer);
     void DrawBuffers(const char* funcName, const dom::Sequence<GLenum>& buffers);
     void ReadBuffer(const char* funcName, GLenum attachPoint);
 
     JS::Value GetAttachmentParameter(const char* funcName, JSContext* cx, GLenum target,
                                      GLenum attachment, GLenum pname,
                                      ErrorResult* const out_error);
 
     static void BlitFramebuffer(WebGLContext* webgl,
-                                const WebGLFramebuffer* src, GLint srcX0, GLint srcY0,
-                                GLint srcX1, GLint srcY1,
-                                const WebGLFramebuffer* dst, GLint dstX0, GLint dstY0,
-                                GLint dstX1, GLint dstY1,
+                                GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+                                GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                                 GLbitfield mask, GLenum filter);
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_FRAMEBUFFER_H_
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -2116,18 +2116,18 @@ WebGLTexture::CopyTexImage2D(TexImageTar
     MOZ_ASSERT(imageInfo);
 
     ////////////////////////////////////
     // Get source info
 
     const webgl::FormatUsageInfo* srcUsage;
     uint32_t srcTotalWidth;
     uint32_t srcTotalHeight;
-    if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcTotalWidth,
-                                        &srcTotalHeight))
+    if (!mContext->BindCurFBForColorRead(funcName, &srcUsage, &srcTotalWidth,
+                                         &srcTotalHeight))
     {
         return;
     }
 
     if (!ValidateCopyTexImageForFeedback(funcName, level))
         return;
 
     ////////////////////////////////////
@@ -2150,18 +2150,16 @@ WebGLTexture::CopyTexImage2D(TexImageTar
     }
 
     if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
         return;
 
     ////////////////////////////////////
     // Do the thing!
 
-    mContext->OnBeforeReadCall();
-
     const bool isSubImage = false;
     if (!DoCopyTexOrSubImage(mContext, funcName, isSubImage, this, target, level, x, y,
                              srcTotalWidth, srcTotalHeight, srcUsage, 0, 0, 0, width,
                              height, dstUsage))
     {
         return;
     }
 
@@ -2210,18 +2208,18 @@ WebGLTexture::CopyTexSubImage(const char
     }
 
     ////////////////////////////////////
     // Get source info
 
     const webgl::FormatUsageInfo* srcUsage;
     uint32_t srcTotalWidth;
     uint32_t srcTotalHeight;
-    if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcTotalWidth,
-                                        &srcTotalHeight))
+    if (!mContext->BindCurFBForColorRead(funcName, &srcUsage, &srcTotalWidth,
+                                         &srcTotalHeight))
     {
         return;
     }
 
     if (!ValidateCopyTexImageForFeedback(funcName, level, zOffset))
         return;
 
     ////////////////////////////////////
@@ -2229,18 +2227,16 @@ WebGLTexture::CopyTexSubImage(const char
 
     auto srcFormat = srcUsage->format;
     if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
         return;
 
     ////////////////////////////////////
     // Do the thing!
 
-    mContext->OnBeforeReadCall();
-
     bool uploadWillInitialize;
     if (!EnsureImageDataInitializedForUpload(this, funcName, target, level, xOffset,
                                              yOffset, zOffset, width, height, depth,
                                              imageInfo, &uploadWillInitialize))
     {
         return;
     }
 
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -196,16 +196,17 @@ LOCAL_INCLUDES += [
     '/js/xpconnect/wrappers',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '../workers',
+    '/', # Allow including relpaths from root.
     '/dom/base',
     '/dom/html',
     '/dom/svg',
     '/dom/workers',
     '/dom/xul',
     '/gfx/gl',
     '/image',
     '/js/xpconnect/src',
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -2515,18 +2515,16 @@ SplitByChar(const nsACString& str, const
     out->push_back(nsCString(substr));
 }
 
 bool
 GLContext::Readback(SharedSurface* src, gfx::DataSourceSurface* dest)
 {
     MOZ_ASSERT(src && dest);
     MOZ_ASSERT(dest->GetSize() == src->mSize);
-    MOZ_ASSERT(dest->GetFormat() == (src->mHasAlpha ? SurfaceFormat::B8G8R8A8
-                                                    : SurfaceFormat::B8G8R8X8));
 
     if (!MakeCurrent()) {
         return false;
     }
 
     SharedSurface* prev = GetLockedSurface();
 
     const bool needsSwap = src != prev;
--- a/gfx/layers/CopyableCanvasRenderer.cpp
+++ b/gfx/layers/CopyableCanvasRenderer.cpp
@@ -150,18 +150,18 @@ CopyableCanvasRenderer::ReadbackSurface(
   }
 
   if (!frontbuffer) {
     NS_WARNING("Null frame received.");
     return nullptr;
   }
 
   IntSize readSize(frontbuffer->mSize);
-  SurfaceFormat format =
-    mOpaque ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
+  SurfaceFormat format = frontbuffer->mHasAlpha ? SurfaceFormat::B8G8R8X8
+                                                : SurfaceFormat::B8G8R8A8;
   bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
 
   RefPtr<DataSourceSurface> resultSurf = GetTempSurface(readSize, format);
   // There will already be a warning from inside of GetTempSurface, but
   // it doesn't hurt to complain:
   if (NS_WARN_IF(!resultSurf)) {
     return nullptr;
   }
--- a/gfx/layers/ShareableCanvasRenderer.cpp
+++ b/gfx/layers/ShareableCanvasRenderer.cpp
@@ -46,17 +46,16 @@ ShareableCanvasRenderer::Initialize(cons
   if (mGLFrontbuffer) {
     // The screen caps are irrelevant if we're using a separate frontbuffer.
     caps = mGLFrontbuffer->mHasAlpha ? gl::SurfaceCaps::ForRGBA()
                                      : gl::SurfaceCaps::ForRGB();
   } else {
     MOZ_ASSERT(screen);
     caps = screen->mCaps;
   }
-  MOZ_ASSERT(caps.alpha == aData.mHasAlpha);
 
   auto forwarder = GetForwarder();
 
   mFlags = TextureFlags::ORIGIN_BOTTOM_LEFT;
   if (!aData.mIsGLAlphaPremult) {
     mFlags |= TextureFlags::NON_PREMULTIPLIED;
   }
 
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -746,16 +746,17 @@ private:
   DECL_GFX_PREF(Once, "webgl.force-layers-readback",           WebGLForceLayersReadback, bool, false);
   DECL_GFX_PREF(Live, "webgl.force-index-validation",          WebGLForceIndexValidation, int32_t, 0);
   DECL_GFX_PREF(Live, "webgl.lose-context-on-memory-pressure", WebGLLoseContextOnMemoryPressure, bool, false);
   DECL_GFX_PREF(Live, "webgl.max-contexts",                    WebGLMaxContexts, uint32_t, 32);
   DECL_GFX_PREF(Live, "webgl.max-contexts-per-principal",      WebGLMaxContextsPerPrincipal, uint32_t, 16);
   DECL_GFX_PREF(Live, "webgl.max-warnings-per-context",        WebGLMaxWarningsPerContext, uint32_t, 32);
   DECL_GFX_PREF(Live, "webgl.min_capability_mode",             WebGLMinCapabilityMode, bool, false);
   DECL_GFX_PREF(Live, "webgl.msaa-force",                      WebGLForceMSAA, bool, false);
+  DECL_GFX_PREF(Live, "webgl.msaa-samples",                    WebGLMsaaSamples, uint32_t, 4);
   DECL_GFX_PREF(Live, "webgl.prefer-16bpp",                    WebGLPrefer16bpp, bool, false);
   DECL_GFX_PREF(Live, "webgl.restore-context-when-visible",    WebGLRestoreWhenVisible, bool, true);
   DECL_GFX_PREF(Live, "webgl.allow-immediate-queries",         WebGLImmediateQueries, bool, false);
   DECL_GFX_PREF(Live, "webgl.allow-fb-invalidation",           WebGLFBInvalidation, bool, false);
 
   DECL_GFX_PREF(Live, "webgl.perf.max-warnings",                    WebGLMaxPerfWarnings, int32_t, 0);
   DECL_GFX_PREF(Live, "webgl.perf.max-acceptable-fb-status-invals", WebGLMaxAcceptableFBStatusInvals, int32_t, 0);
   DECL_GFX_PREF(Live, "webgl.perf.spew-frame-allocs",          WebGLSpewFrameAllocs, bool, true);