r?jrmuizel - Detect feedback loops in CopyTex*Image. draft
authorJeff Gilbert <jdashg@gmail.com>
Thu, 25 Feb 2016 17:13:00 -0800
changeset 350576 95e0f1ffb0c8b32d895dd0097f354421390aef27
parent 350575 28413f3d88b61006c07c839dea91dbc2940b4b88
child 518367 761391e23c8b1a9815993871061d4f65c12dc92a
push id15377
push userjgilbert@mozilla.com
push dateThu, 14 Apr 2016 01:19:52 +0000
milestone48.0a1
r?jrmuizel - Detect feedback loops in CopyTex*Image. From 8de067a13046da0b45fd30b098034c28bcdf7aa8 Mon Sep 17 00:00:00 2001 --- dom/canvas/WebGLContext.h | 6 +++++ dom/canvas/WebGLContextDraw.cpp | 7 +----- dom/canvas/WebGLFramebuffer.cpp | 18 ++++++++++--- dom/canvas/WebGLFramebuffer.h | 10 +++++++- dom/canvas/WebGLTextureUpload.cpp | 53 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 11 deletions(-) MozReview-Commit-ID: 4L9dgY6s8g3
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextDraw.cpp
dom/canvas/WebGLFramebuffer.cpp
dom/canvas/WebGLFramebuffer.h
dom/canvas/WebGLTextureUpload.cpp
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -197,16 +197,22 @@ class WebGLContext
     friend class WebGLExtensionCompressedTextureS3TC;
     friend class WebGLExtensionDepthTexture;
     friend class WebGLExtensionDisjointTimerQuery;
     friend class WebGLExtensionDrawBuffers;
     friend class WebGLExtensionLoseContext;
     friend class WebGLExtensionVertexArray;
     friend class WebGLMemoryTracker;
 
