Add a GetVariableInfo helper method.

This method replaces the similar logic in storing uniforms,
varyings, and interface blocks when collecting variable info.
We can also re-use this method for both GLSL and HLSL programs.

BUG=angle:466

Change-Id: Ie6c13abe0f09f38b2f9b36e117caae4833eaccbf
Reviewed-on: https://chromium-review.googlesource.com/206566
Tested-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Nicolas Capens <capn@chromium.org>
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 226d025..7d14070 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -2900,31 +2900,29 @@
     return constUnion;
 }
 
-void OutputHLSL::declareVaryingToList(const TType &type, TQualifier baseTypeQualifier, const TString &name, std::vector<Varying> &fieldsOut)
+class DeclareVaryingTraverser : public GetVariableTraverser<Varying>
 {
-    const TStructure *structure = type.getStruct();
+  public:
+    DeclareVaryingTraverser(std::vector<Varying> *output,
+                            InterpolationType interpolation)
+        : GetVariableTraverser(output),
+          mInterpolation(interpolation)
+    {}
 
-    InterpolationType interpolation = GetInterpolationType(baseTypeQualifier);
-    if (!structure)
+  private:
+    void visitVariable(Varying *varying)
     {
-        sh::Varying varying(GLVariableType(type), GLVariablePrecision(type), name.c_str(), (unsigned int)type.getArraySize(), interpolation);
-        fieldsOut.push_back(varying);
+        varying->interpolation = mInterpolation;
     }
-    else
-    {
-        sh::Varying structVarying(GL_STRUCT_ANGLEX, GL_NONE, name.c_str(), (unsigned int)type.getArraySize(), interpolation);
-        const TFieldList &fields = structure->fields();
 
-        structVarying.structName = structure->name().c_str();
+    InterpolationType mInterpolation;
+};
 
-        for (size_t fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
-        {
-            const TField &field = *fields[fieldIndex];
-            declareVaryingToList(*field.type(), baseTypeQualifier, field.name(), structVarying.fields);
-        }
-
-        fieldsOut.push_back(structVarying);
-    }
+void OutputHLSL::declareVaryingToList(const TType &type, TQualifier baseTypeQualifier,
+                                      const TString &name, std::vector<Varying> &fieldsOut)
+{
+    DeclareVaryingTraverser traverser(&fieldsOut, GetInterpolationType(baseTypeQualifier));
+    traverser.traverse(type, name);
 }
 
 }
diff --git a/src/compiler/translator/UniformHLSL.cpp b/src/compiler/translator/UniformHLSL.cpp
index fb2208c..18c5f37 100644
--- a/src/compiler/translator/UniformHLSL.cpp
+++ b/src/compiler/translator/UniformHLSL.cpp
@@ -136,54 +136,57 @@
 {
     int registerIndex = (IsSampler(type.getBasicType()) ? mSamplerRegister : mUniformRegister);
 
-    const Uniform &uniform = declareUniformToList(type, name, registerIndex, &mActiveUniforms);
+    declareUniformToList(type, name, registerIndex, &mActiveUniforms);
+
+    unsigned int registerCount = HLSLVariableRegisterCount(mActiveUniforms.back(), mOutputType);
 
     if (IsSampler(type.getBasicType()))
     {
-        mSamplerRegister += HLSLVariableRegisterCount(uniform, mOutputType);
+        mSamplerRegister += registerCount;
     }
     else
     {
-        mUniformRegister += HLSLVariableRegisterCount(uniform, mOutputType);
+        mUniformRegister += registerCount;
     }
 
     return registerIndex;
 }
 
