Backed out changeset 6e571ab7b558 (bug 1382104) for asserting at dom/canvas/WebGLContextUtils.cpp:714 in browser-chrome tests and failing GTest's Gfx.CompositorSimpleTree. r=backout
authorSebastian Hengst <archaeopteryx@coole-files.de>
Wed, 19 Jul 2017 09:53:13 +0200
changeset 369489 aef0921e8682aef490ffb00fab256f0b4b0a0954
parent 369488 32c1e468ca21f2a25ec04431e70170af2c4270bb
child 369490 86c771ec0f6c9de8659ffad5abf1517d0a67629e
push id92712
push userarchaeopteryx@coole-files.de
push dateWed, 19 Jul 2017 07:53:29 +0000
treeherdermozilla-inbound@aef0921e8682 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs1382104
milestone56.0a1
backs out6e571ab7b558eeee419a8c9032f756f0c44bb554
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset 6e571ab7b558 (bug 1382104) for asserting at dom/canvas/WebGLContextUtils.cpp:714 in browser-chrome tests and failing GTest's Gfx.CompositorSimpleTree. r=backout
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextGL.cpp
dom/canvas/WebGLContextUtils.cpp
gfx/gl/GLBlitHelper.cpp
gfx/gl/GLBlitHelper.h
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
gfx/gl/GLScreenBuffer.cpp
gfx/gl/GLScreenBuffer.h
gfx/gl/ScopedGLHelpers.cpp
gfx/gl/SharedSurface.cpp
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -820,18 +820,16 @@ WebGLContext::ResizeBackbuffer(uint32_t 
 
       width /= 2;
       height /= 2;
     }
 
     if (!resized)
         return false;
 
-    RebindFramebuffers();
-
     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)
     {
@@ -839,36 +837,16 @@ WebGLContext::ResizeBackbuffer(uint32_t 
                           " to %dx%d succeeded.",
                         requestedWidth, requestedHeight,
                         width, height);
     }
     return true;
 }
 
 void
-WebGLContext::RebindFramebuffers() const
-{
-    const auto fnBind = [&](const GLenum target, const WebGLFramebuffer* const fb) {
-        if (fb) {
-            gl->fBindFramebuffer(target, fb->mGLName);
-        } else {
-            gl->Screen()->BindAsFramebuffer(target);
-        }
-    };
-
-    if (IsWebGL2()) {
-        fnBind(LOCAL_GL_DRAW_FRAMEBUFFER, mBoundDrawFramebuffer);
-        fnBind(LOCAL_GL_READ_FRAMEBUFFER, mBoundReadFramebuffer);
-    } else {
-        MOZ_ASSERT(mBoundDrawFramebuffer == mBoundReadFramebuffer);
-        fnBind(LOCAL_GL_FRAMEBUFFER, mBoundDrawFramebuffer);
-    }
-}
-
-void
 WebGLContext::ThrowEvent_WebGLContextCreationError(const nsACString& text)
 {
     RefPtr<EventTarget> target = mCanvasElement;
     if (!target && mOffscreenCanvas) {
         target = mOffscreenCanvas;
     } else if (!target) {
         GenerateWarning("Failed to create WebGL context: %s", text.BeginReading());
         return;
@@ -1130,17 +1108,17 @@ WebGLContext::SetDimensions(int32_t sign
     MakeContextCurrent();
 
     gl->fViewport(0, 0, mWidth, mHeight);
     mViewportX = mViewportY = 0;
     mViewportWidth = mWidth;
     mViewportHeight = mHeight;
 
     gl->fScissor(0, 0, mWidth, mHeight);
-    gl->Screen()->BindAsFramebuffer();
+    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
 
     //////
     // Check everything
 
     AssertCachedBindings();
     AssertCachedGlobalState();
 
     MOZ_ASSERT(gl->Caps().color);
@@ -1511,18 +1489,17 @@ WebGLContext::MozGetUnderlyingParamStrin
 
     return NS_OK;
 }
 
 void
 WebGLContext::ClearScreen()
 {
     MakeContextCurrent();
-    ScopedBindFramebuffer autoFB(gl);
-    gl->Screen()->BindAsFramebuffer();
+    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)
@@ -1645,17 +1622,16 @@ WebGLContext::PresentScreenBuffer()
 
     GLScreenBuffer* screen = gl->Screen();
     MOZ_ASSERT(screen);
 
     if (!screen->PublishFrame(screen->Size())) {
         ForceLoseContext();
         return false;
     }
-    RebindFramebuffers();
 
     if (!mOptions.preserveDrawingBuffer) {
         mBackbufferNeedsClear = true;
     }
 
     mShouldPresent = false;
     OnEndOfFrame();
 
@@ -1985,18 +1961,17 @@ WebGLContext::GetSurfaceSnapshot(gfxAlph
     surf = Factory::CreateDataSourceSurfaceWithStride(IntSize(mWidth, mHeight),
                                                       surfFormat,
                                                       mWidth * 4);
     if (NS_WARN_IF(!surf))
         return nullptr;
 
     gl->MakeCurrent();
     {
-        ScopedBindFramebuffer autoFB(gl);
-        gl->Screen()->BindAsFramebuffer();
+        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);
         }
@@ -2213,16 +2188,34 @@ ScopedUnpackReset::UnwrapImpl()
         }
 
         mGL->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, pbo);
     }
 }
 
 ////////////////////
 
