Use the block encoder class to store uniform offsets.

This will allow us to use the same code path for assigning register
information in all places. Because of this it fixes some long-broken
dEQP struct uniform tests.

BUG=angle:466
BUG=angle:505

Change-Id: I4161a388503aa09bbe2d21ff47bfb3352ec93881
Reviewed-on: https://chromium-review.googlesource.com/207255
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Tested-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Nicolas Capens <capn@chromium.org>
diff --git a/src/common/blocklayout.cpp b/src/common/blocklayout.cpp
index 9bf14cb..0bdfa1e 100644
--- a/src/common/blocklayout.cpp
+++ b/src/common/blocklayout.cpp
@@ -161,6 +161,12 @@
 {
 }
 
+HLSLBlockEncoder::HLSLBlockEncoder(ShShaderOutput outputType)
+    : BlockLayoutEncoder(NULL),
+      mEncoderStrategy(GetStrategyFor(outputType))
+{
+}
+
 void HLSLBlockEncoder::enterAggregateType()
 {
     nextRegister();
@@ -245,6 +251,16 @@
     mCurrentOffset += (numRegisters * ComponentsPerRegister);
 }
 
+HLSLBlockEncoder::HLSLBlockEncoderStrategy HLSLBlockEncoder::GetStrategyFor(ShShaderOutput outputType)
+{
+    switch (outputType)
+    {
+      case SH_HLSL9_OUTPUT: return ENCODE_LOOSE;
+      case SH_HLSL11_OUTPUT: return ENCODE_PACKED;
+      default: UNREACHABLE(); return ENCODE_PACKED;
+    }
+}
+
 size_t HLSLInterfaceBlockDataSize(const sh::InterfaceBlock &interfaceBlock)
 {
     switch (interfaceBlock.layout)
@@ -353,10 +369,7 @@
 
 unsigned int HLSLVariableRegisterCount(const Uniform &variable, ShShaderOutput outputType)
 {
-    HLSLBlockEncoder encoder(NULL,
-                             outputType == SH_HLSL9_OUTPUT ? HLSLBlockEncoder::ENCODE_LOOSE
-                                                           : HLSLBlockEncoder::ENCODE_PACKED);
-
+    HLSLBlockEncoder encoder(outputType);
     HLSLVariableRegisterCount(variable, &encoder);
 
     const size_t registerBytes = (encoder.BytesPerComponent * encoder.ComponentsPerRegister);
diff --git a/src/common/blocklayout.h b/src/common/blocklayout.h
index be0c340..2df6db9 100644
--- a/src/common/blocklayout.h
+++ b/src/common/blocklayout.h
@@ -33,6 +33,8 @@
     void encodeInterfaceBlockField(const InterfaceBlockField &field);
     void encodeType(GLenum type, unsigned int arraySize, bool isRowMajorMatrix);
     size_t getBlockSize() const { return mCurrentOffset * BytesPerComponent; }
+    size_t getCurrentRegister() const { return mCurrentOffset / ComponentsPerRegister; }
+    size_t getCurrentElement() const { return mCurrentOffset % ComponentsPerRegister; }
 
     static const size_t BytesPerComponent = 4u;
     static const unsigned int ComponentsPerRegister = 4u;
@@ -82,6 +84,7 @@
 
     HLSLBlockEncoder(std::vector<BlockMemberInfo> *blockInfoOut,
                      HLSLBlockEncoderStrategy strategy);
+    HLSLBlockEncoder(ShShaderOutput outputType);
 
     virtual void enterAggregateType();
     virtual void exitAggregateType();
@@ -89,6 +92,8 @@
 
     bool isPacked() const { return mEncoderStrategy == ENCODE_PACKED; }
 
+    static HLSLBlockEncoderStrategy GetStrategyFor(ShShaderOutput outputType);
+
   protected:
     virtual void getBlockLayoutInfo(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut);
     virtual void advanceOffset(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int arrayStride, int matrixStride);
diff --git a/src/libGLESv2/ProgramBinary.cpp b/src/libGLESv2/ProgramBinary.cpp
index a57cabd..50bae23 100644
--- a/src/libGLESv2/ProgramBinary.cpp
+++ b/src/libGLESv2/ProgramBinary.cpp
@@ -1648,7 +1648,7 @@
         success = false;
     }
 
-    if (!linkUniforms(infoLog, vertexShader->getUniforms(), fragmentShader->getUniforms()))
+    if (!linkUniforms(infoLog, *vertexShader, *fragmentShader))
     {
         success = false;
     }
@@ -1900,8 +1900,11 @@
     return true;
 }
 