-Uniform UniformHLSL::declareUniformToList(const TType &type, const TString &name, int registerIndex, std::vector<Uniform> *output)
+class DeclareUniformsTraverser : public GetVariableTraverser<Uniform>
 {
-    const TStructure *structure = type.getStruct();
+  public:
+    DeclareUniformsTraverser(std::vector<Uniform> *output,
+                             unsigned int registerIndex,
+                             ShShaderOutput outputType)
+        : GetVariableTraverser(output),
+          mRegisterIndex(registerIndex),
+          mOutputType(outputType)
+    {}
 
-    if (!structure)
+  private:
+    virtual void visitVariable(Uniform *uniform)
     {
-        Uniform uniform(GLVariableType(type), GLVariablePrecision(type), name.c_str(),
-                            (unsigned int)type.getArraySize(), (unsigned int)registerIndex, 0);
-        output->push_back(uniform);
-
-        return uniform;
-   }
-    else
-    {
-        Uniform structUniform(GL_STRUCT_ANGLEX, GL_NONE, name.c_str(), (unsigned int)type.getArraySize(),
-                                  (unsigned int)registerIndex, GL_INVALID_INDEX);
-
-        const TFieldList &fields = structure->fields();
-
-        for (size_t fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
+        if (!uniform->isStruct())
         {
-            TField *field = fields[fieldIndex];
-            TType *fieldType = field->type();
-
-            declareUniformToList(*fieldType, field->name(), GL_INVALID_INDEX, &structUniform.fields);
+            uniform->registerIndex = mRegisterIndex;
+            uniform->elementIndex = 0;
         }
-
-        // assign register offset information -- this will override the information in any sub-structures.
-        HLSLVariableGetRegisterInfo(registerIndex, &structUniform, mOutputType);
-
-        output->push_back(structUniform);
-
-        return structUniform;
+        else
+        {
+            // Assign register offset information.
+            // This will override the offsets in any nested structures.
+            HLSLVariableGetRegisterInfo(mRegisterIndex, uniform, mOutputType);
+        }
     }
+
+    unsigned int mRegisterIndex;
+    ShShaderOutput mOutputType;
+};
+
+void UniformHLSL::declareUniformToList(const TType &type, const TString &name, int registerIndex, std::vector<Uniform> *output)
+{
+    DeclareUniformsTraverser traverser(output, registerIndex, mOutputType);
+    traverser.traverse(type, name);
 }
 
 TString UniformHLSL::uniformsHeader(ShShaderOutput outputType, const ReferencedSymbols &referencedUniforms)
@@ -237,8 +240,11 @@
         for (unsigned int typeIndex = 0; typeIndex < fieldList.size(); typeIndex++)
         {
             const TField &field = *fieldList[typeIndex];
-            const TString &fullUniformName = InterfaceBlockFieldName(interfaceBlock, field);
-            declareInterfaceBlockField(*field.type(), fullUniformName, activeBlock.fields);
+            const TString &fullFieldName = InterfaceBlockFieldName(interfaceBlock, field);
+
+            bool isRowMajor = (field.type()->getLayoutQualifier().matrixPacking == EmpRowMajor);
+            GetInterfaceBlockFieldTraverser traverser(&activeBlock.fields, isRowMajor);
+            traverser.traverse(*field.type(), fullFieldName);
         }
 
         mInterfaceBlockRegister += std::max(1u, arraySize);
@@ -356,36 +362,4 @@
            "};\n\n";
 }
 
-void UniformHLSL::declareInterfaceBlockField(const TType &type, const TString &name, std::vector<InterfaceBlockField>& output)
-{
-    const TStructure *structure = type.getStruct();
-
-    if (!structure)
-    {
-        const bool isRowMajorMatrix = (type.isMatrix() && type.getLayoutQualifier().matrixPacking == EmpRowMajor);
-        InterfaceBlockField field(GLVariableType(type), GLVariablePrecision(type), name.c_str(),
-            (unsigned int)type.getArraySize(), isRowMajorMatrix);
-        output.push_back(field);
-    }
-    else
-    {
-        InterfaceBlockField structField(GL_STRUCT_ANGLEX, GL_NONE, name.c_str(), (unsigned int)type.getArraySize(), false);
-
-        const TFieldList &fields = structure->fields();
-
-        for (size_t fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
-        {
-            TField *field = fields[fieldIndex];
-            TType *fieldType = field->type();
-
-            // make sure to copy matrix packing information
-            fieldType->setLayoutQualifier(type.getLayoutQualifier());
-
-            declareInterfaceBlockField(*fieldType, field->name(), structField.fields);
-        }
-
-        output.push_back(structField);
-    }
-}
-
 }
diff --git a/src/compiler/translator/UniformHLSL.h b/src/compiler/translator/UniformHLSL.h
index 3060661..7de3112 100644
--- a/src/compiler/translator/UniformHLSL.h
+++ b/src/compiler/translator/UniformHLSL.h
@@ -40,8 +40,7 @@
 
     // Returns the uniform's register index
     int declareUniformAndAssignRegister(const TType &type, const TString &name);