+void
+ScopedFBRebinder::UnwrapImpl()
+{
+    const auto fnName = [&](WebGLFramebuffer* fb) {
+        return fb ? fb->mGLName : 0;
+    };
+
+    if (mWebGL->IsWebGL2()) {
+        mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fnName(mWebGL->mBoundDrawFramebuffer));
+        mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, fnName(mWebGL->mBoundReadFramebuffer));
+    } else {
+        MOZ_ASSERT(mWebGL->mBoundDrawFramebuffer == mWebGL->mBoundReadFramebuffer);
+        mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fnName(mWebGL->mBoundDrawFramebuffer));
+    }
+}
+
+////////////////////
+
 static GLenum
 TargetIfLazy(GLenum target)
 {
     switch (target) {
     case LOCAL_GL_PIXEL_PACK_BUFFER:
     case LOCAL_GL_PIXEL_UNPACK_BUFFER:
         return target;
 
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1883,18 +1883,16 @@ protected:
                           uint8_t bytesPerPixel, uint32_t* const out_rowStride,
                           uint32_t* const out_endOffset);
 
     GLenum mPixelStore_ColorspaceConversion;
     bool mPixelStore_FlipY;
     bool mPixelStore_PremultiplyAlpha;
     bool mPixelStore_RequireFastPath;
 
-    void RebindFramebuffers() const;
-
     ////////////////////////////////////
     class FakeBlackTexture {
     public:
         static UniquePtr<FakeBlackTexture> Create(gl::GLContext* gl,
                                                   TexTarget target,
                                                   FakeBlackType type);
         gl::GLContext* const mGL;
         const GLuint mGLName;
@@ -2157,19 +2155,17 @@ private:
 
 public:
     explicit ScopedFBRebinder(WebGLContext* webgl)
         : ScopedGLWrapper<ScopedFBRebinder>(webgl->gl)
         , mWebGL(webgl)
     { }
 
 private:
-    void UnwrapImpl() {
-        mWebGL->RebindFramebuffers();
-    }
+    void UnwrapImpl();
 };
 
 class ScopedLazyBind final
     : public gl::ScopedGLWrapper<ScopedLazyBind>
 {
     friend struct gl::ScopedGLWrapper<ScopedLazyBind>;
 
     const GLenum mTarget;
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -136,19 +136,20 @@ WebGLContext::BindFramebuffer(GLenum tar
         return;
 
     if (wfb && !ValidateObject("bindFramebuffer", *wfb))
         return;
 
     MakeContextCurrent();
 
     if (!wfb) {
-        gl->Screen()->BindAsFramebuffer(target);
+        gl->fBindFramebuffer(target, 0);
     } else {
-        gl->fBindFramebuffer(target, wfb->mGLName);
+        GLuint framebuffername = wfb->mGLName;
+        gl->fBindFramebuffer(target, framebuffername);
 #ifdef ANDROID
         wfb->mIsFB = true;
 #endif
     }
 
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
         mBoundDrawFramebuffer = wfb;
@@ -156,17 +157,17 @@ WebGLContext::BindFramebuffer(GLenum tar
         break;
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         mBoundDrawFramebuffer = wfb;
         break;
     case LOCAL_GL_READ_FRAMEBUFFER:
         mBoundReadFramebuffer = wfb;
         break;
     default:
-        MOZ_CRASH();
+        break;
     }
 }
 
 void
 WebGLContext::BindRenderbuffer(GLenum target, WebGLRenderbuffer* wrb)
 {
     if (IsContextLost())
         return;
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -749,26 +749,25 @@ WebGLContext::AssertCachedBindings()
     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
-                                             : gl->Screen()->DrawFB();
+                                             : 0;
         AssertUintParamCorrect(gl, LOCAL_GL_DRAW_FRAMEBUFFER_BINDING, bound);
 
-        bound = mBoundReadFramebuffer ? mBoundReadFramebuffer->mGLName
-                                      : gl->Screen()->ReadFB();
+        bound = mBoundReadFramebuffer ? mBoundReadFramebuffer->mGLName : 0;
         AssertUintParamCorrect(gl, LOCAL_GL_READ_FRAMEBUFFER_BINDING, bound);
     } else {
         MOZ_ASSERT(mBoundDrawFramebuffer == mBoundReadFramebuffer);
         GLuint bound = mBoundDrawFramebuffer ? mBoundDrawFramebuffer->mGLName
-                                             : gl->Screen()->ReadFB();
+                                             : 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);
--- a/gfx/gl/GLBlitHelper.cpp
+++ b/gfx/gl/GLBlitHelper.cpp
@@ -548,36 +548,68 @@ GLBlitHelper::DeleteTexBlitProgram()
         mGL->fDeleteProgram(mTexNV12PlanarBlit_Program);
         mTexNV12PlanarBlit_Program = 0;
     }
 }
 
 void
 GLBlitHelper::BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB,
                                            const gfx::IntSize& srcSize,
-                                           const gfx::IntSize& destSize)
+                                           const gfx::IntSize& destSize,
+                                           bool internalFBs)
 {
     MOZ_ASSERT(!srcFB || mGL->fIsFramebuffer(srcFB));
     MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB));
 
     MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
 
     ScopedBindFramebuffer boundFB(mGL);
-    mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, srcFB);
-    mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destFB);
+    ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
 
-    ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
+    if (internalFBs) {
+        mGL->Screen()->BindReadFB_Internal(srcFB);
+        mGL->Screen()->BindDrawFB_Internal(destFB);
+    } else {
+        mGL->BindReadFB(srcFB);
+        mGL->BindDrawFB(destFB);
+    }
 
     mGL->fBlitFramebuffer(0, 0,  srcSize.width,  srcSize.height,
                           0, 0, destSize.width, destSize.height,
                           LOCAL_GL_COLOR_BUFFER_BIT,
                           LOCAL_GL_NEAREST);
 }
 
 void
+GLBlitHelper::BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB,
+                                           const gfx::IntSize& srcSize,
+                                           const gfx::IntSize& destSize,
+                                           const GLFormats& srcFormats,
+                                           bool internalFBs)
+{
+    MOZ_ASSERT(!srcFB || mGL->fIsFramebuffer(srcFB));
+    MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB));
+
+    if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
+        BlitFramebufferToFramebuffer(srcFB, destFB,
+                                     srcSize, destSize,
+                                     internalFBs);
+        return;
+    }
+
+    GLuint tex = CreateTextureForOffscreen(mGL, srcFormats, srcSize);
+    MOZ_ASSERT(tex);
+
+    BlitFramebufferToTexture(srcFB, tex, srcSize, srcSize, internalFBs);
+    BlitTextureToFramebuffer(tex, destFB, srcSize, destSize, internalFBs);
+
+    mGL->fDeleteTextures(1, &tex);
+}
+
+void
 GLBlitHelper::BindAndUploadYUVTexture(Channel which,
                                       uint32_t width,
                                       uint32_t height,
                                       void* data,
                                       bool needsAllocation)
 {
     MOZ_ASSERT(which < Channel_Max, "Invalid channel!");
     GLuint* srcTexArr[3] = {&mSrcTexY, &mSrcTexCb, &mSrcTexCr};
@@ -856,54 +888,64 @@ GLBlitHelper::BlitImageToTexture(layers:
 
     return BlitImageToFramebuffer(srcImage, destSize, autoFBForTex.FB(), destOrigin);
 }
 
 void
 GLBlitHelper::BlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
                                        const gfx::IntSize& srcSize,
                                        const gfx::IntSize& destSize,
-                                       GLenum srcTarget)
+                                       GLenum srcTarget,
+                                       bool internalFBs)
 {
     MOZ_ASSERT(mGL->fIsTexture(srcTex));
     MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB));
 
     if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
         ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget);
         MOZ_DIAGNOSTIC_ASSERT(srcWrapper.IsComplete());
 
-        BlitFramebufferToFramebuffer(srcWrapper.FB(), destFB, srcSize, destSize);
+        BlitFramebufferToFramebuffer(srcWrapper.FB(), destFB,
+                                     srcSize, destSize,
+                                     internalFBs);
         return;
     }
 
-    DrawBlitTextureToFramebuffer(srcTex, destFB, srcSize, destSize, srcTarget);
+    DrawBlitTextureToFramebuffer(srcTex, destFB, srcSize, destSize, srcTarget,
+                                 internalFBs);
 }
 
 
 void
 GLBlitHelper::DrawBlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
                                            const gfx::IntSize& srcSize,
                                            const gfx::IntSize& destSize,
