ES31: Enable shader storage buffer support for OpenGL backend

BUG=angleproject:1951
TEST=angle_end2end_tests:ShaderStorageBuffer

Change-Id: I1afc3cd005ad2e595c6ce937fc53e17423f8ec8b
Reviewed-on: https://chromium-review.googlesource.com/618132
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 63b0c09..b7c5f1a 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -193,6 +193,27 @@
     return false;
 }
 
+bool validateInterfaceBlocksCount(GLuint maxInterfaceBlocks,
+                                  const std::vector<sh::InterfaceBlock> &interfaceBlocks,
+                                  const std::string &errorMessage,
+                                  InfoLog &infoLog)
+{
+    GLuint blockCount = 0;
+    for (const sh::InterfaceBlock &block : interfaceBlocks)
+    {
+        if (block.staticUse || block.layout != sh::BLOCKLAYOUT_PACKED)
+        {
+            blockCount += (block.arraySize ? block.arraySize : 1);
+            if (blockCount > maxInterfaceBlocks)
+            {
+                infoLog << errorMessage << maxInterfaceBlocks << ")";
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
 }  // anonymous namespace
 
 const char *const g_fakepath = "C:\\fakepath";
@@ -693,7 +714,7 @@
             return NoError();
         }
 
-        if (!linkUniformBlocks(context, mInfoLog))
+        if (!linkInterfaceBlocks(context, mInfoLog))
         {
             return NoError();
         }
@@ -740,7 +761,7 @@
             return NoError();
         }
 
-        if (!linkUniformBlocks(context, mInfoLog))
+        if (!linkInterfaceBlocks(context, mInfoLog))
         {
             return NoError();
         }
@@ -1633,13 +1654,18 @@
     return static_cast<GLuint>(mState.mUniformBlocks.size());
 }
 
