Bug 739775 - Refactor ResizeOffscreenFBO - r=bjacob
authorJeff Gilbert <jgilbert@mozilla.com>
Wed, 25 Apr 2012 15:10:43 +0100
changeset 92425 832debbbdedb703e177aa8f59b3b97cc7cd01464
parent 92424 e420eb1ea2d68371dd82979ccbf34bb96de9634d
child 92426 f7f0bcab1995b1fbd8f8fea9b5a384272eb52ab9
push id8675
push userjgilbert@mozilla.com
push dateWed, 25 Apr 2012 14:11:29 +0000
treeherdermozilla-inbound@832debbbdedb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbjacob
bugs739775
milestone15.0a1
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
Bug 739775 - Refactor ResizeOffscreenFBO - r=bjacob
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
gfx/gl/GLContextProviderCGL.mm
gfx/gl/GLContextProviderEGL.cpp
gfx/gl/GLContextProviderGLX.cpp
gfx/gl/GLContextProviderWGL.cpp
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -37,16 +37,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 
 #include <string.h>
 #include <stdio.h>
+#include <algorithm>
 
 #include "prlink.h"
 #include "prenv.h"
 
 #include "nsThreadUtils.h"
 
 #include "gfxPlatform.h"
 #include "GLContext.h"
@@ -1290,371 +1291,344 @@ void TiledTextureImage::Resize(const nsI
     mCurrentImage = 0;
 }
 
 PRUint32 TiledTextureImage::GetTileCount()
 {
     return mImages.Length();
 }
 