-                                           GLenum srcTarget)
+                                           GLenum srcTarget,
+                                           bool internalFBs)
 {
     BlitType type;
     switch (srcTarget) {
     case LOCAL_GL_TEXTURE_2D:
         type = BlitTex2D;
         break;
     case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
         type = BlitTexRect;
         break;
     default:
         MOZ_CRASH("GFX: Fatal Error: Bad `srcTarget`.");
         break;
     }
 
     ScopedGLDrawState autoStates(mGL);
-    const ScopedBindFramebuffer bindFB(mGL, destFB);
+    const ScopedBindFramebuffer bindFB(mGL);
+    if (internalFBs) {
+        mGL->Screen()->BindFB_Internal(destFB);
+    } else {
+        mGL->BindFB(destFB);
+    }
 
     // Does destructive things to (only!) what we just saved above.
     bool good = UseTexQuadProgram(type, srcSize);
     if (!good) {
         // We're up against the wall, so bail.
         MOZ_DIAGNOSTIC_ASSERT(false,
                               "Error: Failed to prepare to blit texture->framebuffer.\n");
         mGL->fScissor(0, 0, destSize.width, destSize.height);
@@ -915,33 +957,40 @@ GLBlitHelper::DrawBlitTextureToFramebuff
     const ScopedBindTexture bindTex(mGL, srcTex, srcTarget);
     mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
 }
 
 void
 GLBlitHelper::BlitFramebufferToTexture(GLuint srcFB, GLuint destTex,
                                        const gfx::IntSize& srcSize,
                                        const gfx::IntSize& destSize,
-                                       GLenum destTarget)
+                                       GLenum destTarget,
+                                       bool internalFBs)
 {
     // On the Android 4.3 emulator, IsFramebuffer may return false incorrectly.
     MOZ_ASSERT_IF(mGL->Renderer() != GLRenderer::AndroidEmulator, !srcFB || mGL->fIsFramebuffer(srcFB));
     MOZ_ASSERT(mGL->fIsTexture(destTex));
 
     if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
         ScopedFramebufferForTexture destWrapper(mGL, destTex, destTarget);
 
         BlitFramebufferToFramebuffer(srcFB, destWrapper.FB(),
-                                     srcSize, destSize);
+                                     srcSize, destSize,
+                                     internalFBs);
         return;
     }
 
     ScopedBindTexture autoTex(mGL, destTex, destTarget);
 
-    ScopedBindFramebuffer boundFB(mGL, srcFB);
+    ScopedBindFramebuffer boundFB(mGL);
+    if (internalFBs) {
+        mGL->Screen()->BindFB_Internal(srcFB);
+    } else {
+        mGL->BindFB(srcFB);
+    }
 
     ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
     mGL->fCopyTexSubImage2D(destTarget, 0,
                        0, 0,
                        0, 0,
                        srcSize.width, srcSize.height);
 }
 
--- a/gfx/gl/GLBlitHelper.h
+++ b/gfx/gl/GLBlitHelper.h
@@ -120,29 +120,38 @@ class GLBlitHelper final
 public:
     ~GLBlitHelper();
 
     // If you don't have |srcFormats| for the 2nd definition,
     // then you'll need the framebuffer_blit extensions to use
     // the first BlitFramebufferToFramebuffer.
     void BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB,
                                       const gfx::IntSize& srcSize,
-                                      const gfx::IntSize& destSize);
+                                      const gfx::IntSize& destSize,
+                                      bool internalFBs = false);
+    void BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB,
+                                      const gfx::IntSize& srcSize,
+                                      const gfx::IntSize& destSize,
+                                      const GLFormats& srcFormats,
+                                      bool internalFBs = false);
     void BlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
                                   const gfx::IntSize& srcSize,
                                   const gfx::IntSize& destSize,
-                                  GLenum srcTarget = LOCAL_GL_TEXTURE_2D);
+                                  GLenum srcTarget = LOCAL_GL_TEXTURE_2D,
+                                  bool internalFBs = false);
     void DrawBlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
                                       const gfx::IntSize& srcSize,
                                       const gfx::IntSize& destSize,
-                                      GLenum srcTarget = LOCAL_GL_TEXTURE_2D);
+                                      GLenum srcTarget = LOCAL_GL_TEXTURE_2D,
+                                      bool internalFBs = false);
     void BlitFramebufferToTexture(GLuint srcFB, GLuint destTex,
                                   const gfx::IntSize& srcSize,
                                   const gfx::IntSize& destSize,
-                                  GLenum destTarget = LOCAL_GL_TEXTURE_2D);
+                                  GLenum destTarget = LOCAL_GL_TEXTURE_2D,
+                                  bool internalFBs = false);
     void BlitTextureToTexture(GLuint srcTex, GLuint destTex,
                               const gfx::IntSize& srcSize,
                               const gfx::IntSize& destSize,
                               GLenum srcTarget = LOCAL_GL_TEXTURE_2D,
                               GLenum destTarget = LOCAL_GL_TEXTURE_2D);
     bool BlitImageToFramebuffer(layers::Image* srcImage, const gfx::IntSize& destSize,
                                 GLuint destFB, OriginPos destOrigin);
     bool BlitImageToTexture(layers::Image* srcImage, const gfx::IntSize& destSize,
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -958,16 +958,17 @@ GLContext::InitWithPrefixImpl(const char
 
     if (IsSupported(GLFeature::framebuffer_multisample)) {
         fGetIntegerv(LOCAL_GL_MAX_SAMPLES, (GLint*)&mMaxSamples);
     }
 
     ////////////////////////////////////////////////////////////////////////////
 
     // We're ready for final setup.
+    fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
 
     // TODO: Remove SurfaceCaps::any.
     if (mCaps.any) {
         mCaps.any = false;
         mCaps.color = true;
         mCaps.alpha = false;
     }
 
@@ -1957,36 +1958,36 @@ GLContext::AssembleOffscreenFBs(const GL
     ScopedBindFramebuffer autoFB(this);
 
     GLuint drawFB = 0;
     GLuint readFB = 0;
 
     if (texture) {
         readFB = 0;
         fGenFramebuffers(1, &readFB);
-        fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, readFB);
+        BindFB(readFB);
         fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
                               LOCAL_GL_COLOR_ATTACHMENT0,
                               LOCAL_GL_TEXTURE_2D,
                               texture,
                               0);
     }
 
     if (colorMSRB) {
         drawFB = 0;
         fGenFramebuffers(1, &drawFB);
-        fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, drawFB);
+        BindFB(drawFB);
         fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
                                  LOCAL_GL_COLOR_ATTACHMENT0,
                                  LOCAL_GL_RENDERBUFFER,
                                  colorMSRB);
     } else {
         drawFB = readFB;
     }
