Add uniform block linking between the API, the program binary, and the compiler.

TRAC #22892

Signed-off-by: Nicolas Capens
Signed-off-by: Shannon Woods
Author: Jamie Madill

git-svn-id: https://angleproject.googlecode.com/svn/branches/es3proto@2338 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/ProgramBinary.cpp b/src/libGLESv2/ProgramBinary.cpp
index 9298c89..10283d6 100644
--- a/src/libGLESv2/ProgramBinary.cpp
+++ b/src/libGLESv2/ProgramBinary.cpp
@@ -1863,6 +1863,11 @@
         mUniforms.push_back(new Uniform(GL_FLOAT, GL_HIGH_FLOAT, "gl_DepthRange.diff", 0, -1, sh::BlockMemberInfo::defaultBlockInfo));
     }
 
+    if (!linkUniformBlocks(infoLog, vertexShader->getInterfaceBlocks(), fragmentShader->getInterfaceBlocks()))
+    {
+        success = false;
+    }
+
     return success;
 }
 
@@ -2008,17 +2013,17 @@
         }
     }
 
-    for (sh::ActiveUniforms::const_iterator uniform = vertexUniforms.begin(); uniform != vertexUniforms.end(); uniform++)
+    for (unsigned int uniformIndex = 0; uniformIndex < vertexUniforms.size(); uniformIndex++)
     {
-        if (!defineUniform(GL_VERTEX_SHADER, *uniform, infoLog))
+        if (!defineUniform(GL_VERTEX_SHADER, vertexUniforms[uniformIndex], infoLog))
         {
             return false;
         }
     }
 
-    for (sh::ActiveUniforms::const_iterator uniform = fragmentUniforms.begin(); uniform != fragmentUniforms.end(); uniform++)
+    for (unsigned int uniformIndex = 0; uniformIndex < fragmentUniforms.size(); uniformIndex++)
     {
-        if (!defineUniform(GL_FRAGMENT_SHADER, *uniform, infoLog))
+        if (!defineUniform(GL_FRAGMENT_SHADER, fragmentUniforms[uniformIndex], infoLog))
         {
             return false;
         }
@@ -2171,9 +2176,9 @@
     mUniforms.push_back(uniform);
     unsigned int uniformIndex = mUniforms.size() - 1;
 
-    for (unsigned int i = 0; i < uniform->elementCount(); i++)
+    for (unsigned int arrayElementIndex = 0; arrayElementIndex < uniform->elementCount(); arrayElementIndex++)
     {
-        mUniformIndex.push_back(UniformLocation(constant.name, i, uniformIndex));
+        mUniformIndex.push_back(UniformLocation(uniform->name, arrayElementIndex, uniformIndex));
     }
 
     if (shader == GL_VERTEX_SHADER)
@@ -2197,6 +2202,178 @@
     return true;
 }
 
