Bug 1048747 - Cleanup how uniform interface blocks are handled. r=jgilbert
authorDan Glastonbury <dglastonbury@mozilla.com>
Wed, 18 Mar 2015 13:30:52 +1000
changeset 240129 7e2bf34ef49d70c69b5086ed5d34a84174f15eaa
parent 240096 1ff8096ab54b9b8e198f1cd8334503f3d752ecb5
child 240130 760d5062419a4974c25f9e13fabf410791843085
push id28623
push usercbook@mozilla.com
push dateTue, 21 Apr 2015 13:17:41 +0000
treeherdermozilla-central@b8d59286a581 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs1048747
milestone40.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 1048747 - Cleanup how uniform interface blocks are handled. r=jgilbert
dom/canvas/WebGL2ContextUniforms.cpp
dom/canvas/WebGLActiveInfo.cpp
dom/canvas/WebGLProgram.cpp
dom/canvas/WebGLProgram.h
dom/canvas/WebGLShader.cpp
dom/canvas/WebGLShader.h
--- a/dom/canvas/WebGL2ContextUniforms.cpp
+++ b/dom/canvas/WebGL2ContextUniforms.cpp
@@ -6,18 +6,17 @@
 #include "WebGL2Context.h"
 #include "GLContext.h"
 #include "WebGLContext.h"
 #include "WebGLProgram.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
+namespace mozilla {
 
 typedef union { GLint i; GLfloat f; GLuint u; } fi_t;
 
 static inline
 GLfloat PuntToFloat(GLint i)
 {
    fi_t tmp;
    tmp.i = i;
@@ -533,134 +532,70 @@ WebGL2Context::GetUniformBlockIndex(WebG
                                     const nsAString& uniformBlockName)
 {
     if (IsContextLost())
         return 0;
 
     if (!ValidateObject("getUniformBlockIndex: program", program))
         return 0;
 
-    // Leave this unchecked for now.
-
-    const NS_LossyConvertUTF16toASCII cname(uniformBlockName);
-
-    GLuint progname = program->mGLName;
-
-    MakeContextCurrent();
-    return gl->fGetUniformBlockIndex(progname, cname.BeginReading());
-}
-
-static bool
-GetUniformBlockActiveUniforms(gl::GLContext* gl, JSContext* cx,
-                              WebGL2Context* owner, GLuint progname,
-                              GLuint uniformBlockIndex,
-                              JS::MutableHandleObject out_array)
-{
-    GLint length = 0;
-    gl->fGetActiveUniformBlockiv(progname, uniformBlockIndex,
-                                 LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &length);
-    JS::RootedObject obj(cx, Uint32Array::Create(cx, owner, length, nullptr));
-    if (!obj)
-        return false;
-
-    Uint32Array result;
-    DebugOnly<bool> inited = result.Init(obj);
-    MOZ_ASSERT(inited);
-    result.ComputeLengthAndData();
-    gl->fGetActiveUniformBlockiv(progname, uniformBlockIndex,
-                                 LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES,
-                                 (GLint*) result.Data());
-
-    out_array.set(obj);
-    return true;
+    return program->GetUniformBlockIndex(uniformBlockName);
 }
 
 void
 WebGL2Context::GetActiveUniformBlockParameter(JSContext* cx, WebGLProgram* program,
                                               GLuint uniformBlockIndex, GLenum pname,
                                               Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval,
                                               ErrorResult& rv)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
     if (!ValidateObject("getActiveUniformBlockParameter: program", program))
         return;
 
-    GLuint progname = program->mGLName;
-    GLint param = 0;
-
     MakeContextCurrent();
 
     switch(pname) {
     case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
     case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
-        gl->fGetActiveUniformBlockiv(progname, uniformBlockIndex, pname, &param);
-        retval.SetValue().SetAsBoolean() = (param != 0);
-        return;
-
     case LOCAL_GL_UNIFORM_BLOCK_BINDING:
     case LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE:
-    case LOCAL_GL_UNIFORM_BLOCK_NAME_LENGTH:
     case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
-        gl->fGetActiveUniformBlockiv(progname, uniformBlockIndex, pname, &param);
-        retval.SetValue().SetAsUnsignedLong() = param;
+        program->GetActiveUniformBlockParam(uniformBlockIndex, pname, retval);
         return;
 
     case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES:
-        JS::RootedObject array(cx);
-        if (!GetUniformBlockActiveUniforms(gl, cx, this, progname, uniformBlockIndex,
-                                           &array))
-        {
-            rv = NS_ERROR_OUT_OF_MEMORY;
-            return;
-        }
-
-        DebugOnly<bool> inited = retval.SetValue().SetAsUint32Array().Init(array);
-        MOZ_ASSERT(inited);
-
+        program->GetActiveUniformBlockActiveUniforms(cx, uniformBlockIndex, retval, rv);
         return;
     }
 
     ErrorInvalidEnumInfo("getActiveUniformBlockParameter: parameter", pname);
 }
 
-#define WEBGL_MAX_UNIFORM_BLOCK_NAME_LENGTH 256
-
 void
 WebGL2Context::GetActiveUniformBlockName(WebGLProgram* program, GLuint uniformBlockIndex,
                                          nsAString& retval)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObject("getActiveUniformBlockName: program", program))
         return;
 
