Bug 1251487 - r?jrmuizel - Detect and error on RTT+texture use on draft
authorJeff Gilbert <jdashg@gmail.com>
Thu, 25 Feb 2016 14:43:54 -0800
changeset 350575 28413f3d88b61006c07c839dea91dbc2940b4b88
parent 349384 e847cfcb315f511f4928b03fd47dcf57aad05e1e
child 350576 95e0f1ffb0c8b32d895dd0097f354421390aef27
push id15377
push userjgilbert@mozilla.com
push dateThu, 14 Apr 2016 01:19:52 +0000
reviewersjrmuizel
bugs1251487
milestone48.0a1
Bug 1251487 - r?jrmuizel - Detect and error on RTT+texture use on From 10348dab4268f8ee1bb037f004f42d35c187e924 Mon Sep 17 00:00:00 2001 draw. --- dom/canvas/WebGLActiveInfo.cpp | 2 + dom/canvas/WebGLActiveInfo.h | 6 ++- dom/canvas/WebGLContext.cpp | 34 +++++++++++++ dom/canvas/WebGLContext.h | 5 ++ dom/canvas/WebGLContextDraw.cpp | 104 ++++++++++++++++++++++------------------ dom/canvas/WebGLContextGL.cpp | 16 +++++++ dom/canvas/WebGLFramebuffer.cpp | 24 +++++++++- dom/canvas/WebGLFramebuffer.h | 1 + dom/canvas/WebGLProgram.cpp | 14 ++++-- dom/canvas/WebGLProgram.h | 2 + 10 files changed, 156 insertions(+), 52 deletions(-) MozReview-Commit-ID: 10FotSyWesA
dom/canvas/WebGLActiveInfo.cpp
dom/canvas/WebGLActiveInfo.h
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextDraw.cpp
dom/canvas/WebGLContextGL.cpp
dom/canvas/WebGLFramebuffer.cpp
dom/canvas/WebGLFramebuffer.h
dom/canvas/WebGLProgram.cpp
dom/canvas/WebGLProgram.h
--- a/dom/canvas/WebGLActiveInfo.cpp
+++ b/dom/canvas/WebGLActiveInfo.cpp
@@ -81,16 +81,18 @@ WebGLActiveInfo::WebGLActiveInfo(WebGLCo
                                  const nsACString& baseMappedName)
     : mWebGL(webgl)
     , mElemCount(elemCount)
     , mElemType(elemType)
     , mBaseUserName(baseUserName)
     , mIsArray(isArray)
     , mElemSize(ElemSizeFromType(elemType))
     , mBaseMappedName(baseMappedName)
+    , mTexArrayForUniformSampler(webgl->GetTexArrayForUniformSamplerType(elemType))
+    , mUniformSamplerValue{0}
 { }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 JSObject*
 WebGLActiveInfo::WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLActiveInfoBinding::Wrap(js, this, givenProto);
--- a/dom/canvas/WebGLActiveInfo.h
+++ b/dom/canvas/WebGLActiveInfo.h
@@ -25,29 +25,31 @@ public:
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLActiveInfo)
 
     virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto) override;
 
     WebGLContext* GetParentObject() const {
         return mWebGL;
     }
 
-
     WebGLContext* const mWebGL;
 
     // ActiveInfo state:
     const GLint mElemCount; // `size`
     const GLenum mElemType; // `type`
     const nsCString mBaseUserName; // `name`, but ASCII, and without any final "[0]".
 
     // Not actually part of ActiveInfo:
     const bool mIsArray;
     const uint8_t mElemSize;
     const nsCString mBaseMappedName; // Without any final "[0]".
 