+bool ProgramBinary::areMatchingInterfaceBlocks(InfoLog &infoLog, const sh::InterfaceBlock &vertexInterfaceBlock, const sh::InterfaceBlock &fragmentInterfaceBlock)
+{
+    const char* blockName = vertexInterfaceBlock.name.c_str();
+
+    // validate blocks for the same member types
+    if (vertexInterfaceBlock.activeUniforms.size() != fragmentInterfaceBlock.activeUniforms.size())
+    {
+        infoLog.append("Types for interface block '%s' differ between vertex and fragment shaders", blockName);
+        return false;
+    }
+
+    if (vertexInterfaceBlock.arraySize != fragmentInterfaceBlock.arraySize)
+    {
+        infoLog.append("Array sizes differ for interface block '%s' between vertex and fragment shaders", blockName);
+        return false;
+    }
+
+    const unsigned int numBlockMembers = vertexInterfaceBlock.activeUniforms.size();
+    for (unsigned int blockMemberIndex = 0; blockMemberIndex < numBlockMembers; blockMemberIndex++)
+    {
+        const sh::Uniform &vertexMember = vertexInterfaceBlock.activeUniforms[blockMemberIndex];
+        const sh::Uniform &fragmentMember = fragmentInterfaceBlock.activeUniforms[blockMemberIndex];
+
+        if (vertexMember.name != fragmentMember.name)
+        {
+            infoLog.append("Name mismatch for field %d of interface block %s: (in vertex: '%s', in fragment: '%s')",
+                           blockMemberIndex, blockName, vertexMember.name.c_str(), fragmentMember.name.c_str());
+            return false;
+        }
+
+        std::string uniformName = "interface block " + vertexInterfaceBlock.name + " member " + vertexMember.name;
+        if (!areMatchingUniforms(infoLog, uniformName, vertexMember, fragmentMember))
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool ProgramBinary::linkUniformBlocks(InfoLog &infoLog, const sh::ActiveInterfaceBlocks &vertexInterfaceBlocks, const sh::ActiveInterfaceBlocks &fragmentInterfaceBlocks)
+{
+    // Check that interface blocks defined in the vertex and fragment shaders are identical
+    typedef std::map<std::string, const sh::InterfaceBlock*> UniformBlockMap;
+    UniformBlockMap linkedUniformBlocks;
+
+    for (unsigned int blockIndex = 0; blockIndex < vertexInterfaceBlocks.size(); blockIndex++)
+    {
+        const sh::InterfaceBlock &vertexInterfaceBlock = vertexInterfaceBlocks[blockIndex];
+        linkedUniformBlocks[vertexInterfaceBlock.name] = &vertexInterfaceBlock;
+    }
+
+    for (unsigned int blockIndex = 0; blockIndex < fragmentInterfaceBlocks.size(); blockIndex++)
+    {
+        const sh::InterfaceBlock &fragmentInterfaceBlock = fragmentInterfaceBlocks[blockIndex];
+        UniformBlockMap::const_iterator entry = linkedUniformBlocks.find(fragmentInterfaceBlock.name);
+        if (entry != linkedUniformBlocks.end())
+        {
+            const sh::InterfaceBlock &vertexInterfaceBlock = *entry->second;
+            if (!areMatchingInterfaceBlocks(infoLog, vertexInterfaceBlock, fragmentInterfaceBlock))
+            {
+                return false;
+            }
+        }
+    }
+
+    for (unsigned int blockIndex = 0; blockIndex < vertexInterfaceBlocks.size(); blockIndex++)
+    {
+        if (!defineUniformBlock(infoLog, GL_VERTEX_SHADER, vertexInterfaceBlocks[blockIndex]))
+        {
+            return false;
+        }
+    }
+
+    for (unsigned int blockIndex = 0; blockIndex < fragmentInterfaceBlocks.size(); blockIndex++)
+    {
+        if (!defineUniformBlock(infoLog, GL_FRAGMENT_SHADER, fragmentInterfaceBlocks[blockIndex]))
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool ProgramBinary::defineUniformBlock(InfoLog &infoLog, GLenum shader, const sh::InterfaceBlock &interfaceBlock)
+{
+    // create uniform block entries if they do not exist
+    if (getUniformBlockIndex(interfaceBlock.name) == GL_INVALID_INDEX)
+    {
+        std::vector<unsigned int> blockUniformIndexes;
+        const unsigned int blockIndex = mUniformBlocks.size();
+
+        // define member uniforms
+        for (unsigned int activeUniformIndex = 0; activeUniformIndex < interfaceBlock.activeUniforms.size(); activeUniformIndex++)
+        {
+            const sh::Uniform &constant = interfaceBlock.activeUniforms[activeUniformIndex];
+            Uniform *uniform = new Uniform(constant.type, constant.precision, constant.name, constant.arraySize,
+                                           blockIndex, interfaceBlock.blockInfo[activeUniformIndex]);
+
+            // add to uniform list, but not index, since uniform block uniforms have no location
+            blockUniformIndexes.push_back(mUniforms.size());
+            mUniforms.push_back(uniform);
+        }
+
+        // create all the uniform blocks
+        if (interfaceBlock.arraySize > 0)
+        {
+            for (unsigned int uniformBlockElement = 0; uniformBlockElement < interfaceBlock.arraySize; uniformBlockElement++)
+            {
+                gl::UniformBlock *newUniformBlock = new UniformBlock(interfaceBlock.name, uniformBlockElement, interfaceBlock.dataSize);
+                newUniformBlock->memberUniformIndexes = blockUniformIndexes;
+                mUniformBlocks.push_back(newUniformBlock);
+            }
+        }
+        else
+        {
+            gl::UniformBlock *newUniformBlock = new UniformBlock(interfaceBlock.name, GL_INVALID_INDEX, interfaceBlock.dataSize);
+            newUniformBlock->memberUniformIndexes = blockUniformIndexes;
+            mUniformBlocks.push_back(newUniformBlock);
+        }
+    }
+
+    // Assign registers to the uniform blocks
+    const GLuint blockIndex = getUniformBlockIndex(interfaceBlock.name);
+    const unsigned int elementCount = std::max(1u, interfaceBlock.arraySize);
+    ASSERT(blockIndex != GL_INVALID_INDEX);
+    ASSERT(blockIndex + elementCount <= mUniformBlocks.size());
+
+    for (unsigned int uniformBlockElement = 0; uniformBlockElement < elementCount; uniformBlockElement++)
+    {
+        gl::UniformBlock *uniformBlock = mUniformBlocks[blockIndex + uniformBlockElement];
+        ASSERT(uniformBlock->name == interfaceBlock.name);
+
+        if (!assignUniformBlockRegister(infoLog, uniformBlock, shader, interfaceBlock.registerIndex + uniformBlockElement))
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool ProgramBinary::assignUniformBlockRegister(InfoLog &infoLog, UniformBlock *uniformBlock, GLenum shader, unsigned int registerIndex)
+{
+    if (shader == GL_VERTEX_SHADER)
+    {
+        uniformBlock->vsRegisterIndex = registerIndex;
+        unsigned int maximumBlocks = mRenderer->getMaxVertexShaderUniformBuffers();
+
+        if (registerIndex - mRenderer->getReservedVertexUniformBuffers() >= maximumBlocks)
+        {
+            infoLog.append("Vertex shader uniform block count exceed GL_MAX_VERTEX_UNIFORM_BLOCKS (%u)", maximumBlocks);
+            return false;
+        }
+    }
+    else if (shader == GL_FRAGMENT_SHADER)
+    {
+        uniformBlock->psRegisterIndex = registerIndex;
+        unsigned int maximumBlocks = mRenderer->getMaxFragmentShaderUniformBuffers();
+
+        if (registerIndex - mRenderer->getReservedFragmentUniformBuffers() >= maximumBlocks)
+        {
+            infoLog.append("Vertex shader uniform block count exceed GL_MAX_VERTEX_UNIFORM_BLOCKS (%u)", maximumBlocks);
+            return false;
+        }
+    }
+    else UNREACHABLE();
+
+    return true;
+}
+
 std::string ProgramBinary::generateGeometryShaderHLSL(int registers, const Varying *packing[][4], FragmentShader *fragmentShader, VertexShader *vertexShader) const
 {
     // for now we only handle point sprite emulation