-    GLuint progname = program->mGLName;
-    GLchar nameBuffer[WEBGL_MAX_UNIFORM_BLOCK_NAME_LENGTH];
-    GLsizei length = 0;
-
-    MakeContextCurrent();
-    gl->fGetActiveUniformBlockName(progname, uniformBlockIndex,
-                                   WEBGL_MAX_UNIFORM_BLOCK_NAME_LENGTH, &length,
-                                   nameBuffer);
-    retval.Assign(NS_ConvertASCIItoUTF16(nsDependentCString(nameBuffer)));
+    program->GetActiveUniformBlockName(uniformBlockIndex, retval);
 }
 
-#undef WEBGL_MAX_UNIFORM_BLOCK_NAME_LENGTH
-
 void
 WebGL2Context::UniformBlockBinding(WebGLProgram* program, GLuint uniformBlockIndex,
                                    GLuint uniformBlockBinding)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObject("uniformBlockBinding: program", program))
         return;
 
-    GLuint progname = program->mGLName;
+    program->UniformBlockBinding(uniformBlockIndex, uniformBlockBinding);
+}
 
-    MakeContextCurrent();
-    gl->fUniformBlockBinding(progname, uniformBlockIndex, uniformBlockBinding);
-}
+} // namespace mozilla
--- a/dom/canvas/WebGLActiveInfo.cpp
+++ b/dom/canvas/WebGLActiveInfo.cpp
@@ -24,19 +24,19 @@ ElemSizeFromType(GLenum elemType)
     case LOCAL_GL_SAMPLER_2D_ARRAY:
     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
     case LOCAL_GL_INT_SAMPLER_2D:
     case LOCAL_GL_INT_SAMPLER_3D:
     case LOCAL_GL_INT_SAMPLER_CUBE:
     case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