-    MOZ_ASSERT(GetIntAs<GLuint>(LOCAL_GL_FRAMEBUFFER_BINDING) == drawFB);
+    MOZ_ASSERT(GetFB() == drawFB);
 
     if (depthRB) {
         fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
                                  LOCAL_GL_DEPTH_ATTACHMENT,
                                  LOCAL_GL_RENDERBUFFER,
                                  depthRB);
     }
 
@@ -2355,43 +2356,64 @@ GLContext::GuaranteeResolve()
 const gfx::IntSize&
 GLContext::OffscreenSize() const
 {
     MOZ_ASSERT(IsOffscreen());
     return mScreen->Size();
 }
 
 bool
-GLContext::CreateScreenBuffer(const IntSize& size, const SurfaceCaps& caps)
+GLContext::CreateScreenBufferImpl(const IntSize& size, const SurfaceCaps& caps)
 {
-    if (!IsOffscreenSizeAllowed(size))
-        return false;
-
     UniquePtr<GLScreenBuffer> newScreen = GLScreenBuffer::Create(this, size, caps);
     if (!newScreen)
         return false;
 
     if (!newScreen->Resize(size)) {
         return false;
     }
 
+    // This will rebind to 0 (Screen) if needed when
+    // it falls out of scope.
+    ScopedBindFramebuffer autoFB(this);
+
     mScreen = Move(newScreen);
 
     return true;
 }
 
 bool
 GLContext::ResizeScreenBuffer(const IntSize& size)
 {
     if (!IsOffscreenSizeAllowed(size))
         return false;
 
     return mScreen->Resize(size);
 }
 
+void
+GLContext::ForceDirtyScreen()
+{
+    ScopedBindFramebuffer autoFB(0);
+
+    BeforeGLDrawCall();
+    // no-op; just pretend we did something
+    AfterGLDrawCall();
+}
+
+void
+GLContext::CleanDirtyScreen()
+{
+    ScopedBindFramebuffer autoFB(0);
+
+    BeforeGLReadCall();
+    // no-op; we just want to make sure the Read FBO is updated if it needs to be
+    AfterGLReadCall();
+}
+
 bool
 GLContext::IsOffscreenSizeAllowed(const IntSize& aSize) const
 {
     int32_t biggerDimension = std::max(aSize.width, aSize.height);
     int32_t maxAllowed = std::min(mMaxRenderbufferSize, mMaxTextureSize);
     return biggerDimension <= maxAllowed;
 }
 
@@ -2513,17 +2535,17 @@ GLContext::Readback(SharedSurface* src, 
     {
         ScopedBindFramebuffer autoFB(this);
 
         // We're consuming from the producer side, so which do we use?
         // Really, we just want a read-only lock, so ConsumerAcquire is the best match.
         src->ProducerReadAcquire();
 
         if (src->mAttachType == AttachmentType::Screen) {
-            mScreen->BindAsFramebuffer();
+            fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
         } else {
             fGenFramebuffers(1, &tempFB);
             fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, tempFB);
 
             switch (src->mAttachType) {
             case AttachmentType::GLTexture:
                 fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
                                       src->ProdTextureTarget(), src->ProdTexture(), 0);
@@ -2588,29 +2610,47 @@ GLContext::AfterGLDrawCall()
     mHeavyGLCallsSinceLastFlush = true;
 }
 
 // Do whatever setup is necessary to read from our offscreen FBO, if it's
 // bound.
 void
 GLContext::BeforeGLReadCall()
 {
-    if (mScreen) {
+    if (mScreen)
         mScreen->BeforeReadCall();
-    }
 }
 
 void
 GLContext::fBindFramebuffer(GLenum target, GLuint framebuffer)
 {
+    if (!mScreen) {
+        raw_fBindFramebuffer(target, framebuffer);
+        return;
+    }
+
+    switch (target) {
+        case LOCAL_GL_DRAW_FRAMEBUFFER_EXT:
+            mScreen->BindDrawFB(framebuffer);
+            return;
+
+        case LOCAL_GL_READ_FRAMEBUFFER_EXT:
+            mScreen->BindReadFB(framebuffer);
+            return;
+
+        case LOCAL_GL_FRAMEBUFFER:
+            mScreen->BindFB(framebuffer);
+            return;
+
+        default:
+            // Nothing we care about, likely an error.
+            break;
+    }
+
     raw_fBindFramebuffer(target, framebuffer);
-
-    if (mScreen) {
-        mScreen->OnBindFramebuffer(target, framebuffer);
-    }
 }
 
 void
 GLContext::fCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x,
                            GLint y, GLsizei width, GLsizei height, GLint border)
 {
     if (!IsTextureSizeSafeToPassToDriver(target, width, height)) {
         // pass wrong values to cause the GL to generate GL_INVALID_VALUE.
@@ -2634,16 +2674,35 @@ GLContext::fCopyTexImage2D(GLenum target
     }
     AfterGLReadCall();
 }
 
 void
 GLContext::fGetIntegerv(GLenum pname, GLint* params)
 {
     switch (pname) {
+        // LOCAL_GL_FRAMEBUFFER_BINDING is equal to
+        // LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT,
+        // so we don't need two cases.
+        case LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT:
+            if (mScreen) {
+                *params = mScreen->GetDrawFB();
+            } else {
+                raw_fGetIntegerv(pname, params);
+            }
+            break;
+
+        case LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT:
+            if (mScreen) {
+                *params = mScreen->GetReadFB();
+            } else {
+                raw_fGetIntegerv(pname, params);
+            }
+            break;
+
         case LOCAL_GL_MAX_TEXTURE_SIZE:
             MOZ_ASSERT(mMaxTextureSize>0);
             *params = mMaxTextureSize;
             break;
 
         case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE:
             MOZ_ASSERT(mMaxCubeMapTextureSize>0);
             *params = mMaxCubeMapTextureSize;
@@ -2721,17 +2780,17 @@ GLContext::fReadPixels(GLint x, GLint y,
 
 void
 GLContext::fDeleteFramebuffers(GLsizei n, const GLuint* names)
 {
     if (mScreen) {
         // Notify mScreen which framebuffers we're deleting.
         // Otherwise, we will get framebuffer binding mispredictions.
         for (int i = 0; i < n; i++) {
-            mScreen->OnDeleteFramebuffer(names[i]);
+            mScreen->DeletingFB(names[i]);
         }
     }
 
     // Avoid crash by flushing before glDeleteFramebuffers. See bug 1194923.
     if (mNeedsFlushBeforeDeleteFB) {
         fFlush();
     }
 
@@ -2787,24 +2846,65 @@ GLContext::fTexImage2D(GLenum target, GL
         if (!WillTextureMapSucceed(width, height, internalformat, type)) {
             return;
         }
     }
 #endif
     raw_fTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
 }
 
+GLuint
+GLContext::GetDrawFB()
+{
+    if (mScreen)
+        return mScreen->GetDrawFB();
+
+    GLuint ret = 0;
+    GetUIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT, &ret);
+    return ret;
+}
+
+GLuint
+GLContext::GetReadFB()
+{
+    if (mScreen)
+        return mScreen->GetReadFB();
+
+    GLenum bindEnum = IsSupported(GLFeature::split_framebuffer)
+                        ? LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT
+                        : LOCAL_GL_FRAMEBUFFER_BINDING;
+
+    GLuint ret = 0;
+    GetUIntegerv(bindEnum, &ret);
+    return ret;
+}
+
+GLuint
+GLContext::GetFB()
+{
+    if (mScreen) {
+        // This has a very important extra assert that checks that we're
+        // not accidentally ignoring a situation where the draw and read
+        // FBs differ.
+        return mScreen->GetFB();
+    }
+
+    GLuint ret = 0;
+    GetUIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &ret);
+    return ret;
+}
+
 bool
 GLContext::InitOffscreen(const gfx::IntSize& size, const SurfaceCaps& caps)
 {
     if (!CreateScreenBuffer(size, caps))
         return false;
 
     MakeCurrent();
-    mScreen->BindAsFramebuffer();
+    fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
     fScissor(0, 0, size.width, size.height);
     fViewport(0, 0, size.width, size.height);
 
     mCaps = mScreen->mCaps;
     MOZ_ASSERT(!mCaps.any);
 
     return true;
 }
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -3335,16 +3335,35 @@ public:
 
     /*
      * Return size of this offscreen context.
      *
      * Only valid if IsOffscreen() returns true.
      */
     const gfx::IntSize& OffscreenSize() const;
 
+    void BindFB(GLuint fb) {
+        fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb);
+        MOZ_ASSERT(!fb || fIsFramebuffer(fb));
+    }
+
+    void BindDrawFB(GLuint fb) {
+        fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, fb);
+    }
+
+    void BindReadFB(GLuint fb) {
+        fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, fb);
+    }
+
+    GLuint GetDrawFB();
+
+    GLuint GetReadFB();
+
+    GLuint GetFB();
+
 private:
     void GetShaderPrecisionFormatNonES2(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) {
         switch (precisiontype) {
             case LOCAL_GL_LOW_FLOAT:
             case LOCAL_GL_MEDIUM_FLOAT:
             case LOCAL_GL_HIGH_FLOAT:
                 // Assume IEEE 754 precision
                 range[0] = 127;
@@ -3359,16 +3378,20 @@ private:
                 range[0] = 24;
                 range[1] = 24;
                 *precision = 0;
                 break;
         }
     }
 
 public:
+
+    void ForceDirtyScreen();
+    void CleanDirtyScreen();
+
     virtual GLenum GetPreferredARGB32Format() const { return LOCAL_GL_RGBA; }
 
     virtual GLenum GetPreferredEGLImageTextureTarget() const {
         return IsExtensionSupported(OES_EGL_image_external) ?
             LOCAL_GL_TEXTURE_EXTERNAL : LOCAL_GL_TEXTURE_2D;
     }
 
     virtual bool RenewSurface(widget::CompositorWidget* aWidget) { return false; }
@@ -3430,17 +3453,25 @@ public:
 
         return thisShared == otherShared;
     }
 
     bool InitOffscreen(const gfx::IntSize& size, const SurfaceCaps& caps);
 
 protected:
     // Note that it does -not- clear the resized buffers.
-    bool CreateScreenBuffer(const gfx::IntSize& size, const SurfaceCaps& caps);
+    bool CreateScreenBuffer(const gfx::IntSize& size, const SurfaceCaps& caps) {
+        if (!IsOffscreenSizeAllowed(size))
+            return false;
+
+       return CreateScreenBufferImpl(size, caps);
+    }
+
+    bool CreateScreenBufferImpl(const gfx::IntSize& size,
+                                const SurfaceCaps& caps);
 
 public:
     bool ResizeScreenBuffer(const gfx::IntSize& size);
 
 protected:
     SurfaceCaps mCaps;
 
 public:
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -141,121 +141,267 @@ GLScreenBuffer::GLScreenBuffer(GLContext
                                const SurfaceCaps& caps,
                                UniquePtr<SurfaceFactory> factory)
     : mGL(gl)
     , mCaps(caps)
     , mFactory(Move(factory))
     , mNeedsBlit(true)
     , mUserReadBufferMode(LOCAL_GL_BACK)
     , mUserDrawBufferMode(LOCAL_GL_BACK)
-    , mBoundDrawFB(0)
-    , mBoundReadFB(0)
+    , mUserDrawFB(0)
+    , mUserReadFB(0)
+    , mInternalDrawFB(0)
+    , mInternalReadFB(0)
+#ifdef DEBUG
+    , mInInternalMode_DrawFB(true)
+    , mInInternalMode_ReadFB(true)
+#endif
 { }
 
 GLScreenBuffer::~GLScreenBuffer()
 {
     mFactory = nullptr;
     mDraw = nullptr;
     mRead = nullptr;
 
     if (!mBack)
         return;
 
     // Detach mBack cleanly.
     mBack->Surf()->ProducerRelease();
 }
 
 void
-GLScreenBuffer::BindAsFramebuffer(const GLenum target) const
+GLScreenBuffer::BindAsFramebuffer(GLContext* const gl, GLenum target) const
 {
-    const auto drawFB = DrawFB();
-    const auto readFB = ReadFB();
+    GLuint drawFB = DrawFB();
+    GLuint readFB = ReadFB();
+
+    if (!gl->IsSupported(GLFeature::split_framebuffer)) {
+        MOZ_ASSERT(drawFB == readFB);
+        gl->raw_fBindFramebuffer(target, readFB);
+        return;
+    }
 
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
-        if (drawFB == readFB) {
-            mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, readFB);
-        } else {
-            mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, drawFB);
-            mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, readFB);
-        }
+        gl->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, drawFB);
+        gl->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, readFB);
         break;
 
     case LOCAL_GL_DRAW_FRAMEBUFFER_EXT:
-        mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, drawFB);
+        gl->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, drawFB);
         break;
 
     case LOCAL_GL_READ_FRAMEBUFFER_EXT:
-        mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, readFB);
+        gl->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, readFB);
         break;
 
     default:
         MOZ_CRASH("GFX: Bad `target` for BindFramebuffer.");
     }
 }
 
 void