-bool
-GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize, const bool aUseReadFBO, const bool aDisableAA)
+GLContext::GLFormats
+GLContext::ChooseGLFormats(ContextFormat& aCF)
 {
-    if (!IsOffscreenSizeAllowed(aSize))
-        return false;
-
-    MakeCurrent();
-
-    const bool alpha = mCreationFormat.alpha > 0;
-    const int depth = mCreationFormat.depth;
-    const int stencil = mCreationFormat.stencil;
-    int samples = mCreationFormat.samples;
-
-    GLint maxSamples = 0;
-    if (SupportsFramebufferMultisample() && !aDisableAA)
-        fGetIntegerv(LOCAL_GL_MAX_SAMPLES, &maxSamples);
-
+    GLFormats formats;
+
+    if (aCF.alpha) {
+        formats.texColor = LOCAL_GL_RGBA;
+        if (mIsGLES2 && !IsExtensionSupported(OES_rgb8_rgba8)) {
+            formats.rbColor = LOCAL_GL_RGBA4;
+            aCF.red = aCF.green = aCF.blue = aCF.alpha = 4;
+        } else {
+            formats.rbColor = LOCAL_GL_RGBA8;
+            aCF.red = aCF.green = aCF.blue = aCF.alpha = 8;
+        }
+    } else {
+        formats.texColor = LOCAL_GL_RGB;
+        if (mIsGLES2 && !IsExtensionSupported(OES_rgb8_rgba8)) {
+            formats.rbColor = LOCAL_GL_RGB565;
+            aCF.red = 5;
+            aCF.green = 6;
+            aCF.blue = 5;
+        } else {
+            formats.rbColor = LOCAL_GL_RGB8;
+            aCF.red = aCF.green = aCF.blue = 8;
+        }
+        aCF.alpha = 0;
+    }
+    formats.texColorType = LOCAL_GL_UNSIGNED_BYTE;
+
+
+    GLsizei samples = aCF.samples;
+
+    GLsizei maxSamples = 0;
+    if (SupportsFramebufferMultisample())
+        fGetIntegerv(LOCAL_GL_MAX_SAMPLES, (GLint*)&maxSamples);
     samples = NS_MIN(samples, maxSamples);
 
-    const bool useDrawMSFBO = (samples > 0);
-
-    if (!useDrawMSFBO && !aUseReadFBO) {
-        // Early out, as no FBO resize work is necessary.
-        return true;
-    }
-
-    GLuint curBoundFramebufferDraw = 0;
-    GLuint curBoundFramebufferRead = 0;
-    GLuint curBoundRenderbuffer = 0;
-    GLuint curBoundTexture = 0;
-
-    GLint viewport[4];
-
+    formats.samples = samples;
+    aCF.samples = samples;
+
+
+    const int depth = aCF.depth;
+    const int stencil = aCF.stencil;
     const bool useDepthStencil =
-            !mIsGLES2 || IsExtensionSupported(OES_packed_depth_stencil);
-
-    // save a few things for later restoring
-    curBoundFramebufferDraw = GetUserBoundDrawFBO();
-    curBoundFramebufferRead = GetUserBoundReadFBO();
-    fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, (GLint*) &curBoundRenderbuffer);
-    fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, (GLint*) &curBoundTexture);
-    fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
-
-    // the context format of what we're defining
-    // This becomes mActualFormat on success
-    ContextFormat cf(mCreationFormat);
-
-    // Create everything we need for the resize, so if it fails, we haven't broken anything
-    // If successful, these new resized objects will replace their associated member vars in GLContext
-    GLuint newOffscreenDrawFBO = 0;
-    GLuint newOffscreenReadFBO = 0;
-    GLuint newOffscreenTexture = 0;
-    GLuint newOffscreenColorRB = 0;
-    GLuint newOffscreenDepthRB = 0;
-    GLuint newOffscreenStencilRB = 0;
-
-    // Create the buffers and texture
-    if (aUseReadFBO) {
-        fGenFramebuffers(1, &newOffscreenReadFBO);
-        fGenTextures(1, &newOffscreenTexture);
-    }
-
-    if (useDrawMSFBO) {
-        fGenFramebuffers(1, &newOffscreenDrawFBO);
-        fGenRenderbuffers(1, &newOffscreenColorRB);
-    } else {
-        newOffscreenDrawFBO = newOffscreenReadFBO;
-    }
-
+        !mIsGLES2 || IsExtensionSupported(OES_packed_depth_stencil);
+
+    formats.depthStencil = 0;
+    formats.depth = 0;
+    formats.stencil = 0;
     if (depth && stencil && useDepthStencil) {
-        fGenRenderbuffers(1, &newOffscreenDepthRB);
+        formats.depthStencil = LOCAL_GL_DEPTH24_STENCIL8;
+        aCF.depth = 24;
+        aCF.stencil = 8;
     } else {
         if (depth) {
-            fGenRenderbuffers(1, &newOffscreenDepthRB);
-        }
-
-        if (stencil) {
-            fGenRenderbuffers(1, &newOffscreenStencilRB);
-        }
-    }
-
-   // Allocate texture
-    if (aUseReadFBO) {
-        fBindTexture(LOCAL_GL_TEXTURE_2D, newOffscreenTexture);
-        fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
-        fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
-        fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
-        fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
-
-        if (alpha) {
-            fTexImage2D(LOCAL_GL_TEXTURE_2D,
-                        0,
-                        LOCAL_GL_RGBA,
-                        aSize.width, aSize.height,
-                        0,
-                        LOCAL_GL_RGBA,
-                        LOCAL_GL_UNSIGNED_BYTE,
-                        NULL);
-
-            cf.red = cf.green = cf.blue = cf.alpha = 8;
-        } else {
-            fTexImage2D(LOCAL_GL_TEXTURE_2D,
-                        0,
-                        LOCAL_GL_RGB,
-                        aSize.width, aSize.height,
-                        0,
-                        LOCAL_GL_RGB,
-#ifdef XP_WIN
-                        LOCAL_GL_UNSIGNED_BYTE,
-#else
-                        mIsGLES2 ? LOCAL_GL_UNSIGNED_SHORT_5_6_5
-                                 : LOCAL_GL_UNSIGNED_BYTE,
-#endif
-                        NULL);
-
-#ifdef XP_WIN
-            cf.red = cf.green = cf.blue = 8;
-#else
-            cf.red = 5;
-            cf.green = 6;
-            cf.blue = 5;
-#endif
-            cf.alpha = 0;
-        }
-    }
-    cf.samples = samples;
-
-    // Allocate color buffer
-    if (useDrawMSFBO) {
-        GLenum colorFormat;
-        if (!mIsGLES2 || IsExtensionSupported(OES_rgb8_rgba8))
-            colorFormat = alpha ? LOCAL_GL_RGBA8 : LOCAL_GL_RGB8;
-        else
-            colorFormat = alpha ? LOCAL_GL_RGBA4 : LOCAL_GL_RGB565;
-
-        fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, newOffscreenColorRB);
-        fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER,
-                                        samples,
-                                        colorFormat,
-                                        aSize.width, aSize.height);
-    }
-
-    // Allocate depth and stencil buffers
-    if (depth && stencil && useDepthStencil) {
-        fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, newOffscreenDepthRB);
-        if (useDrawMSFBO) {
-            fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER,
-                                            samples,
-                                            LOCAL_GL_DEPTH24_STENCIL8,
-                                            aSize.width, aSize.height);
-        } else {
-            fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
-                                 LOCAL_GL_DEPTH24_STENCIL8,
-                                 aSize.width, aSize.height);
-        }
-        cf.depth = 24;
-        cf.stencil = 8;
-    } else {
-        if (depth) {
-            GLenum depthType;
             if (mIsGLES2) {
-                if (IsExtensionSupported(OES_depth32)) {
-                    depthType = LOCAL_GL_DEPTH_COMPONENT32;
-                    cf.depth = 32;
-                } else if (IsExtensionSupported(OES_depth24)) {
-                    depthType = LOCAL_GL_DEPTH_COMPONENT24;
-                    cf.depth = 24;
+                if (IsExtensionSupported(OES_depth24)) {
+                    formats.depth = LOCAL_GL_DEPTH_COMPONENT24;
+                    aCF.depth = 24;
                 } else {
-                    depthType = LOCAL_GL_DEPTH_COMPONENT16;
-                   cf.depth = 16;
+                    formats.depth = LOCAL_GL_DEPTH_COMPONENT16;
+                    aCF.depth = 16;
                 }
             } else {
-                depthType = LOCAL_GL_DEPTH_COMPONENT24;
-                cf.depth = 24;
-            }
-
-            fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, newOffscreenDepthRB);
-            if (useDrawMSFBO) {
-                fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER,
-                                                samples,
-                                                depthType,
-                                                aSize.width, aSize.height);
-            } else {
-                fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
-                                     depthType,
-                                     aSize.width, aSize.height);
+                formats.depth = LOCAL_GL_DEPTH_COMPONENT24;
+                aCF.depth = 24;
             }
         }
 
         if (stencil) {
-            fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, newOffscreenStencilRB);
-            if (useDrawMSFBO) {
-                fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER,
-                                                samples,
-                                                LOCAL_GL_STENCIL_INDEX8,
-                                                aSize.width, aSize.height);
-            } else {
-                fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
-                                     LOCAL_GL_STENCIL_INDEX8,
-                                     aSize.width, aSize.height);
-            }
-            cf.stencil = 8;
+            formats.stencil = LOCAL_GL_STENCIL_INDEX8;
+            aCF.stencil = 8;
         }
     }
 