-    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
         return 1;
 
     case LOCAL_GL_BOOL_VEC2:
     case LOCAL_GL_FLOAT_VEC2:
     case LOCAL_GL_INT_VEC2:
     case LOCAL_GL_UNSIGNED_INT_VEC2:
         return 2;
 
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -73,16 +73,26 @@ AddActiveInfo(WebGLContext* webgl, GLint
     nsRefPtr<WebGLActiveInfo> info = new WebGLActiveInfo(webgl, elemCount, elemType,
                                                          isArray, baseUserName,
                                                          baseMappedName);
     activeInfoList->push_back(info);
 
     infoLocMap->insert(std::make_pair(info->mBaseUserName, 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);
+
+    activeInfoList->push_back(info);
+}
+
 //#define DUMP_SHADERVAR_MAPPINGS
 
 static TemporaryRef<const webgl::LinkedProgramInfo>
 QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl)
 {
     RefPtr<webgl::LinkedProgramInfo> info(new webgl::LinkedProgramInfo(prog));
 
     GLuint maxAttribLenWithNull = 0;
@@ -92,19 +102,28 @@ QueryProgramInfo(WebGLProgram* prog, gl:
         maxAttribLenWithNull = 1;
 
     GLuint maxUniformLenWithNull = 0;
     gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH,
                       (GLint*)&maxUniformLenWithNull);
     if (maxUniformLenWithNull < 1)
         maxUniformLenWithNull = 1;
 
+    GLuint maxUniformBlockLenWithNull = 0;
+    if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
+        gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH,
+                          (GLint*)&maxUniformBlockLenWithNull);
+        if (maxUniformBlockLenWithNull < 1)
+            maxUniformBlockLenWithNull = 1;
+    }
+
 #ifdef DUMP_SHADERVAR_MAPPINGS
     printf_stderr("maxAttribLenWithNull: %d\n", maxAttribLenWithNull);
     printf_stderr("maxUniformLenWithNull: %d\n", maxUniformLenWithNull);
+    printf_stderr("maxUniformBlockLenWithNull: %d\n", maxUniformBlockLenWithNull);
 #endif
 
     // Attribs
 
     GLuint numActiveAttribs = 0;
     gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_ATTRIBUTES,
                       (GLint*)&numActiveAttribs);
 
@@ -199,16 +218,64 @@ QueryProgramInfo(WebGLProgram* prog, gl:
         printf_stderr("    lengthWithoutNull: %d\n", lengthWithoutNull);
         printf_stderr("    isArray: %d\n", (int)isArray);
 #endif
 
         AddActiveInfo(prog->Context(), elemCount, elemType, isArray, baseUserName,
                       baseMappedName, &info->activeUniforms, &info->uniformMap);
     }
 
+    // Uniform Blocks
+
+    if (gl->IsSupported(gl::GLFeature::uniform_buffer_object)) {
+        GLuint numActiveUniformBlocks = 0;
+        gl->fGetProgramiv(prog->mGLName, LOCAL_GL_ACTIVE_UNIFORM_BLOCKS,
+                          (GLint*)&numActiveUniformBlocks);
+
+        for (GLuint i = 0; i < numActiveAttribs; i++) {
+            nsAutoCString mappedName;
+            mappedName.SetLength(maxUniformBlockLenWithNull - 1);
+
+            GLint lengthWithoutNull;
+            gl->fGetActiveUniformBlockiv(prog->mGLName, i, LOCAL_GL_UNIFORM_BLOCK_NAME_LENGTH, &lengthWithoutNull);
+            gl->fGetActiveUniformBlockName(prog->mGLName, i, maxUniformBlockLenWithNull, &lengthWithoutNull, mappedName.BeginWriting());
+            mappedName.SetLength(lengthWithoutNull);
+
+            nsAutoCString baseMappedName;
+            bool isArray;
+            size_t arrayIndex;
+            if (!ParseName(mappedName, &baseMappedName, &isArray, &arrayIndex))
+                MOZ_CRASH("Failed to parse `mappedName` received from driver.");
+
+            nsAutoCString baseUserName;
+            if (!prog->FindUniformBlockByMappedName(baseMappedName, &baseUserName, &isArray)) {
+                baseUserName = baseMappedName;
+
+                if (needsCheckForArrays && !isArray) {
+                    std::string mappedName = baseMappedName.BeginReading();
+                    mappedName += "[0]";
+
+                    GLuint loc = gl->fGetUniformBlockIndex(prog->mGLName, mappedName.c_str());
+                    if (loc != LOCAL_GL_INVALID_INDEX)
+                        isArray = true;
+                }
+            }
+
+#ifdef DUMP_SHADERVAR_MAPPINGS
+            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);
+        }
+    }
+
     return info.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 
 webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* aProg)
     : prog(aProg)
