Bug 1321450 - Fix Invalidate[Sub]Framebuffer. - r=ethlin
authorJeff Gilbert <jdashg@gmail.com>
Wed, 30 Nov 2016 18:46:06 -0800
changeset 325319 80afe5f38d073ea432de589bed7451a3a7c8def7
parent 325318 70791e56e69ae0ce66c430a6e453eff5aeeef09e
child 325320 0d7106de13327e1446d0034c10e7e9986a50a4e7
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersethlin
bugs1321450
milestone53.0a1
Bug 1321450 - Fix Invalidate[Sub]Framebuffer. - r=ethlin MozReview-Commit-ID: nrZP6E412k
dom/canvas/WebGL2ContextFramebuffers.cpp
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextValidate.cpp
gfx/thebes/gfxPrefs.h
modules/libpref/init/all.js
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -100,60 +100,73 @@ WebGL2Context::GetFramebufferAttachmentP
                                                  GLenum attachment,
                                                  GLenum pname,
                                                  ErrorResult& out_error)
 {
     return WebGLContext::GetFramebufferAttachmentParameter(cx, target, attachment, pname,
                                                            out_error);
 }
 
-// Map attachments intended for the default buffer, to attachments for a non-
-// default buffer.
+////
+
 static bool
-TranslateDefaultAttachments(const dom::Sequence<GLenum>& in, dom::Sequence<GLenum>* out)
+ValidateBackbufferAttachmentEnum(WebGLContext* webgl, const char* funcName,
+                                 GLenum attachment)
 {
-    for (size_t i = 0; i < in.Length(); i++) {
-        switch (in[i]) {
-            case LOCAL_GL_COLOR:
-                if (!out->AppendElement(LOCAL_GL_COLOR_ATTACHMENT0, fallible)) {
-                    return false;
-                }
-                break;
+    switch (attachment) {
+    case LOCAL_GL_COLOR:
+    case LOCAL_GL_DEPTH:
+    case LOCAL_GL_STENCIL:
+        return true;
 
-            case LOCAL_GL_DEPTH:
-                if (!out->AppendElement(LOCAL_GL_DEPTH_ATTACHMENT, fallible)) {
-                    return false;
-                }
-                break;
+    default:
+        webgl->ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.",
+                                funcName, attachment);
+        return false;
+    }
+}
 
-            case LOCAL_GL_STENCIL:
-                if (!out->AppendElement(LOCAL_GL_STENCIL_ATTACHMENT, fallible)) {
-                    return false;
-                }
-                break;
-        }
+static bool
+ValidateFramebufferAttachmentEnum(WebGLContext* webgl, const char* funcName,
+                                  GLenum attachment)
+{
+    if (attachment >= LOCAL_GL_COLOR_ATTACHMENT0 &&
+        attachment <= webgl->LastColorAttachmentEnum())
+    {
+        return true;
     }
 
-    return true;
+    switch (attachment) {
+    case LOCAL_GL_DEPTH_ATTACHMENT:
+    case LOCAL_GL_STENCIL_ATTACHMENT:
+    case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
+        return true;
+
+    default:
+        webgl->ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.",
+                                funcName, attachment);
+        return false;
+    }
 }
 