+    friend bool CheckForCopyTexImageFeedback(const char* funcName, WebGLContext* webgl,
+                                             const WebGLTexture* dstTex,
+                                             TexImageTarget dstTarget, GLint dstLevel,
+                                             const webgl::FormatInfo* dstFormat,
+                                             GLint dstZOffset);
+
     enum {
         UNPACK_FLIP_Y_WEBGL = 0x9240,
         UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241,
         CONTEXT_LOST_WEBGL = 0x9242,
         UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243,
         BROWSER_DEFAULT_WEBGL = 0x9244,
         UNMASKED_VENDOR_WEBGL = 0x9245,
         UNMASKED_RENDERER_WEBGL = 0x9246
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -194,28 +194,23 @@ WebGLContext::Draw_check(const char* fun
         if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(funcName))
             return false;
     } else {
         ClearBackbufferIfNeeded();
     }
 
     // Check for sampling+render-to-texture (rtt) feedback loops.
     if (mBoundDrawFramebuffer) {
-        MOZ_ASSERT(mBoundDrawFramebuffer->mIsKnownFBComplete);
-        const auto& rttSet = mBoundDrawFramebuffer->mCached_AttachedTextures;
-
-        const auto rttSetEnd = rttSet.cend();
-
         for (const auto& sampler : mActiveProgramLinkInfo->activeSamplerUniforms) {
             MOZ_ASSERT(sampler->mTexArrayForUniformSampler);
             const auto& texArrayForCurSampler = *(sampler->mTexArrayForUniformSampler);
 
             for (const auto& texIndex : sampler->mUniformSamplerValue) {
                 const auto& tex = texArrayForCurSampler[texIndex];
-                if (rttSet.find(tex.get()) != rttSetEnd) {
+                if (mBoundDrawFramebuffer->IsTextureAttached(tex.get())) {
                     ErrorInvalidOperation("%s: Feedback loop detected: A sampled texture"
                                           " is also attached to draw framebuffer.",
                                           funcName);
                     return false;
                 }
             }
         }
     }
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -941,27 +941,26 @@ WebGLFramebuffer::CheckFramebufferStatus
 
     // TODO: This should not be unconditionally GL_FRAMEBUFFER.
     ret = mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
 
     if (ret == LOCAL_GL_FRAMEBUFFER_COMPLETE) {
         mIsKnownFBComplete = true;
 
         // Cache anything that only invalidates on FB completeness changes.
-        auto& attachedTextures = mCached_AttachedTextures;
-        attachedTextures.clear();
-        const auto fnAddIfTexture = [&attachedTextures](const WebGLFBAttachPoint& attach)
+        mCached_TextureAttachments.clear();
+        const auto fnAddIfTexture = [this](const WebGLFBAttachPoint& attach)
         {
             if (!attach.HasImage())
                 return;
 
             if (!attach.Texture())
                 return;
 
-            attachedTextures.insert(attach.Texture());
+            this->mCached_TextureAttachments.push_back(&attach);
         };
 
         fnAddIfTexture(mColorAttachment0);
         fnAddIfTexture(mDepthAttachment);
         fnAddIfTexture(mStencilAttachment);
         fnAddIfTexture(mDepthStencilAttachment);
         for (const auto& cur : mMoreColorAttachments) {
             fnAddIfTexture(cur);
@@ -969,16 +968,27 @@ WebGLFramebuffer::CheckFramebufferStatus
     } else {
         out_info->AssignLiteral("Bad status according to the driver");
     }
 
     return ret;
 }
 
 bool
+WebGLFramebuffer::IsTextureAttached(const WebGLTexture* tex) const
+{
+    MOZ_ASSERT(mIsKnownFBComplete);
+    for (const auto& cur : mCached_TextureAttachments) {
+        if (cur->Texture() == tex)
+            return true;
+    }
+    return false;
+}
+
+bool
 WebGLFramebuffer::ValidateAndInitAttachments(const char* funcName)
 {
     MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this ||
                mContext->mBoundReadFramebuffer == this);
 
     nsCString fbStatusInfo;
     const auto fbStatus = CheckFramebufferStatus(&fbStatusInfo);
     if (fbStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -164,24 +164,30 @@ class WebGLFramebuffer final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLFramebuffer>
     , public LinkedListElement<WebGLFramebuffer>
     , public WebGLContextBoundObject
     , public SupportsWeakPtr<WebGLFramebuffer>
 {
     friend class WebGLContext;
 
+    friend bool CheckForCopyTexImageFeedback(const char* funcName, WebGLContext* webgl,
+                                             const WebGLTexture* dstTex,
+                                             TexImageTarget dstTarget, GLint dstLevel,
+                                             const webgl::FormatInfo* dstFormat,
+                                             GLint dstZOffset);
+
 public:
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLFramebuffer)
 
     const GLuint mGLName;
 
 private:
     mutable bool mIsKnownFBComplete;
-    mutable std::set<const WebGLTexture*> mCached_AttachedTextures;
+    mutable std::vector<const WebGLFBAttachPoint*> mCached_TextureAttachments;
 
     GLenum mReadBufferMode;
 
     // No need to chase pointers for the oft-used color0.
     WebGLFBAttachPoint mColorAttachment0;
     WebGLFBAttachPoint mDepthAttachment;
     WebGLFBAttachPoint mStencilAttachment;
     WebGLFBAttachPoint mDepthStencilAttachment;
@@ -253,16 +259,18 @@ public:
     const WebGLFBAttachPoint& DepthStencilAttachment() const {
         return mDepthStencilAttachment;
     }
 
     void SetReadBufferMode(GLenum readBufferMode) {
         mReadBufferMode = readBufferMode;
     }
 
+    bool IsTextureAttached(const WebGLTexture* tex) const;
+
 protected:
     WebGLFBAttachPoint* GetAttachPoint(GLenum attachment); // Fallible
 
 public:
     void DetachTexture(const WebGLTexture* tex);
 
     void DetachRenderbuffer(const WebGLRenderbuffer* rb);
 
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -1687,16 +1687,56 @@ ScopedCopyTexImageSource::~ScopedCopyTex
                          ToGLHandle(mWebGL->mBoundReadFramebuffer));
 
     gl->fDeleteFramebuffers(1, &mFB);
     gl->fDeleteRenderbuffers(1, &mRB);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
+static bool
+CheckForCopyTexImageFeedback(const char* funcName, WebGLContext* webgl,
+                             const WebGLTexture* dstTex, TexImageTarget dstTarget,
+                             GLint dstLevel, const webgl::FormatInfo* dstFormat,
+                             GLint dstZOffset)
+{
+    const auto& srcFB = webgl->mBoundReadFramebuffer;
+    if (!srcFB)
+        return true;
+
+    const WebGLFBAttachPoint* srcAttachPoint;
+    if (dstFormat->isColorFormat) {
+        srcAttachPoint = &(srcFB->mColorAttachment0);
+
+    } else if (dstFormat->hasDepth) {
+        MOZ_ASSERT(!srcFB->mDepthStencilAttachment.IsDefined());
+        srcAttachPoint = &(srcFB->mDepthAttachment);
+
+    } else if (dstFormat->hasStencil) {
+        MOZ_ASSERT(!srcFB->mDepthStencilAttachment.IsDefined());
+        srcAttachPoint = &(srcFB->mStencilAttachment);
+
+    } else {
+        MOZ_CRASH("Bad dstUsage.");
+    }
+
+    if (dstTex != srcAttachPoint->Texture() ||
+        dstTarget != srcAttachPoint->ImageTarget() ||
+        dstLevel != srcAttachPoint->MipLevel() ||
+        dstZOffset != srcAttachPoint->Layer())
+    {
+        return true;
+    }
+
+    webgl->ErrorInvalidOperation("%s: Destination texImage is attached to the read"
+                                 " framebuffer, creating a feedback loop.",
+                                 funcName);
+    return false;
+}
+
 // There is no CopyTexImage3D.
 void
 WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internalFormat,
                              GLint x, GLint y, GLsizei width, GLsizei height,
                              GLint border)
 {
     const char funcName[] = "CopyTexImage2D";
 
@@ -1774,16 +1814,23 @@ WebGLTexture::CopyTexImage2D(TexImageTar
         mContext->ErrorInvalidOperation("%s: Function may not be called with format %s.",
                                         funcName, dstFormat->name);
         return;
     }
 
     if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
         return;
 
+    const GLint zOffset = 0;
+    if (!CheckForCopyTexImageFeedback(funcName, mContext, this, target, level, dstFormat,
+                                      zOffset))
+    {
+        return;
+    }
+
     ////////////////////////////////////
     // Do the thing!
 
     gl::GLContext* gl = mContext->gl;
     gl->MakeCurrent();
 
     ScopedCopyTexImageSource maybeSwizzle(mContext, funcName, srcWidth, srcHeight,
                                           srcFormat, dstUsage);
@@ -1896,16 +1943,22 @@ WebGLTexture::CopyTexSubImage(const char
     }
 
     ////////////////////////////////////
     // Check that source and dest info are compatible
 
     if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat))
         return;
 
+    if (!CheckForCopyTexImageFeedback(funcName, mContext, this, target, level, dstFormat,
+                                      zOffset))
+    {
+        return;
+    }
+
     ////////////////////////////////////
     // Do the thing!
 
     mContext->gl->MakeCurrent();
 
     ScopedCopyTexImageSource maybeSwizzle(mContext, funcName, srcWidth, srcHeight,
                                           srcFormat, dstUsage);