Bug 978407 - Use glClear to clear deferred-init textures. - r=kamidphish
authorJeff Gilbert <jgilbert@mozilla.com>
Wed, 30 Apr 2014 14:30:21 -0700
changeset 181504 e7576bf025af238825b9ddebdfc17ce7dcca7dc5
parent 181503 a0666ad474fa9b487958acddb53832649df649c5
child 181505 af970f26f6cecec2d4267984c888e1309c95fb2c
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewerskamidphish
bugs978407
milestone32.0a1
Bug 978407 - Use glClear to clear deferred-init textures. - r=kamidphish
content/canvas/src/WebGLTexture.cpp
content/canvas/src/WebGLTexture.h
gfx/gl/ScopedGLHelpers.cpp
gfx/gl/ScopedGLHelpers.h
--- a/content/canvas/src/WebGLTexture.cpp
+++ b/content/canvas/src/WebGLTexture.cpp
@@ -1,19 +1,22 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "WebGLContext.h"
 #include "WebGLTexture.h"
+
 #include "GLContext.h"
+#include "mozilla/dom/WebGLRenderingContextBinding.h"
+#include "mozilla/Scoped.h"
 #include "ScopedGLHelpers.h"
+#include "WebGLContext.h"
 #include "WebGLTexelConversions.h"
-#include "mozilla/dom/WebGLRenderingContextBinding.h"
+
 #include <algorithm>
 
 using namespace mozilla;
 
 JSObject*
 WebGLTexture::WrapObject(JSContext *cx) {
     return dom::WebGLTextureBinding::Wrap(cx, this);
 }
@@ -428,50 +431,163 @@ WebGLTexture::ResolvedFakeBlackStatus() 
     if (mFakeBlackStatus == WebGLTextureFakeBlackStatus::Unknown) {
         mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded;
     }
 
     MOZ_ASSERT(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown);
     return mFakeBlackStatus;
 }
 
+
+static bool
+ClearByMask(WebGLContext* context, GLbitfield mask)
+{
+    gl::GLContext* gl = context->GL();
+    MOZ_ASSERT(gl->IsCurrent());
+
+    GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+    if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
+        return false;
+
+    bool colorAttachmentsMask[context->sMaxColorAttachments] = {false};
+    if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
+        colorAttachmentsMask[0] = true;
+    }
+
+    context->ForceClearFramebufferWithDefaultValues(mask, colorAttachmentsMask);
+    return true;
+}
+
+// `mask` from glClear.
+static bool
+ClearWithTempFB(WebGLContext* context, GLuint tex,
+                GLenum texImageTarget, GLint level,
+                GLenum baseInternalFormat,
+                GLsizei width, GLsizei height)
+{
+    if (texImageTarget != LOCAL_GL_TEXTURE_2D)
+        return false;
+
+    gl::GLContext* gl = context->GL();
+    MOZ_ASSERT(gl->IsCurrent());
+
+    gl::ScopedFramebuffer fb(gl);
+    gl::ScopedBindFramebuffer autoFB(gl, fb.FB());
+    GLbitfield mask = 0;
+
+    switch (baseInternalFormat) {
+    case LOCAL_GL_LUMINANCE:
+    case LOCAL_GL_LUMINANCE_ALPHA:
+    case LOCAL_GL_ALPHA:
+    case LOCAL_GL_RGB:
+    case LOCAL_GL_RGBA:
+    case LOCAL_GL_BGR:
+    case LOCAL_GL_BGRA:
+        mask = LOCAL_GL_COLOR_BUFFER_BIT;
+        gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
+                                  texImageTarget, tex, level);
+        break;
+
+    case LOCAL_GL_DEPTH_COMPONENT:
+        mask = LOCAL_GL_DEPTH_BUFFER_BIT;
+        gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
+                                  texImageTarget, tex, level);
+        break;
+
+    case LOCAL_GL_DEPTH_STENCIL:
+        mask = LOCAL_GL_DEPTH_BUFFER_BIT |
+               LOCAL_GL_STENCIL_BUFFER_BIT;
+        gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT,
+                                  texImageTarget, tex, level);
+        gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT,
+                                  texImageTarget, tex, level);
+        break;
+
+    default:
+        return false;
+    }
+    MOZ_ASSERT(mask);
+
+    if (ClearByMask(context, mask))
+        return true;
+
+    // Failed to simply build an FB from the tex, but maybe it needs a
+    // color buffer to be complete.
+
+    if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
+        // Nope, it already had one.
+        return false;
+    }
+
+    gl::ScopedRenderbuffer rb(gl);
+    {
+        gl::ScopedBindRenderbuffer(gl, rb.RB());
+        gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
+                                 LOCAL_GL_RGBA4,
+                                 width, height);
+    }
+
+    gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
+                                 LOCAL_GL_RENDERBUFFER, rb.RB());
+    mask |= LOCAL_GL_COLOR_BUFFER_BIT;
+
+    // Last chance!
+    return ClearByMask(context, mask);
+}
+
+
 void
 WebGLTexture::DoDeferredImageInitialization(GLenum imageTarget, GLint level)
 {
     const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level);
     MOZ_ASSERT(imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData);
 
     mContext->MakeContextCurrent();
