Bug 607774 - Implement the spec on Renderbuffers and Framebuffers - r=vladimir
authorBenoit Jacob <bjacob@mozilla.com>
Fri, 05 Nov 2010 15:57:58 -0400
changeset 56959 84906fd350717090877c84a15c146e2890220319
parent 56958 0518ede13821896959a71c5a97b3b32d0fd13f8e
child 56960 3769e11d18d6a18b05c277242f53180165eb9c17
push id16738
push userbjacob@mozilla.com
push dateFri, 05 Nov 2010 19:58:30 +0000
treeherdermozilla-central@3769e11d18d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvladimir
bugs607774
milestone2.0b8pre
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 607774 - Implement the spec on Renderbuffers and Framebuffers - r=vladimir
content/canvas/src/WebGLContext.h
content/canvas/src/WebGLContextGL.cpp
content/canvas/src/WebGLContextValidate.cpp
content/canvas/test/webgl/failing_tests.txt
dom/interfaces/canvas/nsICanvasRenderingContextWebGL.idl
gfx/thebes/GLDefs.h
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -469,20 +469,17 @@ protected:
     // textures bound to 
     nsTArray<WebGLObjectRefPtr<WebGLTexture> > mBound2DTextures;
     nsTArray<WebGLObjectRefPtr<WebGLTexture> > mBoundCubeMapTextures;
 
     WebGLObjectRefPtr<WebGLBuffer> mBoundArrayBuffer;
     WebGLObjectRefPtr<WebGLBuffer> mBoundElementArrayBuffer;
     WebGLObjectRefPtr<WebGLProgram> mCurrentProgram;
 
-    // XXX these 3 are wrong types, and aren't used atm (except for the length of the attachments)
-    nsTArray<WebGLObjectRefPtr<WebGLTexture> > mFramebufferColorAttachments;
-    nsRefPtr<WebGLFramebuffer> mFramebufferDepthAttachment;
-    nsRefPtr<WebGLFramebuffer> mFramebufferStencilAttachment;
+    PRUint32 mMaxFramebufferColorAttachments;
 
     nsRefPtr<WebGLFramebuffer> mBoundFramebuffer;
     nsRefPtr<WebGLRenderbuffer> mBoundRenderbuffer;
 
     // lookup tables for GL name -> object wrapper
     nsRefPtrHashtable<nsUint32HashKey, WebGLTexture> mMapTextures;
     nsRefPtrHashtable<nsUint32HashKey, WebGLBuffer> mMapBuffers;
     nsRefPtrHashtable<nsUint32HashKey, WebGLProgram> mMapPrograms;
@@ -505,16 +502,17 @@ protected:
 public:
     // console logging helpers
     static void LogMessage(const char *fmt, ...);
     static void LogMessage(const char *fmt, va_list ap);
     void LogMessageIfVerbose(const char *fmt, ...);
     void LogMessageIfVerbose(const char *fmt, va_list ap);
 
     friend class WebGLTexture;