@@ -491,16 +558,151 @@ WebGLProgram::GetProgramParameter(GLenum
 
     default:
         mContext->ErrorInvalidEnumInfo("getProgramParameter: `pname`",
                                        pname);
         return JS::NullValue();
     }
 }
 
+GLuint
+WebGLProgram::GetUniformBlockIndex(const nsAString& userName_wide) const
+{
+    if (!ValidateGLSLVariableName(userName_wide, mContext, "getUniformBlockIndex"))
+        return LOCAL_GL_INVALID_INDEX;
+
+    if (!IsLinked()) {
+        mContext->ErrorInvalidOperation("getUniformBlockIndex: `program` must be linked.");
+        return LOCAL_GL_INVALID_INDEX;
+    }
+
+    const NS_LossyConvertUTF16toASCII userName(userName_wide);
+
+    nsDependentCString baseUserName;
+    bool isArray;
+    size_t arrayIndex;
+    if (!ParseName(userName, &baseUserName, &isArray, &arrayIndex))
+        return LOCAL_GL_INVALID_INDEX;
+
+    RefPtr<const webgl::UniformBlockInfo> info;
+    if (!LinkInfo()->FindUniformBlock(baseUserName, &info)) {
+        return LOCAL_GL_INVALID_INDEX;
+    }
+
+    const nsCString& baseMappedName = info->mBaseMappedName;
+    nsAutoCString mappedName(baseMappedName);
+    if (isArray) {
+        mappedName.AppendLiteral("[");
+        mappedName.AppendInt(uint32_t(arrayIndex));
+        mappedName.AppendLiteral("]");
+    }
+
+    gl::GLContext* gl = mContext->GL();
+    gl->MakeCurrent();
+
+    return gl->fGetUniformBlockIndex(mGLName, mappedName.BeginReading());
+}
+
+void
+WebGLProgram::GetActiveUniformBlockName(GLuint uniformBlockIndex, nsAString& retval) const
+{
+    if (!IsLinked()) {
+        mContext->ErrorInvalidOperation("getActiveUniformBlockName: `program` must be linked.");
+        return;
+    }
+
+    const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
+    GLuint uniformBlockCount = (GLuint) linkInfo->uniformBlocks.size();
+    if (uniformBlockIndex >= uniformBlockCount) {
+        mContext->ErrorInvalidValue("getActiveUniformBlockName: index %u invalid.", uniformBlockIndex);
+        return;
+    }
+
+    const webgl::UniformBlockInfo* blockInfo = linkInfo->uniformBlocks[uniformBlockIndex];
+
+    retval.Assign(NS_ConvertASCIItoUTF16(blockInfo->mBaseUserName));
+}
+
+void
+WebGLProgram::GetActiveUniformBlockParam(GLuint uniformBlockIndex, GLenum pname,
+                                         Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval) const
+{
+    retval.SetNull();
+    if (!IsLinked()) {
+        mContext->ErrorInvalidOperation("getActiveUniformBlockParameter: `program` must be linked.");
+        return;
+    }
+
+    const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
+    GLuint uniformBlockCount = (GLuint)linkInfo->uniformBlocks.size();
+    if (uniformBlockIndex >= uniformBlockCount) {
+        mContext->ErrorInvalidValue("getActiveUniformBlockParameter: index %u invalid.", uniformBlockIndex);
+        return;
+    }
+
+    gl::GLContext* gl = mContext->GL();
+    GLint param = 0;
+
+    switch (pname) {
+    case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
+    case LOCAL_GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
+        gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex, pname, &param);
+        retval.SetValue().SetAsBoolean() = (param != 0);
+        return;
+
+    case LOCAL_GL_UNIFORM_BLOCK_BINDING:
+    case LOCAL_GL_UNIFORM_BLOCK_DATA_SIZE:
+    case LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
+        gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex, pname, &param);
+        retval.SetValue().SetAsUnsignedLong() = param;
+        return;
+    }
+}
+
+void
+WebGLProgram::GetActiveUniformBlockActiveUniforms(JSContext* cx, GLuint uniformBlockIndex,
+                                                  Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval,
+                                                  ErrorResult& rv) const
+{
+    if (!IsLinked()) {
+        mContext->ErrorInvalidOperation("getActiveUniformBlockParameter: `program` must be linked.");
+        return;
+    }
+
+    const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
+    GLuint uniformBlockCount = (GLuint)linkInfo->uniformBlocks.size();
+    if (uniformBlockIndex >= uniformBlockCount) {
+        mContext->ErrorInvalidValue("getActiveUniformBlockParameter: index %u invalid.", uniformBlockIndex);
+        return;
+    }
+
+    gl::GLContext* gl = mContext->GL();
+    GLint activeUniformCount = 0;
+    gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex,
+                                 LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS,
+                                 &activeUniformCount);
+    JS::RootedObject obj(cx, dom::Uint32Array::Create(cx, mContext, activeUniformCount,
+                                                      nullptr));
+    if (!obj) {
+        rv = NS_ERROR_OUT_OF_MEMORY;
+        return;
+    }
+
+    dom::Uint32Array result;
+    DebugOnly<bool> inited = result.Init(obj);
+    MOZ_ASSERT(inited);
+    result.ComputeLengthAndData();
+    gl->fGetActiveUniformBlockiv(mGLName, uniformBlockIndex,
+                                 LOCAL_GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES,
+                                 (GLint*)result.Data());
+
+    inited = retval.SetValue().SetAsUint32Array().Init(obj);
+    MOZ_ASSERT(inited);
+}
+
 already_AddRefed<WebGLUniformLocation>
 WebGLProgram::GetUniformLocation(const nsAString& userName_wide) const
 {
     if (!ValidateGLSLVariableName(userName_wide, mContext, "getUniformLocation"))
         return nullptr;
 
     if (!IsLinked()) {
         mContext->ErrorInvalidOperation("getUniformLocation: `program` must be linked.");
@@ -535,16 +737,41 @@ WebGLProgram::GetUniformLocation(const n
     if (loc == -1)
         return nullptr;
 
     nsRefPtr<WebGLUniformLocation> locObj = new WebGLUniformLocation(mContext, LinkInfo(),
                                                                      loc, activeInfo);
     return locObj.forget();
 }
 
+void
+WebGLProgram::UniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) const
+{
+    if (!IsLinked()) {
+        mContext->ErrorInvalidOperation("getActiveUniformBlockName: `program` must be linked.");
+        return;
+    }
+
+    const webgl::LinkedProgramInfo* linkInfo = LinkInfo();
+    GLuint uniformBlockCount = (GLuint)linkInfo->uniformBlocks.size();
+    if (uniformBlockIndex >= uniformBlockCount) {
+        mContext->ErrorInvalidValue("getActiveUniformBlockName: index %u invalid.", uniformBlockIndex);
+        return;
+    }
+
+    if (uniformBlockBinding > mContext->mGLMaxUniformBufferBindings) {
+        mContext->ErrorInvalidEnum("getActiveUniformBlockName: binding %u invalid.", uniformBlockBinding);
+        return;
+    }
+
+    gl::GLContext* gl = mContext->GL();
+    gl->MakeCurrent();
+    gl->fUniformBlockBinding(mGLName, uniformBlockIndex, uniformBlockBinding);
+}
+
 bool
 WebGLProgram::LinkProgram()
 {
     mContext->InvalidateBufferFetching(); // we do it early in this function
     // as some of the validation below changes program state
 
     mLinkLog.Truncate();
     mMostRecentLinkInfo = nullptr;
@@ -702,16 +929,30 @@ WebGLProgram::FindUniformByMappedName(co
         return true;
 
     if (mFragShader->FindUniformByMappedName(mappedName, out_userName, out_isArray))
         return true;
 
     return false;
 }
 
+bool
+WebGLProgram::FindUniformBlockByMappedName(const nsACString& mappedName,
+                                           nsCString* const out_userName,
+                                           bool* const out_isArray) const
+{
+    if (mVertShader->FindUniformBlockByMappedName(mappedName, out_userName, out_isArray))
+        return true;
+
+    if (mFragShader->FindUniformBlockByMappedName(mappedName, out_userName, out_isArray))
+        return true;
+
+    return false;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 JSObject*
 WebGLProgram::WrapObject(JSContext* js, JS::Handle<JSObject*> aGivenProto)
 {
     return dom::WebGLProgramBinding::Wrap(js, this, aGivenProto);
 }
 
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -4,47 +4,65 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WEBGL_PROGRAM_H_
 #define WEBGL_PROGRAM_H_
 
 #include <map>
 #include "mozilla/CheckedInt.h"
 #include "mozilla/LinkedList.h"
+#include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 #include <set>
 #include <vector>
 #include "WebGLObjectModel.h"
 #include "WebGLShader.h"
 
 namespace mozilla {
 
 class WebGLActiveInfo;
 class WebGLProgram;
 class WebGLUniformLocation;
 
 namespace webgl {
 
+struct UniformBlockInfo final
+    : public RefCounted<UniformBlockInfo>
+{
+    MOZ_DECLARE_REFCOUNTED_TYPENAME(UniformBlockInfo);
+
+    const nsCString mBaseUserName;
+    const nsCString mBaseMappedName;
+
+    UniformBlockInfo(const nsACString& baseUserName,
+                     const nsACString& baseMappedName)
+        : mBaseUserName(baseUserName)
+        , mBaseMappedName(baseMappedName)
+    {}
+};
+
 struct LinkedProgramInfo final
     : public RefCounted<LinkedProgramInfo>
     , public SupportsWeakPtr<LinkedProgramInfo>
 {
     MOZ_DECLARE_REFCOUNTED_TYPENAME(LinkedProgramInfo)
 
     WebGLProgram* const prog;
     std::vector<nsRefPtr<WebGLActiveInfo>> activeAttribs;
     std::vector<nsRefPtr<WebGLActiveInfo>> activeUniforms;
 
     // 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 nsCString>* fragDataMap;
 
+    std::vector<RefPtr<UniformBlockInfo>> uniformBlocks;
+
     // Needed for draw call validation.
     std::set<GLuint> activeAttribLocs;
 
     explicit LinkedProgramInfo(WebGLProgram* aProg);
 
     bool FindAttrib(const nsCString& baseUserName,
                     const WebGLActiveInfo** const out_activeInfo) const
     {
@@ -62,16 +80,30 @@ struct LinkedProgramInfo final
         auto itr = uniformMap.find(baseUserName);
         if (itr == uniformMap.end())
             return false;
 
         *out_activeInfo = itr->second;
         return true;
     }
 
+    bool FindUniformBlock(const nsCString& baseUserName,
+                          RefPtr<const UniformBlockInfo>* const out_info) const
+    {
+        const size_t count = uniformBlocks.size();
+        for (size_t i = 0; i < count; i++) {
+            if (baseUserName == uniformBlocks[i]->mBaseUserName) {
+                *out_info = uniformBlocks[i].get();
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     bool FindFragData(const nsCString& baseUserName,
                       nsCString* const out_baseMappedName) const
     {
         if (!fragDataMap) {
             *out_baseMappedName = baseUserName;
             return true;
         }
 
@@ -110,28 +142,40 @@ public:
     void DetachShader(WebGLShader* shader);
     already_AddRefed<WebGLActiveInfo> GetActiveAttrib(GLuint index) const;
     already_AddRefed<WebGLActiveInfo> GetActiveUniform(GLuint index) const;
     void GetAttachedShaders(nsTArray<nsRefPtr<WebGLShader>>* const out) const;
     GLint GetAttribLocation(const nsAString& name) const;
     GLint GetFragDataLocation(const nsAString& name) const;
     void GetProgramInfoLog(nsAString* const out) const;
     JS::Value GetProgramParameter(GLenum pname) const;
+    GLuint GetUniformBlockIndex(const nsAString& name) const;
+    void GetActiveUniformBlockName(GLuint uniformBlockIndex, nsAString& name) const;
+    void GetActiveUniformBlockParam(GLuint uniformBlockIndex, GLenum pname,
+                                    Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval) const;
+    void GetActiveUniformBlockActiveUniforms(JSContext* cx, GLuint uniformBlockIndex,
+                                             Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval,
+                                             ErrorResult& rv) const;
     already_AddRefed<WebGLUniformLocation> GetUniformLocation(const nsAString& name) const;
+    void UniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) const;
+
     bool LinkProgram();
     bool UseProgram() const;
     void ValidateProgram() const;
 
     ////////////////
 
     bool FindAttribUserNameByMappedName(const nsACString& mappedName,
                                         nsDependentCString* const out_userName) const;
     bool FindUniformByMappedName(const nsACString& mappedName,
                                  nsCString* const out_userName,
                                  bool* const out_isArray) const;
+    bool FindUniformBlockByMappedName(const nsACString& mappedName,
+                                      nsCString* const out_userName,
+                                      bool* const out_isArray) const;
 
     bool IsLinked() const { return mMostRecentLinkInfo; }
 
     const webgl::LinkedProgramInfo* LinkInfo() const {
         return mMostRecentLinkInfo.get();
     }
 
     WebGLContext* GetParentObject() const {
--- a/dom/canvas/WebGLShader.cpp
+++ b/dom/canvas/WebGLShader.cpp
@@ -340,16 +340,25 @@ WebGLShader::FindUniformByMappedName(con
     std::string userNameStr;
     if (!mValidator->FindUniformByMappedName(mappedNameStr, &userNameStr, out_isArray))
         return false;
 
     *out_userName = userNameStr.c_str();
     return true;
 }
 
+bool
+WebGLShader::FindUniformBlockByMappedName(const nsACString& mappedName,
+                                          nsCString* const out_userName,
+                                          bool* const out_isArray) const
+{
+    // TODO: Extract block information from shader validator.
+    return false;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Boilerplate
 
 JSObject*
 WebGLShader::WrapObject(JSContext* js, JS::Handle<JSObject*> aGivenProto)
 {
     return dom::WebGLShaderBinding::Wrap(js, this, aGivenProto);
 }
--- a/dom/canvas/WebGLShader.h
+++ b/dom/canvas/WebGLShader.h
@@ -46,16 +46,19 @@ public:
     bool CanLinkTo(const WebGLShader* prev, nsCString* const out_log) const;
     size_t CalcNumSamplerUniforms() const;
     void BindAttribLocation(GLuint prog, const nsCString& userName, GLuint index) const;
     bool FindAttribUserNameByMappedName(const nsACString& mappedName,
                                         nsDependentCString* const out_userName) const;
     bool FindUniformByMappedName(const nsACString& mappedName,
                                  nsCString* const out_userName,
                                  bool* const out_isArray) const;
+    bool FindUniformBlockByMappedName(const nsACString& mappedName,
+                                      nsCString* const out_userName,
+                                      bool* const out_isArray) const;
 
     bool IsCompiled() const {
         return mTranslationSuccessful && mCompilationSuccessful;
     }
 
     // Other funcs
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
     void Delete();