-GLScreenBuffer::OnBindFramebuffer(const GLenum target, const GLuint fb)
+GLScreenBuffer::BindFB(GLuint fb)
 {
-    switch (target) {
-    case LOCAL_GL_FRAMEBUFFER:
-        mBoundDrawFB = fb;
-        mBoundReadFB = fb;
-        break;
+    GLuint drawFB = DrawFB();
+    GLuint readFB = ReadFB();
+
+    mUserDrawFB = fb;
+    mUserReadFB = fb;
+    mInternalDrawFB = (fb == 0) ? drawFB : fb;
+    mInternalReadFB = (fb == 0) ? readFB : fb;
+
+    if (mInternalDrawFB == mInternalReadFB) {
+        mGL->raw_fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mInternalDrawFB);
+    } else {
+        MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
+        mGL->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, mInternalDrawFB);
+        mGL->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, mInternalReadFB);
+    }
+
+#ifdef DEBUG
+    mInInternalMode_DrawFB = false;
+    mInInternalMode_ReadFB = false;
+#endif
+}
+
+void
+GLScreenBuffer::BindDrawFB(GLuint fb)
+{
+    MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
+
+    GLuint drawFB = DrawFB();
+    mUserDrawFB = fb;
+    mInternalDrawFB = (fb == 0) ? drawFB : fb;
+
+    mGL->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, mInternalDrawFB);
+
+#ifdef DEBUG
+    mInInternalMode_DrawFB = false;
+#endif
+}
 
-    case LOCAL_GL_DRAW_FRAMEBUFFER_EXT:
-        mBoundDrawFB = fb;
-        break;
+void
+GLScreenBuffer::BindReadFB(GLuint fb)
+{
+    MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
+
+    GLuint readFB = ReadFB();
+    mUserReadFB = fb;
+    mInternalReadFB = (fb == 0) ? readFB : fb;
+
+    mGL->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, mInternalReadFB);
+
+#ifdef DEBUG
+    mInInternalMode_ReadFB = false;
+#endif
+}
 
-    case LOCAL_GL_READ_FRAMEBUFFER_EXT:
-        mBoundReadFB = fb;
-        break;
+void
+GLScreenBuffer::BindFB_Internal(GLuint fb)
+{
+    mInternalDrawFB = mUserDrawFB = fb;
+    mInternalReadFB = mUserReadFB = fb;
+    mGL->raw_fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mInternalDrawFB);
 
-    default:
-        break;
-    }
+#ifdef DEBUG
+    mInInternalMode_DrawFB = true;
+    mInInternalMode_ReadFB = true;
+#endif
+}
+
+void
+GLScreenBuffer::BindDrawFB_Internal(GLuint fb)
+{
+    MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
+
+    mInternalDrawFB = mUserDrawFB = fb;
+    mGL->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, mInternalDrawFB);
+
+#ifdef DEBUG
+    mInInternalMode_DrawFB = true;
+#endif
 }
 
 void
-GLScreenBuffer::OnDeleteFramebuffer(const GLuint fb)
+GLScreenBuffer::BindReadFB_Internal(GLuint fb)
+{
+    MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
+
+    mInternalReadFB = mUserReadFB = fb;
+    mGL->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, mInternalReadFB);
+
+#ifdef DEBUG
+    mInInternalMode_ReadFB = true;
+#endif
+}
+
+
+GLuint
+GLScreenBuffer::GetDrawFB() const
+{
+#ifdef DEBUG
+    MOZ_ASSERT(mGL->IsCurrent());
+    MOZ_ASSERT(!mInInternalMode_DrawFB);
+
+    // Don't need a branch here, because:
+    // LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT == LOCAL_GL_FRAMEBUFFER_BINDING == 0x8CA6
+    // We use raw_ here because this is debug code and we need to see what
+    // the driver thinks.
+    GLuint actual = 0;
+    mGL->raw_fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT, (GLint*)&actual);
+
+    GLuint predicted = mInternalDrawFB;
+    if (predicted != actual) {
+        printf_stderr("Misprediction: Bound draw FB predicted: %d. Was: %d.\n",
+                      predicted, actual);
+        MOZ_ASSERT(false, "Draw FB binding misprediction!");
+    }
+#endif
+
+    return mUserDrawFB;
+}
+
+GLuint
+GLScreenBuffer::GetReadFB() const
 {
-    if (fb == mBoundDrawFB) {
-        mBoundDrawFB = 0;
+#ifdef DEBUG
+    MOZ_ASSERT(mGL->IsCurrent());
+    MOZ_ASSERT(!mInInternalMode_ReadFB);
+
+    // We use raw_ here because this is debug code and we need to see what
+    // the driver thinks.
+    GLuint actual = 0;
+    if (mGL->IsSupported(GLFeature::split_framebuffer))
+        mGL->raw_fGetIntegerv(LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT, (GLint*)&actual);
+    else
+        mGL->raw_fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*)&actual);
+
+    GLuint predicted = mInternalReadFB;
+    if (predicted != actual) {
+        printf_stderr("Misprediction: Bound read FB predicted: %d. Was: %d.\n",
+                      predicted, actual);
+        MOZ_ASSERT(false, "Read FB binding misprediction!");
     }