-void
-WebGL2Context::InvalidateFramebuffer(GLenum target,
-                                     const dom::Sequence<GLenum>& attachments,
-                                     ErrorResult& rv)
+bool
+WebGLContext::ValidateInvalidateFramebuffer(const char* funcName, GLenum target,
+                                            const dom::Sequence<GLenum>& attachments,
+                                            ErrorResult* const out_rv,
+                                            std::vector<GLenum>* const scopedVector,
+                                            GLsizei* const out_glNumAttachments,
+                                            const GLenum** const out_glAttachments)
 {
-    const char funcName[] = "invalidateSubFramebuffer";
+    if (IsContextLost())
+        return false;
 
-    if (IsContextLost())
-        return;
-
-    MakeContextCurrent();
+    gl->MakeCurrent();
 
     if (!ValidateFramebufferTarget(target, funcName))
-        return;
+        return false;
 
     const WebGLFramebuffer* fb;
     bool isDefaultFB;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         fb = mBoundDrawFramebuffer;
         isDefaultFB = gl->Screen()->IsDrawFramebufferDefault();
@@ -163,111 +176,118 @@ WebGL2Context::InvalidateFramebuffer(GLe
         fb = mBoundReadFramebuffer;
         isDefaultFB = gl->Screen()->IsReadFramebufferDefault();
         break;
 
     default:
         MOZ_CRASH("GFX: Bad target.");
     }
 
-    const bool badColorAttachmentIsInvalidOp = true;
-    for (size_t i = 0; i < attachments.Length(); i++) {
-        if (!ValidateFramebufferAttachment(fb, attachments[i], funcName,
-                                           badColorAttachmentIsInvalidOp))
-        {
-            return;
+    *out_glNumAttachments = attachments.Length();
+    *out_glAttachments = attachments.Elements();
+
+    if (fb) {
+        for (const auto& attachment : attachments) {
+            if (!ValidateFramebufferAttachmentEnum(this, funcName, attachment))
+                return false;
+        }
+    } else {
+        for (const auto& attachment : attachments) {
+            if (!ValidateBackbufferAttachmentEnum(this, funcName, attachment))
+                return false;
+        }
+
+        if (!isDefaultFB) {
+            MOZ_ASSERT(scopedVector->empty());
+            scopedVector->reserve(attachments.Length());
+            for (const auto& attachment : attachments) {
+                switch (attachment) {
+                case LOCAL_GL_COLOR:
+                    scopedVector->push_back(LOCAL_GL_COLOR_ATTACHMENT0);
+                    break;
+
+                case LOCAL_GL_DEPTH:
+                    scopedVector->push_back(LOCAL_GL_DEPTH_ATTACHMENT);
+                    break;
+
+                case LOCAL_GL_STENCIL:
+                    scopedVector->push_back(LOCAL_GL_STENCIL_ATTACHMENT);
+                    break;
+
+                default:
+                    MOZ_CRASH();
+                }
+            }
+            *out_glNumAttachments = scopedVector->size();
+            *out_glAttachments = scopedVector->data();
         }
     }
 
-    // InvalidateFramebuffer is a hint to the driver. Should be OK to
-    // skip calls if not supported, for example by OSX 10.9 GL
-    // drivers.
-    if (!gl->IsSupported(gl::GLFeature::invalidate_framebuffer))
-        return;
+    return true;
+}
+
+void
+WebGL2Context::InvalidateFramebuffer(GLenum target,
+                                     const dom::Sequence<GLenum>& attachments,
+                                     ErrorResult& rv)
+{
+    const char funcName[] = "invalidateSubFramebuffer";
 
-    if (!fb && !isDefaultFB) {
-        dom::Sequence<GLenum> tmpAttachments;
-        if (!TranslateDefaultAttachments(attachments, &tmpAttachments)) {
-            rv.Throw(NS_ERROR_OUT_OF_MEMORY);
-            return;
-        }
+    std::vector<GLenum> scopedVector;
+    GLsizei glNumAttachments;
+    const GLenum* glAttachments;
+    if (!ValidateInvalidateFramebuffer(funcName, target, attachments, &rv, &scopedVector,
+                                       &glNumAttachments, &glAttachments))
+    {
+        return;
+    }
+
+    ////
 
-        gl->fInvalidateFramebuffer(target, tmpAttachments.Length(),
-                                   tmpAttachments.Elements());
-    } else {
-        gl->fInvalidateFramebuffer(target, attachments.Length(), attachments.Elements());
+    // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer.
+    const bool useFBInvalidation = (mAllowFBInvalidation &&
+                                    gl->IsSupported(gl::GLFeature::invalidate_framebuffer));
+    if (useFBInvalidation) {
+        gl->fInvalidateFramebuffer(target, glNumAttachments, glAttachments);
+        return;
     }
+
+    // Use clear instead?
+    // No-op for now.
 }
 
 void
 WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence<GLenum>& attachments,
                                         GLint x, GLint y, GLsizei width, GLsizei height,
                                         ErrorResult& rv)
 {
     const char funcName[] = "invalidateSubFramebuffer";
 
-    if (IsContextLost())
-        return;
-
-    MakeContextCurrent();
-
-    if (!ValidateFramebufferTarget(target, funcName))
-        return;
-
-    if (width < 0 || height < 0) {
-        ErrorInvalidValue("%s: width and height must be >= 0.", funcName);
+    std::vector<GLenum> scopedVector;
+    GLsizei glNumAttachments;
+    const GLenum* glAttachments;
+    if (!ValidateInvalidateFramebuffer(funcName, target, attachments, &rv, &scopedVector,
+                                       &glNumAttachments, &glAttachments))
+    {
         return;
     }
 
-    const WebGLFramebuffer* fb;
-    bool isDefaultFB;
-    switch (target) {
-    case LOCAL_GL_FRAMEBUFFER:
-    case LOCAL_GL_DRAW_FRAMEBUFFER:
-        fb = mBoundDrawFramebuffer;
-        isDefaultFB = gl->Screen()->IsDrawFramebufferDefault();
-        break;
+    ////
 
-    case LOCAL_GL_READ_FRAMEBUFFER:
-        fb = mBoundReadFramebuffer;
-        isDefaultFB = gl->Screen()->IsReadFramebufferDefault();
-        break;
-
-    default:
-        MOZ_CRASH("GFX: Bad target.");
+    // Some drivers (like OSX 10.9 GL) just don't support invalidate_framebuffer.
+    const bool useFBInvalidation = (mAllowFBInvalidation &&
+                                    gl->IsSupported(gl::GLFeature::invalidate_framebuffer));
+    if (useFBInvalidation) {
+        gl->fInvalidateSubFramebuffer(target, glNumAttachments, glAttachments, x, y,
+                                      width, height);
+        return;
     }
 
-    const bool badColorAttachmentIsInvalidOp = true;
-    for (size_t i = 0; i < attachments.Length(); i++) {
-        if (!ValidateFramebufferAttachment(fb, attachments[i], funcName,
-                                           badColorAttachmentIsInvalidOp))
-        {
-            return;
-        }
-    }
-
-    // InvalidateFramebuffer is a hint to the driver. Should be OK to
-    // skip calls if not supported, for example by OSX 10.9 GL
-    // drivers.
-    if (!gl->IsSupported(gl::GLFeature::invalidate_framebuffer))
-        return;
-
-    if (!fb && !isDefaultFB) {
-        dom::Sequence<GLenum> tmpAttachments;
-        if (!TranslateDefaultAttachments(attachments, &tmpAttachments)) {
-            rv.Throw(NS_ERROR_OUT_OF_MEMORY);
-            return;
-        }
-
-        gl->fInvalidateSubFramebuffer(target, tmpAttachments.Length(),
-                                      tmpAttachments.Elements(), x, y, width, height);
-    } else {
-        gl->fInvalidateSubFramebuffer(target, attachments.Length(),
-                                      attachments.Elements(), x, y, width, height);
-    }
+    // Use clear instead?
+    // No-op for now.
 }
 
 void
 WebGL2Context::ReadBuffer(GLenum mode)
 {
     const char funcName[] = "readBuffer";
     if (IsContextLost())
         return;
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -121,16 +121,17 @@ WebGLContext::WebGLContext()
     , mLayerIsMirror(false)
     , mBypassShaderValidation(false)
     , mBuffersForUB_Dirty(true)
     , mContextLossHandler(this)
     , mNeedsFakeNoAlpha(false)
     , mNeedsFakeNoDepth(false)
     , mNeedsFakeNoStencil(false)
     , mNeedsEmulatedLoneDepthStencil(false)
+    , mAllowFBInvalidation(gfxPrefs::WebGLFBInvalidation())
 {
     mGeneration = 0;
     mInvalidated = false;
     mCapturedFrameInvalidated = false;
     mShouldPresent = true;
     mResetLayer = true;
     mOptionsFrozen = false;
     mMinCapability = false;
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -531,21 +531,16 @@ public:
     void Flush();
     void Finish();
     void FramebufferRenderbuffer(GLenum target, GLenum attachment,
                                  GLenum rbTarget, WebGLRenderbuffer* rb);
     void FramebufferTexture2D(GLenum target, GLenum attachment,
                               GLenum texImageTarget, WebGLTexture* tex,
                               GLint level);
 
-    // Framebuffer validation
-    bool ValidateFramebufferAttachment(const WebGLFramebuffer* fb, GLenum attachment,
-                                       const char* funcName,
-                                       bool badColorAttachmentIsInvalidOp = false);
-
     void FrontFace(GLenum mode);
     already_AddRefed<WebGLActiveInfo> GetActiveAttrib(const WebGLProgram& prog,
                                                       GLuint index);
     already_AddRefed<WebGLActiveInfo> GetActiveUniform(const WebGLProgram& prog,
                                                        GLuint index);
 
     void
     GetAttachedShaders(const WebGLProgram& prog,
@@ -1682,16 +1677,22 @@ protected:
     nsTArray<WebGLRefPtr<WebGLSampler> > mBoundSamplers;
 
     void ResolveTexturesForDraw() const;
 
     WebGLRefPtr<WebGLProgram> mCurrentProgram;
     RefPtr<const webgl::LinkedProgramInfo> mActiveProgramLinkInfo;
 
     bool ValidateFramebufferTarget(GLenum target, const char* const info);
+    bool ValidateInvalidateFramebuffer(const char* funcName, GLenum target,
+                                       const dom::Sequence<GLenum>& attachments,
+                                       ErrorResult* const out_rv,
+                                       std::vector<GLenum>* const scopedVector,
+                                       GLsizei* const out_glNumAttachments,
+                                       const GLenum** const out_glAttachments);
 
     WebGLRefPtr<WebGLFramebuffer> mBoundDrawFramebuffer;
     WebGLRefPtr<WebGLFramebuffer> mBoundReadFramebuffer;
     WebGLRefPtr<WebGLRenderbuffer> mBoundRenderbuffer;
     WebGLRefPtr<WebGLTransformFeedback> mBoundTransformFeedback;
     WebGLRefPtr<WebGLVertexArray> mBoundVertexArray;
 
     LinkedList<WebGLBuffer> mBuffers;
@@ -1823,16 +1824,18 @@ protected:
 
     uint64_t mLastUseIndex;
 
     bool mNeedsFakeNoAlpha;
     bool mNeedsFakeNoDepth;
     bool mNeedsFakeNoStencil;
     bool mNeedsEmulatedLoneDepthStencil;
 
+    const bool mAllowFBInvalidation;
+
     bool Has64BitTimestamps() const;
 
     struct ScopedMaskWorkaround {
         WebGLContext& mWebGL;
         const bool mFakeNoAlpha;
         const bool mFakeNoDepth;
         const bool mFakeNoStencil;
 
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -192,60 +192,16 @@ WebGLContext::ValidateDrawModeEnum(GLenu
 
     default:
         ErrorInvalidEnumInfo(info, mode);
         return false;
     }
 }
 
 bool
-WebGLContext::ValidateFramebufferAttachment(const WebGLFramebuffer* fb, GLenum attachment,
-                                            const char* funcName,
-                                            bool badColorAttachmentIsInvalidOp)
-{
-    if (!fb) {
-        switch (attachment) {
-        case LOCAL_GL_COLOR:
-        case LOCAL_GL_DEPTH:
-        case LOCAL_GL_STENCIL:
-            return true;
-
-        default:
-            ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.",
-                             funcName, attachment);
-            return false;
-        }
-    }
-
-    if (attachment == LOCAL_GL_DEPTH_ATTACHMENT ||
-        attachment == LOCAL_GL_STENCIL_ATTACHMENT ||
-        attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
-    {
-        return true;
-    }
-
-    if (attachment >= LOCAL_GL_COLOR_ATTACHMENT0 &&
-        attachment <= LastColorAttachmentEnum())
-    {
-        return true;
-    }
-
-    if (badColorAttachmentIsInvalidOp &&
-        attachment >= LOCAL_GL_COLOR_ATTACHMENT0)
-    {
-        const uint32_t offset = attachment - LOCAL_GL_COLOR_ATTACHMENT0;
-        ErrorInvalidOperation("%s: Bad color attachment: COLOR_ATTACHMENT%u. (0x%04x)",
-                              funcName, offset, attachment);
-    } else {
-        ErrorInvalidEnum("%s: attachment: Bad attachment 0x%x.", funcName, attachment);
-    }
-    return false;
-}
-
-bool
 WebGLContext::ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName)
 {
     /* GLES 2.0.25, p38:
      *   If the value of location is -1, the Uniform* commands will silently
      *   ignore the data passed in, and the current uniform values will not be
      *   changed.
      */
     if (!loc)
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -603,16 +603,17 @@ private:
   DECL_GFX_PREF(Once, "webgl.force-layers-readback",           WebGLForceLayersReadback, bool, false);
   DECL_GFX_PREF(Live, "webgl.lose-context-on-memory-pressure", WebGLLoseContextOnMemoryPressure, bool, false);
   DECL_GFX_PREF(Live, "webgl.max-warnings-per-context",        WebGLMaxWarningsPerContext, uint32_t, 32);
   DECL_GFX_PREF(Live, "webgl.min_capability_mode",             WebGLMinCapabilityMode, bool, false);
   DECL_GFX_PREF(Live, "webgl.msaa-force",                      WebGLForceMSAA, bool, false);
   DECL_GFX_PREF(Live, "webgl.prefer-16bpp",                    WebGLPrefer16bpp, bool, false);
   DECL_GFX_PREF(Live, "webgl.restore-context-when-visible",    WebGLRestoreWhenVisible, bool, true);
   DECL_GFX_PREF(Live, "webgl.allow-immediate-queries",         WebGLImmediateQueries, bool, false);
+  DECL_GFX_PREF(Live, "webgl.allow-fb-invalidation",           WebGLFBInvalidation, bool, false);
 
   DECL_GFX_PREF(Live, "webgl.webgl2-compat-mode",              WebGL2CompatMode, bool, false);
 
   // WARNING:
   // Please make sure that you've added your new preference to the list above in alphabetical order.
   // Please do not just append it to the end of the list.
 
 public:
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4489,16 +4489,17 @@ pref("webgl.lose-context-on-memory-press
 pref("webgl.can-lose-context-in-foreground", true);
 pref("webgl.restore-context-when-visible", true);
 pref("webgl.max-warnings-per-context", 32);
 pref("webgl.enable-draft-extensions", false);
 pref("webgl.enable-privileged-extensions", false);
 pref("webgl.bypass-shader-validation", false);
 pref("webgl.disable-fail-if-major-performance-caveat", false);
 pref("webgl.disable-DOM-blit-uploads", false);
+pref("webgl.allow-fb-invalidation", false);
 pref("webgl.webgl2-compat-mode", false);
 
 pref("webgl.enable-webgl2", true);
 
 #ifdef RELEASE_OR_BETA
 // Keep this disabled on Release and Beta for now. (see bug 1171228)
 pref("webgl.enable-debug-renderer-info", false);
 #else