Fix conformance2/renderbuffers/invalidate-framebuffer. draft
authorJeff Gilbert <jdashg@gmail.com>
Thu, 17 Dec 2015 16:16:52 -0800
changeset 316088 f66102a05e932beb93ae7cf7315c36300f1a6b22
parent 316087 13db279cebff0eda48c55c04807c406f00db79fe
child 316089 23c3937dddc34c59ebb7c616d03ab92d69eb3ac2
push id8514
push userjgilbert@mozilla.com
push dateFri, 18 Dec 2015 00:24:33 +0000
milestone45.0a1
Fix conformance2/renderbuffers/invalidate-framebuffer.
dom/canvas/WebGL2ContextFramebuffers.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextValidate.cpp
dom/canvas/WebGLFramebuffer.cpp
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -342,125 +342,18 @@ WebGL2Context::FramebufferTextureLayer(G
 
 JS::Value
 WebGL2Context::GetFramebufferAttachmentParameter(JSContext* cx,
                                                  GLenum target,
                                                  GLenum attachment,
                                                  GLenum pname,
                                                  ErrorResult& out_error)
 {
-    const char funcName[] = "getFramebufferAttachmentParameter";
-
-    if (IsContextLost())
-        return JS::NullValue();
-
-    // OpenGL ES 3.0.4 (August 27, 2014) 6.1. QUERYING GL STATE 240
-    // "getFramebufferAttachmentParamter returns information about attachments of a bound
-    // framebuffer object. target must be DRAW_FRAMEBUFFER, READ_FRAMEBUFFER, or
-    // FRAMEBUFFER."
-
-    if (!ValidateFramebufferTarget(target, funcName))
-        return JS::NullValue();
-
-    // FRAMEBUFFER is equivalent to DRAW_FRAMEBUFFER.
-    if (target == LOCAL_GL_FRAMEBUFFER)
-        target = LOCAL_GL_DRAW_FRAMEBUFFER;
-
-    WebGLFramebuffer* boundFB = nullptr;
-    switch (target) {
-    case LOCAL_GL_DRAW_FRAMEBUFFER: boundFB = mBoundDrawFramebuffer; break;
-    case LOCAL_GL_READ_FRAMEBUFFER: boundFB = mBoundReadFramebuffer; break;
-    }
-
-    if (boundFB) {
-        return boundFB->GetAttachmentParameter(funcName, cx, target, attachment, pname,
-                                               &out_error);
-    }
-
-    ////////////////
-    // Handle default FB
-
-    // If the default framebuffer is bound to target, then attachment must be BACK,
-    // identifying the color buffer; DEPTH, identifying the depth buffer; or STENCIL,
-    // identifying the stencil buffer.
-    switch (attachment) {
-    case LOCAL_GL_BACK:
-    case LOCAL_GL_DEPTH:
-    case LOCAL_GL_STENCIL:
-    case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
-        break;
-
-    default:
-        ErrorInvalidEnum("%s: Can only query attachment BACK, DEPTH, or STENCIL from"
-                         " default framebuffer.",
-                         funcName);
-        return JS::NullValue();
-    }
-
-    switch (pname) {
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
-        return JS::Int32Value(LOCAL_GL_FRAMEBUFFER_DEFAULT);
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
-        if (attachment != LOCAL_GL_BACK)
-            return JS::Int32Value(0);
-
-        return JS::Int32Value(8);
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
-        if (attachment != LOCAL_GL_BACK)
-            return JS::Int32Value(0);
-
-        return JS::Int32Value(mOptions.alpha ? 8 : 0);
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
-        if (attachment != LOCAL_GL_DEPTH &&
-            attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
-        {
-            return JS::Int32Value(0);
-        }
-
-        return JS::Int32Value(mOptions.depth ? 16 : 0);
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
-        if (attachment != LOCAL_GL_STENCIL &&
-            attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
-        {
-            return JS::Int32Value(0);
-        }
-
-        return JS::Int32Value(mOptions.stencil ? 8 : 0);
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
-        switch (attachment) {
-        case LOCAL_GL_BACK:    return JS::Int32Value(LOCAL_GL_UNSIGNED_NORMALIZED);
-        case LOCAL_GL_DEPTH:   return JS::Int32Value(LOCAL_GL_UNSIGNED_NORMALIZED);
-        case LOCAL_GL_STENCIL: return JS::Int32Value(LOCAL_GL_UNSIGNED_INT);
-
-        case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT:
-            ErrorInvalidOperation("getFramebufferAttachmentParameter: Querying "
-                                  "FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE against "
-                                  "DEPTH_STENCIL_ATTACHMENT is an error.");
-            return JS::NullValue();
-        }
-        MOZ_CRASH("unreachable");
-
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
-        if (attachment != LOCAL_GL_BACK)
-            return JS::Int32Value(0);
-
-        return JS::Int32Value(LOCAL_GL_LINEAR);
-    }
-
-    // Any combinations of framebuffer type and pname not described above will generate an
-    // INVALID_ENUM error.
-    ErrorInvalidEnum("getFramebufferAttachmentParameter: Invalid combination of params.");
-    return JS::NullValue();
+    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)
 {
     for (size_t i = 0; i < in.Length(); i++) {
@@ -488,22 +381,24 @@ TranslateDefaultAttachments(const dom::S
     return true;
 }
 
 void
 WebGL2Context::InvalidateFramebuffer(GLenum target,
                                      const dom::Sequence<GLenum>& attachments,
                                      ErrorResult& rv)
 {
+    const char funcName[] = "invalidateSubFramebuffer";
+
     if (IsContextLost())
         return;
 
     MakeContextCurrent();
 
-    if (!ValidateFramebufferTarget(target, "framebufferRenderbuffer"))
+    if (!ValidateFramebufferTarget(target, funcName))
         return;
 
     const WebGLFramebuffer* fb;
     bool isDefaultFB;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         fb = mBoundDrawFramebuffer;
@@ -514,57 +409,65 @@ WebGL2Context::InvalidateFramebuffer(GLe
         fb = mBoundReadFramebuffer;
         isDefaultFB = gl->Screen()->IsReadFramebufferDefault();
         break;
 
     default:
         MOZ_CRASH("Bad target.");
     }
 
+    const bool badColorAttachmentIsInvalidOp = true;
     for (size_t i = 0; i < attachments.Length(); i++) {
-        if (!ValidateFramebufferAttachment(fb, attachments[i],
-                                           "invalidateFramebuffer"))
+        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.
-    static bool invalidateFBSupported = gl->IsSupported(gl::GLFeature::invalidate_framebuffer);
-    if (!invalidateFBSupported)
+    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->fInvalidateFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements());
+        gl->fInvalidateFramebuffer(target, tmpAttachments.Length(),
+                                   tmpAttachments.Elements());
     } else {
         gl->fInvalidateFramebuffer(target, attachments.Length(), attachments.Elements());
     }
 }
 
 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, "framebufferRenderbuffer"))
+    if (!ValidateFramebufferTarget(target, funcName))
         return;
 
+    if (width < 0 || height < 0) {
+        ErrorInvalidValue("%s: width and height must be >= 0.", funcName);
+        return;
+    }
+
     const WebGLFramebuffer* fb;
     bool isDefaultFB;
     switch (target) {
     case LOCAL_GL_FRAMEBUFFER:
     case LOCAL_GL_DRAW_FRAMEBUFFER:
         fb = mBoundDrawFramebuffer;
         isDefaultFB = gl->Screen()->IsDrawFramebufferDefault();
         break;
@@ -573,43 +476,43 @@ WebGL2Context::InvalidateSubFramebuffer(
         fb = mBoundReadFramebuffer;
         isDefaultFB = gl->Screen()->IsReadFramebufferDefault();
         break;
 
     default:
         MOZ_CRASH("Bad target.");
     }
 
+    const bool badColorAttachmentIsInvalidOp = true;
     for (size_t i = 0; i < attachments.Length(); i++) {
-        if (!ValidateFramebufferAttachment(fb, attachments[i],
-                                           "invalidateSubFramebuffer"))
+        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.
-    static bool invalidateFBSupported = gl->IsSupported(gl::GLFeature::invalidate_framebuffer);
-    if (!invalidateFBSupported)
+    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);
+        gl->fInvalidateSubFramebuffer(target, tmpAttachments.Length(),
+                                      tmpAttachments.Elements(), x, y, width, height);
     } else {
-        gl->fInvalidateSubFramebuffer(target, attachments.Length(), attachments.Elements(),
-                                      x, y, width, height);
+        gl->fInvalidateSubFramebuffer(target, attachments.Length(),
+                                      attachments.Elements(), x, y, width, height);
     }
 }
 
 void
 WebGL2Context::ReadBuffer(GLenum mode)
 {
     if (IsContextLost())
         return;
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -441,18 +441,19 @@ public:
     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 ValidateFramebufferAttachment(const WebGLFramebuffer* fb, GLenum attachment,
+                                       const char* funcName,
+                                       bool badColorAttachmentIsInvalidOp = false);
 
     void FrontFace(GLenum mode);
     already_AddRefed<WebGLActiveInfo> GetActiveAttrib(WebGLProgram* prog,
                                                       GLuint index);
     already_AddRefed<WebGLActiveInfo> GetActiveUniform(WebGLProgram* prog,
                                                        GLuint index);
 
     void
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -257,17 +257,18 @@ WebGLContext::ValidateDrawModeEnum(GLenu
     default:
         ErrorInvalidEnumInfo(info, mode);
         return false;
     }
 }
 
 bool
 WebGLContext::ValidateFramebufferAttachment(const WebGLFramebuffer* fb, GLenum attachment,
-                                            const char* funcName)
+                                            const char* funcName,
+                                            bool badColorAttachmentIsInvalidOp)
 {
     if (!fb) {
         switch (attachment) {
         case LOCAL_GL_COLOR:
         case LOCAL_GL_DEPTH:
         case LOCAL_GL_STENCIL:
             return true;
 
@@ -286,18 +287,25 @@ WebGLContext::ValidateFramebufferAttachm
     }
 
     if (attachment >= LOCAL_GL_COLOR_ATTACHMENT0 &&
         attachment <= LastColorAttachmentEnum())
     {
         return true;
     }
 
-    ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.", funcName,
-                     attachment);
+    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;
 }
 
 /**
  * Return true if pname is valid for GetSamplerParameter calls.
  */
 bool
 WebGLContext::ValidateSamplerParameterName(GLenum pname, const char* info)
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -39,19 +39,22 @@ WebGLFBAttachPoint::IsDeleteRequested() 
     return Texture() ? Texture()->IsDeleteRequested()
          : Renderbuffer() ? Renderbuffer()->IsDeleteRequested()
          : false;
 }
 
 bool
 WebGLFBAttachPoint::IsDefined() const
 {
+    /*
     return (Renderbuffer() && Renderbuffer()->IsDefined()) ||
            (Texture() && Texture()->ImageInfoAt(mTexImageTarget,
                                                 mTexImageLevel).IsDefined());
+    */
+    return (Renderbuffer() || Texture());
 }
 
 const webgl::FormatUsageInfo*
 WebGLFBAttachPoint::Format() const
 {
     MOZ_ASSERT(IsDefined());
 
     if (Texture())
@@ -213,16 +216,18 @@ void
 WebGLFBAttachPoint::OnBackingStoreRespecified() const
 {
     mFB->InvalidateFramebufferStatus();
 }
 
 bool
 WebGLFBAttachPoint::IsComplete() const
 {
+    MOZ_ASSERT(IsDefined());
+
     if (!HasImage())
         return false;
 
     uint32_t width;
     uint32_t height;
     Size(&width, &height);
     if (!width || !height)
         return false;
@@ -647,34 +652,35 @@ WebGLFramebuffer::HasDefinedAttachments(
 
     for (const auto& cur : mMoreColorAttachments) {
         hasAttachments |= cur.IsDefined();
     }
 
     return hasAttachments;
 }
 
-static bool
-IsIncomplete(const WebGLFBAttachPoint& cur)
-{
-    return cur.IsDefined() && !cur.IsComplete();
-}
-
 bool
 WebGLFramebuffer::HasIncompleteAttachments() const
 {
+    const auto fnIsIncomplete = [](const auto& cur) {
+        if (!cur.IsDefined())
+            return false; // Not defined, so can't count as incomplete.
+
+        return !cur.IsComplete();
+    };
+
     bool hasIncomplete = false;
 
-    hasIncomplete |= IsIncomplete(mColorAttachment0);
-    hasIncomplete |= IsIncomplete(mDepthAttachment);
-    hasIncomplete |= IsIncomplete(mStencilAttachment);
-    hasIncomplete |= IsIncomplete(mDepthStencilAttachment);
+    hasIncomplete |= fnIsIncomplete(mColorAttachment0);
+    hasIncomplete |= fnIsIncomplete(mDepthAttachment);
+    hasIncomplete |= fnIsIncomplete(mStencilAttachment);
+    hasIncomplete |= fnIsIncomplete(mDepthStencilAttachment);
 
     for (const auto& cur : mMoreColorAttachments) {
-        hasIncomplete |= IsIncomplete(cur);
+        hasIncomplete |= fnIsIncomplete(cur);
     }
 
     return hasIncomplete;
 }
 
 static bool
 MatchOrReplaceSize(const WebGLFBAttachPoint& cur, uint32_t* const out_width,
                    uint32_t* const out_height)