-    // Now assemble the FBO
-    BindInternalFBO(newOffscreenDrawFBO);    // If we're not using a separate draw FBO, this will be the read FBO
-    if (useDrawMSFBO) {
+    return formats;
+}
+
+void
+GLContext::CreateTextureForOffscreen(const GLFormats& aFormats, const gfxIntSize& aSize, GLuint& texture)
+{
+    GLuint boundTexture = 0;
+    fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, (GLint*)&boundTexture);
+
+    texture = 0;
+    fGenTextures(1, &texture);
+    fBindTexture(LOCAL_GL_TEXTURE_2D, texture);
+    fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
+    fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
+    fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+    fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+
+    fTexImage2D(LOCAL_GL_TEXTURE_2D,
+                0,
+                aFormats.texColor,
+                aSize.width, aSize.height,
+                0,
+                aFormats.texColor,
+                aFormats.texColorType,
+                nsnull);
+
+    fBindTexture(LOCAL_GL_TEXTURE_2D, boundTexture);
+}
+
+static inline void
+RenderbufferStorageBySamples(GLContext* gl, GLsizei samples, GLenum internalFormat, const gfxIntSize& size)
+{
+    if (samples) {
+        gl->fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER,
+                                            samples,
+                                            internalFormat,
+                                            size.width, size.height);
+    } else {
+        gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
+                                 internalFormat,
+                                 size.width, size.height);
+    }
+}
+
+void
+GLContext::CreateRenderbuffersForOffscreen(const GLContext::GLFormats& aFormats, const gfxIntSize& aSize,
+                                           GLuint& colorMSRB, GLuint& depthRB, GLuint& stencilRB)
+{
+    GLuint boundRB = 0;
+    fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, (GLint*)&boundRB);
+
+
+    colorMSRB = 0;
+    depthRB = 0;
+    stencilRB = 0;
+
+    if (aFormats.samples > 0) {
+        fGenRenderbuffers(1, &colorMSRB);
+        fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, colorMSRB);
+        RenderbufferStorageBySamples(this, aFormats.samples, aFormats.rbColor, aSize);
+    }
+
+    // If depthStencil, disallow depth, stencil
+    MOZ_ASSERT(!aFormats.depthStencil || (!aFormats.depth && !aFormats.stencil));
+
+    if (aFormats.depthStencil) {
+        fGenRenderbuffers(1, &depthRB);
+        stencilRB = depthRB;
+        fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, depthRB);
+        RenderbufferStorageBySamples(this, aFormats.samples, aFormats.depthStencil, aSize);
+    }
+
+    if (aFormats.depth) {
+        fGenRenderbuffers(1, &depthRB);
+        fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, depthRB);
+        RenderbufferStorageBySamples(this, aFormats.samples, aFormats.depth, aSize);
+    }
+
+    if (aFormats.stencil) {
+        fGenRenderbuffers(1, &stencilRB);
+        fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, stencilRB);
+        RenderbufferStorageBySamples(this, aFormats.samples, aFormats.stencil, aSize);
+    }
+
+
+    fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, boundRB);
+}
+
+bool
+GLContext::AssembleOffscreenFBOs(const GLuint colorMSRB,
+                                 const GLuint depthRB,
+                                 const GLuint stencilRB,
+                                 const GLuint texture,
+                                 GLuint& drawFBO,
+                                 GLuint& readFBO)
+{
+    drawFBO = 0;
+    readFBO = 0;
+
+    if (!colorMSRB && !texture) {
+        MOZ_ASSERT(!depthRB && !stencilRB);
+        return true;
+    }
+
+    GLuint boundDrawFBO = GetUserBoundDrawFBO();
+    GLuint boundReadFBO = GetUserBoundReadFBO();
+
+    if (texture) {
+        fGenFramebuffers(1, &readFBO);
+        BindInternalFBO(readFBO);
+        fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
+                              LOCAL_GL_COLOR_ATTACHMENT0,
+                              LOCAL_GL_TEXTURE_2D,
+                              texture,
+                              0);
+    }
+
+    if (colorMSRB) {
+        fGenFramebuffers(1, &drawFBO);
+        BindInternalFBO(drawFBO);
         fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
                                  LOCAL_GL_COLOR_ATTACHMENT0,
                                  LOCAL_GL_RENDERBUFFER,
-                                 newOffscreenColorRB);
+                                 colorMSRB);
+    } else {
+        drawFBO = readFBO;
+        // drawFBO==readFBO is already bound from the 'if (texture)' block.
     }
 