+
+    // Try to clear with glCLear.
+    WebGLTexelFormat texelformat = GetWebGLTexelFormat(imageInfo.mInternalFormat, imageInfo.mType);
+    GLenum format = WebGLTexelConversions::GLFormatForTexelFormat(texelformat);
+
+    bool cleared = ClearWithTempFB(mContext, GLName(),
+                                   imageTarget, level,
+                                   format, imageInfo.mHeight, imageInfo.mWidth);
+    if (cleared)
+        return;
+
+    // That didn't work. Try uploading zeros then.
     gl::ScopedBindTexture autoBindTex(mContext->gl, GLName(), mTarget);
 
-    WebGLTexelFormat texelformat = GetWebGLTexelFormat(imageInfo.mInternalFormat, imageInfo.mType);
     uint32_t texelsize = WebGLTexelConversions::TexelBytesForFormat(texelformat);
     CheckedUint32 checked_byteLength
         = WebGLContext::GetImageSize(
                         imageInfo.mHeight,
                         imageInfo.mWidth,
                         texelsize,
                         mContext->mPixelStoreUnpackAlignment);
     MOZ_ASSERT(checked_byteLength.isValid()); // should have been checked earlier
-    void *zeros = calloc(1, checked_byteLength.value());
+    ScopedFreePtr<void> zeros;
+    zeros = calloc(1, checked_byteLength.value());
 
-    GLenum format = WebGLTexelConversions::GLFormatForTexelFormat(texelformat);
     mContext->GetAndFlushUnderlyingGLErrors();
     mContext->gl->fTexImage2D(imageTarget, level, imageInfo.mInternalFormat,
                               imageInfo.mWidth, imageInfo.mHeight,
                               0, format, imageInfo.mType,
                               zeros);
     GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
-
-    free(zeros);
-    SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);
+    if (error) {
+        // Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover from this here.
+        MOZ_CRASH(); // errors on texture upload have been related to video memory exposure in the past.
+    }
 
-    if (error) {
-      // Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover from this here.
-      MOZ_CRASH(); // errors on texture upload have been related to video memory exposure in the past.
-      return;
-    }
+    SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);
+}
+
+void
+WebGLTexture::SetFakeBlackStatus(WebGLTextureFakeBlackStatus x)
+{
+	mFakeBlackStatus = x;
+	mContext->SetFakeBlackStatus(WebGLContextFakeBlackStatus::Unknown);
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTexture, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTexture, Release)
--- a/content/canvas/src/WebGLTexture.h
+++ b/content/canvas/src/WebGLTexture.h
@@ -6,16 +6,17 @@
 #ifndef WEBGLTEXTURE_H_
 #define WEBGLTEXTURE_H_
 
 #include "WebGLObjectModel.h"
 #include "WebGLFramebufferAttachable.h"
 
 #include "nsWrapperCache.h"
 
+#include "mozilla/CheckedInt.h"
 #include "mozilla/LinkedList.h"
 #include <algorithm>
 
 namespace mozilla {
 
 // Zero is not an integer power of two.
 inline bool is_pot_assuming_nonnegative(GLsizei x)
 {
@@ -268,20 +269,18 @@ public:
     bool AreAllLevel0ImageInfosEqual() const;
 
     bool IsMipmapTexture2DComplete() const;
 
     bool IsCubeComplete() const;
 
     bool IsMipmapCubeComplete() const;
 
-    void SetFakeBlackStatus(WebGLTextureFakeBlackStatus x) {
-        mFakeBlackStatus = x;
-        mContext->SetFakeBlackStatus(WebGLContextFakeBlackStatus::Unknown);
-    }
+    void SetFakeBlackStatus(WebGLTextureFakeBlackStatus x);
+    
     // Returns the current fake-black-status, except if it was Unknown,
     // in which case this function resolves it first, so it never returns Unknown.
     WebGLTextureFakeBlackStatus ResolvedFakeBlackStatus();
 };
 
 } // namespace mozilla
 
 #endif