+GLuint Program::getActiveShaderStorageBlockCount() const
+{
+    return static_cast<GLuint>(mState.mShaderStorageBlocks.size());
+}
+
 void Program::getActiveUniformBlockName(GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName) const
 {
     ASSERT(
         uniformBlockIndex <
         mState.mUniformBlocks.size());  // index must be smaller than getActiveUniformBlockCount()
 
-    const UniformBlock &uniformBlock = mState.mUniformBlocks[uniformBlockIndex];
+    const InterfaceBlock &uniformBlock = mState.mUniformBlocks[uniformBlockIndex];
 
     if (bufSize > 0)
     {
@@ -1662,7 +1688,7 @@
         unsigned int numUniformBlocks = static_cast<unsigned int>(mState.mUniformBlocks.size());
         for (unsigned int uniformBlockIndex = 0; uniformBlockIndex < numUniformBlocks; uniformBlockIndex++)
         {
-            const UniformBlock &uniformBlock = mState.mUniformBlocks[uniformBlockIndex];
+            const InterfaceBlock &uniformBlock = mState.mUniformBlocks[uniformBlockIndex];
             if (!uniformBlock.name.empty())
             {
                 int length = static_cast<int>(uniformBlock.nameWithArrayIndex().length());
@@ -1682,7 +1708,7 @@
     unsigned int numUniformBlocks = static_cast<unsigned int>(mState.mUniformBlocks.size());
     for (unsigned int blockIndex = 0; blockIndex < numUniformBlocks; blockIndex++)
     {
-        const UniformBlock &uniformBlock = mState.mUniformBlocks[blockIndex];
+        const InterfaceBlock &uniformBlock = mState.mUniformBlocks[blockIndex];
         if (uniformBlock.name == baseName)
         {
             const bool arrayElementZero =
@@ -1698,7 +1724,7 @@
     return GL_INVALID_INDEX;
 }
 
-const UniformBlock &Program::getUniformBlockByIndex(GLuint index) const
+const InterfaceBlock &Program::getUniformBlockByIndex(GLuint index) const
 {
     ASSERT(index < static_cast<GLuint>(mState.mUniformBlocks.size()));
     return mState.mUniformBlocks[index];
@@ -1716,6 +1742,11 @@
     return mState.getUniformBlockBinding(uniformBlockIndex);
 }
 
+GLuint Program::getShaderStorageBlockBinding(GLuint shaderStorageBlockIndex) const
+{
+    return mState.getShaderStorageBlockBinding(shaderStorageBlockIndex);
+}
+
 void Program::setTransformFeedbackVaryings(GLsizei count, const GLchar *const *varyings, GLenum bufferMode)
 {
     mState.mTransformFeedbackVaryingNames.resize(count);
@@ -2110,26 +2141,6 @@
     return true;
 }
 
-bool Program::validateUniformBlocksCount(GLuint maxUniformBlocks,
-                                         const std::vector<sh::InterfaceBlock> &intefaceBlocks,
-                                         const std::string &errorMessage,
-                                         InfoLog &infoLog) const
-{
-    GLuint blockCount = 0;
-    for (const sh::InterfaceBlock &block : intefaceBlocks)
-    {
-        if (block.staticUse || block.layout != sh::BLOCKLAYOUT_PACKED)
-        {
-            if (++blockCount > maxUniformBlocks)
-            {
-                infoLog << errorMessage << maxUniformBlocks << ")";
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
 bool Program::validateVertexAndFragmentInterfaceBlocks(
     const std::vector<sh::InterfaceBlock> &vertexInterfaceBlocks,
     const std::vector<sh::InterfaceBlock> &fragmentInterfaceBlocks,
@@ -2137,18 +2148,18 @@
     bool webglCompatibility) const
 {
     // Check that interface blocks defined in the vertex and fragment shaders are identical
-    typedef std::map<std::string, const sh::InterfaceBlock *> UniformBlockMap;
-    UniformBlockMap linkedUniformBlocks;
+    typedef std::map<std::string, const sh::InterfaceBlock *> InterfaceBlockMap;
+    InterfaceBlockMap linkedInterfaceBlocks;
 
     for (const sh::InterfaceBlock &vertexInterfaceBlock : vertexInterfaceBlocks)
     {
-        linkedUniformBlocks[vertexInterfaceBlock.name] = &vertexInterfaceBlock;
+        linkedInterfaceBlocks[vertexInterfaceBlock.name] = &vertexInterfaceBlock;
     }
 
     for (const sh::InterfaceBlock &fragmentInterfaceBlock : fragmentInterfaceBlocks)
     {
-        auto entry = linkedUniformBlocks.find(fragmentInterfaceBlock.name);
-        if (entry != linkedUniformBlocks.end())
+        auto entry = linkedInterfaceBlocks.find(fragmentInterfaceBlock.name);
+        if (entry != linkedInterfaceBlocks.end())
         {
             const sh::InterfaceBlock &vertexInterfaceBlock = *entry->second;
             if (!areMatchingInterfaceBlocks(infoLog, vertexInterfaceBlock, fragmentInterfaceBlock,
@@ -2157,43 +2168,55 @@
                 return false;
             }
         }
+        // TODO(jiajia.qin@intel.com): Add
+        // MAX_COMBINED_UNIFORM_BLOCKS/MAX_COMBINED_SHADER_STORAGE_BLOCKS validation.
     }
     return true;
 }
 
-bool Program::linkUniformBlocks(const Context *context, InfoLog &infoLog)
+bool Program::linkInterfaceBlocks(const Context *context, InfoLog &infoLog)
 {
     const auto &caps = context->getCaps();
 
     if (mState.mAttachedComputeShader)
     {
         Shader &computeShader              = *mState.mAttachedComputeShader;
-        const auto &computeInterfaceBlocks = computeShader.getUniformBlocks(context);
+        const auto &computeUniformBlocks   = computeShader.getUniformBlocks(context);
 
-        if (!validateUniformBlocksCount(
-                caps.maxComputeUniformBlocks, computeInterfaceBlocks,
+        if (!validateInterfaceBlocksCount(
+                caps.maxComputeUniformBlocks, computeUniformBlocks,
                 "Compute shader uniform block count exceeds GL_MAX_COMPUTE_UNIFORM_BLOCKS (",
                 infoLog))
         {
             return false;
         }
+
+        const auto &computeShaderStorageBlocks = computeShader.getShaderStorageBlocks(context);
+        if (!validateInterfaceBlocksCount(caps.maxComputeShaderStorageBlocks,
+                                          computeShaderStorageBlocks,
+                                          "Compute shader shader storage block count exceeds "
+                                          "GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS (",
+                                          infoLog))
+        {
+            return false;
+        }
         return true;
     }
 
     Shader &vertexShader   = *mState.mAttachedVertexShader;
     Shader &fragmentShader = *mState.mAttachedFragmentShader;
 
-    const auto &vertexInterfaceBlocks   = vertexShader.getUniformBlocks(context);
-    const auto &fragmentInterfaceBlocks = fragmentShader.getUniformBlocks(context);
+    const auto &vertexUniformBlocks   = vertexShader.getUniformBlocks(context);
+    const auto &fragmentUniformBlocks = fragmentShader.getUniformBlocks(context);
 
-    if (!validateUniformBlocksCount(
-            caps.maxVertexUniformBlocks, vertexInterfaceBlocks,
+    if (!validateInterfaceBlocksCount(
+            caps.maxVertexUniformBlocks, vertexUniformBlocks,
             "Vertex shader uniform block count exceeds GL_MAX_VERTEX_UNIFORM_BLOCKS (", infoLog))
     {
         return false;
     }
-    if (!validateUniformBlocksCount(
-            caps.maxFragmentUniformBlocks, fragmentInterfaceBlocks,
+    if (!validateInterfaceBlocksCount(
+            caps.maxFragmentUniformBlocks, fragmentUniformBlocks,
             "Fragment shader uniform block count exceeds GL_MAX_FRAGMENT_UNIFORM_BLOCKS (",
             infoLog))
     {
@@ -2202,12 +2225,42 @@
     }
 
     bool webglCompatibility = context->getExtensions().webglCompatibility;
-    if (!validateVertexAndFragmentInterfaceBlocks(vertexInterfaceBlocks, fragmentInterfaceBlocks,
+    if (!validateVertexAndFragmentInterfaceBlocks(vertexUniformBlocks, fragmentUniformBlocks,
                                                   infoLog, webglCompatibility))
     {
         return false;
     }
 
+    if (context->getClientVersion() >= Version(3, 1))
+    {
+        const auto &vertexShaderStorageBlocks   = vertexShader.getShaderStorageBlocks(context);
+        const auto &fragmentShaderStorageBlocks = fragmentShader.getShaderStorageBlocks(context);
+
+        if (!validateInterfaceBlocksCount(caps.maxVertexShaderStorageBlocks,
+                                          vertexShaderStorageBlocks,
+                                          "Vertex shader shader storage block count exceeds "
+                                          "GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS (",
+                                          infoLog))
+        {
+            return false;
+        }
+        if (!validateInterfaceBlocksCount(caps.maxFragmentShaderStorageBlocks,
+                                          fragmentShaderStorageBlocks,
+                                          "Fragment shader shader storage block count exceeds "
+                                          "GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS (",
+                                          infoLog))
+        {
+
+            return false;
+        }
+
+        if (!validateVertexAndFragmentInterfaceBlocks(vertexShaderStorageBlocks,
+                                                      fragmentShaderStorageBlocks, infoLog,
+                                                      webglCompatibility))
+        {
+            return false;
+        }
+    }
     return true;
 }
 
@@ -2741,54 +2794,36 @@
     // TODO(jie.a.chen@intel.com): Get the actual BUFFER_DATA_SIZE from backend for each buffer.
 }
 
-void Program::gatherInterfaceBlockInfo(const Context *context)
+void Program::gatherComputeBlockInfo(const std::vector<sh::InterfaceBlock> &computeBlocks)
 {
-    ASSERT(mState.mUniformBlocks.empty());
-
-    if (mState.mAttachedComputeShader)
+    for (const sh::InterfaceBlock &computeBlock : computeBlocks)
     {
-        Shader *computeShader = mState.getAttachedComputeShader();
 
-        for (const sh::InterfaceBlock &computeBlock : computeShader->getUniformBlocks(context))
-        {
+        // Only 'packed' blocks are allowed to be considered inactive.
+        if (!computeBlock.staticUse && computeBlock.layout == sh::BLOCKLAYOUT_PACKED)
+            continue;
 
-            // Only 'packed' blocks are allowed to be considered inactive.
-            if (!computeBlock.staticUse && computeBlock.layout == sh::BLOCKLAYOUT_PACKED)
-                continue;
-
-            for (UniformBlock &block : mState.mUniformBlocks)
-            {
-                if (block.name == computeBlock.name)
-                {
-                    block.computeStaticUse = computeBlock.staticUse;
-                }
-            }
-
-            defineUniformBlock(computeBlock, GL_COMPUTE_SHADER);
-        }
-        return;
+        defineInterfaceBlock(computeBlock, GL_COMPUTE_SHADER);
     }
+}
 
+void Program::gatherVertexAndFragmentBlockInfo(
+    const std::vector<sh::InterfaceBlock> &vertexInterfaceBlocks,
+    const std::vector<sh::InterfaceBlock> &fragmentInterfaceBlocks)
+{
     std::set<std::string> visitedList;
 
-    Shader *vertexShader = mState.getAttachedVertexShader();
-
-    for (const sh::InterfaceBlock &vertexBlock : vertexShader->getUniformBlocks(context))
+    for (const sh::InterfaceBlock &vertexBlock : vertexInterfaceBlocks)
     {
         // Only 'packed' blocks are allowed to be considered inactive.
         if (!vertexBlock.staticUse && vertexBlock.layout == sh::BLOCKLAYOUT_PACKED)
             continue;
 
-        if (visitedList.count(vertexBlock.name) > 0)
-            continue;
-
-        defineUniformBlock(vertexBlock, GL_VERTEX_SHADER);
+        defineInterfaceBlock(vertexBlock, GL_VERTEX_SHADER);
         visitedList.insert(vertexBlock.name);
     }
 
-    Shader *fragmentShader = mState.getAttachedFragmentShader();
-
-    for (const sh::InterfaceBlock &fragmentBlock : fragmentShader->getUniformBlocks(context))
+    for (const sh::InterfaceBlock &fragmentBlock : fragmentInterfaceBlocks)
     {
         // Only 'packed' blocks are allowed to be considered inactive.
         if (!fragmentBlock.staticUse && fragmentBlock.layout == sh::BLOCKLAYOUT_PACKED)
@@ -2796,24 +2831,65 @@
 
         if (visitedList.count(fragmentBlock.name) > 0)
         {
-            for (UniformBlock &block : mState.mUniformBlocks)
+            if (fragmentBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
             {
-                if (block.name == fragmentBlock.name)
+                for (InterfaceBlock &block : mState.mUniformBlocks)
                 {
-                    block.fragmentStaticUse = fragmentBlock.staticUse;
+                    if (block.name == fragmentBlock.name)
+                    {
+                        block.fragmentStaticUse = fragmentBlock.staticUse;
+                    }
+                }
+            }
+            else
+            {
+                ASSERT(fragmentBlock.blockType == sh::BlockType::BLOCK_BUFFER);
+                for (InterfaceBlock &block : mState.mShaderStorageBlocks)
+                {
+                    if (block.name == fragmentBlock.name)
+                    {
+                        block.fragmentStaticUse = fragmentBlock.staticUse;
+                    }
                 }
             }
 
             continue;
         }
 
-        defineUniformBlock(fragmentBlock, GL_FRAGMENT_SHADER);
+        defineInterfaceBlock(fragmentBlock, GL_FRAGMENT_SHADER);
         visitedList.insert(fragmentBlock.name);
     }
+}
+
+void Program::gatherInterfaceBlockInfo(const Context *context)
+{
+    ASSERT(mState.mUniformBlocks.empty());
+    ASSERT(mState.mShaderStorageBlocks.empty());
+
+    if (mState.mAttachedComputeShader)
+    {
+        Shader *computeShader = mState.getAttachedComputeShader();
+
+        gatherComputeBlockInfo(computeShader->getUniformBlocks(context));
+        gatherComputeBlockInfo(computeShader->getShaderStorageBlocks(context));
+        return;
+    }
+
+    Shader *vertexShader   = mState.getAttachedVertexShader();
+    Shader *fragmentShader = mState.getAttachedFragmentShader();
+
+    gatherVertexAndFragmentBlockInfo(vertexShader->getUniformBlocks(context),
+                                     fragmentShader->getUniformBlocks(context));
+    if (context->getClientVersion() >= Version(3, 1))
+    {
+        gatherVertexAndFragmentBlockInfo(vertexShader->getShaderStorageBlocks(context),
+                                         fragmentShader->getShaderStorageBlocks(context));
+    }
+
     // Set initial bindings from shader.
     for (unsigned int blockIndex = 0; blockIndex < mState.mUniformBlocks.size(); blockIndex++)
     {
-        UniformBlock &uniformBlock = mState.mUniformBlocks[blockIndex];
+        InterfaceBlock &uniformBlock = mState.mUniformBlocks[blockIndex];
         bindUniformBlock(blockIndex, uniformBlock.binding);
     }
 }
@@ -2863,23 +2939,31 @@
     }
 }
 
-void Program::defineUniformBlock(const sh::InterfaceBlock &interfaceBlock, GLenum shaderType)
+void Program::defineInterfaceBlock(const sh::InterfaceBlock &interfaceBlock, GLenum shaderType)
 {
-    int blockIndex   = static_cast<int>(mState.mUniformBlocks.size());
     size_t blockSize = 0;
+    std::vector<unsigned int> blockIndexes;
 
-    // Track the first and last uniform index to determine the range of active uniforms in the
-    // block.
-    size_t firstBlockUniformIndex = mState.mUniforms.size();
-    defineUniformBlockMembers(interfaceBlock.fields, interfaceBlock.fieldPrefix(),
-                              interfaceBlock.fieldMappedPrefix(), blockIndex);
-    size_t lastBlockUniformIndex = mState.mUniforms.size();
-
-    std::vector<unsigned int> blockUniformIndexes;
-    for (size_t blockUniformIndex = firstBlockUniformIndex;
-         blockUniformIndex < lastBlockUniformIndex; ++blockUniformIndex)
+    if (interfaceBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
     {
-        blockUniformIndexes.push_back(static_cast<unsigned int>(blockUniformIndex));
+        int blockIndex = static_cast<int>(mState.mUniformBlocks.size());
+        // Track the first and last uniform index to determine the range of active uniforms in the
+        // block.
+        size_t firstBlockUniformIndex = mState.mUniforms.size();
+        defineUniformBlockMembers(interfaceBlock.fields, interfaceBlock.fieldPrefix(),
+                                  interfaceBlock.fieldMappedPrefix(), blockIndex);
+        size_t lastBlockUniformIndex = mState.mUniforms.size();
+
+        for (size_t blockUniformIndex = firstBlockUniformIndex;
+             blockUniformIndex < lastBlockUniformIndex; ++blockUniformIndex)
+        {
+            blockIndexes.push_back(static_cast<unsigned int>(blockUniformIndex));
+        }
+    }
+    else
+    {
+        // TODO(jiajia.qin@intel.com) : Add buffer variables support and calculate the block index.
+        ASSERT(interfaceBlock.blockType == sh::BlockType::BLOCK_BUFFER);
     }
     // ESSL 3.10 section 4.4.4 page 58:
     // Any uniform or shader storage block declared without a binding qualifier is initially
@@ -2889,16 +2973,22 @@
     {
         for (unsigned int arrayElement = 0; arrayElement < interfaceBlock.arraySize; ++arrayElement)
         {
-            // Don't define this block at all if it's not active in the implementation.
-            if (!mProgram->getUniformBlockSize(
-                    interfaceBlock.name + ArrayString(arrayElement),
-                    interfaceBlock.mappedName + ArrayString(arrayElement), &blockSize))
+            // TODO(jiajia.qin@intel.com) : use GetProgramResourceiv to calculate BUFFER_DATA_SIZE
+            // of UniformBlock and ShaderStorageBlock.
+            if (interfaceBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
             {
-                continue;
+                // Don't define this block at all if it's not active in the implementation.
+                if (!mProgram->getUniformBlockSize(
+                        interfaceBlock.name + ArrayString(arrayElement),
+                        interfaceBlock.mappedName + ArrayString(arrayElement), &blockSize))
+                {
+                    continue;
+                }
             }
-            UniformBlock block(interfaceBlock.name, interfaceBlock.mappedName, true, arrayElement,
-                               blockBinding + arrayElement);
-            block.memberIndexes = blockUniformIndexes;
+
+            InterfaceBlock block(interfaceBlock.name, interfaceBlock.mappedName, true, arrayElement,
+                                 blockBinding + arrayElement);
+            block.memberIndexes = blockIndexes;
 
             switch (shaderType)
             {
@@ -2921,22 +3011,37 @@
                     UNREACHABLE();
             }
 
-            // Since all block elements in an array share the same active uniforms, they will all be
-            // active once any uniform member is used. So, since interfaceBlock.name[0] was active,
-            // here we will add every block element in the array.
+            // Since all block elements in an array share the same active interface blocks, they
+            // will all be active once any block member is used. So, since interfaceBlock.name[0]
+            // was active, here we will add every block element in the array.
             block.dataSize = static_cast<unsigned int>(blockSize);
-            mState.mUniformBlocks.push_back(block);
+            if (interfaceBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
+            {
+                mState.mUniformBlocks.push_back(block);
+            }
+            else
+            {
+                ASSERT(interfaceBlock.blockType == sh::BlockType::BLOCK_BUFFER);
+                mState.mShaderStorageBlocks.push_back(block);
+            }
         }
     }
     else
     {
-        if (!mProgram->getUniformBlockSize(interfaceBlock.name, interfaceBlock.mappedName,
-                                           &blockSize))
+        // TODO(jiajia.qin@intel.com) : use GetProgramResourceiv to calculate BUFFER_DATA_SIZE
+        // of UniformBlock and ShaderStorageBlock.
+        if (interfaceBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
         {
-            return;
+            if (!mProgram->getUniformBlockSize(interfaceBlock.name, interfaceBlock.mappedName,
+                                               &blockSize))
+            {
+                return;
+            }
         }
-        UniformBlock block(interfaceBlock.name, interfaceBlock.mappedName, false, 0, blockBinding);
-        block.memberIndexes = blockUniformIndexes;
+
+        InterfaceBlock block(interfaceBlock.name, interfaceBlock.mappedName, false, 0,
+                             blockBinding);
+        block.memberIndexes = blockIndexes;
 
         switch (shaderType)
         {
@@ -2960,7 +3065,15 @@
         }
 
         block.dataSize = static_cast<unsigned int>(blockSize);
-        mState.mUniformBlocks.push_back(block);
+        if (interfaceBlock.blockType == sh::BlockType::BLOCK_UNIFORM)
+        {
+            mState.mUniformBlocks.push_back(block);
+        }
+        else
+        {
+            ASSERT(interfaceBlock.blockType == sh::BlockType::BLOCK_BUFFER);
+            mState.mShaderStorageBlocks.push_back(block);
+        }
     }
 }