Bug 1136494 - Add WebGLTexture::IsFeedback. - r=mtseng
authorJeff Gilbert <jgilbert@mozilla.com>
Thu, 14 Jul 2016 12:13:19 -0700
changeset 347469 be7d2dd9f84fd2b05d862d5715621189f9ccf6d3
parent 347468 5a13fc6c82c4b8bdf83e9727c7548af55789b123
child 347470 d13fc459266516180467bd3342c866f30c06eecb
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmtseng
bugs1136494
milestone50.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 1136494 - Add WebGLTexture::IsFeedback. - r=mtseng MozReview-Commit-ID: 9kyomMFgDmS
dom/canvas/WebGLContextDraw.cpp
dom/canvas/WebGLFramebuffer.cpp
dom/canvas/WebGLFramebuffer.h
dom/canvas/WebGLTexture.cpp
dom/canvas/WebGLTexture.h
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -37,58 +37,109 @@ class ScopedResolveTexturesForDraw
     std::vector<TexRebindRequest> mRebindRequests;
 
 public:
     ScopedResolveTexturesForDraw(WebGLContext* webgl, const char* funcName,
                                  bool* const out_error);
     ~ScopedResolveTexturesForDraw();
 };
 
+bool
+WebGLTexture::IsFeedback(WebGLContext* webgl, const char* funcName, uint32_t texUnit,
+                         const std::vector<const WebGLFBAttachPoint*>& fbAttachments) const
+{
+    auto itr = fbAttachments.cbegin();
+    for (; itr != fbAttachments.cend(); ++itr) {
+        const auto& attach = *itr;
+        if (attach->Texture() == this)
+            break;
+    }
+
+    if (itr == fbAttachments.cend())
+        return false;
+
+    ////
+
+    const auto minLevel = mBaseMipmapLevel;
+    uint32_t maxLevel;
+    if (!MaxEffectiveMipmapLevel(texUnit, &maxLevel)) {
+        // No valid mips. Will need fake-black.
+        return false;
+    }
+
+    ////
+
+    for (; itr != fbAttachments.cend(); ++itr) {
+        const auto& attach = *itr;
+        if (attach->Texture() != this)
+            continue;
+
+        const auto dstLevel = attach->MipLevel();
+
+        if (minLevel <= dstLevel && dstLevel <= maxLevel) {
+            webgl->ErrorInvalidOperation("%s: Feedback loop detected between tex target"
+                                         " 0x%04x, tex unit %u, levels %u-%u; and"
+                                         " framebuffer attachment 0x%04x, level %u.",
+                                         funcName, mTarget.get(), texUnit, minLevel,
+                                         maxLevel, attach->mAttachmentPoint, dstLevel);
+            return true;
+        }
+    }
+
+    return false;
+}
+
 ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw(WebGLContext* webgl,
                                                            const char* funcName,
                                                            bool* const out_error)
     : mWebGL(webgl)
 {
-    MOZ_ASSERT(webgl->gl->IsCurrent());
+    MOZ_ASSERT(mWebGL->gl->IsCurrent());
 
-    typedef decltype(WebGLContext::mBound2DTextures) TexturesT;
+    std::vector<const WebGLFBAttachPoint*> fbAttachments;
+    if (mWebGL->mBoundDrawFramebuffer) {
+        const auto& fb = mWebGL->mBoundDrawFramebuffer;
+        fb->GatherAttachments(&fbAttachments);
+    }
 
-    const auto fnResolveAll = [this, funcName](const TexturesT& textures)
-    {
-        const auto len = textures.Length();
-        for (uint32_t texUnit = 0; texUnit < len; ++texUnit) {
-            WebGLTexture* tex = textures[texUnit];
+    MOZ_ASSERT(mWebGL->mActiveProgramLinkInfo);
+    const auto& uniformSamplers = mWebGL->mActiveProgramLinkInfo->uniformSamplers;
+    for (const auto& uniform : uniformSamplers) {
+        const auto& texList = *(uniform->mSamplerTexList);
+
+        for (const auto& texUnit : uniform->mSamplerValues) {
+            if (texUnit >= texList.Length())
+                continue;
+
+            const auto& tex = texList[texUnit];
             if (!tex)
                 continue;
 
+            if (tex->IsFeedback(mWebGL, funcName, texUnit, fbAttachments)) {
+                *out_error = true;
+                return;
+            }
+
             FakeBlackType fakeBlack;
-            if (!tex->ResolveForDraw(funcName, texUnit, &fakeBlack))
-                return false;
+            if (!tex->ResolveForDraw(funcName, texUnit, &fakeBlack)) {
+                mWebGL->ErrorOutOfMemory("%s: Failed to resolve textures for draw.",
+                                         funcName);
+                *out_error = true;
+                return;
+            }
 
             if (fakeBlack == FakeBlackType::None)
                 continue;
 
             mWebGL->BindFakeBlack(texUnit, tex->Target(), fakeBlack);
             mRebindRequests.push_back({texUnit, tex});
         }
-
-        return true;
-    };
-
-    bool ok = true;
-    ok &= fnResolveAll(mWebGL->mBound2DTextures);
-    ok &= fnResolveAll(mWebGL->mBoundCubeMapTextures);
-    ok &= fnResolveAll(mWebGL->mBound3DTextures);
-    ok &= fnResolveAll(mWebGL->mBound2DArrayTextures);
-
-    if (!ok) {
-        mWebGL->ErrorOutOfMemory("%s: Failed to resolve textures for draw.", funcName);
     }
 
-    *out_error = !ok;
+    *out_error = false;
 }
 
 ScopedResolveTexturesForDraw::~ScopedResolveTexturesForDraw()
 {
     if (!mRebindRequests.size())
         return;
 
     gl::GLContext* gl = mWebGL->gl;
@@ -598,18 +649,18 @@ WebGLContext::ValidateBufferFetching(con
             return false;
         }
 
         ++i;
     }
 
     mBufferFetch_IsAttrib0Active = false;
 
-    for (const auto& pair : mActiveProgramLinkInfo->activeAttribLocs) {
-        const uint32_t attribLoc = pair.second;
+    for (const auto& attrib : mActiveProgramLinkInfo->attribs) {
+        const auto& attribLoc = attrib.mLoc;
 
         if (attribLoc >= attribCount)
             continue;
 
         if (attribLoc == 0) {
             mBufferFetch_IsAttrib0Active = true;
         }
 
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -1276,16 +1276,44 @@ WebGLFramebuffer::GetAttachmentParameter
     }
 
     FinalizeAttachments();
 
     return attachPoint->GetParameter(funcName, mContext, cx, target, attachment, pname,
                                      out_error);
 }
 
+
+void
+WebGLFramebuffer::GatherAttachments(std::vector<const WebGLFBAttachPoint*>* const out) const
+{
+    auto itr = mDrawBuffers.cbegin();
+    if (itr != mDrawBuffers.cend() &&
+        *itr != LOCAL_GL_NONE)
+    {
+        out->push_back(&mColorAttachment0);
+        ++itr;
+    }
+
+    size_t i = 0;
+    for (; itr != mDrawBuffers.cend(); ++itr) {
+        if (i >= mMoreColorAttachments.Size())
+            break;
+
+        if (*itr != LOCAL_GL_NONE) {
+            out->push_back(&mMoreColorAttachments[i]);
+        }
+        ++i;
+    }
+
+    out->push_back(&mDepthAttachment);
+    out->push_back(&mStencilAttachment);
+    out->push_back(&mDepthStencilAttachment);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Goop.
 
 JSObject*
 WebGLFramebuffer::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLFramebufferBinding::Wrap(cx, this, givenProto);
 }
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -33,17 +33,17 @@ class WebGLFBAttachPoint
 public:
     WebGLFramebuffer* const mFB;
     const GLenum mAttachmentPoint;
 private:
     WebGLRefPtr<WebGLTexture> mTexturePtr;
     WebGLRefPtr<WebGLRenderbuffer> mRenderbufferPtr;
     TexImageTarget mTexImageTarget;
     GLint mTexImageLayer;
-    GLint mTexImageLevel;
+    uint32_t mTexImageLevel;
 
     // PlacementArray needs a default constructor.
     template<typename T>
     friend class PlacementArray;
 
     WebGLFBAttachPoint()
         : mFB(nullptr)
         , mAttachmentPoint(0)
@@ -84,17 +84,17 @@ public:
         return mRenderbufferPtr;
     }
     TexImageTarget ImageTarget() const {
         return mTexImageTarget;
     }
     GLint Layer() const {
         return mTexImageLayer;
     }
-    GLint MipLevel() const {
+    uint32_t MipLevel() const {
         return mTexImageLevel;
     }
     void AttachmentName(nsCString* out) const;
 
     bool HasUninitializedImageData() const;
     void SetImageDataStatus(WebGLImageDataStatus x);
 
     void Size(uint32_t* const out_width, uint32_t* const out_height) const;
@@ -254,16 +254,18 @@ public:
     }
 
     void SetReadBufferMode(GLenum readBufferMode) {
         mReadBufferMode = readBufferMode;
     }
 
     GLenum ReadBufferMode() const { return mReadBufferMode; }
 
+    void GatherAttachments(std::vector<const WebGLFBAttachPoint*>* const out) const;
+
 protected:
     WebGLFBAttachPoint* GetAttachPoint(GLenum attachment); // Fallible
 
 public:
     void DetachTexture(const WebGLTexture* tex);
 
     void DetachRenderbuffer(const WebGLRenderbuffer* rb);
 
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -190,26 +190,26 @@ WebGLTexture::SetImageInfosAtLevel(uint3
 }
 
 bool
 WebGLTexture::IsMipmapComplete(uint32_t texUnit) const
 {
     MOZ_ASSERT(DoesMinFilterRequireMipmap());
     // GLES 3.0.4, p161
 
-    const uint32_t maxLevel = MaxEffectiveMipmapLevel(texUnit);
+    uint32_t maxLevel;
+    if (!MaxEffectiveMipmapLevel(texUnit, &maxLevel))
+        return false;
 
     // "* `level_base <= level_max`"
     if (mBaseMipmapLevel > maxLevel)
         return false;
 
     // Make a copy so we can modify it.
     const ImageInfo& baseImageInfo = BaseImageInfo();
-    if (!baseImageInfo.IsDefined())
-        return false;
 
     // Reference dimensions based on the current level.
     uint32_t refWidth = baseImageInfo.mWidth;
     uint32_t refHeight = baseImageInfo.mHeight;
     uint32_t refDepth = baseImageInfo.mDepth;
     MOZ_ASSERT(refWidth && refHeight && refDepth);
 
     for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
@@ -420,34 +420,36 @@ WebGLTexture::IsComplete(uint32_t texUni
         //    image is not cube complete, or TEXTURE_MIN_FILTER is one that requires a
         //    mipmap and the texture is not mipmap cube complete."
         // (already covered)
     }
 
     return true;
 }
 
-
-uint32_t
-WebGLTexture::MaxEffectiveMipmapLevel(uint32_t texUnit) const
+bool
+WebGLTexture::MaxEffectiveMipmapLevel(uint32_t texUnit, uint32_t* const out) const
 {
     WebGLSampler* sampler = mContext->mBoundSamplers[texUnit];
     TexMinFilter minFilter = sampler ? sampler->mMinFilter : mMinFilter;
     if (minFilter == LOCAL_GL_NEAREST ||
         minFilter == LOCAL_GL_LINEAR)
     {
-        // No mips used.
-        return mBaseMipmapLevel;
+        // No extra mips used.
+        *out = mBaseMipmapLevel;
+        return true;
     }
 
     const auto& imageInfo = BaseImageInfo();
-    MOZ_ASSERT(imageInfo.IsDefined());
+    if (!imageInfo.IsDefined())
+        return false;
 
-    uint32_t maxLevelBySize = mBaseMipmapLevel + imageInfo.MaxMipmapLevels() - 1;
-    return std::min<uint32_t>(maxLevelBySize, mMaxMipmapLevel);
+    uint32_t maxLevelBySize = mBaseMipmapLevel + imageInfo.PossibleMipmapLevels() - 1;
+    *out = std::min<uint32_t>(maxLevelBySize, mMaxMipmapLevel);
+    return true;
 }
 
 bool
 WebGLTexture::GetFakeBlackType(const char* funcName, uint32_t texUnit,
                                FakeBlackType* const out_fakeBlack)
 {
     const char* incompleteReason;
     if (!IsComplete(texUnit, &incompleteReason)) {
@@ -461,17 +463,19 @@ WebGLTexture::GetFakeBlackType(const cha
         *out_fakeBlack = FakeBlackType::RGBA0001;
         return true;
     }
 
     // We may still want FakeBlack as an optimization for uninitialized image data.
     bool hasUninitializedData = false;
     bool hasInitializedData = false;
 
-    const auto maxLevel = MaxEffectiveMipmapLevel(texUnit);
+    uint32_t maxLevel;
+    MOZ_ALWAYS_TRUE( MaxEffectiveMipmapLevel(texUnit, &maxLevel) );
+
     MOZ_ASSERT(mBaseMipmapLevel <= maxLevel);
     for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) {
         for (uint8_t face = 0; face < mFaceCount; face++) {
             const auto& cur = ImageInfoAtFace(face, level);
             if (cur.IsDataInitialized())
                 hasInitializedData = true;
             else
                 hasUninitializedData = true;
@@ -785,18 +789,18 @@ WebGLTexture::GenerateMipmap(TexTarget t
                            mMinFilter.get());
     } else {
         gl->fGenerateMipmap(texTarget.get());
     }
 
     // Record the results.
     // Note that we don't use MaxEffectiveMipmapLevel() here, since that returns
     // mBaseMipmapLevel if the min filter doesn't require mipmaps.
-    const uint32_t lastLevel = mBaseMipmapLevel + baseImageInfo.MaxMipmapLevels() - 1;
-    PopulateMipChain(mBaseMipmapLevel, lastLevel);
+    const uint32_t maxLevel = mBaseMipmapLevel + baseImageInfo.PossibleMipmapLevels() - 1;
+    PopulateMipChain(mBaseMipmapLevel, maxLevel);
 }
 
 JS::Value
 WebGLTexture::GetTexParameter(TexTarget texTarget, GLenum pname)
 {
     mContext->MakeContextCurrent();
 
     GLint i = 0;
--- a/dom/canvas/WebGLTexture.h
+++ b/dom/canvas/WebGLTexture.h
@@ -149,19 +149,20 @@ public:
             if (!IsDefined())
                 Clear();
         }
 
     protected:
         ImageInfo& operator =(const ImageInfo& a);
 
     public:
-        uint32_t MaxMipmapLevels() const {
+        uint32_t PossibleMipmapLevels() const {
             // GLES 3.0.4, 3.8 - Mipmapping: `floor(log2(largest_of_dims)) + 1`
-            uint32_t largest = std::max(std::max(mWidth, mHeight), mDepth);
+            const uint32_t largest = std::max(std::max(mWidth, mHeight), mDepth);
+            MOZ_ASSERT(largest != 0);
             return FloorLog2Size(largest) + 1;
         }
 
         bool IsPowerOfTwo() const;
 
         void AddAttachPoint(WebGLFBAttachPoint* attachPoint);
         void RemoveAttachPoint(WebGLFBAttachPoint* attachPoint);
         void OnRespecify() const;
@@ -283,17 +284,17 @@ public:
 
     ////////////////////////////////////
 
 protected:
     void ClampLevelBaseAndMax();
 
     void PopulateMipChain(uint32_t baseLevel, uint32_t maxLevel);
 
-    uint32_t MaxEffectiveMipmapLevel(uint32_t texUnit) const;
+    bool MaxEffectiveMipmapLevel(uint32_t texUnit, uint32_t* const out) const;
 
     static uint8_t FaceForTarget(TexImageTarget texImageTarget) {
         GLenum rawTexImageTarget = texImageTarget.get();
         switch (rawTexImageTarget) {
         case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X:
         case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
         case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
         case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
@@ -383,16 +384,19 @@ public:
 
     bool IsCubeMap() const { return (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP); }
 
     // Resolve cache optimizations
 protected:
     bool GetFakeBlackType(const char* funcName, uint32_t texUnit,
                           FakeBlackType* const out_fakeBlack);
 public:
+    bool IsFeedback(WebGLContext* webgl, const char* funcName, uint32_t texUnit,
+                    const std::vector<const WebGLFBAttachPoint*>& fbAttachments) const;
+
     bool ResolveForDraw(const char* funcName, uint32_t texUnit,
                         FakeBlackType* const out_fakeBlack);
 
     void InvalidateResolveCache() { mIsResolved = false; }
 };
 
 inline TexImageTarget
 TexImageTargetForTargetAndFace(TexTarget target, uint8_t face)