-    if (fb == mBoundReadFB) {
-        mBoundDrawFB = 0;
+#endif
+
+    return mUserReadFB;
+}
+
+GLuint
+GLScreenBuffer::GetFB() const
+{
+    MOZ_ASSERT(GetDrawFB() == GetReadFB());
+    return GetDrawFB();
+}
+
+
+void
+GLScreenBuffer::DeletingFB(GLuint fb)
+{
+    if (fb == mInternalDrawFB) {
+        mInternalDrawFB = 0;
+        mUserDrawFB = 0;
+    }
+    if (fb == mInternalReadFB) {
+        mInternalReadFB = 0;
+        mUserReadFB = 0;
     }
 }
 
 
 void
 GLScreenBuffer::AfterDrawCall()
 {
-    if (mBoundDrawFB == DrawFB()) {
-        RequireBlit();
-    }
+    if (mUserDrawFB != 0)
+        return;
+
+    RequireBlit();
 }
 
 void
 GLScreenBuffer::BeforeReadCall()
 {
-    if (mBoundReadFB == ReadFB()) {
-        AssureBlitted();
-    }
+    if (mUserReadFB != 0)
+        return;
+
+    AssureBlitted();
 }
 
 bool
 GLScreenBuffer::CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x,
                                GLint y, GLsizei width, GLsizei height, GLint border)
 {
     SharedSurface* surf;
-    if (mBoundReadFB == ReadFB()) {
+    if (GetReadFB() == 0) {
         surf = SharedSurf();
     } else {
-        surf = mGL->mFBOMapping[mBoundReadFB];
+        surf = mGL->mFBOMapping[GetReadFB()];
     }
     if (surf) {
         return surf->CopyTexImage2D(target, level, internalformat,  x, y, width, height, border);
     }
 
     return false;
 }
 
@@ -266,20 +412,20 @@ GLScreenBuffer::ReadPixels(GLint x, GLin
                            GLvoid* pixels)
 {
     // If the currently bound framebuffer is backed by a SharedSurface
     // then it might want to override how we read pixel data from it.
     // This is normally only the default framebuffer, but we can also
     // have SharedSurfaces bound to other framebuffers when doing
     // readback for BasicLayers.
     SharedSurface* surf;
-    if (mBoundReadFB == ReadFB()) {
+    if (GetReadFB() == 0) {
         surf = SharedSurf();
     } else {
-        surf = mGL->mFBOMapping[mBoundReadFB];
+        surf = mGL->mFBOMapping[GetReadFB()];
     }
     if (surf) {
         return surf->ReadPixels(x, y, width, height, format, type, pixels);
     }
 
     return false;
 }
 
@@ -291,29 +437,29 @@ GLScreenBuffer::RequireBlit()
 
 void
 GLScreenBuffer::AssureBlitted()
 {
     if (!mNeedsBlit)
         return;
 
     if (mDraw) {
-        GLuint srcFB = DrawFB();
-        GLuint destFB = ReadFB();
+        GLuint drawFB = DrawFB();
+        GLuint readFB = ReadFB();
 
-        MOZ_ASSERT(srcFB != 0);
-        MOZ_ASSERT(srcFB != destFB);
+        MOZ_ASSERT(drawFB != 0);
+        MOZ_ASSERT(drawFB != readFB);
         MOZ_ASSERT(mGL->IsSupported(GLFeature::split_framebuffer));
         MOZ_ASSERT(mDraw->mSize == mRead->Size());
 
         ScopedBindFramebuffer boundFB(mGL);
         ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
 
-        mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, srcFB);
-        mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destFB);
+        BindReadFB_Internal(drawFB);
+        BindDrawFB_Internal(readFB);
 
         if (mGL->IsSupported(GLFeature::framebuffer_blit)) {
             const gfx::IntSize&  srcSize = mDraw->mSize;
             const gfx::IntSize& destSize = mRead->Size();
 
             mGL->raw_fBlitFramebuffer(0, 0,  srcSize.width,  srcSize.height,
                                       0, 0, destSize.width, destSize.height,
                                       LOCAL_GL_COLOR_BUFFER_BIT,
@@ -334,16 +480,18 @@ GLScreenBuffer::Morph(UniquePtr<SurfaceF
 {
     MOZ_RELEASE_ASSERT(newFactory, "newFactory must not be null");
     mFactory = Move(newFactory);
 }
 
 bool
 GLScreenBuffer::Attach(SharedSurface* surf, const gfx::IntSize& size)
 {
+    ScopedBindFramebuffer autoFB(mGL);
+
     const bool readNeedsUnlock = (mRead && SharedSurf());
     if (readNeedsUnlock) {
         SharedSurf()->UnlockProd();
     }
 
     surf->LockProd();
 
     if (mRead &&
@@ -383,25 +531,25 @@ GLScreenBuffer::Attach(SharedSurface* su
             mDraw = Move(draw);
 
         mRead = Move(read);
     }
 
     // Check that we're all set up.
     MOZ_ASSERT(SharedSurf() == surf);
 
-    BindAsFramebuffer();
-
     // Update the ReadBuffer mode.
     if (mGL->IsSupported(gl::GLFeature::read_buffer)) {
+        BindFB(0);
         mRead->SetReadBuffer(mUserReadBufferMode);
     }
 
     // Update the DrawBuffer mode.
     if (mGL->IsSupported(gl::GLFeature::draw_buffers)) {
+        BindFB(0);
         SetDrawBuffer(mUserDrawBufferMode);
     }
 
     RequireBlit();
 
     return true;
 }
 
@@ -513,26 +661,30 @@ GLScreenBuffer::CreateRead(SharedSurface
 
     return ReadBuffer::Create(gl, caps, formats, surf);
 }
 
 void
 GLScreenBuffer::SetDrawBuffer(GLenum mode)
 {
     MOZ_ASSERT(mGL->IsSupported(gl::GLFeature::draw_buffers));
+    MOZ_ASSERT(GetDrawFB() == 0);
+
+    if (!mGL->IsSupported(GLFeature::draw_buffers))
+        return;
 
     mUserDrawBufferMode = mode;
 
-    const auto drawFB = DrawFB();
+    GLuint fb = mDraw ? mDraw->mFB : mRead->mFB;
     GLenum internalMode;
 
     switch (mode) {
     case LOCAL_GL_BACK:
-        internalMode = (drawFB == 0) ? LOCAL_GL_BACK
-                                     : LOCAL_GL_COLOR_ATTACHMENT0;
+        internalMode = (fb == 0) ? LOCAL_GL_BACK
+                                 : LOCAL_GL_COLOR_ATTACHMENT0;
         break;
 
     case LOCAL_GL_NONE:
         internalMode = LOCAL_GL_NONE;
         break;
 
     default:
         MOZ_CRASH("GFX: Bad value.");
@@ -541,16 +693,17 @@ GLScreenBuffer::SetDrawBuffer(GLenum mod
     mGL->MakeCurrent();
     mGL->fDrawBuffers(1, &internalMode);
 }
 
 void
 GLScreenBuffer::SetReadBuffer(GLenum mode)
 {
     MOZ_ASSERT(mGL->IsSupported(gl::GLFeature::read_buffer));
+    MOZ_ASSERT(GetReadFB() == 0);
 
     mUserReadBufferMode = mode;
     mRead->SetReadBuffer(mUserReadBufferMode);
 }
 
 bool
 GLScreenBuffer::IsDrawFramebufferDefault() const
 {
--- a/gfx/gl/GLScreenBuffer.h
+++ b/gfx/gl/GLScreenBuffer.h
@@ -161,18 +161,25 @@ protected:
     UniquePtr<ReadBuffer> mRead;
 
     bool mNeedsBlit;
 
     GLenum mUserReadBufferMode;
     GLenum mUserDrawBufferMode;
 
     // Below are the parts that help us pretend to be framebuffer 0:
-    GLuint mBoundDrawFB;
-    GLuint mBoundReadFB;
+    GLuint mUserDrawFB;
+    GLuint mUserReadFB;
+    GLuint mInternalDrawFB;
+    GLuint mInternalReadFB;
+
+#ifdef DEBUG
+    bool mInInternalMode_DrawFB;
+    bool mInInternalMode_ReadFB;
+#endif
 
     GLScreenBuffer(GLContext* gl,
                    const SurfaceCaps& caps,
                    UniquePtr<SurfaceFactory> factory);
 
 public:
     virtual ~GLScreenBuffer();
 
@@ -216,20 +223,17 @@ public:
     void DeletingFB(GLuint fb);
 
     const gfx::IntSize& Size() const {
         MOZ_ASSERT(mRead);
         MOZ_ASSERT(!mDraw || mDraw->mSize == mRead->Size());
         return mRead->Size();
     }
 
-    void BindAsFramebuffer(GLenum target = LOCAL_GL_FRAMEBUFFER) const;
-
-    void OnBindFramebuffer(GLenum target, GLuint fb);
-    void OnDeleteFramebuffer(GLuint fb);
+    void BindAsFramebuffer(GLContext* const gl, GLenum target) const;
 
     void RequireBlit();
     void AssureBlitted();
     void AfterDrawCall();
     void BeforeReadCall();
 
     bool CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x,
                         GLint y, GLsizei width, GLsizei height, GLint border);
@@ -264,16 +268,34 @@ public:
 
 protected:
     bool Attach(SharedSurface* surf, const gfx::IntSize& size);
 
     bool CreateDraw(const gfx::IntSize& size, UniquePtr<DrawBuffer>* out_buffer);
     UniquePtr<ReadBuffer> CreateRead(SharedSurface* surf);
 
 public:
+    /* `fb` in these functions is the framebuffer the GLContext is hoping to
+     * bind. When this is 0, we intercept the call and bind our own
+     * framebuffers. As a client of these functions, just bind 0 when you want
+     * to draw to the default framebuffer/'screen'.
+     */
+    void BindFB(GLuint fb);
+    void BindDrawFB(GLuint fb);
+    void BindReadFB(GLuint fb);
+    GLuint GetFB() const;
+    GLuint GetDrawFB() const;
+    GLuint GetReadFB() const;
+
+    // Here `fb` is the actual framebuffer you want bound. Binding 0 will
+    // bind the (generally useless) default framebuffer.
+    void BindFB_Internal(GLuint fb);
+    void BindDrawFB_Internal(GLuint fb);
+    void BindReadFB_Internal(GLuint fb);
+
     bool IsDrawFramebufferDefault() const;
     bool IsReadFramebufferDefault() const;
 };
 
 } // namespace gl
 } // namespace mozilla
 
 #endif  // SCREEN_BUFFER_H_
--- a/gfx/gl/ScopedGLHelpers.cpp
+++ b/gfx/gl/ScopedGLHelpers.cpp
@@ -58,44 +58,44 @@ ScopedGLState::UnwrapImpl()
 
 
 /* ScopedBindFramebuffer - Saves and restores with GetUserBoundFB and BindUserFB. */
 
 void
 ScopedBindFramebuffer::Init()
 {
     if (mGL->IsSupported(GLFeature::split_framebuffer)) {
-        mOldReadFB = mGL->GetIntAs<GLuint>(LOCAL_GL_READ_FRAMEBUFFER_BINDING);
-        mOldDrawFB = mGL->GetIntAs<GLuint>(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING);
+        mOldReadFB = mGL->GetReadFB();
+        mOldDrawFB = mGL->GetDrawFB();
     } else {
-        mOldReadFB = mOldDrawFB = mGL->GetIntAs<GLuint>(LOCAL_GL_FRAMEBUFFER_BINDING);
+        mOldReadFB = mOldDrawFB = mGL->GetFB();
     }
 }
 
 ScopedBindFramebuffer::ScopedBindFramebuffer(GLContext* aGL)
     : ScopedGLWrapper<ScopedBindFramebuffer>(aGL)
 {
     Init();
 }
 
 ScopedBindFramebuffer::ScopedBindFramebuffer(GLContext* aGL, GLuint aNewFB)
     : ScopedGLWrapper<ScopedBindFramebuffer>(aGL)
 {
     Init();
-    mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, aNewFB);
+    mGL->BindFB(aNewFB);
 }
 
 void
 ScopedBindFramebuffer::UnwrapImpl()
 {
     if (mOldReadFB == mOldDrawFB) {
-        mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mOldDrawFB);
+        mGL->BindFB(mOldDrawFB);
     } else {
-        mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, mOldDrawFB);
-        mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, mOldReadFB);
+        mGL->BindDrawFB(mOldDrawFB);
+        mGL->BindReadFB(mOldReadFB);
     }
 }
 
 
 /* ScopedBindTextureUnit ******************************************************/
 
 ScopedBindTextureUnit::ScopedBindTextureUnit(GLContext* aGL, GLenum aTexUnit)
     : ScopedGLWrapper<ScopedBindTextureUnit>(aGL)
--- a/gfx/gl/SharedSurface.cpp
+++ b/gfx/gl/SharedSurface.cpp
@@ -63,25 +63,27 @@ SharedSurface::ProdCopy(SharedSurface* s
 
         if (dest->mAttachType == AttachmentType::GLTexture) {
             GLuint destTex = dest->ProdTexture();
             GLenum destTarget = dest->ProdTextureTarget();
 
             gl->BlitHelper()->BlitFramebufferToTexture(0, destTex,
                                                        src->mSize,
                                                        dest->mSize,
-                                                       destTarget);
+                                                       destTarget,
+                                                       true);
         } else if (dest->mAttachType == AttachmentType::GLRenderbuffer) {
             GLuint destRB = dest->ProdRenderbuffer();
             ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
 
             gl->BlitHelper()->BlitFramebufferToFramebuffer(0,
                                                            destWrapper.FB(),
                                                            src->mSize,
-                                                           dest->mSize);
+                                                           dest->mSize,
+                                                           true);
         } else {
             MOZ_CRASH("GFX: Unhandled dest->mAttachType 1.");
         }
 
         if (srcNeedsUnlock)
             src->UnlockProd();
 
         if (origNeedsRelock)