+    friend class WebGLFramebuffer;
 };
 
 // this class is a mixin for the named type wrappers, and is used
 // by WebGLObjectRefPtr to tell the object who holds references, so that
 // we can zero them out appropriately when the object is deleted, because
 // it will be unbound in the GL.
 class WebGLZeroingObject
 {
@@ -1231,16 +1229,17 @@ public:
     PRBool NextGeneration()
     {
         if (!(mGeneration+1).valid())
             return PR_FALSE; // must exit without changing mGeneration
         ++mGeneration;
         mMapUniformLocations.Clear();
         return PR_TRUE;
     }
+    
 
     already_AddRefed<WebGLUniformLocation> GetUniformLocationObject(GLint glLocation);
 
     /* Called only after LinkProgram */
     PRBool UpdateInfo(gl::GLContext *gl);
 
     /* Getters for cached program info */
     WebGLint UniformMaxNameLength() const { return mUniformMaxNameLength; }
@@ -1262,84 +1261,371 @@ protected:
     GLint mAttribMaxNameLength;
     GLint mUniformCount;
     GLint mAttribCount;
     std::vector<bool> mAttribsInUse;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(WebGLProgram, WEBGLPROGRAM_PRIVATE_IID)
 
+#define WEBGLRENDERBUFFER_PRIVATE_IID \
+    {0x3cbc2067, 0x5831, 0x4e3f, {0xac, 0x52, 0x7e, 0xf4, 0x5c, 0x04, 0xff, 0xae}}
+class WebGLRenderbuffer :
+    public nsIWebGLRenderbuffer,
+    public WebGLZeroingObject,
+    public WebGLRectangleObject,
+    public WebGLContextBoundObject
+{
+public:
+    NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLRENDERBUFFER_PRIVATE_IID)
+
+    WebGLRenderbuffer(WebGLContext *context, WebGLuint name, WebGLuint secondBufferName = 0) :
+        WebGLContextBoundObject(context),
+        mName(name),
+        mInternalFormat(0),
+        mDeleted(PR_FALSE), mInitialized(PR_FALSE)
+    { }
+
+    void Delete() {
+        if (mDeleted)
+            return;
+        ZeroOwners();
+        mDeleted = PR_TRUE;
+    }
+    PRBool Deleted() const { return mDeleted; }
+    WebGLuint GLName() const { return mName; }
+
+    PRBool Initialized() const { return mInitialized; }
+    void SetInitialized(PRBool aInitialized) { mInitialized = aInitialized; }
+
+    WebGLenum InternalFormat() const { return mInternalFormat; }
+    void SetInternalFormat(WebGLenum aInternalFormat) { mInternalFormat = aInternalFormat; }
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIWEBGLRENDERBUFFER
+
+protected:
+    WebGLuint mName;
+    WebGLenum mInternalFormat;
+
+    PRBool mDeleted;
+    PRBool mInitialized;
+
+    friend class WebGLFramebuffer;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(WebGLRenderbuffer, WEBGLRENDERBUFFER_PRIVATE_IID)
+
 #define WEBGLFRAMEBUFFER_PRIVATE_IID \
     {0x0052a16f, 0x4bc9, 0x4a55, {0x9d, 0xa3, 0x54, 0x95, 0xaa, 0x4e, 0x80, 0xb9}}
 class WebGLFramebuffer :
     public nsIWebGLFramebuffer,
     public WebGLZeroingObject,
     public WebGLRectangleObject,
     public WebGLContextBoundObject
 {
 public:
     NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLFRAMEBUFFER_PRIVATE_IID)
 
     WebGLFramebuffer(WebGLContext *context, WebGLuint name) :
         WebGLContextBoundObject(context),
-        mName(name), mDeleted(PR_FALSE)
+        mName(name), mDeleted(PR_FALSE),
+        mHasDepthAttachment(PR_FALSE),
+        mHasStencilAttachment(PR_FALSE),
+        mHasDepthStencilAttachment(PR_FALSE)
     { }
 
     void Delete() {
         if (mDeleted)
             return;
         ZeroOwners();
         mDeleted = PR_TRUE;
     }
     PRBool Deleted() { return mDeleted; }
     WebGLuint GLName() { return mName; }
 
+    nsresult FramebufferRenderbuffer(WebGLenum target,
+                                     WebGLenum attachment,
+                                     WebGLenum rbtarget,
+                                     nsIWebGLRenderbuffer *rbobj)
+    {
+        WebGLuint renderbuffername;
+        PRBool isNull;
+        WebGLRenderbuffer *wrb;
+
+        if (!mContext->GetConcreteObjectAndGLName("framebufferRenderbuffer: renderbuffer",
+                                                  rbobj, &wrb, &renderbuffername, &isNull))
+        {
+            return NS_OK;
+        }
+
+        if (target != LOCAL_GL_FRAMEBUFFER)
+            return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: target", target);
+
+        if (rbtarget != LOCAL_GL_RENDERBUFFER)
+            return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget);
+
+        const char *badAttachmentFormatMsg =
+            "framebufferRenderbuffer: this renderbuffer does not have a suitable format for this attachment point";
+
+        switch (attachment) {
+        case LOCAL_GL_DEPTH_ATTACHMENT:
+            if (!isNull) {
+                if (wrb->mInternalFormat != LOCAL_GL_DEPTH_COMPONENT16)
+                    return mContext->ErrorInvalidOperation(badAttachmentFormatMsg);
+            }
+            mDepthOrStencilRenderbufferAttachment = wrb;
+            mHasDepthAttachment = !isNull;
+            break;
+        case LOCAL_GL_STENCIL_ATTACHMENT:
+            if (!isNull) {
+                if (wrb->mInternalFormat != LOCAL_GL_STENCIL_INDEX8)
+                    return mContext->ErrorInvalidOperation(badAttachmentFormatMsg);
+            }
+            mDepthOrStencilRenderbufferAttachment = wrb;
+            mHasStencilAttachment = !isNull;
+            break;
+        case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
+            if (!isNull) {
+                if (wrb->mInternalFormat != LOCAL_GL_DEPTH_STENCIL)
+                    return mContext->ErrorInvalidOperation(badAttachmentFormatMsg);
+            }
+            mDepthOrStencilRenderbufferAttachment = wrb;
+            mHasDepthStencilAttachment = !isNull;
+            break;
+        default:
+            // finish checking that the 'attachment' parameter is among the allowed values
+            if ((attachment < LOCAL_GL_COLOR_ATTACHMENT0 ||
+                 attachment >= LOCAL_GL_COLOR_ATTACHMENT0 + mContext->mMaxFramebufferColorAttachments))
+            {
+                return mContext->ErrorInvalidEnumInfo("framebufferRenderbuffer: attachment", attachment);
+            }
+            if (!isNull) {
+                if (wrb->mInternalFormat != LOCAL_GL_RGBA4 &&
+                    wrb->mInternalFormat != LOCAL_GL_RGB565 &&
+                    wrb->mInternalFormat != LOCAL_GL_RGB5_A1)
+                {
+                    return mContext->ErrorInvalidOperation(badAttachmentFormatMsg);
+                }
+            }
+            mColorRenderbufferAttachment = wrb;
+            break;
+        }
+
+        // dimensions are kept for readPixels primarily, function only uses COLOR_ATTACHMENT0
+        if (attachment == LOCAL_GL_COLOR_ATTACHMENT0)
+            setDimensions(wrb);
+
+        mContext->MakeContextCurrent();
+        mContext->gl->fFramebufferRenderbuffer(target, attachment, rbtarget, renderbuffername);
+
+        return NS_OK;
+    }
+
+    nsresult FramebufferTexture2D(WebGLenum target,
+                                  WebGLenum attachment,
+                                  WebGLenum textarget,
+                                  nsIWebGLTexture *tobj,
+                                  WebGLint level)
+    {
+        WebGLuint texturename;
+        PRBool isNull;
+        WebGLTexture *wtex;
+
+        if (!mContext->GetConcreteObjectAndGLName("framebufferTexture2D: texture",
+                                                  tobj, &wtex, &texturename, &isNull))
+        {
+            return NS_OK;
+        }
+
+        if (target != LOCAL_GL_FRAMEBUFFER)
+            return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: target", target);
+
+        if (!isNull && textarget != LOCAL_GL_TEXTURE_2D &&
+            (textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
+            textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
+            return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: invalid texture target", textarget);
+
+        if (!isNull && level > 0)
+            return mContext->ErrorInvalidValue("framebufferTexture2D: level must be 0");
+
+        switch (attachment) {
+        case LOCAL_GL_DEPTH_ATTACHMENT:
+        case LOCAL_GL_STENCIL_ATTACHMENT:
+        case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
+            return mContext->ErrorInvalidOperation("framebufferTexture2D: depth and stencil attachments can "
+                          "only be renderbuffers, not textures, as there is no suitable texture format.");
+            break;
+        default:
+            if ((attachment < LOCAL_GL_COLOR_ATTACHMENT0 ||
+                 attachment >= LOCAL_GL_COLOR_ATTACHMENT0 + mContext->mMaxFramebufferColorAttachments))
+            {
+                return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: attachment", attachment);
+            }
+            // nothing to do for color buffers. all textures have a color-renderable format.
+            break;
+        }
+
+        // dimensions are kept for readPixels primarily, function only uses COLOR_ATTACHMENT0
+        if (attachment == LOCAL_GL_COLOR_ATTACHMENT0)
+            setDimensions(wtex);
+
+        mContext->MakeContextCurrent();
+        mContext->gl->fFramebufferTexture2D(target, attachment, textarget, texturename, level);
+
+        return NS_OK;
+    }
+
+    // implement inline, as it's performance critical (called by draw-functions).
+    // the generic case for which we're optimizing is the case where there's nothing to initialize.
+    inline PRBool CheckAndInitializeRenderbuffers()
+    {
+        if (HasConflictingAttachments()) {
+            mContext->SynthesizeGLError(LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION);
+            return PR_FALSE;
+        }
+
+        if ((mColorRenderbufferAttachment          && !mColorRenderbufferAttachment->Initialized()) ||
+            (mDepthOrStencilRenderbufferAttachment && !mDepthOrStencilRenderbufferAttachment->Initialized()))
+        {
+            InitializeRenderbuffers();
+        }
+
+        return PR_TRUE;
+    }
+
     NS_DECL_ISUPPORTS
     NS_DECL_NSIWEBGLFRAMEBUFFER
+
+    PRBool HasConflictingAttachments() const {
+        return int(mHasDepthAttachment) +
+               int(mHasStencilAttachment) +
+               int(mHasDepthStencilAttachment) > 1;
+    }
+
 protected:
+
+    // protected because WebGLContext should only call InitializeRenderbuffers
+    void InitializeRenderbuffers()
+    {
+        mContext->MakeContextCurrent();
+
+        if (mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) != LOCAL_GL_FRAMEBUFFER_COMPLETE)
+            return;
+
+        PRBool initializeColorBuffer = mColorRenderbufferAttachment &&
+                                       !mColorRenderbufferAttachment->Initialized();
+        PRBool initializeDepthOrStencilBuffer = mDepthOrStencilRenderbufferAttachment &&
+                                                !mDepthOrStencilRenderbufferAttachment->Initialized();
+        PRBool initializeDepthBuffer = initializeDepthOrStencilBuffer && HasDepthBuffer();
+        PRBool initializeStencilBuffer = initializeDepthOrStencilBuffer && HasStencilBuffer();
+
+        realGLboolean savedColorMask[] = {0}, savedDepthMask = 0;
+        GLuint savedStencilMask = 0;
+        GLfloat savedColorClearValue[] = {0.f}, savedDepthClearValue = 0.f;
+        GLint savedStencilClearValue = 0;
+        GLuint clearBits = 0;
+
+        realGLboolean wasScissorTestEnabled = mContext->gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST);
+        mContext->gl->fDisable(LOCAL_GL_SCISSOR_TEST);
+
+        realGLboolean wasDitherEnabled = mContext->gl->fIsEnabled(LOCAL_GL_DITHER);
+        mContext->gl->fDisable(LOCAL_GL_DITHER);
+
+        mContext->gl->PushViewportRect(nsIntRect(0,0,width(),height()));
+
+        if (initializeColorBuffer) {
+            mContext->gl->fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, savedColorMask);
+            mContext->gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, savedColorClearValue);
+            mContext->gl->fColorMask(1, 1, 1, 1);
+            mContext->gl->fClearColor(0.f, 0.f, 0.f, 0.f);
+            clearBits |= LOCAL_GL_COLOR_BUFFER_BIT;
+        }
+
+        if (initializeDepthBuffer) {
+            mContext->gl->fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK, &savedDepthMask);
+            mContext->gl->fGetFloatv(LOCAL_GL_DEPTH_CLEAR_VALUE, &savedDepthClearValue);
+            mContext->gl->fDepthMask(1);
+            mContext->gl->fClearDepth(0.f);
+            clearBits |= LOCAL_GL_DEPTH_BUFFER_BIT;
+        }
+
+        if (initializeStencilBuffer) {
+            mContext->gl->fGetIntegerv(LOCAL_GL_STENCIL_WRITEMASK, reinterpret_cast<GLint*>(&savedStencilMask));
+            mContext->gl->fGetIntegerv(LOCAL_GL_STENCIL_CLEAR_VALUE, &savedStencilClearValue);
+            mContext->gl->fStencilMask(0xffffffff);
+            mContext->gl->fClearStencil(0);
+            clearBits |= LOCAL_GL_STENCIL_BUFFER_BIT;
+        }
+
+        // the one useful line of code
+        mContext->gl->fClear(clearBits);
+
+        if (initializeColorBuffer) {
+            mContext->gl->fColorMask(savedColorMask[0],
+                                     savedColorMask[1],
+                                     savedColorMask[2],
+                                     savedColorMask[3]);
+            mContext->gl->fClearColor(savedColorClearValue[0],
+                                      savedColorClearValue[1],
+                                      savedColorClearValue[2],
+                                      savedColorClearValue[3]);
+            mColorRenderbufferAttachment->SetInitialized(PR_TRUE);
+        }
+
+        if (initializeDepthBuffer) {
+            mContext->gl->fDepthMask(savedDepthMask);
+            mContext->gl->fClearDepth(savedDepthClearValue);
+            mDepthOrStencilRenderbufferAttachment->SetInitialized(PR_TRUE);
+        }
+
+        if (initializeStencilBuffer) {
+            mContext->gl->fStencilMask(savedStencilMask);
+            mContext->gl->fClearStencil(savedStencilClearValue);
+            mDepthOrStencilRenderbufferAttachment->SetInitialized(PR_TRUE);
+        }
+
+        mContext->gl->PopViewportRect();
+
+        if (wasDitherEnabled)
+            mContext->gl->fEnable(LOCAL_GL_DITHER);
+        else
+            mContext->gl->fDisable(LOCAL_GL_DITHER);
+
+        if (wasScissorTestEnabled)
+            mContext->gl->fEnable(LOCAL_GL_DITHER);
+        else
+            mContext->gl->fDisable(LOCAL_GL_SCISSOR_TEST);
+    }
+
+    PRBool HasDepthBuffer() const {
+        return mHasDepthAttachment || mHasDepthStencilAttachment;
+    }
+
+    PRBool HasStencilBuffer() const {
+        return mHasStencilAttachment || mHasDepthStencilAttachment;
+    }
+
     WebGLuint mName;
     PRBool mDeleted;