--- a/gfx/gl/ScopedGLHelpers.cpp
+++ b/gfx/gl/ScopedGLHelpers.cpp
@@ -97,27 +97,65 @@ ScopedBindTextureUnit::UnwrapImpl() {
 }
 
 
 /* ScopedTexture **************************************************************/
 
 ScopedTexture::ScopedTexture(GLContext* aGL)
     : ScopedGLWrapper<ScopedTexture>(aGL)
 {
+    MOZ_ASSERT(mGL->IsCurrent());
     mGL->fGenTextures(1, &mTexture);
 }
 
 void
 ScopedTexture::UnwrapImpl()
 {
     // Check that we're not falling out of scope after
     // the current context changed.
     MOZ_ASSERT(mGL->IsCurrent());
+    mGL->fDeleteTextures(1, &mTexture);
+}
 
-    mGL->fDeleteTextures(1, &mTexture);
+
+/* ScopedFramebuffer **************************************************************/
+
+ScopedFramebuffer::ScopedFramebuffer(GLContext* aGL)
+    : ScopedGLWrapper<ScopedFramebuffer>(aGL)
+{
+    MOZ_ASSERT(mGL->IsCurrent());
+    mGL->fGenFramebuffers(1, &mFB);
+}
+
+void
+ScopedFramebuffer::UnwrapImpl()
+{
+    // Check that we're not falling out of scope after
+    // the current context changed.
+    MOZ_ASSERT(mGL->IsCurrent());
+    mGL->fDeleteFramebuffers(1, &mFB);
+}
+
+
+/* ScopedRenderbuffer **************************************************************/
+
+ScopedRenderbuffer::ScopedRenderbuffer(GLContext* aGL)
+    : ScopedGLWrapper<ScopedRenderbuffer>(aGL)
+{
+    MOZ_ASSERT(mGL->IsCurrent());
+    mGL->fGenRenderbuffers(1, &mRB);
+}
+
+void
+ScopedRenderbuffer::UnwrapImpl()
+{
+    // Check that we're not falling out of scope after
+    // the current context changed.
+    MOZ_ASSERT(mGL->IsCurrent());
+    mGL->fDeleteRenderbuffers(1, &mRB);
 }
 
 /* ScopedBindTexture **********************************************************/
 void
 ScopedBindTexture::Init(GLenum aTarget)
 {
     mTarget = aTarget;
     mOldTex = 0;
--- a/gfx/gl/ScopedGLHelpers.h
+++ b/gfx/gl/ScopedGLHelpers.h
@@ -115,16 +115,50 @@ public:
     ScopedTexture(GLContext* aGL);
     GLuint Texture() { return mTexture; }
 
 protected:
     void UnwrapImpl();
 };
 
 
+struct ScopedFramebuffer
+    : public ScopedGLWrapper<ScopedFramebuffer>
+{
+    friend struct ScopedGLWrapper<ScopedFramebuffer>;
+
+protected:
+    GLuint mFB;
+
+public:
+    ScopedFramebuffer(GLContext* aGL);
+    GLuint FB() { return mFB; }
+
+protected:
+    void UnwrapImpl();
+};
+
+
+struct ScopedRenderbuffer
+    : public ScopedGLWrapper<ScopedRenderbuffer>
+{
+    friend struct ScopedGLWrapper<ScopedRenderbuffer>;
+
+protected:
+    GLuint mRB;
+
+public:
+    ScopedRenderbuffer(GLContext* aGL);
+    GLuint RB() { return mRB; }
+
+protected:
+    void UnwrapImpl();
+};
+
+
 struct ScopedBindTexture
     : public ScopedGLWrapper<ScopedBindTexture>
 {
     friend struct ScopedGLWrapper<ScopedBindTexture>;
 
 protected:
     GLuint mOldTex;
     GLenum mTarget;