-    void declareInterfaceBlockField(const TType &type, const TString &name, std::vector<InterfaceBlockField>& output);
-    Uniform declareUniformToList(const TType &type, const TString &name, int registerIndex, std::vector<Uniform> *output);
+    void declareUniformToList(const TType &type, const TString &name, int registerIndex, std::vector<Uniform> *output);
 
     unsigned int mUniformRegister;
     unsigned int mInterfaceBlockRegister;
diff --git a/src/compiler/translator/util.cpp b/src/compiler/translator/util.cpp
index 2b6e0ee..b9fc0b4 100644
--- a/src/compiler/translator/util.cpp
+++ b/src/compiler/translator/util.cpp
@@ -10,6 +10,7 @@
 
 #include "compiler/preprocessor/numeric_lex.h"
 #include "common/shadervars.h"
+#include "common/utilities.h"
 
 bool atof_clamp(const char *str, float *value)
 {
@@ -279,4 +280,66 @@
     }
 }
 
+template <typename VarT>
+void GetVariableTraverser<VarT>::traverse(const TType &type, const TString &name)
+{
+    const TStructure *structure = type.getStruct();
+
+    VarT variable;
+    variable.name = name.c_str();
+    variable.arraySize = static_cast<unsigned int>(type.getArraySize());
+
+    if (!structure)
+    {
+        variable.type = GLVariableType(type);
+        variable.precision = GLVariablePrecision(type);
+    }
+    else
+    {
+        variable.type = GL_STRUCT_ANGLEX;
+
+        mOutputStack.push(&variable.fields);
+
+        const TFieldList &fields = structure->fields();
+
+        for (size_t fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
+        {
+            TField *field = fields[fieldIndex];
+            traverse(*field->type(), field->name());
+        }
+
+        mOutputStack.pop();
+    }
+
+    visitVariable(&variable);
+
+    ASSERT(!mOutputStack.empty());
+    mOutputStack.top()->push_back(variable);
+}
+
+template <typename VarT>
+GetVariableTraverser<VarT>::GetVariableTraverser(std::vector<VarT> *output)
+{
+    ASSERT(output);
+    mOutputStack.push(output);
+}
+
+template class GetVariableTraverser<Uniform>;
+template class GetVariableTraverser<Varying>;
+template class GetVariableTraverser<InterfaceBlockField>;
+
+GetInterfaceBlockFieldTraverser::GetInterfaceBlockFieldTraverser(std::vector<InterfaceBlockField> *output, bool isRowMajorMatrix)
+    : GetVariableTraverser(output),
+      mIsRowMajorMatrix(isRowMajorMatrix)
+{
+}
+
+void GetInterfaceBlockFieldTraverser::visitVariable(InterfaceBlockField *newField)
+{
+    if (gl::IsMatrixType(newField->type))
+    {
+        newField->isRowMajorMatrix = mIsRowMajorMatrix;
+    }
+}
+
 }
diff --git a/src/compiler/translator/util.h b/src/compiler/translator/util.h
index 4dbaf3c..7eac07b 100644
--- a/src/compiler/translator/util.h
+++ b/src/compiler/translator/util.h
@@ -7,6 +7,8 @@
 #ifndef COMPILER_UTIL_H
 #define COMPILER_UTIL_H
 
+#include <stack>
+
 #include "compiler/translator/Types.h"
 #include "angle_gl.h"
 #include "common/shadervars.h"
@@ -32,6 +34,33 @@
 InterpolationType GetInterpolationType(TQualifier qualifier);
 TString ArrayString(const TType &type);
 
+template <typename VarT>
+class GetVariableTraverser
+{
+  public:
+    void traverse(const TType &type, const TString &name);
+
+  protected:
+    GetVariableTraverser(std::vector<VarT> *output);
+
+    // Must be overloaded
+    virtual void visitVariable(VarT *newVar) = 0;
+
+  private:
+    std::stack<std::vector<VarT> *> mOutputStack;
+};
+
+struct GetInterfaceBlockFieldTraverser : public GetVariableTraverser<InterfaceBlockField>
+{
+  public:
+    GetInterfaceBlockFieldTraverser(std::vector<InterfaceBlockField> *output, bool isRowMajorMatrix);
+
+  private:
+    virtual void visitVariable(InterfaceBlockField *newField);
+
+    bool mIsRowMajorMatrix;
+};
+
 }
 
 #endif // COMPILER_UTIL_H