-    if (depth && stencil && useDepthStencil) {
+    if (depthRB) {
         fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
                                  LOCAL_GL_DEPTH_ATTACHMENT,
                                  LOCAL_GL_RENDERBUFFER,
-                                 newOffscreenDepthRB);
+                                 depthRB);
+    }
+
+    if (stencilRB) {
         fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
                                  LOCAL_GL_STENCIL_ATTACHMENT,
                                  LOCAL_GL_RENDERBUFFER,
-                                 newOffscreenDepthRB);
-    } else {
-        if (depth) {
-            fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
-                                     LOCAL_GL_DEPTH_ATTACHMENT,
-                                     LOCAL_GL_RENDERBUFFER,
-                                     newOffscreenDepthRB);
-        }
-
-        if (stencil) {
-            fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER,
-                                     LOCAL_GL_STENCIL_ATTACHMENT,
-                                     LOCAL_GL_RENDERBUFFER,
-                                     newOffscreenStencilRB);
-        }
-    }
-
-    if (aUseReadFBO) {
-        BindInternalFBO(newOffscreenReadFBO);
-        fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
-                              LOCAL_GL_COLOR_ATTACHMENT0,
-                              LOCAL_GL_TEXTURE_2D,
-                              newOffscreenTexture,
-                              0);
+                                 stencilRB);
     }
 
     // We should be all resized.  Check for framebuffer completeness.
     GLenum status;
-    bool framebuffersComplete = true;
-
-    BindInternalFBO(newOffscreenDrawFBO);
+    bool isComplete = true;
+
+    BindInternalFBO(drawFBO);
     status = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
     if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
         NS_WARNING("DrawFBO: Incomplete");
-#ifdef DEBUG
+  #ifdef DEBUG
         printf_stderr("Framebuffer status: %X\n", status);
-#endif
-        framebuffersComplete = false;
+  #endif
+        isComplete = false;
     }
 
-    BindInternalFBO(newOffscreenReadFBO);
+    BindInternalFBO(readFBO);
     status = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
     if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
         NS_WARNING("ReadFBO: Incomplete");
-#ifdef DEBUG
+  #ifdef DEBUG
         printf_stderr("Framebuffer status: %X\n", status);
-#endif
-        framebuffersComplete = false;
+  #endif
+        isComplete = false;
     }
 
