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 411131 aef0921e8682aef490ffb00fab256f0b4b0a0954
parent 411130 32c1e468ca21f2a25ec04431e70170af2c4270bb
child 411132 86c771ec0f6c9de8659ffad5abf1517d0a67629e
push id61
push userfmarier@mozilla.com
push dateTue, 25 Jul 2017 22:37:21 +0000
reviewersbackout
bugs1382104
milestone56.0a1
backs out6e571ab7b558eeee419a8c9032f756f0c44bb554
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);