+    const nsTArray<WebGLRefPtr<WebGLTexture>>* const mTexArrayForUniformSampler;
+    mutable GLint mUniformSamplerValue[4];
+
     WebGLActiveInfo(WebGLContext* webgl, GLint elemCount, GLenum elemType, bool isArray,
                     const nsACString& baseUserName, const nsACString& baseMappedName);
 
     /* GLES 2.0.25, p33:
      *   This command will return as much information about active
      *   attributes as possible. If no information is available, length will
      *   be set to zero and name will be an empty string. This situation
      *   could arise if GetActiveAttrib is issued after a failed link.
@@ -77,16 +79,18 @@ private:
     explicit WebGLActiveInfo(WebGLContext* webgl)
         : mWebGL(webgl)
         , mElemCount(0)
         , mElemType(0)
         , mBaseUserName("")
         , mIsArray(false)
         , mElemSize(0)
         , mBaseMappedName("")
+        , mTexArrayForUniformSampler(nullptr)
+        , mUniformSamplerValue{0}
     { }
 
     // Private destructor, to discourage deletion outside of Release():
     ~WebGLActiveInfo() { }
 };
 
 } // namespace mozilla
 
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -2166,16 +2166,50 @@ WebGLContext::GetUnpackSize(bool isFunc3
     CheckedUint32 totalBytes = strideBytesPerImage * (usedImages - 1);
     totalBytes += strideBytesPerRow * (usedRowsPerImage - 1);
     totalBytes += usedBytesPerRow;
 
     return totalBytes;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+
+const nsTArray<WebGLRefPtr<WebGLTexture> >*
+WebGLContext::GetTexArrayForUniformSamplerType(GLenum elemType) const
+{
+    switch (elemType) {
+    case LOCAL_GL_SAMPLER_2D:
+    case LOCAL_GL_SAMPLER_2D_SHADOW:
+    case LOCAL_GL_INT_SAMPLER_2D:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
+        return &mBound2DTextures;
+
+    case LOCAL_GL_SAMPLER_CUBE:
+    case LOCAL_GL_SAMPLER_CUBE_SHADOW:
+    case LOCAL_GL_INT_SAMPLER_CUBE:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
+        return &mBoundCubeMapTextures;
+
+    case LOCAL_GL_SAMPLER_3D:
+    case LOCAL_GL_INT_SAMPLER_3D:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
+        return &mBound3DTextures;
+
+    case LOCAL_GL_SAMPLER_2D_ARRAY:
+    case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
+    case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
+        return &mBound2DArrayTextures;
+
+    default:
+        return nullptr;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // XPCOM goop
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext,
   mCanvasElement,
   mOffscreenCanvas,
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -183,16 +183,17 @@ class WebGLContext
     , public nsICanvasRenderingContextInternal
     , public nsSupportsWeakReference
     , public WebGLContextUnchecked
     , public WebGLRectangleObject
     , public nsWrapperCache
     , public SupportsWeakPtr<WebGLContext>
 {
     friend class WebGL2Context;
+    friend class WebGLActiveInfo;
     friend class WebGLContextUserData;
     friend class WebGLExtensionCompressedTextureATC;
     friend class WebGLExtensionCompressedTextureES3;
     friend class WebGLExtensionCompressedTextureETC1;
     friend class WebGLExtensionCompressedTexturePVRTC;
     friend class WebGLExtensionCompressedTextureS3TC;
     friend class WebGLExtensionDepthTexture;
     friend class WebGLExtensionDisjointTimerQuery;
@@ -1030,16 +1031,17 @@ public:
 private:
     // Cache the max number of vertices and instances that can be read from
     // bound VBOs (result of ValidateBuffers).
     bool mBufferFetchingIsVerified;
     bool mBufferFetchingHasPerVertex;
     uint32_t mMaxFetchedVertices;
     uint32_t mMaxFetchedInstances;
 
+    bool Draw_check(const char* funcName);
     bool DrawArrays_check(GLint first, GLsizei count, GLsizei primcount,
                           const char* info);
     bool DrawElements_check(GLsizei count, GLenum type, WebGLintptr byteOffset,
                             GLsizei primcount, const char* info,
                             GLuint* out_upperBound);
     bool DrawInstanced_check(const char* info);
     void Draw_cleanup(const char* funcName);
 
@@ -1372,16 +1374,19 @@ protected:
     void ForceRestoreContext();
 
     nsTArray<WebGLRefPtr<WebGLTexture> > mBound2DTextures;
     nsTArray<WebGLRefPtr<WebGLTexture> > mBoundCubeMapTextures;
     nsTArray<WebGLRefPtr<WebGLTexture> > mBound3DTextures;
     nsTArray<WebGLRefPtr<WebGLTexture> > mBound2DArrayTextures;
     nsTArray<WebGLRefPtr<WebGLSampler> > mBoundSamplers;
 
+    const nsTArray<WebGLRefPtr<WebGLTexture> >*
+    GetTexArrayForUniformSamplerType(GLenum elemType) const;
+
     void ResolveTexturesForDraw() const;
 
     WebGLRefPtr<WebGLProgram> mCurrentProgram;
     RefPtr<const webgl::LinkedProgramInfo> mActiveProgramLinkInfo;
 
     bool ValidateFramebufferTarget(GLenum target, const char* const info);
 
     WebGLRefPtr<WebGLFramebuffer> mBoundDrawFramebuffer;
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -168,47 +168,87 @@ WebGLContext::DrawInstanced_check(const 
         ErrorInvalidOperation("%s: at least one vertex attribute divisor should be 0", info);
         return false;
     }
 
     return true;
 }
 
 bool
+WebGLContext::Draw_check(const char* funcName)
+{
+    if (!ValidateStencilParamsForDrawCall())
+        return false;
+
+    // Any checks below this depend on a program being available.
+    if (!mCurrentProgram) {
+        ErrorInvalidOperation("%s: null CURRENT_PROGRAM", funcName);
+        return false;
+    }
+    MOZ_ASSERT(mActiveProgramLinkInfo);
+
+    if (!ValidateBufferFetching(funcName))
+        return false;
+
+    MakeContextCurrent();
+
+    if (mBoundDrawFramebuffer) {
+        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) {
+                    ErrorInvalidOperation("%s: Feedback loop detected: A sampled texture"
+                                          " is also attached to draw framebuffer.",
+                                          funcName);
+                    return false;
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
+bool
 WebGLContext::DrawArrays_check(GLint first, GLsizei count, GLsizei primcount,
                                const char* info)
 {
     if (first < 0 || count < 0) {
         ErrorInvalidValue("%s: negative first or count", info);
         return false;
     }
 
     if (primcount < 0) {
         ErrorInvalidValue("%s: negative primcount", info);
         return false;
     }
 
-    if (!ValidateStencilParamsForDrawCall()) {
-        return false;
-    }
-
     // If count is 0, there's nothing to do.
     if (count == 0 || primcount == 0) {
         return false;
     }
 
-    // Any checks below this depend on a program being available.
-    if (!mCurrentProgram) {
-        ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
-        return false;
-    }
-
-    if (!ValidateBufferFetching(info)) {
-        return false;
-    }
+    if (!Draw_check(info))
+      return false;
 
     CheckedInt<GLsizei> checked_firstPlusCount = CheckedInt<GLsizei>(first) + count;
 
     if (!checked_firstPlusCount.isValid()) {
         ErrorInvalidOperation("%s: overflow in first+count", info);
         return false;
     }
 
@@ -217,28 +257,18 @@ WebGLContext::DrawArrays_check(GLint fir
         return false;
     }
 
     if (uint32_t(primcount) > mMaxFetchedInstances) {
         ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info);
         return false;
     }
 
-    MOZ_ASSERT(gl->IsCurrent());
-
-    if (mBoundDrawFramebuffer) {
-        if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(info))
-            return false;
-    } else {
-        ClearBackbufferIfNeeded();
-    }
-
-    if (!DoFakeVertexAttrib0(checked_firstPlusCount.value())) {
+    if (!DoFakeVertexAttrib0(checked_firstPlusCount.value()))
         return false;
-    }
 
     return true;
 }
 
 void
 WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei count)
 {
     const char funcName[] = "drawArrays";
@@ -311,20 +341,16 @@ WebGLContext::DrawElements_check(GLsizei
         return false;
     }
 
     if (primcount < 0) {
         ErrorInvalidValue("%s: negative primcount", info);
         return false;
     }
 
-    if (!ValidateStencilParamsForDrawCall()) {
-        return false;
-    }
-
     // If count is 0, there's nothing to do.
     if (count == 0 || primcount == 0)
         return false;
 
     uint8_t bytesPerElem = 0;
     switch (type) {
     case LOCAL_GL_UNSIGNED_BYTE:
         bytesPerElem = 1;
@@ -355,22 +381,16 @@ WebGLContext::DrawElements_check(GLsizei
     const GLsizei first = byteOffset / bytesPerElem;
     const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(count);
 
     if (!checked_byteCount.isValid()) {
         ErrorInvalidValue("%s: overflow in byteCount", info);
         return false;
     }
 
-    // Any checks below this depend on a program being available.
-    if (!mCurrentProgram) {
-        ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
-        return false;
-    }
-
     if (!mBoundVertexArray->mElementArrayBuffer) {
         ErrorInvalidOperation("%s: must have element array buffer binding", info);
         return false;
     }
 
     WebGLBuffer& elemArrayBuffer = *mBoundVertexArray->mElementArrayBuffer;
 
     if (!elemArrayBuffer.ByteLength()) {
@@ -385,19 +405,16 @@ WebGLContext::DrawElements_check(GLsizei
         return false;
     }
 
     if (uint32_t(checked_neededByteCount.value()) > elemArrayBuffer.ByteLength()) {
         ErrorInvalidOperation("%s: bound element array buffer is too small for given count and offset", info);
         return false;
     }
 
-    if (!ValidateBufferFetching(info))
-        return false;
-
     if (!mMaxFetchedVertices ||
         !elemArrayBuffer.Validate(type, mMaxFetchedVertices - 1, first, count, out_upperBound))
     {
         ErrorInvalidOperation(
                               "%s: bound vertex attribute buffers do not have sufficient "
                               "size for given indices from the bound element array", info);
         return false;
     }
@@ -410,28 +427,21 @@ WebGLContext::DrawElements_check(GLsizei
     // Bug 1008310 - Check if buffer has been used with a different previous type
     if (elemArrayBuffer.IsElementArrayUsedWithMultipleTypes()) {
         GenerateWarning("%s: bound element array buffer previously used with a type other than "
                         "%s, this will affect performance.",
                         info,
                         WebGLContext::EnumName(type));
     }
 
-    MOZ_ASSERT(gl->IsCurrent());
+    if (!Draw_check(info))
+        return false;
 
-    if (mBoundDrawFramebuffer) {
-        if (!mBoundDrawFramebuffer->ValidateAndInitAttachments(info))
-            return false;
-    } else {
-        ClearBackbufferIfNeeded();
-    }
-
-    if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) {
+    if (!DoFakeVertexAttrib0(mMaxFetchedVertices))
         return false;
-    }
 
     return true;
 }
 
 void
 WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type,
                            WebGLintptr byteOffset)
 {
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1858,16 +1858,18 @@ WebGLContext::Uniform1i(WebGLUniformLoca
         return;
 
     // Only uniform1i can take sampler settings.
     if (!loc->ValidateSamplerSetter(a1, this, "uniform1i"))
         return;
 
     MakeContextCurrent();
     gl->fUniform1i(rawLoc, a1);
+
+    loc->mActiveInfo->mUniformSamplerValue[0] = a1;
 }
 
 void
 WebGLContext::Uniform2i(WebGLUniformLocation* loc, GLint a1, GLint a2)
 {
     GLuint rawLoc;
     if (!ValidateUniformSetter(loc, 2, LOCAL_GL_INT, "uniform2i", &rawLoc))
         return;
@@ -1961,16 +1963,18 @@ WebGLContext::Uniform1iv_base(WebGLUnifo
         return;
     }
 
     if (!loc->ValidateSamplerSetter(data[0], this, "uniform1iv"))
         return;
 
     MakeContextCurrent();
     gl->fUniform1iv(rawLoc, numElementsToUpload, data);
+
+    loc->mActiveInfo->mUniformSamplerValue[0] = data[0];
 }
 
 void
 WebGLContext::Uniform2iv_base(WebGLUniformLocation* loc, size_t arrayLength,
                               const GLint* data)
 {
     GLuint rawLoc;
     GLsizei numElementsToUpload;
@@ -1984,16 +1988,19 @@ WebGLContext::Uniform2iv_base(WebGLUnifo
     if (!loc->ValidateSamplerSetter(data[0], this, "uniform2iv") ||
         !loc->ValidateSamplerSetter(data[1], this, "uniform2iv"))
     {
         return;
     }
 
     MakeContextCurrent();
     gl->fUniform2iv(rawLoc, numElementsToUpload, data);
+
+    loc->mActiveInfo->mUniformSamplerValue[0] = data[0];
+    loc->mActiveInfo->mUniformSamplerValue[1] = data[1];
 }
 
 void
 WebGLContext::Uniform3iv_base(WebGLUniformLocation* loc, size_t arrayLength,
                               const GLint* data)
 {
     GLuint rawLoc;
     GLsizei numElementsToUpload;
@@ -2008,16 +2015,20 @@ WebGLContext::Uniform3iv_base(WebGLUnifo
         !loc->ValidateSamplerSetter(data[1], this, "uniform3iv") ||
         !loc->ValidateSamplerSetter(data[2], this, "uniform3iv"))
     {
         return;
     }
 
     MakeContextCurrent();
     gl->fUniform3iv(rawLoc, numElementsToUpload, data);
+
+    loc->mActiveInfo->mUniformSamplerValue[0] = data[0];
+    loc->mActiveInfo->mUniformSamplerValue[1] = data[1];
+    loc->mActiveInfo->mUniformSamplerValue[2] = data[2];
 }
 
 void
 WebGLContext::Uniform4iv_base(WebGLUniformLocation* loc, size_t arrayLength,
                               const GLint* data)
 {
     GLuint rawLoc;
     GLsizei numElementsToUpload;
@@ -2033,16 +2044,21 @@ WebGLContext::Uniform4iv_base(WebGLUnifo
         !loc->ValidateSamplerSetter(data[2], this, "uniform4iv") ||
         !loc->ValidateSamplerSetter(data[3], this, "uniform4iv"))
     {
         return;
     }
 
     MakeContextCurrent();
     gl->fUniform4iv(rawLoc, numElementsToUpload, data);
+
+    loc->mActiveInfo->mUniformSamplerValue[0] = data[0];
+    loc->mActiveInfo->mUniformSamplerValue[1] = data[1];
+    loc->mActiveInfo->mUniformSamplerValue[2] = data[2];
+    loc->mActiveInfo->mUniformSamplerValue[3] = data[3];
 }
 
 void
 WebGLContext::Uniform1fv_base(WebGLUniformLocation* loc, size_t arrayLength,
                               const GLfloat* data)
 {
     GLuint rawLoc;
     GLsizei numElementsToUpload;
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -861,17 +861,17 @@ WebGLFramebuffer::AllImageSamplesMatch()
 
     bool needsInit = true;
     uint32_t samples = 0;
 
     const auto fnInitializeOrMatch = [&needsInit,
                                       &samples](const WebGLFBAttachPoint& attach)
     {
         if (!attach.HasImage())
-          return true;
+            return true;
 
         const uint32_t curSamples = attach.Samples();
 
         if (needsInit) {
             needsInit = false;
             samples = curSamples;
             return true;
         }
@@ -939,16 +939,38 @@ WebGLFramebuffer::CheckFramebufferStatus
     // Ok, attach our chosen flavor of {DEPTH, STENCIL, DEPTH_STENCIL}.
     FinalizeAttachments();
 
     // 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)
+        {
+            if (!attach.HasImage())
+                return;
+
+            if (!attach.Texture())
+                return;
+
+            attachedTextures.insert(attach.Texture());
+        };
+
+        fnAddIfTexture(mColorAttachment0);
+        fnAddIfTexture(mDepthAttachment);
+        fnAddIfTexture(mStencilAttachment);
+        fnAddIfTexture(mDepthStencilAttachment);
+        for (const auto& cur : mMoreColorAttachments) {
+            fnAddIfTexture(cur);
+        }
     } else {
         out_info->AssignLiteral("Bad status according to the driver");
     }
 
     return ret;
 }
 
 bool
--- a/dom/canvas/WebGLFramebuffer.h
+++ b/dom/canvas/WebGLFramebuffer.h
@@ -171,16 +171,17 @@ class WebGLFramebuffer final
 
 public:
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLFramebuffer)
 
     const GLuint mGLName;
 
 private:
     mutable bool mIsKnownFBComplete;
+    mutable std::set<const WebGLTexture*> mCached_AttachedTextures;
 
     GLenum mReadBufferMode;
 
     // No need to chase pointers for the oft-used color0.
     WebGLFBAttachPoint mColorAttachment0;
     WebGLFBAttachPoint mDepthAttachment;
     WebGLFBAttachPoint mStencilAttachment;
     WebGLFBAttachPoint mDepthStencilAttachment;
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -63,28 +63,30 @@ ParseName(const nsCString& name, nsCStri
         return false;
 
     *out_baseName = StringHead(name, indexOpenBracket);
     *out_isArray = true;
     *out_arrayIndex = indexNum;
     return true;
 }
 
-static void
+static WebGLActiveInfo*
 AddActiveInfo(WebGLContext* webgl, GLint elemCount, GLenum elemType, bool isArray,
               const nsACString& baseUserName, const nsACString& baseMappedName,
               std::vector<RefPtr<WebGLActiveInfo>>* activeInfoList,
               std::map<nsCString, const WebGLActiveInfo*>* infoLocMap)
 {
     RefPtr<WebGLActiveInfo> info = new WebGLActiveInfo(webgl, elemCount, elemType,
                                                        isArray, baseUserName,
                                                        baseMappedName);
     activeInfoList->push_back(info);
 
     infoLocMap->insert(std::make_pair(info->mBaseUserName, info.get()));
+
+    return info.get();
 }
 
 static void
 AddActiveBlockInfo(const nsACString& baseUserName,
                    const nsACString& baseMappedName,
                    std::vector<RefPtr<webgl::UniformBlockInfo>>* activeInfoList)
 {
     RefPtr<webgl::UniformBlockInfo> info = new webgl::UniformBlockInfo(baseUserName, baseMappedName);
@@ -229,18 +231,23 @@ QueryProgramInfo(WebGLProgram* prog, gl:
 #ifdef DUMP_SHADERVAR_MAPPINGS
         printf_stderr("[uniform %i] %s/%i/%s/%s\n", i, mappedName.BeginReading(),
                       (int)isArray, baseMappedName.BeginReading(),
                       baseUserName.BeginReading());
         printf_stderr("    lengthWithoutNull: %d\n", lengthWithoutNull);
         printf_stderr("    isArray: %d\n", (int)isArray);
 #endif
 
-        AddActiveInfo(prog->mContext, elemCount, elemType, isArray, baseUserName,
-                      baseMappedName, &info->activeUniforms, &info->uniformMap);
+        auto activeInfo = AddActiveInfo(prog->mContext, elemCount, elemType, isArray,
+                                        baseUserName, baseMappedName,
+                                        &info->activeUniforms, &info->uniformMap);
+
+        if (activeInfo->mTexArrayForUniformSampler) {
+            info->activeSamplerUniforms.push_back(activeInfo);
+        }
     }
 
     // Uniform Blocks
 
     if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
         GLuint numActiveUniformBlocks = 0;
         gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCKS,
                           (GLint*)&numActiveUniformBlocks);
@@ -281,16 +288,17 @@ QueryProgramInfo(WebGLProgram* prog, gl:
             printf_stderr("[uniform block %i] %s/%i/%s/%s\n", i, mappedName.BeginReading(),
                           (int)isArray, baseMappedName.BeginReading(),
                           baseUserName.BeginReading());
             printf_stderr("    lengthWithoutNull: %d\n", lengthWithoutNull);
             printf_stderr("    isArray: %d\n", (int)isArray);
 #endif
 
             AddActiveBlockInfo(baseUserName, baseMappedName, &info->uniformBlocks);
+            // Thankfully, uniform blocks cannot contain samplers.
         }
     }
 
     // Transform feedback varyings
 
     if (gl->IsSupported(gl::GLFeature::transform_feedback2)) {
         GLuint numTransformFeedbackVaryings = 0;
         gl->fGetProgramiv(prog->mGLName, LOCAL_GL_TRANSFORM_FEEDBACK_VARYINGS,
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -57,16 +57,18 @@ struct LinkedProgramInfo final
     MOZ_DECLARE_REFCOUNTED_TYPENAME(LinkedProgramInfo)
     MOZ_DECLARE_WEAKREFERENCE_TYPENAME(LinkedProgramInfo)
 
     WebGLProgram* const prog;
     std::vector<RefPtr<WebGLActiveInfo>> activeAttribs;
     std::vector<RefPtr<WebGLActiveInfo>> activeUniforms;
     std::vector<RefPtr<WebGLActiveInfo>> transformFeedbackVaryings;
 
+    std::vector<const WebGLActiveInfo*> activeSamplerUniforms;
+
     // Needed for Get{Attrib,Uniform}Location. The keys for these are non-mapped
     // user-facing `GLActiveInfo::name`s, without any final "[0]".
     std::map<nsCString, const WebGLActiveInfo*> attribMap;
     std::map<nsCString, const WebGLActiveInfo*> uniformMap;
     std::map<nsCString, const WebGLActiveInfo*> transformFeedbackVaryingsMap;
 
     std::vector<RefPtr<UniformBlockInfo>> uniformBlocks;