-    if (!framebuffersComplete) {
-        NS_WARNING("Error resizing offscreen framebuffer -- framebuffer(s) not complete");
-
-        // Clean up the mess
-        fDeleteFramebuffers(1, &newOffscreenDrawFBO);
-        fDeleteFramebuffers(1, &newOffscreenReadFBO);
-        fDeleteTextures(1, &newOffscreenTexture);
-        fDeleteRenderbuffers(1, &newOffscreenColorRB);
-        fDeleteRenderbuffers(1, &newOffscreenDepthRB);
-        fDeleteRenderbuffers(1, &newOffscreenStencilRB);
-
-        BindUserDrawFBO(curBoundFramebufferDraw);
-        BindUserReadFBO(curBoundFramebufferRead);
-        fBindTexture(LOCAL_GL_TEXTURE_2D, curBoundTexture);
-        fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, curBoundRenderbuffer);
-        fViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
+    BindUserDrawFBO(boundDrawFBO);
+    BindUserReadFBO(boundReadFBO);
+
+    return isComplete;
+}
+
+bool
+GLContext::ResizeOffscreenFBOs(const ContextFormat& aCF, const gfxIntSize& aSize, const bool aNeedsReadBuffer)
+{
+    // Early out for when we're rendering directly to the context's 'screen'.
+    if (!aNeedsReadBuffer && !aCF.samples)
+        return true;
+
+    MakeCurrent();
+    ContextFormat cf(aCF);
+    GLFormats formats = ChooseGLFormats(cf);
+
+    GLuint texture = 0;
+    if (aNeedsReadBuffer)
+        CreateTextureForOffscreen(formats, aSize, texture);
+
+    GLuint colorMSRB = 0;
+    GLuint depthRB = 0;
+    GLuint stencilRB = 0;
+    CreateRenderbuffersForOffscreen(formats, aSize, colorMSRB, depthRB, stencilRB);
+
+    GLuint drawFBO = 0;
+    GLuint readFBO = 0;
+    if (!AssembleOffscreenFBOs(colorMSRB, depthRB, stencilRB, texture,
+                               drawFBO, readFBO))
+    {
+        fDeleteFramebuffers(1, &drawFBO);
+        fDeleteFramebuffers(1, &readFBO);
+        fDeleteRenderbuffers(1, &colorMSRB);
+        fDeleteRenderbuffers(1, &depthRB);
+        fDeleteRenderbuffers(1, &stencilRB);
+        fDeleteTextures(1, &texture);
 
         return false;
     }
 
-    // Success, so delete the old and busted
-    fDeleteFramebuffers(1, &mOffscreenDrawFBO);
-    fDeleteFramebuffers(1, &mOffscreenReadFBO);
-    fDeleteTextures(1, &mOffscreenTexture);
-    fDeleteRenderbuffers(1, &mOffscreenColorRB);
-    fDeleteRenderbuffers(1, &mOffscreenDepthRB);
-    fDeleteRenderbuffers(1, &mOffscreenStencilRB);
-
-    // Update currently bound references if we're changing what they were point to
-    // This way we don't rebind to old buffers when we're done here
-    if (curBoundFramebufferDraw == mOffscreenDrawFBO)
-        curBoundFramebufferDraw = newOffscreenDrawFBO;
-    if (curBoundFramebufferRead == mOffscreenReadFBO)
-        curBoundFramebufferRead = newOffscreenReadFBO;
-    if (curBoundTexture == mOffscreenTexture)
-        curBoundTexture = newOffscreenTexture;
-    if (curBoundRenderbuffer == mOffscreenColorRB)
-        curBoundRenderbuffer = newOffscreenColorRB;
-    else if (curBoundRenderbuffer == mOffscreenDepthRB)
-        curBoundRenderbuffer = newOffscreenDepthRB;
-    else if (curBoundRenderbuffer == mOffscreenStencilRB)
-        curBoundRenderbuffer = newOffscreenStencilRB;
+    // Success, so switch everything out.
+    // Store current user FBO bindings.
+    GLuint boundDrawFBO = GetUserBoundDrawFBO();
+    GLuint boundReadFBO = GetUserBoundReadFBO();
 
     // Replace with the new hotness
-    mOffscreenDrawFBO = newOffscreenDrawFBO;
-    mOffscreenReadFBO = newOffscreenReadFBO;
-    mOffscreenTexture = newOffscreenTexture;
-    mOffscreenColorRB = newOffscreenColorRB;
-    mOffscreenDepthRB = newOffscreenDepthRB;
-    mOffscreenStencilRB = newOffscreenStencilRB;
-
+    std::swap(mOffscreenDrawFBO, drawFBO);
+    std::swap(mOffscreenReadFBO, readFBO);
+    std::swap(mOffscreenColorRB, colorMSRB);
+    std::swap(mOffscreenDepthRB, depthRB);
+    std::swap(mOffscreenStencilRB, stencilRB);
+    std::swap(mOffscreenTexture, texture);
+
+    // Delete the old and busted
+    fDeleteFramebuffers(1, &drawFBO);
+    fDeleteFramebuffers(1, &readFBO);
+    fDeleteRenderbuffers(1, &colorMSRB);
+    fDeleteRenderbuffers(1, &depthRB);
+    fDeleteRenderbuffers(1, &stencilRB);
+    fDeleteTextures(1, &texture);
+
+    // Rebind user FBOs, in case anything changed internally.
+    BindUserDrawFBO(boundDrawFBO);
+    BindUserReadFBO(boundReadFBO);
+
+    // Newly-created buffers are...unlikely to match.
+    ForceDirtyFBOs();
+
+    // Finish up.
     mOffscreenSize = aSize;
     mOffscreenActualSize = aSize;
-
     mActualFormat = cf;
 
-#ifdef DEBUG
     if (DebugMode()) {
         printf_stderr("Resized %dx%d offscreen FBO: r: %d g: %d b: %d a: %d depth: %d stencil: %d samples: %d\n",
                       mOffscreenActualSize.width, mOffscreenActualSize.height,
                       mActualFormat.red, mActualFormat.green, mActualFormat.blue, mActualFormat.alpha,
                       mActualFormat.depth, mActualFormat.stencil, mActualFormat.samples);
     }
-#endif
-
-    // We're good, and the framebuffer is already attached.
-    // Now restore the GL state back to what it was before the resize took place.
-    // If the user was using fb 0, this will bind the offscreen framebuffer we
-    // just created.
-    BindUserDrawFBO(curBoundFramebufferDraw);
-    BindUserReadFBO(curBoundFramebufferRead);
-    fBindTexture(LOCAL_GL_TEXTURE_2D, curBoundTexture);
-    fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, curBoundRenderbuffer);
-    fViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
-
-    // Make sure we know that the buffers are new and thus dirty:
-    ForceDirtyFBOs();
 
     return true;
 }
 
 void