-bool ProgramBinary::linkUniforms(InfoLog &infoLog, const std::vector<sh::Uniform> &vertexUniforms, const std::vector<sh::Uniform> &fragmentUniforms)
+bool ProgramBinary::linkUniforms(InfoLog &infoLog, const VertexShader &vertexShader, const FragmentShader &fragmentShader)
 {
+    const std::vector<sh::Uniform> &vertexUniforms = vertexShader.getUniforms();
+    const std::vector<sh::Uniform> &fragmentUniforms = fragmentShader.getUniforms();
+
     // Check that uniforms defined in the vertex and fragment shaders are identical
     typedef std::map<std::string, const sh::Uniform*> UniformMap;
     UniformMap linkedUniforms;
@@ -1929,12 +1932,14 @@
 
     for (unsigned int uniformIndex = 0; uniformIndex < vertexUniforms.size(); uniformIndex++)
     {
-        defineUniformBase(GL_VERTEX_SHADER, vertexUniforms[uniformIndex]);
+        const sh::Uniform &uniform = vertexUniforms[uniformIndex];
+        defineUniformBase(GL_VERTEX_SHADER, uniform, vertexShader.getUniformRegister(uniform.name));
     }
 
     for (unsigned int uniformIndex = 0; uniformIndex < fragmentUniforms.size(); uniformIndex++)
     {
-        defineUniformBase(GL_FRAGMENT_SHADER, fragmentUniforms[uniformIndex]);
+        const sh::Uniform &uniform = fragmentUniforms[uniformIndex];
+        defineUniformBase(GL_FRAGMENT_SHADER, uniform, fragmentShader.getUniformRegister(uniform.name));
     }
 
     if (!indexUniforms(infoLog))
@@ -1947,46 +1952,45 @@
     return true;
 }
 
