ES31: Add BUFFER_VARIABLE and SHADER_STORAGE_BLOCK program interfaces

This patch collects the shader storage block members information.
It implements getShaderStorageBlockMemberInfo and getShaderStorageBlockSize
for OpenGL backend. Meanwhile, it implements BUFFER_VARIABLE and SHADER_STORAGE_BLOCK
interfaces for program query.

BUG=angleproject:1920
TEST=angle_end2end_tests:ProgramInterfaceTest*
     dEQP-GLES31.functional.layout_binding.ssbo*
     dEQP-GLES31.functional.compute.basic.empty
     dEQP-GLES31.functional.compute.basic.ssbo_rw*
     dEQP-GLES31.functional.compute.basic.ssbo_local_barrier*
     dEQP-GLES31.functional.compute.basic.copy_image_to_ssbo_small
     dEQP-GLES31.functional.compute.basic.copy_ssbo_multiple_groups
     dEQP-GLES31.functional.compute.basic.copy_ssbo_multiple_invocations
     dEQP-GLES31.functional.compute.basic.copy_ssbo_single_invocation
     dEQP-GLES31.functional.compute.basic.copy_ssbo_to_image_small
     dEQP-GLES31.functional.compute.basic.shared_var*
     dEQP-GLES31.functional.compute.basic.ubo_to_ssbo*
     dEQP-GLES31.functional.compute.basic.write_multiple_arr*
     dEQP-GLES31.functional.compute.shared_var.basic_type.*
     dEQP-GLES31.functional.compute.shared_var.work_group_size.*
     dEQP-GLES31.functional.atomic_counter.*

Change-Id: Ie8b81fde5a2e919aab77adb3d137c9ff2f193409
Reviewed-on: https://chromium-review.googlesource.com/712235
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/ProgramLinkedResources.cpp b/src/libANGLE/ProgramLinkedResources.cpp
index a7f64d6..7a2a5db 100644
--- a/src/libANGLE/ProgramLinkedResources.cpp
+++ b/src/libANGLE/ProgramLinkedResources.cpp
@@ -660,6 +660,7 @@
                         if (block.name == priorBlock.name)
                         {
                             priorBlock.setStaticUse(shaderType, true);
+                            // TODO(jiajia.qin@intel.com): update the block members static use.
                         }
                     }
                 }
@@ -678,7 +679,9 @@
                                               const std::vector<VarT> &fields,
                                               const std::string &prefix,
                                               const std::string &mappedPrefix,
-                                              int blockIndex) const
+                                              int blockIndex,
+                                              bool outsideTopLevelArray,
+                                              int topLevelArraySize) const
 {
     for (const VarT &field : fields)
     {
@@ -689,14 +692,26 @@
 
         if (field.isStruct())
         {
-            for (unsigned int arrayElement = 0; arrayElement < field.elementCount(); arrayElement++)
+            int nextArraySize         = topLevelArraySize;
+            unsigned int elementCount = field.elementCount();
+
+            if (outsideTopLevelArray)
+            {
+                nextArraySize = elementCount;
+                // In OpenGL ES 3.10 spec, session 7.3.1.1 'For an active shader storage block
+                // member declared as an array of an aggregate type, an entry will be generated only
+                // for the first array element, regardless of its type.'
+                elementCount = 1;
+            }
+
+            for (unsigned int arrayElement = 0; arrayElement < elementCount; arrayElement++)
             {
                 const std::string elementName =
                     fullName + (field.isArray() ? ArrayString(arrayElement) : "");
                 const std::string elementMappedName =
                     fullMappedName + (field.isArray() ? ArrayString(arrayElement) : "");
                 defineBlockMembers(getMemberInfo, field.fields, elementName, elementMappedName,
-                                   blockIndex);
+                                   blockIndex, false, nextArraySize);
             }
         }
         else
@@ -714,7 +729,8 @@
                 fullMappedName += "[0]";
             }
 
-            defineBlockMember(field, fullName, fullMappedName, blockIndex, memberInfo);
+            defineBlockMember(field, fullName, fullMappedName, blockIndex, memberInfo,
+                              topLevelArraySize);
         }
     }
 }
@@ -728,11 +744,12 @@
     std::vector<unsigned int> blockIndexes;
 
     int blockIndex = static_cast<int>(mBlocksOut->size());