-GLContext::DeleteOffscreenFBO()
+GLContext::DeleteOffscreenFBOs()
 {
     fDeleteFramebuffers(1, &mOffscreenDrawFBO);
     fDeleteFramebuffers(1, &mOffscreenReadFBO);
     fDeleteTextures(1, &mOffscreenTexture);
     fDeleteRenderbuffers(1, &mOffscreenColorRB);
     fDeleteRenderbuffers(1, &mOffscreenDepthRB);
     fDeleteRenderbuffers(1, &mOffscreenStencilRB);
 
@@ -1765,17 +1739,17 @@ GLContext::UpdateActualFormat()
 
 void
 GLContext::MarkDestroyed()
 {
     if (IsDestroyed())
         return;
 
     MakeCurrent();
-    DeleteOffscreenFBO();
+    DeleteOffscreenFBOs();
 
     fDeleteProgram(mBlitProgram);
     mBlitProgram = 0;
     fDeleteFramebuffers(1, &mBlitFramebuffer);
     mBlitFramebuffer = 0;
 
     mSymbols.Zero();
 }
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -826,17 +826,17 @@ public:
      * If it returns false, the context should be treated as unusable
      * and should be recreated.  After the resize, the viewport is not
      * changed; glViewport should be called as appropriate.
      *
      * Only valid if IsOffscreen() returns true.
      */
     virtual bool ResizeOffscreen(const gfxIntSize& aNewSize) {
         if (mOffscreenDrawFBO || mOffscreenReadFBO)
-            return ResizeOffscreenFBO(aNewSize, mOffscreenReadFBO != 0);
+            return ResizeOffscreenFBOs(aNewSize, mOffscreenReadFBO != 0);
         return false;
     }
 
     /*
      * Return size of this offscreen context.
      *
      * Only valid if IsOffscreen() returns true.
      */
@@ -1631,40 +1631,72 @@ protected:
     // lazy-initialized things
     GLuint mBlitProgram, mBlitFramebuffer;
     void UseBlitProgram();
     void SetBlitFramebufferForDestTexture(GLuint aTexture);
 
     // Helper to create/resize an offscreen FBO,
     // for offscreen implementations that use FBOs.
     // Note that it does -not- clear the resized buffers.
-    bool ResizeOffscreenFBO(const gfxIntSize& aSize, const bool aUseReadFBO, const bool aDisableAA);
-    bool ResizeOffscreenFBO(const gfxIntSize& aSize, const bool aUseReadFBO) {
-        if (ResizeOffscreenFBO(aSize, aUseReadFBO, false))
-            return true;
-
-        if (!mCreationFormat.samples) {
-            NS_WARNING("ResizeOffscreenFBO failed to resize non-AA context!");
+    bool ResizeOffscreenFBOs(const ContextFormat& aCF, const gfxIntSize& aSize, const bool aNeedsReadBuffer);
+    bool ResizeOffscreenFBOs(const gfxIntSize& aSize, const bool aNeedsReadBuffer) {
+        if (!IsOffscreenSizeAllowed(aSize))
             return false;
-        } else {
-            NS_WARNING("ResizeOffscreenFBO failed to resize AA context! Falling back to no AA...");
+
+        ContextFormat format(mCreationFormat);
+
+        if (format.samples) {
+            // AA path
+            if (ResizeOffscreenFBOs(format, aSize, aNeedsReadBuffer))
+                return true;
+
+            NS_WARNING("ResizeOffscreenFBOs failed to resize an AA context! Falling back to no AA...");
+            format.samples = 0;
         }
 
-        if (DebugMode()) {
-            printf_stderr("Requested level of multisampling is unavailable, continuing without multisampling\n");
-        }
-
-        if (ResizeOffscreenFBO(aSize, aUseReadFBO, true))
+        if (ResizeOffscreenFBOs(format, aSize, aNeedsReadBuffer))
             return true;
 
-        NS_WARNING("ResizeOffscreenFBO failed to resize AA context even without AA!");
+        NS_WARNING("ResizeOffscreenFBOs failed to resize non-AA context!");
         return false;
     }
 
-    void DeleteOffscreenFBO();
+    struct GLFormats {
+        GLFormats()
+            : texColor(0)
+            , texColorType(0)
+            , rbColor(0)
+            , depthStencil(0)
+            , depth(0)
+            , stencil(0)
+            , samples(0)
+        {}
+
+        GLenum texColor;
+        GLenum texColorType;
+        GLenum rbColor;
+        GLenum depthStencil;
+        GLenum depth;
+        GLenum stencil;
+        GLsizei samples;
+    };
+
+    GLFormats ChooseGLFormats(ContextFormat& aCF);
+    void CreateTextureForOffscreen(const GLFormats& aFormats, const gfxIntSize& aSize,
+                                   GLuint& texture);
+    void CreateRenderbuffersForOffscreen(const GLContext::GLFormats& aFormats, const gfxIntSize& aSize,
+                                         GLuint& colorMSRB, GLuint& depthRB, GLuint& stencilRB);
+    bool AssembleOffscreenFBOs(const GLuint colorMSRB,
+                               const GLuint depthRB,
+                               const GLuint stencilRB,
+                               const GLuint texture,
+                               GLuint& drawFBO,
+                               GLuint& readFBO);
+
+    void DeleteOffscreenFBOs();
 
     GLuint mOffscreenDrawFBO;
     GLuint mOffscreenReadFBO;
     GLuint mOffscreenColorRB;
     GLuint mOffscreenDepthRB;
     GLuint mOffscreenStencilRB;
 
     ExtensionBitset<Extensions_Max> mAvailableExtensions;
--- a/gfx/gl/GLContextProviderCGL.mm
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -284,17 +284,17 @@ GLContextCGL::ResizeOffscreen(const gfxI
                                    textureInternalFormat:(mCreationFormat.alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB)
                                    textureMaxMipMapLevel:0
                                    pixelsWide:aNewSize.width
                                    pixelsHigh:aNewSize.height];
         if (!pb) {
             return false;
         }
 
-        if (!ResizeOffscreenFBO(aNewSize, false)) {
+        if (!ResizeOffscreenFBOs(aNewSize, false)) {
             [pb release];
             return false;
         }
 
         [mPBuffer release];
         mPBuffer = pb;
 
         mOffscreenSize = aNewSize;
@@ -304,17 +304,17 @@ GLContextCGL::ResizeOffscreen(const gfxI
          currentVirtualScreen:[mContext currentVirtualScreen]];
 
         MakeCurrent();
         ClearSafely();
 
         return true;
     }
 
-    return ResizeOffscreenFBO(aNewSize, true);
+    return ResizeOffscreenFBOs(aNewSize, true);
 }
 
 class TextureImageCGL : public BasicTextureImage
 {
     friend already_AddRefed<TextureImage>
     GLContextCGL::CreateBasicTextureImage(GLuint,
                                           const nsIntSize&,
                                           GLenum,
@@ -596,30 +596,30 @@ GLContextProviderCGL::CreateOffscreen(co
     
     NS_ENSURE_TRUE(Preferences::GetRootBranch(), nsnull);
     const bool preferFBOs = Preferences::GetBool("cgl.prefer-fbo", true);
     if (!preferFBOs)
     {
         glContext = CreateOffscreenPBufferContext(aSize, actualFormat);
         if (glContext &&
             glContext->Init() &&
-            glContext->ResizeOffscreenFBO(aSize, false))
+            glContext->ResizeOffscreenFBOs(aSize, false))
         {
             glContext->mOffscreenSize = aSize;
             glContext->mOffscreenActualSize = aSize;
 
             return glContext.forget();
         }
     }
 
     // try a FBO as second choice
     glContext = CreateOffscreenFBOContext(actualFormat);
     if (glContext &&
         glContext->Init() &&
-        glContext->ResizeOffscreenFBO(aSize, true))
+        glContext->ResizeOffscreenFBOs(aSize, true))
     {
         return glContext.forget();
     }
 
     // everything failed
     return nsnull;
 }
 
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -719,17 +719,17 @@ GLContextEGL::ResizeOffscreen(const gfxI
                                                     : LOCAL_EGL_TEXTURE_RGB)
                                                  : LOCAL_EGL_NONE,
                                                  pbsize);
         if (!surface) {
             NS_WARNING("Failed to resize pbuffer");
             return false;
         }
 
-        if (!ResizeOffscreenFBO(pbsize, false))
+        if (!ResizeOffscreenFBOs(pbsize, false))
             return false;
 
         SetOffscreenSize(aNewSize, pbsize);
 
         if (mSurface && !mPlatformContext) {
             sEGLLibrary.fDestroySurface(EGL_DISPLAY(), mSurface);
         }
 
@@ -766,33 +766,33 @@ GLContextEGL::ResizeOffscreen(const gfxI
 
         EGLSurface surface;
         EGLConfig config = 0;
         int depth = gfxUtils::ImageFormatToDepth(gfxPlatform::GetPlatform()->GetOffscreenFormat());
         surface = CreateEGLSurfaceForXSurface(xsurface, &config, depth);
         if (!config) {
             return false;
         }
-        if (!ResizeOffscreenFBO(aNewSize, true))
+        if (!ResizeOffscreenFBOs(aNewSize, true))
             return false;
 
         mThebesSurface = xsurface;
 
         return true;
     }
 #endif
 
 #if defined(MOZ_X11) && defined(MOZ_EGL_XRENDER_COMPOSITE)
     if (ResizeOffscreenPixmapSurface(aNewSize)) {
-        if (ResizeOffscreenFBO(aNewSize, true))
+        if (ResizeOffscreenFBOs(aNewSize, true))
             return true;
     }
 #endif
 
-    return ResizeOffscreenFBO(aNewSize, true);
+    return ResizeOffscreenFBOs(aNewSize, true);
 }
 
 
 static GLContextEGL *
 GetGlobalContextEGL()
 {
     return static_cast<GLContextEGL*>(GLContextProviderEGL::GetGlobalContext());
 }