-void ProgramBinary::defineUniformBase(GLenum shader, const sh::Uniform &uniform)
+void ProgramBinary::defineUniformBase(GLenum shader, const sh::Uniform &uniform, unsigned int uniformRegister)
 {
-    defineUniform(shader, uniform, uniform.name, uniform.registerIndex);
+    ShShaderOutput outputType = Shader::getCompilerOutputType(shader);
+    sh::HLSLBlockEncoder encoder(outputType);
+    encoder.skipRegisters(uniformRegister);
+
+    defineUniform(shader, uniform, uniform.name, &encoder);
 }
 
 void ProgramBinary::defineUniform(GLenum shader, const sh::Uniform &uniform,
-                                  const std::string &fullName, unsigned int baseRegisterIndex)
+                                  const std::string &fullName, sh::HLSLBlockEncoder *encoder)
 {
     if (uniform.isStruct())
     {
-        if (uniform.arraySize > 0)
+        for (unsigned int elementIndex = 0; elementIndex < uniform.elementCount(); elementIndex++)
         {
-            ShShaderOutput outputType = Shader::getCompilerOutputType(shader);
-            const unsigned int elementRegisterCount = HLSLVariableRegisterCount(uniform, outputType) / uniform.arraySize;
+            const std::string &elementString = (uniform.isArray() ? ArrayString(elementIndex) : "");
 
-            for (unsigned int elementIndex = 0; elementIndex < uniform.arraySize; elementIndex++)
-            {
-                const unsigned int elementRegisterOffset = elementRegisterCount * elementIndex;
+            encoder->enterAggregateType();
 
-                for (size_t fieldIndex = 0; fieldIndex < uniform.fields.size(); fieldIndex++)
-                {
-                    const sh::Uniform &field = uniform.fields[fieldIndex];
-                    const std::string &fieldFullName = fullName + ArrayString(elementIndex) + "." + field.name;
-                    const unsigned int fieldRegisterIndex = field.registerIndex + elementRegisterOffset;
-                    defineUniform(shader, field, fieldFullName, fieldRegisterIndex);
-                }
-            }
-        }
-        else
-        {
             for (size_t fieldIndex = 0; fieldIndex < uniform.fields.size(); fieldIndex++)
             {
                 const sh::Uniform &field = uniform.fields[fieldIndex];
-                const std::string &fieldFullName = fullName + "." + field.name;
-                defineUniform(shader, field, fieldFullName, field.registerIndex);
+                const std::string &fieldFullName = (fullName + elementString + "." + field.name);
+
+                defineUniform(shader, field, fieldFullName, encoder);
             }
+
+            encoder->exitAggregateType();
         }
     }
     else // Not a struct
     {
+        // Arrays are treated as aggregate types
+        if (uniform.isArray())
+        {
+            encoder->enterAggregateType();
+        }
+
         LinkedUniform *linkedUniform = getUniformByName(fullName);
 
         if (!linkedUniform)
@@ -1994,19 +1998,30 @@
             linkedUniform = new LinkedUniform(uniform.type, uniform.precision, fullName, uniform.arraySize,
                                               -1, sh::BlockMemberInfo::getDefaultBlockInfo());
             ASSERT(linkedUniform);
-            linkedUniform->registerElement = uniform.elementIndex;
+            linkedUniform->registerElement = encoder->getCurrentElement();
             mUniforms.push_back(linkedUniform);
         }
 
+        ASSERT(linkedUniform->registerElement == encoder->getCurrentElement());
+
         if (shader == GL_FRAGMENT_SHADER)
         {
-            linkedUniform->psRegisterIndex = baseRegisterIndex;
+            linkedUniform->psRegisterIndex = encoder->getCurrentRegister();
         }
         else if (shader == GL_VERTEX_SHADER)
         {
-            linkedUniform->vsRegisterIndex = baseRegisterIndex;
+            linkedUniform->vsRegisterIndex = encoder->getCurrentRegister();
         }
         else UNREACHABLE();
+
+        // Advance the uniform offset, to track registers allocation for structs
+        encoder->encodeType(uniform.type, uniform.arraySize, false);
+
+        // Arrays are treated as aggregate types
+        if (uniform.isArray())
+        {
+            encoder->exitAggregateType();
+        }
     }
 }
 
diff --git a/src/libGLESv2/ProgramBinary.h b/src/libGLESv2/ProgramBinary.h
index f3a6430..dcfeaf3 100644
--- a/src/libGLESv2/ProgramBinary.h
+++ b/src/libGLESv2/ProgramBinary.h
@@ -24,6 +24,11 @@
 #include "libGLESv2/renderer/d3d/VertexDataManager.h"
 #include "libGLESv2/DynamicHLSL.h"
 
