diff --git a/Test/baseResults/spv.debugInfo.1.1.frag.out b/Test/baseResults/spv.debugInfo.1.1.frag.out
index b730376250bb23f43e8823479c1c31acfeac111b..9bd14910bfd940b6b4d715a31ed1ee92e3740cbf 100644
--- a/Test/baseResults/spv.debugInfo.1.1.frag.out
+++ b/Test/baseResults/spv.debugInfo.1.1.frag.out
@@ -94,6 +94,7 @@ void main()
                               MemberDecorate 54(ubuf) 0 Offset 0
                               Decorate 54(ubuf) Block
                               Decorate 56 DescriptorSet 3
+                              Decorate 69(s2d) Location 0
                               Decorate 69(s2d) DescriptorSet 3
                3:             TypeVoid
                4:             TypeFunction 3
diff --git a/Test/baseResults/spv.debugInfo.frag.out b/Test/baseResults/spv.debugInfo.frag.out
index de54415127da24566c35d77d52797a4f74495a58..0bb266bde7245c736257cfba52f41eda103ae358 100644
--- a/Test/baseResults/spv.debugInfo.frag.out
+++ b/Test/baseResults/spv.debugInfo.frag.out
@@ -97,6 +97,7 @@ void main()
                               Decorate 54(ubuf) Block
                               Decorate 56 DescriptorSet 3
                               Decorate 56 Binding 0
+                              Decorate 69(s2d) Location 0
                               Decorate 69(s2d) DescriptorSet 3
                               Decorate 69(s2d) Binding 1
                3:             TypeVoid
diff --git a/glslang/MachineIndependent/iomapper.cpp b/glslang/MachineIndependent/iomapper.cpp
index a7deb9d504c9690c3d38b6784f13f84a9f662573..68172538d323d2ead6c3b3e3ef965bf9538fed56 100644
--- a/glslang/MachineIndependent/iomapper.cpp
+++ b/glslang/MachineIndependent/iomapper.cpp
@@ -431,7 +431,8 @@ struct TDefaultIoResolverBase : public glslang::TIoMapResolver
 
         // no locations added if already present, a built-in variable, a block, or an opaque
         if (type.getQualifier().hasLocation() || type.isBuiltIn() ||
-            type.getBasicType() == EbtBlock || type.containsOpaque())
+            type.getBasicType() == EbtBlock ||
+            (type.containsOpaque() && intermediate.getSpv().openGl == 0))
             return -1;
 
         // no locations on blocks of built-in variables
@@ -442,7 +443,11 @@ struct TDefaultIoResolverBase : public glslang::TIoMapResolver
                 return -1;
         }
 
-        return nextUniformLocation++;
+        int location = nextUniformLocation;
+
+        nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type);
+
+        return location;
     }
     bool validateInOut(EShLanguage /*stage*/, const char* /*name*/, const TType& /*type*/, bool /*is_live*/) override
     {
diff --git a/glslang/MachineIndependent/linkValidate.cpp b/glslang/MachineIndependent/linkValidate.cpp
index 9aba27922c93804040e0fd19d86bf048c0a95bdb..aa9c5121bc1332f28d5e098bb7ed8ed03a771ed8 100644
--- a/glslang/MachineIndependent/linkValidate.cpp
+++ b/glslang/MachineIndependent/linkValidate.cpp
@@ -962,6 +962,36 @@ int TIntermediate::computeTypeLocationSize(const TType& type, EShLanguage stage)
     return 1;
 }
 
+// Same as computeTypeLocationSize but for uniforms
+int TIntermediate::computeTypeUniformLocationSize(const TType& type)
+{
+    // "Individual elements of a uniform array are assigned
+    // consecutive locations with the first element taking location
+    // location."
+    if (type.isArray()) {
+        // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness
+        TType elementType(type, 0);
+        if (type.isImplicitlySizedArray()) {
+            // TODO: are there valid cases of having an implicitly-sized array with a location?  If so, running this code too early.
+            return computeTypeUniformLocationSize(elementType);
+        } else
+            return type.getOuterArraySize() * computeTypeUniformLocationSize(elementType);
+    }
+
+    // "Each subsequent inner-most member or element gets incremental
+    // locations for the entire structure or array."
+    if (type.isStruct()) {
+        int size = 0;
+        for (int member = 0; member < (int)type.getStruct()->size(); ++member) {
+            TType memberType(type, member);
+            size += computeTypeUniformLocationSize(memberType);
+        }
+        return size;
+    }
+
+    return 1;
+}
+
 // Accumulate xfb buffer ranges and check for collisions as the accumulation is done.
 //
 // Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value.
diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h
index d6ecbe340ce8ece9e0a3b73fe274142d63d7b9e7..d6f13ae4ed49b4c2ad56997ff9149381e0c69c38 100644
--- a/glslang/MachineIndependent/localintermediate.h
+++ b/glslang/MachineIndependent/localintermediate.h
@@ -590,6 +590,7 @@ public:
     int addUsedOffsets(int binding, int offset, int numOffsets);
     bool addUsedConstantId(int id);
     static int computeTypeLocationSize(const TType&, EShLanguage);
+    static int computeTypeUniformLocationSize(const TType&);
 
     bool setXfbBufferStride(int buffer, unsigned stride)
     {