@@ -106,25 +108,27 @@ SharedSurface::ProdCopy(SharedSurface* s
 
         if (src->mAttachType == AttachmentType::GLTexture) {
             GLuint srcTex = src->ProdTexture();
             GLenum srcTarget = src->ProdTextureTarget();
 
             gl->BlitHelper()->BlitTextureToFramebuffer(srcTex, 0,
                                                        src->mSize,
                                                        dest->mSize,
-                                                       srcTarget);
+                                                       srcTarget,
+                                                       !!gl->Screen());
         } else if (src->mAttachType == AttachmentType::GLRenderbuffer) {
             GLuint srcRB = src->ProdRenderbuffer();
             ScopedFramebufferForRenderbuffer srcWrapper(gl, srcRB);
 
             gl->BlitHelper()->BlitFramebufferToFramebuffer(srcWrapper.FB(),
                                                            0,
                                                            src->mSize,
-                                                           dest->mSize);
+                                                           dest->mSize,
+                                                           true);
         } else {
             MOZ_CRASH("GFX: Unhandled src->mAttachType 2.");
         }
 
         if (destNeedsUnlock)
             dest->UnlockProd();
 
         if (origNeedsRelock)
@@ -440,17 +444,20 @@ ScopedReadbackFB::ScopedReadbackFB(Share
                     mSurfToLock = origLocked;
                     mSurfToLock->UnlockProd();
                 }
 
                 mSurfToUnlock = src;
                 mSurfToUnlock->LockProd();
             }
 
-            mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
+            // TODO: This should just be BindFB, but we don't have
+            // the patch for this yet. (bug 1045955)
+            MOZ_ASSERT(mGL->Screen());
+            mGL->Screen()->BindReadFB_Internal(0);
             break;
         }
     default:
         MOZ_CRASH("GFX: Unhandled `mAttachType`.");
     }
 
     if (src->NeedsIndirectReads()) {
         mGL->fGenTextures(1, &mTempTex);