+namespace sh
+{
+class HLSLBlockEncoder;
+}
+
 namespace rx
 {
 class ShaderExecutable;
@@ -201,9 +206,9 @@
     bool linkValidateVariables(InfoLog &infoLog, const std::string &uniformName, const sh::Uniform &vertexUniform, const sh::Uniform &fragmentUniform);
     bool linkValidateVariables(InfoLog &infoLog, const std::string &varyingName, const sh::Varying &vertexVarying, const sh::Varying &fragmentVarying);
     bool linkValidateVariables(InfoLog &infoLog, const std::string &uniformName, const sh::InterfaceBlockField &vertexUniform, const sh::InterfaceBlockField &fragmentUniform);
-    bool linkUniforms(InfoLog &infoLog, const std::vector<sh::Uniform> &vertexUniforms, const std::vector<sh::Uniform> &fragmentUniforms);
-    void defineUniformBase(GLenum shader, const sh::Uniform &uniform);
-    void defineUniform(GLenum shader, const sh::Uniform &uniform, const std::string &fullName, unsigned int baseRegisterIndex);
+    bool linkUniforms(InfoLog &infoLog, const VertexShader &vertexShader, const FragmentShader &fragmentShader);
+    void defineUniformBase(GLenum shader, const sh::Uniform &uniform, unsigned int uniformRegister);
+    void defineUniform(GLenum shader, const sh::Uniform &uniform, const std::string &fullName, sh::HLSLBlockEncoder *encoder);
     bool indexSamplerUniform(const LinkedUniform &uniform, InfoLog &infoLog);
     bool indexUniforms(InfoLog &infoLog);
     static bool assignSamplers(unsigned int startSamplerIndex, GLenum samplerType, unsigned int samplerCount,
diff --git a/src/libGLESv2/Shader.cpp b/src/libGLESv2/Shader.cpp
index ea5a8d1..eda0298 100644
--- a/src/libGLESv2/Shader.cpp
+++ b/src/libGLESv2/Shader.cpp
@@ -115,6 +115,12 @@
     getSourceImpl(mHlsl, bufSize, length, buffer);
 }
 
+unsigned int Shader::getUniformRegister(const std::string &uniformName) const
+{
+    ASSERT(mUniformRegisterMap.count(uniformName) > 0);
+    return mUniformRegisterMap.find(uniformName)->second;
+}
+
 unsigned int Shader::getInterfaceBlockRegister(const std::string &blockName) const
 {
     ASSERT(mInterfaceBlockRegisterMap.count(blockName) > 0);
@@ -373,6 +379,18 @@
         ShGetInfoPointer(compiler, SH_ACTIVE_UNIFORMS_ARRAY, &activeUniforms);
         mActiveUniforms = *(std::vector<sh::Uniform>*)activeUniforms;
 
+        for (size_t uniformIndex = 0; uniformIndex < mActiveUniforms.size(); uniformIndex++)
+        {
+            const sh::Uniform &uniform = mActiveUniforms[uniformIndex];
+
+            unsigned int index = -1;
+            bool result = ShGetUniformRegister(compiler, uniform.name.c_str(), &index);
+            UNUSED_ASSERTION_VARIABLE(result);
+            ASSERT(result);
+
+            mUniformRegisterMap[uniform.name] = index;
+        }
+
         void *activeInterfaceBlocks;
         ShGetInfoPointer(compiler, SH_ACTIVE_INTERFACE_BLOCKS_ARRAY, &activeInterfaceBlocks);
         mActiveInterfaceBlocks = *(std::vector<sh::InterfaceBlock>*)activeInterfaceBlocks;
diff --git a/src/libGLESv2/Shader.h b/src/libGLESv2/Shader.h
index 95cedba..a40f415 100644
--- a/src/libGLESv2/Shader.h
+++ b/src/libGLESv2/Shader.h
@@ -87,6 +87,7 @@
 
     static void releaseCompiler();
     static ShShaderOutput getCompilerOutputType(GLenum shader);
+    unsigned int getUniformRegister(const std::string &uniformName) const;
     unsigned int getInterfaceBlockRegister(const std::string &blockName) const;
 
     bool usesDepthRange() const { return mUsesDepthRange; }
@@ -136,6 +137,7 @@
     std::string mInfoLog;
     std::vector<sh::Uniform> mActiveUniforms;
     std::vector<sh::InterfaceBlock> mActiveInterfaceBlocks;
+    std::map<std::string, unsigned int> mUniformRegisterMap;
     std::map<std::string, unsigned int> mInterfaceBlockRegisterMap;
 
     ResourceManager *mResourceManager;