@@ -1959,40 +1959,40 @@ GLContextProviderEGL::CreateOffscreen(co
     gfxIntSize pbufferSize = usePBuffers ? aSize : gfxIntSize(16, 16);
     nsRefPtr<GLContextEGL> glContext =
         GLContextEGL::CreateEGLPBufferOffscreenContext(pbufferSize, aFormat, !usePBuffers);
 
     if (!glContext)
         return nsnull;
 
     gfxIntSize fboSize = usePBuffers ? glContext->OffscreenActualSize() : aSize;
-    if (!(aFlags & GLContext::ContextFlagsGlobal) && !glContext->ResizeOffscreenFBO(fboSize, !usePBuffers))
+    if (!(aFlags & GLContext::ContextFlagsGlobal) && !glContext->ResizeOffscreenFBOs(fboSize, !usePBuffers))
         return nsnull;
 
     return glContext.forget();
 #elif defined(MOZ_X11) && defined(MOZ_EGL_XRENDER_COMPOSITE)
     nsRefPtr<GLContextEGL> glContext =
         GLContextEGL::CreateBasicEGLPixmapOffscreenContext(aSize, aFormat);
 
     if (!glContext)
         return nsnull;
 
-    if (!(aFlags & GLContext::ContextFlagsGlobal) && !glContext->ResizeOffscreenFBO(glContext->OffscreenActualSize(), true))
+    if (!(aFlags & GLContext::ContextFlagsGlobal) && !glContext->ResizeOffscreenFBOs(glContext->OffscreenActualSize(), true))
         return nsnull;
 
     return glContext.forget();
 #elif defined(MOZ_X11)
     nsRefPtr<GLContextEGL> glContext =
         GLContextEGL::CreateEGLPixmapOffscreenContext(aSize, aFormat, true);
 
     if (!glContext) {
         return nsnull;
     }
 
-    if (!(aFlags & GLContext::ContextFlagsGlobal) && !gUseBackingSurface && !glContext->ResizeOffscreenFBO(glContext->OffscreenActualSize(), true)) {
+    if (!(aFlags & GLContext::ContextFlagsGlobal) && !gUseBackingSurface && !glContext->ResizeOffscreenFBOs(glContext->OffscreenActualSize(), true)) {
         // we weren't able to create the initial
         // offscreen FBO, so this is dead
         return nsnull;
     }
     return glContext.forget();
 #else
     return nsnull;
 #endif
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -1302,17 +1302,17 @@ GLContextProviderGLX::CreateOffscreen(co
     }
 
     if (!glContext->GetSharedContext()) {
         // no point in returning anything if sharing failed, we can't
         // render from this
         return nsnull;
     }
 
-    if (!glContext->ResizeOffscreenFBO(aSize, true)) {
+    if (!glContext->ResizeOffscreenFBOs(aSize, true)) {
         // we weren't able to create the initial
         // offscreen FBO, so this is dead
         return nsnull;
     }
 
     return glContext.forget();
 }
 
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -519,20 +519,20 @@ GLContextWGL::ResizeOffscreen(const gfxI
         mDC = sWGLLibrary.fGetPbufferDC(mPBuffer);
 
         mOffscreenSize = aNewSize;
         mOffscreenActualSize = aNewSize;
 
         MakeCurrent();
         ClearSafely();
 
-        return ResizeOffscreenFBO(aNewSize, false);
+        return ResizeOffscreenFBOs(aNewSize, false);
     }
 
-    return ResizeOffscreenFBO(aNewSize, true);
+    return ResizeOffscreenFBOs(aNewSize, true);
 }
 
 static GLContextWGL *
 GetGlobalContextWGL()
 {
     return static_cast<GLContextWGL*>(GLContextProviderWGL::GetGlobalContext());
 }
 
@@ -777,17 +777,17 @@ GLContextProviderWGL::CreateOffscreen(co
     }
 
     if (!glContext ||
         !glContext->Init())
     {
         return nsnull;
     }
 
-    if (!glContext->ResizeOffscreenFBO(aSize, !glContext->mPBuffer))
+    if (!glContext->ResizeOffscreenFBOs(aSize, !glContext->mPBuffer))
         return nsnull;
 
     glContext->mOffscreenSize = aSize;
     glContext->mOffscreenActualSize = aSize;
 
     return glContext.forget();
 }