-    // Track the first and last uniform index to determine the range of active uniforms in the
-    // block.
+    // Track the first and last block member index to determine the range of active block members in
+    // the block.
     size_t firstBlockMemberIndex = getCurrentBlockMemberIndex();
     defineBlockMembers(getMemberInfo, interfaceBlock.fields, interfaceBlock.fieldPrefix(),
-                       interfaceBlock.fieldMappedPrefix(), blockIndex);
+                       interfaceBlock.fieldMappedPrefix(), blockIndex,
+                       interfaceBlock.blockType == sh::BlockType::BLOCK_BUFFER, 1);
     size_t lastBlockMemberIndex = getCurrentBlockMemberIndex();
 
     for (size_t blockMemberIndex = firstBlockMemberIndex; blockMemberIndex < lastBlockMemberIndex;
@@ -748,23 +765,18 @@
     for (unsigned int arrayElement = 0; arrayElement < interfaceBlock.elementCount();
          ++arrayElement)
     {
-        // We don't currently have the getBlockSize implemented for SSBOs.
-        // TODO(jiajia.qin@intel.com): Remove the if when we have getBlockSize for SSBOs.
-        if (interfaceBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
+        std::string blockArrayName       = interfaceBlock.name;
+        std::string blockMappedArrayName = interfaceBlock.mappedName;
+        if (interfaceBlock.isArray())
         {
-            std::string blockArrayName       = interfaceBlock.name;
-            std::string blockMappedArrayName = interfaceBlock.mappedName;
-            if (interfaceBlock.isArray())
-            {
-                blockArrayName += ArrayString(arrayElement);
-                blockMappedArrayName += ArrayString(arrayElement);
-            }
+            blockArrayName += ArrayString(arrayElement);
+            blockMappedArrayName += ArrayString(arrayElement);
+        }
 
-            // Don't define this block at all if it's not active in the implementation.
-            if (!getBlockSize(blockArrayName, blockMappedArrayName, &blockSize))
-            {
-                continue;
-            }
+        // Don't define this block at all if it's not active in the implementation.
+        if (!getBlockSize(blockArrayName, blockMappedArrayName, &blockSize))
+        {
+            continue;
         }
 
         InterfaceBlock block(interfaceBlock.name, interfaceBlock.mappedName,
@@ -795,11 +807,13 @@
                                            const std::string &fullName,
                                            const std::string &fullMappedName,
                                            int blockIndex,
-                                           const sh::BlockMemberInfo &memberInfo) const
+                                           const sh::BlockMemberInfo &memberInfo,
+                                           int /* topLevelArraySize */) const
 {
     LinkedUniform newUniform(field.type, field.precision, fullName, field.arraySize, -1, -1, -1,
                              blockIndex, memberInfo);
     newUniform.mappedName = fullMappedName;
+    // TODO(jiajia.qin@intel.com): update the block memeber static use.
 
     // Since block uniforms have no location, we don't need to store them in the uniform locations
     // list.
@@ -812,8 +826,9 @@
 }
 
 // ShaderStorageBlockLinker implementation.
-ShaderStorageBlockLinker::ShaderStorageBlockLinker(std::vector<InterfaceBlock> *blocksOut)
-    : InterfaceBlockLinker(blocksOut)
+ShaderStorageBlockLinker::ShaderStorageBlockLinker(std::vector<InterfaceBlock> *blocksOut,
+                                                   std::vector<BufferVariable> *bufferVariablesOut)
+    : InterfaceBlockLinker(blocksOut), mBufferVariablesOut(bufferVariablesOut)
 {
 }
 
@@ -825,15 +840,22 @@
                                                  const std::string &fullName,
                                                  const std::string &fullMappedName,
                                                  int blockIndex,
-                                                 const sh::BlockMemberInfo &memberInfo) const
+                                                 const sh::BlockMemberInfo &memberInfo,
+                                                 int topLevelArraySize) const
 {
-    // TODO(jiajia.qin@intel.com): Add buffer variables support.
+    BufferVariable newBufferVariable(field.type, field.precision, fullName, field.arraySize,
+                                     blockIndex, memberInfo);
+    newBufferVariable.mappedName = fullMappedName;
+    // TODO(jiajia.qin@intel.com): update the block memeber static use.
+
+    newBufferVariable.topLevelArraySize = topLevelArraySize;
+
+    mBufferVariablesOut->push_back(newBufferVariable);
 }
 
 size_t ShaderStorageBlockLinker::getCurrentBlockMemberIndex() const
 {
-    // TODO(jiajia.qin@intel.com): Add buffer variables support.
-    return 0;
+    return mBufferVariablesOut->size();
 }
 
 }  // namespace gl