+
+    // we only store pointers to attached renderbuffers, not to attached textures, because
+    // we will only need to initialize renderbuffers. Textures are already initialized.
+    nsRefPtr<WebGLRenderbuffer> mColorRenderbufferAttachment;
+    nsRefPtr<WebGLRenderbuffer> mDepthOrStencilRenderbufferAttachment;
+
+    // these boolean values keep track of all attachments: renderbuffers and textures.
+    // thus they are not at all redundant with the above member pointers.
+    PRBool mHasDepthAttachment;
+    PRBool mHasStencilAttachment;
+    PRBool mHasDepthStencilAttachment;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(WebGLFramebuffer, WEBGLFRAMEBUFFER_PRIVATE_IID)
 
-#define WEBGLRENDERBUFFER_PRIVATE_IID \
-    {0x3cbc2067, 0x5831, 0x4e3f, {0xac, 0x52, 0x7e, 0xf4, 0x5c, 0x04, 0xff, 0xae}}
-class WebGLRenderbuffer :
-    public nsIWebGLRenderbuffer,
-    public WebGLZeroingObject,
-    public WebGLRectangleObject,
-    public WebGLContextBoundObject
-{
-public:
-    NS_DECLARE_STATIC_IID_ACCESSOR(WEBGLRENDERBUFFER_PRIVATE_IID)
-
-    WebGLRenderbuffer(WebGLContext *context, WebGLuint name) :
-        WebGLContextBoundObject(context),
-        mName(name), mDeleted(PR_FALSE)
-    { }
-
-    void Delete() {
-        if (mDeleted)
-            return;
-        ZeroOwners();
-        mDeleted = PR_TRUE;
-    }
-    PRBool Deleted() { return mDeleted; }
-    WebGLuint GLName() { return mName; }
-
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIWEBGLRENDERBUFFER
-protected:
-    WebGLuint mName;
-    PRBool mDeleted;
-};
-
-NS_DEFINE_STATIC_IID_ACCESSOR(WebGLRenderbuffer, WEBGLRENDERBUFFER_PRIVATE_IID)
-
 #define WEBGLUNIFORMLOCATION_PRIVATE_IID \
     {0x01a8a614, 0xb109, 0x42f1, {0xb4, 0x40, 0x8d, 0x8b, 0x87, 0x0b, 0x43, 0xa7}}
 class WebGLUniformLocation :
     public nsIWebGLUniformLocation,
     public WebGLZeroingObject,
     public WebGLContextBoundObject
 {
 public:
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -517,26 +517,34 @@ WebGLContext::BufferSubData_array(WebGLe
 
 NS_IMETHODIMP
 WebGLContext::CheckFramebufferStatus(WebGLenum target, WebGLenum *retval)
 {
     *retval = 0;
 
     MakeContextCurrent();
     if (target != LOCAL_GL_FRAMEBUFFER)
-        return ErrorInvalidEnum("CheckFramebufferStatus: target must be FRAMEBUFFER");
-
-    *retval = gl->fCheckFramebufferStatus(target);
+        return ErrorInvalidEnum("checkFramebufferStatus: target must be FRAMEBUFFER");
+
+    if (mBoundFramebuffer && mBoundFramebuffer->HasConflictingAttachments())
+        *retval = LOCAL_GL_FRAMEBUFFER_UNSUPPORTED;
+    else
+        *retval = gl->fCheckFramebufferStatus(target);
+
     return NS_OK;
 }
 
 NS_IMETHODIMP
 WebGLContext::Clear(PRUint32 mask)
 {
     MakeContextCurrent();
+
+    if (mBoundFramebuffer && !mBoundFramebuffer->CheckAndInitializeRenderbuffers())
+        return NS_OK;
+
     gl->fClear(mask);
     Invalidate();
 
     return NS_OK;
 }
 
 GL_SAME_METHOD_4(ClearColor, ClearColor, WebGLfloat, WebGLfloat, WebGLfloat, WebGLfloat)
 
@@ -755,16 +763,17 @@ WebGLContext::DeleteRenderbuffer(nsIWebG
             must be taken when deleting a renderbuffer object if the image of the renderbuffer
             is attached to a framebuffer object. In this case, if the deleted renderbuffer object is
             attached to the currently bound framebuffer object, it is 
             automatically detached.  However, attachments to any other framebuffer objects are the
             responsibility of the application.
     */
 
     gl->fDeleteRenderbuffers(1, &rbufname);
+
     rbuf->Delete();
     mMapRenderbuffers.Remove(rbufname);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 WebGLContext::DeleteTexture(nsIWebGLTexture *tobj)
@@ -1042,16 +1051,19 @@ WebGLContext::DrawArrays(GLenum mode, We
         return ErrorInvalidOperation("DrawArrays: bound vertex attribute buffers do not have sufficient size for given first and count");
 
     // If count is 0, there's nothing to do.
     if (count == 0)
         return NS_OK;
 
     MakeContextCurrent();
 
+    if (mBoundFramebuffer && !mBoundFramebuffer->CheckAndInitializeRenderbuffers())
+        return NS_OK;
+
     BindFakeBlackTextures();
     DoFakeVertexAttrib0(checked_firstPlusCount.value());
 
     gl->fDrawArrays(mode, first, count);
 
     UndoFakeVertexAttrib0();
     UnbindFakeBlackTextures();
 
@@ -1119,16 +1131,19 @@ WebGLContext::DrawElements(WebGLenum mod
     }
 
     // If count is 0, there's nothing to do.
     if (count == 0)
         return NS_OK;
 
     MakeContextCurrent();
 
+    if (mBoundFramebuffer && !mBoundFramebuffer->CheckAndInitializeRenderbuffers())
+        return NS_OK;
+
     BindFakeBlackTextures();
     DoFakeVertexAttrib0(checked_neededCount.value());
 
     gl->fDrawElements(mode, count, type, (GLvoid*) (byteOffset));
 
     UndoFakeVertexAttrib0();
     UnbindFakeBlackTextures();
 
@@ -1166,100 +1181,36 @@ WebGLContext::EnableVertexAttribArray(We
     MakeContextCurrent();
 
     gl->fEnableVertexAttribArray(index);
     mAttribBuffers[index].enabled = PR_TRUE;
 
     return NS_OK;
 }
 
-// XXX need to track this -- see glDeleteRenderbuffer above and man page for DeleteRenderbuffers
 NS_IMETHODIMP
 WebGLContext::FramebufferRenderbuffer(WebGLenum target, WebGLenum attachment, WebGLenum rbtarget, nsIWebGLRenderbuffer *rbobj)
 {
-    WebGLuint renderbuffername;
-    PRBool isNull;
-    WebGLRenderbuffer *wrb;
-
-    if (!GetConcreteObjectAndGLName("framebufferRenderbuffer: renderbuffer", rbobj, &wrb, &renderbuffername, &isNull))
-        return NS_OK;
-
-    if (target != LOCAL_GL_FRAMEBUFFER)
-        return ErrorInvalidEnumInfo("framebufferRenderbuffer: target", target);
-
-    if ((attachment < LOCAL_GL_COLOR_ATTACHMENT0 ||
-         attachment >= LOCAL_GL_COLOR_ATTACHMENT0 + mFramebufferColorAttachments.Length()) &&
-        attachment != LOCAL_GL_DEPTH_ATTACHMENT &&
-        attachment != LOCAL_GL_STENCIL_ATTACHMENT)
-    {
-        return ErrorInvalidEnumInfo("framebufferRenderbuffer: attachment", attachment);
-    }
-
-    if (rbtarget != LOCAL_GL_RENDERBUFFER)
-        return ErrorInvalidEnumInfo("framebufferRenderbuffer: renderbuffer target:", rbtarget);
-
-    if (!mBoundFramebuffer)
-        return ErrorInvalidOperation("FramebufferRenderbuffer: cannot modify framebuffer 0");
-
-    // dimensions are kept for readPixels primarily, function only uses COLOR_ATTACHMENT0
-    if (attachment == LOCAL_GL_COLOR_ATTACHMENT0)
-        mBoundFramebuffer->setDimensions(wrb);
-
-    MakeContextCurrent();
-
-    gl->fFramebufferRenderbuffer(target, attachment, rbtarget, renderbuffername);
-
-    return NS_OK;
+    if (mBoundFramebuffer)
+        return mBoundFramebuffer->FramebufferRenderbuffer(target, attachment, rbtarget, rbobj);
+    else
+        return ErrorInvalidOperation("framebufferRenderbuffer: cannot modify framebuffer 0");
 }
 
 NS_IMETHODIMP
 WebGLContext::FramebufferTexture2D(WebGLenum target,
                                    WebGLenum attachment,
                                    WebGLenum textarget,
                                    nsIWebGLTexture *tobj,
                                    WebGLint level)
 {
-    WebGLuint texturename;
-    PRBool isNull;
-    WebGLTexture *wtex;
-
-    if (!GetConcreteObjectAndGLName("framebufferTexture2D: texture", tobj, &wtex, &texturename, &isNull))
-        return NS_OK;
-
-    if (target != LOCAL_GL_FRAMEBUFFER)
-        return ErrorInvalidEnumInfo("framebufferTexture2D: target", target);
-
-    if ((attachment < LOCAL_GL_COLOR_ATTACHMENT0 ||
-         attachment >= LOCAL_GL_COLOR_ATTACHMENT0 + mFramebufferColorAttachments.Length()) &&
-        attachment != LOCAL_GL_DEPTH_ATTACHMENT &&
-        attachment != LOCAL_GL_STENCIL_ATTACHMENT)
-        return ErrorInvalidEnumInfo("framebufferTexture2D: attachment", attachment);
-
-    if (textarget != LOCAL_GL_TEXTURE_2D &&
-        (textarget < LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X ||
-         textarget > LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))
-        return ErrorInvalidEnumInfo("framebufferTexture2D: invalid texture target", textarget);
-
-    if (level != 0)
-        return ErrorInvalidValue("FramebufferTexture2D: level must be 0");
-
-    if (!mBoundFramebuffer)
-        return ErrorInvalidOperation("FramebufferTexture2D: cannot modify framebuffer 0");
-
-    // dimensions are kept for readPixels primarily, function only uses COLOR_ATTACHMENT0
-    if (attachment == LOCAL_GL_COLOR_ATTACHMENT0)
-        mBoundFramebuffer->setDimensions(wtex);
-
-    // XXXXX we need to store/reference this attachment!
-
-    MakeContextCurrent();
-
-    gl->fFramebufferTexture2D(target, attachment, textarget, texturename, level);
-
-    return NS_OK;
+    if (mBoundFramebuffer)
+        return mBoundFramebuffer->FramebufferTexture2D(target, attachment, textarget, tobj, level);
+    else
+        return ErrorInvalidOperation("framebufferTexture2D: cannot modify framebuffer 0");
 }
 
 GL_SAME_METHOD_0(Flush, Flush)
 
 GL_SAME_METHOD_0(Finish, Finish)
 
 NS_IMETHODIMP
 WebGLContext::FrontFace(WebGLenum mode)
@@ -2535,18 +2486,16 @@ WebGLContext::ReadPixels_base(WebGLint x
 //         case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
 //         case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
       case LOCAL_GL_UNSIGNED_BYTE:
         break;
       default:
         return ErrorInvalidEnumInfo("ReadPixels: type", type);
     }
 
-    MakeContextCurrent();
-
     CheckedUint32 checked_plainRowSize = CheckedUint32(width) * size;
 
     PRUint32 packAlignment = mPixelStorePackAlignment;
 
     // alignedRowSize = row size rounded up to next multiple of packAlignment
     CheckedUint32 checked_alignedRowSize
         = ((checked_plainRowSize + packAlignment-1) / packAlignment) * packAlignment;
 
@@ -2554,16 +2503,22 @@ WebGLContext::ReadPixels_base(WebGLint x
         = (height-1) * checked_alignedRowSize + checked_plainRowSize;
 
     if (!checked_neededByteLength.valid())
         return ErrorInvalidOperation("ReadPixels: integer overflow computing the needed buffer size");
 
     if (checked_neededByteLength.value() > byteLength)
         return ErrorInvalidOperation("ReadPixels: buffer too small");
 
+    MakeContextCurrent();
+
+    // prevent readback of arbitrary video memory through uninitialized renderbuffers!
+    if (mBoundFramebuffer && !mBoundFramebuffer->CheckAndInitializeRenderbuffers())
+        return NS_OK;
+
     if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, boundWidth, boundHeight)) {
         // the easy case: we're not reading out-of-range pixels
         gl->fReadPixels(x, y, width, height, format, type, data);
     } else {
         // the rectangle doesn't fit entirely in the bound buffer. We then have to set to zero the part
         // of the buffer that correspond to out-of-range pixels. We don't want to rely on system OpenGL
         // to do that for us, because passing out of range parameters to a buggy OpenGL implementation
         // could conceivably allow to read memory we shouldn't be allowed to read. So we manually initialize
@@ -2640,42 +2595,67 @@ WebGLContext::ReadPixels_buf(WebGLint x,
     return ReadPixels_base(x, y, width, height, format, type,
                            pixels ? pixels->data : 0,
                            pixels ? pixels->byteLength : 0);
 }
 
 NS_IMETHODIMP
 WebGLContext::RenderbufferStorage(WebGLenum target, WebGLenum internalformat, WebGLsizei width, WebGLsizei height)
 {
+
+    if (!mBoundRenderbuffer || !mBoundRenderbuffer->GLName())
+        return ErrorInvalidOperation("renderbufferStorage called on renderbuffer 0");
+
     if (target != LOCAL_GL_RENDERBUFFER)
-        return ErrorInvalidEnumInfo("RenderbufferStorage: target", target);
+        return ErrorInvalidEnumInfo("renderbufferStorage: target", target);
+
+    if (width <= 0 || height <= 0)
+        return ErrorInvalidValue("renderbufferStorage: width and height must be > 0");
+
+    if (!mBoundRenderbuffer || !mBoundRenderbuffer->GLName())
+        return ErrorInvalidOperation("renderbufferStorage called on renderbuffer 0");
+
+    MakeContextCurrent();
+
+    // certain OpenGL ES renderbuffer formats may not exist on desktop OpenGL
+    WebGLenum internalformatForGL = internalformat;
 
     switch (internalformat) {
-      case LOCAL_GL_RGBA4:
-      // XXX case LOCAL_GL_RGB565:
-      case LOCAL_GL_RGB5_A1:
-      case LOCAL_GL_DEPTH_COMPONENT:
-      case LOCAL_GL_DEPTH_COMPONENT16:
-      case LOCAL_GL_STENCIL_INDEX8:
-          break;
-      default:
-          return ErrorInvalidEnumInfo("RenderbufferStorage: internalformat", internalformat);
+    case LOCAL_GL_RGBA4:
+    case LOCAL_GL_RGB5_A1:
+        // 16-bit RGBA formats are not supported on desktop GL
+        if (!gl->IsGLES2()) internalformatForGL = LOCAL_GL_RGBA8;
+        break;
+    case LOCAL_GL_RGB565:
+        // the RGB565 format is not supported on desktop GL
+        if (!gl->IsGLES2()) internalformatForGL = LOCAL_GL_RGB8;
+        break;
+    case LOCAL_GL_DEPTH_COMPONENT16:
+    case LOCAL_GL_STENCIL_INDEX8:
+        // nothing to do for these ones
+        break;
+    case LOCAL_GL_DEPTH_STENCIL:
+        // this one is available in newer OpenGL (at least since 3.1); will probably become available
+        // in OpenGL ES 3 (at least it will have some DEPTH_STENCIL) and is the same value that
+        // is otherwise provided by EXT_packed_depth_stencil and OES_packed_depth_stencil extensions
+        // which means it's supported on most GL and GL ES systems already.
+        //
+        // So we just use it hoping that it's available (perhaps as an extension) and if it's not available,
+        // we just let the GL generate an error and don't do anything about it ourselves.
+        internalformatForGL = LOCAL_GL_DEPTH24_STENCIL8;
+        break;
+    default:
+        ErrorInvalidEnumInfo("renderbufferStorage: internalformat", internalformat);
     }
 
-    if (width <= 0 || height <= 0)
-        return ErrorInvalidValue("RenderbufferStorage: width and height must be > 0");
-
-    if (mBoundRenderbuffer)
-        mBoundRenderbuffer->setDimensions(width, height);
-
-    MakeContextCurrent();
-    gl->fRenderbufferStorage(target, internalformat, width, height);
-
-    // now we need to initialize the renderbuffer to 0 as per the thread "about RenderBufferStorage"
-    // on the public_webgl list
+    gl->fRenderbufferStorage(target, internalformatForGL, width, height);
+
+    mBoundRenderbuffer->SetInternalFormat(internalformat);
+    mBoundRenderbuffer->setDimensions(width, height);
+    mBoundRenderbuffer->SetInitialized(PR_FALSE);
 
     return NS_OK;
 }
 
 GL_SAME_METHOD_2(SampleCoverage, SampleCoverage, WebGLfloat, WebGLboolean)
 
 NS_IMETHODIMP
 WebGLContext::Scissor(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height)
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -357,20 +357,16 @@ WebGLContext::InitAndValidateGL()
     mUniformTextures.Clear();
     mBound2DTextures.Clear();
     mBoundCubeMapTextures.Clear();
 
     mBoundArrayBuffer = nsnull;
     mBoundElementArrayBuffer = nsnull;
     mCurrentProgram = nsnull;
 
-    mFramebufferColorAttachments.Clear();
-    mFramebufferDepthAttachment = nsnull;
-    mFramebufferStencilAttachment = nsnull;
-
     mBoundFramebuffer = nsnull;
     mBoundRenderbuffer = nsnull;
 
     mMapTextures.Clear();
     mMapBuffers.Clear();
     mMapPrograms.Clear();
     mMapShaders.Clear();
     mMapFramebuffers.Clear();
@@ -428,17 +424,17 @@ WebGLContext::InitAndValidateGL()
 #if 0
     // Leaving this code in here, even though it's ifdef'd out, for
     // when we support more than 1 color attachment.
     gl->fGetIntegerv(LOCAL_GL_MAX_COLOR_ATTACHMENTS, (GLint*) &val);
 #else
     // Always 1 for GLES2
     val = 1;
 #endif
-    mFramebufferColorAttachments.SetLength(val);
+    mMaxFramebufferColorAttachments = val;
 
 #if defined(DEBUG_vladimir) && defined(USE_GLES2)
     gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT, (GLint*) &val);
     fprintf(stderr, "GL_IMPLEMENTATION_COLOR_READ_FORMAT: 0x%04x\n", val);
 
     gl->fGetIntegerv(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE, (GLint*) &val);
     fprintf(stderr, "GL_IMPLEMENTATION_COLOR_READ_TYPE: 0x%04x\n", val);
 #endif
--- a/content/canvas/test/webgl/failing_tests.txt
+++ b/content/canvas/test/webgl/failing_tests.txt
@@ -1,27 +1,22 @@
 conformance/canvas-test.html
 conformance/constants.html
 conformance/context-attributes-alpha-depth-stencil-antialias.html
 conformance/context-attributes.html
 conformance/context-type-test.html
-conformance/framebuffer-object-attachment.html
-conformance/get-active-test.html
-conformance/gl-enum-tests.html
 conformance/gl-get-calls.html
 conformance/gl-teximage.html
 conformance/glsl-conformance.html
 conformance/methods.html
 conformance/program-test.html
 conformance/read-pixels-pack-alignment.html
 conformance/tex-image-and-sub-image-2d-with-video.html
-conformance/tex-image-with-format-and-type.html
 conformance/tex-image-with-invalid-data.html
 conformance/tex-input-validation.html
-conformance/texture-active-bind.html
 more/conformance/constants.html
 more/conformance/getContext.html
 more/conformance/quickCheckAPI.html
 more/conformance/quickCheckAPIBadArgs.html
 more/functions/bufferDataBadArgs.html
 more/functions/copyTexImage2D.html
 more/functions/copyTexImage2DBadArgs.html
 more/functions/copyTexSubImage2D.html
--- a/dom/interfaces/canvas/nsICanvasRenderingContextWebGL.idl
+++ b/dom/interfaces/canvas/nsICanvasRenderingContextWebGL.idl
@@ -505,16 +505,17 @@ interface nsICanvasRenderingContextWebGL
   const unsigned long RENDERBUFFER                   = 0x8D41;
 
   const unsigned long RGBA4                          = 0x8056;
   const unsigned long RGB5_A1                        = 0x8057;
   const unsigned long RGB565                         = 0x8D62;
   const unsigned long DEPTH_COMPONENT16              = 0x81A5;
   const unsigned long STENCIL_INDEX                  = 0x1901;
   const unsigned long STENCIL_INDEX8                 = 0x8D48;
+  const unsigned long DEPTH_STENCIL                  = 0x84F9;
 
   const unsigned long RENDERBUFFER_WIDTH             = 0x8D42;
   const unsigned long RENDERBUFFER_HEIGHT            = 0x8D43;
   const unsigned long RENDERBUFFER_INTERNAL_FORMAT   = 0x8D44;
   const unsigned long RENDERBUFFER_RED_SIZE          = 0x8D50;
   const unsigned long RENDERBUFFER_GREEN_SIZE        = 0x8D51;
   const unsigned long RENDERBUFFER_BLUE_SIZE         = 0x8D52;
   const unsigned long RENDERBUFFER_ALPHA_SIZE        = 0x8D53;
@@ -524,16 +525,17 @@ interface nsICanvasRenderingContextWebGL
   const unsigned long FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE           = 0x8CD0;
   const unsigned long FRAMEBUFFER_ATTACHMENT_OBJECT_NAME           = 0x8CD1;
   const unsigned long FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL         = 0x8CD2;
   const unsigned long FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = 0x8CD3;
 
   const unsigned long COLOR_ATTACHMENT0              = 0x8CE0;
   const unsigned long DEPTH_ATTACHMENT               = 0x8D00;
   const unsigned long STENCIL_ATTACHMENT             = 0x8D20;
+  const unsigned long DEPTH_STENCIL_ATTACHMENT       = 0x821A;
 
   const unsigned long NONE                           = 0;
 
   const unsigned long FRAMEBUFFER_COMPLETE                      = 0x8CD5;
   const unsigned long FRAMEBUFFER_INCOMPLETE_ATTACHMENT         = 0x8CD6;
   const unsigned long FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7;
   const unsigned long FRAMEBUFFER_INCOMPLETE_DIMENSIONS         = 0x8CD9;
   const unsigned long FRAMEBUFFER_UNSUPPORTED                   = 0x8CDD;
--- a/gfx/thebes/GLDefs.h
+++ b/gfx/thebes/GLDefs.h
@@ -545,16 +545,17 @@ typedef ptrdiff_t GLintptr;
 #define LOCAL_GL_RGB5 0x8050
 #define LOCAL_GL_RGB8 0x8051
 #define LOCAL_GL_RGB10 0x8052
 #define LOCAL_GL_RGB12 0x8053
 #define LOCAL_GL_RGB16 0x8054
 #define LOCAL_GL_RGBA2 0x8055
 #define LOCAL_GL_RGBA4 0x8056
 #define LOCAL_GL_RGB5_A1 0x8057
+#define LOCAL_GL_RGB565 0x8D62
 #define LOCAL_GL_RGBA8 0x8058
 #define LOCAL_GL_RGB10_A2 0x8059
 #define LOCAL_GL_RGBA12 0x805A
 #define LOCAL_GL_RGBA16 0x805B
 #define LOCAL_GL_TEXTURE_RED_SIZE 0x805C
 #define LOCAL_GL_TEXTURE_GREEN_SIZE 0x805D
 #define LOCAL_GL_TEXTURE_BLUE_SIZE 0x805E
 #define LOCAL_GL_TEXTURE_ALPHA_SIZE 0x805F
@@ -1804,16 +1805,17 @@ typedef ptrdiff_t GLintptr;
 #define LOCAL_GL_COLOR_ATTACHMENT10 0x8CEA
 #define LOCAL_GL_COLOR_ATTACHMENT11 0x8CEB
 #define LOCAL_GL_COLOR_ATTACHMENT12 0x8CEC
 #define LOCAL_GL_COLOR_ATTACHMENT13 0x8CED
 #define LOCAL_GL_COLOR_ATTACHMENT14 0x8CEE
 #define LOCAL_GL_COLOR_ATTACHMENT15 0x8CEF
 #define LOCAL_GL_DEPTH_ATTACHMENT 0x8D00
 #define LOCAL_GL_STENCIL_ATTACHMENT 0x8D20
+#define LOCAL_GL_DEPTH_STENCIL_ATTACHMENT 0x821A
 #define LOCAL_GL_FRAMEBUFFER 0x8D40
 #define LOCAL_GL_RENDERBUFFER 0x8D41
 #define LOCAL_GL_RENDERBUFFER_WIDTH 0x8D42
 #define LOCAL_GL_RENDERBUFFER_HEIGHT 0x8D43
 #define LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44
 #define LOCAL_GL_STENCIL_INDEX1 0x8D46
 #define LOCAL_GL_STENCIL_INDEX4 0x8D47
 #define LOCAL_GL_STENCIL_INDEX8 0x8D48
@@ -1870,16 +1872,17 @@ typedef ptrdiff_t GLintptr;
 #define LOCAL_GL_SAMPLE_BUFFERS_EXT 0x80A8
 #define LOCAL_GL_SAMPLES_EXT 0x80A9
 #define LOCAL_GL_SAMPLE_MASK_VALUE_EXT 0x80AA
 #define LOCAL_GL_SAMPLE_MASK_INVERT_EXT 0x80AB
 #define LOCAL_GL_SAMPLE_PATTERN_EXT 0x80AC
 #define LOCAL_GL_MULTISAMPLE_BIT_EXT 0x20000000
 #define LOCAL_GL_EXT_packed_depth_stencil 1
 #define LOCAL_GL_DEPTH_STENCIL_EXT 0x84F9
+#define LOCAL_GL_DEPTH_STENCIL 0x84F9
 #define LOCAL_GL_UNSIGNED_INT_24_8_EXT 0x84FA
 #define LOCAL_GL_DEPTH24_STENCIL8_EXT 0x88F0
 #define LOCAL_GL_DEPTH24_STENCIL8 0x88F0
 #define LOCAL_GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1
 #define LOCAL_GL_EXT_packed_pixels 1
 #define LOCAL_GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032
 #define LOCAL_GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033
 #define LOCAL_GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034