Collect shader outputs and interface block information.

This paves the way for returning ES3-specific info from the shader
translator with the new query methods.

BUG=angle:466

Change-Id: Ib13cdb604854cdf11e9dc00dd94f18eadc946561
Reviewed-on: https://chromium-review.googlesource.com/206770
Tested-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
diff --git a/src/common/shadervars.h b/src/common/shadervars.h
index cba4404..252b07e 100644
--- a/src/common/shadervars.h
+++ b/src/common/shadervars.h
@@ -157,20 +157,32 @@
 
 struct InterfaceBlock
 {
+    InterfaceBlock()
+        : arraySize(0),
+          dataSize(0),
+          layout(BLOCKLAYOUT_PACKED),
+          isRowMajorLayout(false),
+          staticUse(false),
+          registerIndex(-1)
+    {}
+
     InterfaceBlock(const char *name, unsigned int arraySize, unsigned int registerIndex)
         : name(name),
           arraySize(arraySize),
           dataSize(0),
           layout(BLOCKLAYOUT_SHARED),
           isRowMajorLayout(false),
+          staticUse(false),
           registerIndex(registerIndex)
     {}
 
     std::string name;
+    std::string mappedName;
     unsigned int arraySize;
     size_t dataSize;
     BlockLayoutType layout;
     bool isRowMajorLayout;
+    bool staticUse;
     std::vector<InterfaceBlockField> fields;
     std::vector<BlockMemberInfo> blockInfo;
 
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 642f8dd..86ef6c3 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -487,7 +487,12 @@
 
 void TCompiler::collectVariables(TIntermNode* root)
 {
-    CollectVariables collect(&attributes, &uniforms, &varyings, hashFunction);
+    CollectVariables collect(&attributes,
+                             &outputVariables,
+                             &uniforms,
+                             &varyings,
+                             &interfaceBlocks,
+                             hashFunction);
     root->traverse(&collect);
 
     // For backwards compatiblity with ShGetVariableInfo, expand struct
diff --git a/src/compiler/translator/UniformHLSL.cpp b/src/compiler/translator/UniformHLSL.cpp
index 18c5f37..bd1c4b5 100644
--- a/src/compiler/translator/UniformHLSL.cpp
+++ b/src/compiler/translator/UniformHLSL.cpp
@@ -49,17 +49,6 @@
     }
 }
 
-static BlockLayoutType ConvertBlockLayoutType(TLayoutBlockStorage blockStorage)
-{
-    switch (blockStorage)
-    {
-      case EbsPacked: return BLOCKLAYOUT_PACKED;
-      case EbsShared: return BLOCKLAYOUT_SHARED;
-      case EbsStd140: return BLOCKLAYOUT_STANDARD;
-      default: UNREACHABLE(); return BLOCKLAYOUT_SHARED;
-    }
-}
-
 static const char *UniformRegisterPrefix(const TType &type)
 {
     if (IsSampler(type.getBasicType()))
@@ -249,7 +238,7 @@
 
         mInterfaceBlockRegister += std::max(1u, arraySize);
 
-        BlockLayoutType blockLayoutType = ConvertBlockLayoutType(interfaceBlock.blockStorage());
+        BlockLayoutType blockLayoutType = GetBlockLayoutType(interfaceBlock.blockStorage());
         SetBlockLayout(&activeBlock, blockLayoutType);
 
         if (interfaceBlock.matrixPacking() == EmpRowMajor)
diff --git a/src/compiler/translator/UtilsHLSL.cpp b/src/compiler/translator/UtilsHLSL.cpp
index dbe35a5..de0c36c 100644
--- a/src/compiler/translator/UtilsHLSL.cpp
+++ b/src/compiler/translator/UtilsHLSL.cpp
@@ -79,7 +79,7 @@
 
 TString Decorate(const TString &string)
 {
-    if (string.compare(0, 3, "gl_"))
+    if (string.compare(0, 3, "gl_") != 0)
     {
         return "_" + string;
     }
diff --git a/src/compiler/translator/VariableInfo.cpp b/src/compiler/translator/VariableInfo.cpp
index 57a5cd7..bb188b1 100644
--- a/src/compiler/translator/VariableInfo.cpp
+++ b/src/compiler/translator/VariableInfo.cpp
@@ -7,6 +7,7 @@
 #include "angle_gl.h"
 #include "compiler/translator/VariableInfo.h"
 #include "compiler/translator/util.h"
+#include "common/utilities.h"
 
 template <typename VarT>
 static void ExpandUserDefinedVariable(const VarT &variable,
@@ -85,7 +86,7 @@
 }
 
 template <class VarT>
-static VarT* findVariable(const TString &name,
+static VarT *FindVariable(const TString &name,
                           std::vector<VarT> *infoList)
 {
     // TODO(zmo): optimize this function.
@@ -94,16 +95,21 @@
         if ((*infoList)[ii].name.c_str() == name)
             return &((*infoList)[ii]);
     }
+
     return NULL;
 }
 
 CollectVariables::CollectVariables(std::vector<sh::Attribute> *attribs,
+                                   std::vector<sh::Attribute> *outputVariables,
                                    std::vector<sh::Uniform> *uniforms,
                                    std::vector<sh::Varying> *varyings,
+                                   std::vector<sh::InterfaceBlock> *interfaceBlocks,
                                    ShHashFunction64 hashFunction)
     : mAttribs(attribs),
+      mOutputVariables(outputVariables),
       mUniforms(uniforms),
       mVaryings(varyings),
+      mInterfaceBlocks(interfaceBlocks),
       mPointCoordAdded(false),
       mFrontFacingAdded(false),
       mFragCoordAdded(false),
@@ -120,17 +126,43 @@
 {
     ASSERT(symbol != NULL);
     sh::ShaderVariable *var = NULL;
+    const TString &symbolName = symbol->getSymbol();
 
     if (sh::IsVarying(symbol->getQualifier()))
     {
-        var = findVariable(symbol->getSymbol(), mVaryings);
+        var = FindVariable(symbolName, mVaryings);
     }
-    else
+    else if (symbol->getType() != EbtInterfaceBlock)
     {
         switch (symbol->getQualifier())
         {
+          case EvqAttribute:
+          case EvqVertexIn:
+            var = FindVariable(symbolName, mAttribs);
+            break;
+          case EvqFragmentOut:
+            var = FindVariable(symbolName, mOutputVariables);
+            break;
           case EvqUniform:
-            var = findVariable(symbol->getSymbol(), mUniforms);
+            {
+                const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();
+                if (interfaceBlock)
+                {
+                    sh::InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mInterfaceBlocks);
+                    ASSERT(namedBlock);
+                    var = FindVariable(symbolName, &namedBlock->fields);
+
+                    // Set static use on the parent interface block here
+                    namedBlock->staticUse = true;
+                }
+                else
+                {
+                    var = FindVariable(symbolName, mUniforms);
+                }
+
+                // It's an internal error to reference an undefined user uniform
+                ASSERT(symbolName.compare(0, 3, "gl_") == 0 || var);
+            }
             break;
           case EvqFragCoord:
             if (!mFragCoordAdded)
@@ -224,6 +256,36 @@
     infoList->push_back(attribute);
 }
 
+template <>
+void CollectVariables::visitVariable(const TIntermSymbol *variable,
+                                     std::vector<sh::InterfaceBlock> *infoList) const
+{
+    sh::InterfaceBlock interfaceBlock;
+    const TInterfaceBlock *blockType = variable->getType().getInterfaceBlock();
+
+    bool isRowMajor = (blockType->matrixPacking() == EmpRowMajor);
+
+    interfaceBlock.name = blockType->name().c_str();
+    interfaceBlock.mappedName = TIntermTraverser::hash(variable->getSymbol(), mHashFunction).c_str();
+    interfaceBlock.arraySize = variable->getArraySize();
+    interfaceBlock.isRowMajorLayout = isRowMajor;
+    interfaceBlock.layout = sh::GetBlockLayoutType(blockType->blockStorage());
+
+    ASSERT(blockType);
+    const TFieldList &blockFields = blockType->fields();
+
+    for (size_t fieldIndex = 0; fieldIndex < blockFields.size(); fieldIndex++)
+    {
+        const TField *field = blockFields[fieldIndex];
+        ASSERT(field);
+
+        sh::GetInterfaceBlockFieldTraverser traverser(&interfaceBlock.fields, isRowMajor);
+        traverser.traverse(*field->type(), field->name());
+    }
+
+    infoList->push_back(interfaceBlock);
+}
+
 template <typename VarT>
 void CollectVariables::visitVariable(const TIntermSymbol *variable,
                                      std::vector<VarT> *infoList) const
@@ -258,9 +320,16 @@
       case EOpDeclaration:
         {
             const TIntermSequence &sequence = node->getSequence();
-            TQualifier qualifier = sequence.front()->getAsTyped()->getQualifier();
-            if (qualifier == EvqAttribute || qualifier == EvqVertexIn || qualifier == EvqUniform ||
-                sh::IsVarying(qualifier))
+            const TIntermTyped &typedNode = *sequence.front()->getAsTyped();
+            TQualifier qualifier = typedNode.getQualifier();
+
+            if (typedNode.getBasicType() == EbtInterfaceBlock)
+            {
+                visitInfoList(sequence, mInterfaceBlocks);
+            }
+            else if (qualifier == EvqAttribute || qualifier == EvqVertexIn ||
+                     qualifier == EvqFragmentOut || qualifier == EvqUniform ||
+                     sh::IsVarying(qualifier))
             {
                 switch (qualifier)
                 {
@@ -268,6 +337,9 @@
                   case EvqVertexIn:
                     visitInfoList(sequence, mAttribs);
                     break;
+                  case EvqFragmentOut:
+                    visitInfoList(sequence, mOutputVariables);
+                    break;
                   case EvqUniform:
                     visitInfoList(sequence, mUniforms);
                     break;
diff --git a/src/compiler/translator/VariableInfo.h b/src/compiler/translator/VariableInfo.h
index ddbfb05..3771819 100644
--- a/src/compiler/translator/VariableInfo.h
+++ b/src/compiler/translator/VariableInfo.h
@@ -15,29 +15,35 @@
 {
   public:
     CollectVariables(std::vector<sh::Attribute> *attribs,
+                     std::vector<sh::Attribute> *outputVariables,
                      std::vector<sh::Uniform> *uniforms,
                      std::vector<sh::Varying> *varyings,
+                     std::vector<sh::InterfaceBlock> *interfaceBlocks,
                      ShHashFunction64 hashFunction);
 
     virtual void visitSymbol(TIntermSymbol *symbol);
     virtual bool visitAggregate(Visit, TIntermAggregate *node);
 
   private:
+    template <typename VarT>
+    void visitVariable(const TIntermSymbol *variable, std::vector<VarT> *infoList) const;
+
+    template <typename VarT>
+    void visitInfoList(const TIntermSequence &sequence, std::vector<VarT> *infoList) const;
+
     std::vector<sh::Attribute> *mAttribs;
+    std::vector<sh::Attribute> *mOutputVariables;
     std::vector<sh::Uniform> *mUniforms;
     std::vector<sh::Varying> *mVaryings;
+    std::vector<sh::InterfaceBlock> *mInterfaceBlocks;
+
+    std::map<std::string, sh::InterfaceBlockField *> mInterfaceBlockFields;
 
     bool mPointCoordAdded;
     bool mFrontFacingAdded;
     bool mFragCoordAdded;
 
     ShHashFunction64 mHashFunction;
-
-    template <typename VarT>
-    void visitVariable(const TIntermSymbol *variable, std::vector<VarT> *infoList) const;
-
-    template <typename VarT>
-    void visitInfoList(const TIntermSequence &sequence, std::vector<VarT> *infoList) const;
 };
 
 // Expand struct variables to flattened lists of split variables
diff --git a/src/compiler/translator/util.cpp b/src/compiler/translator/util.cpp
index b9fc0b4..561d4e0 100644
--- a/src/compiler/translator/util.cpp
+++ b/src/compiler/translator/util.cpp
@@ -342,4 +342,15 @@
     }
 }
 
+BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage)
+{
+    switch (blockStorage)
+    {
+      case EbsPacked:         return BLOCKLAYOUT_PACKED;
+      case EbsShared:         return BLOCKLAYOUT_SHARED;
+      case EbsStd140:         return BLOCKLAYOUT_STANDARD;
+      default: UNREACHABLE(); return BLOCKLAYOUT_SHARED;
+    }
+}
+
 }
diff --git a/src/compiler/translator/util.h b/src/compiler/translator/util.h
index 7eac07b..b9f02f8 100644
--- a/src/compiler/translator/util.h
+++ b/src/compiler/translator/util.h
@@ -32,6 +32,7 @@
 bool IsVaryingOut(TQualifier qualifier);
 bool IsVarying(TQualifier qualifier);
 InterpolationType GetInterpolationType(TQualifier qualifier);
+BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage);
 TString ArrayString(const TType &type);
 
 template <typename VarT>
diff --git a/src/libGLESv2/DynamicHLSL.cpp b/src/libGLESv2/DynamicHLSL.cpp
index 1f1dc20..e3ec391 100644
--- a/src/libGLESv2/DynamicHLSL.cpp
+++ b/src/libGLESv2/DynamicHLSL.cpp
@@ -1066,7 +1066,7 @@
 // This method needs to match OutputHLSL::decorate
 std::string DynamicHLSL::decorateVariable(const std::string &name)
 {
-    if (name.compare(0, 3, "gl_"))
+    if (name.compare(0, 3, "gl_") != 0)
     {
         return "_" + name;
     }
diff --git a/tests/compiler_tests/CollectVariables_test.cpp b/tests/compiler_tests/CollectVariables_test.cpp
new file mode 100644
index 0000000..d823204
--- /dev/null
+++ b/tests/compiler_tests/CollectVariables_test.cpp
@@ -0,0 +1,166 @@
+//
+// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// CollectVariables_test.cpp:
+//   Some tests for shader inspection
+//
+
+#include "angle_gl.h"
+#include "gtest/gtest.h"
+#include "GLSLANG/ShaderLang.h"
+#include "compiler/translator/TranslatorGLSL.h"
+
+class CollectVariablesTest : public testing::Test
+{
+  public:
+    CollectVariablesTest(GLenum shaderType)
+        : mShaderType(shaderType)
+    {}
+
+  protected:
+    virtual void SetUp()
+    {
+        ShBuiltInResources resources;
+        ShInitBuiltInResources(&resources);
+        resources.MaxDrawBuffers = 8;
+
+        mTranslator = new TranslatorGLSL(mShaderType, SH_GLES2_SPEC);
+        ASSERT_TRUE(mTranslator->Init(resources));
+    }
+
+    virtual void TearDown()
+    {
+        delete mTranslator;
+    }
+
+    GLenum mShaderType;
+    TranslatorGLSL *mTranslator;
+};
+
+class CollectVertexVariablesTest : public CollectVariablesTest
+{
+  public:
+    CollectVertexVariablesTest() : CollectVariablesTest(GL_VERTEX_SHADER) {}
+};
+
+class CollectFragmentVariablesTest : public CollectVariablesTest
+{
+  public:
+      CollectFragmentVariablesTest() : CollectVariablesTest(GL_FRAGMENT_SHADER) {}
+};
+
+TEST_F(CollectFragmentVariablesTest, SimpleOutputVar)
+{
+    const std::string &shaderString =
+        "#version 300 es\n"
+        "precision mediump float;\n"
+        "out vec4 out_fragColor;\n"
+        "void main() {\n"
+        "   out_fragColor = vec4(1.0);\n"
+        "}\n";
+
+    const char *shaderStrings[] = { shaderString.c_str() };
+    ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES));
+
+    const std::vector<sh::Attribute> &outputVariables = mTranslator->getOutputVariables();
+    ASSERT_EQ(1u, outputVariables.size());
+
+    const sh::Attribute &outputVariable = outputVariables[0];
+
+    EXPECT_EQ(0, outputVariable.arraySize);
+    EXPECT_EQ(-1, outputVariable.location);
+    EXPECT_EQ(GL_MEDIUM_FLOAT, outputVariable.precision);
+    EXPECT_EQ(true, outputVariable.staticUse);
+    EXPECT_EQ(GL_FLOAT_VEC4, outputVariable.type);
+    EXPECT_EQ("out_fragColor", outputVariable.name);
+}
+
+TEST_F(CollectFragmentVariablesTest, LocationOutputVar)
+{
+    const std::string &shaderString =
+        "#version 300 es\n"
+        "precision mediump float;\n"
+        "layout(location=5) out vec4 out_fragColor;\n"
+        "void main() {\n"
+        "   out_fragColor = vec4(1.0);\n"
+        "}\n";
+
+    const char *shaderStrings[] = { shaderString.c_str() };
+    ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES));
+
+    const std::vector<sh::Attribute> &outputVariables = mTranslator->getOutputVariables();
+    ASSERT_EQ(1u, outputVariables.size());
+
+    const sh::Attribute &outputVariable = outputVariables[0];
+
+    EXPECT_EQ(0, outputVariable.arraySize);
+    EXPECT_EQ(5, outputVariable.location);
+    EXPECT_EQ(GL_MEDIUM_FLOAT, outputVariable.precision);
+    EXPECT_EQ(true, outputVariable.staticUse);
+    EXPECT_EQ(GL_FLOAT_VEC4, outputVariable.type);
+    EXPECT_EQ("out_fragColor", outputVariable.name);
+}
+
+TEST_F(CollectVertexVariablesTest, LocationAttribute)
+{
+    const std::string &shaderString =
+        "#version 300 es\n"
+        "layout(location=5) in vec4 in_Position;\n"
+        "void main() {\n"
+        "   gl_Position = in_Position;\n"
+        "}\n";
+
+    const char *shaderStrings[] = { shaderString.c_str() };
+    ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES));
+
+    const std::vector<sh::Attribute> &attributes = mTranslator->getAttributes();
+    ASSERT_EQ(1u, attributes.size());
+
+    const sh::Attribute &attribute = attributes[0];
+
+    EXPECT_EQ(0, attribute.arraySize);
+    EXPECT_EQ(5, attribute.location);
+    EXPECT_EQ(GL_HIGH_FLOAT, attribute.precision);
+    EXPECT_EQ(true, attribute.staticUse);
+    EXPECT_EQ(GL_FLOAT_VEC4, attribute.type);
+    EXPECT_EQ("in_Position", attribute.name);
+}
+
+TEST_F(CollectVertexVariablesTest, SimpleInterfaceBlock)
+{
+    const std::string &shaderString =
+        "#version 300 es\n"
+        "uniform b {\n"
+        "  float f;\n"
+        "};"
+        "void main() {\n"
+        "   gl_Position = vec4(f, 0.0, 0.0, 1.0);\n"
+        "}\n";
+
+    const char *shaderStrings[] = { shaderString.c_str() };
+    ASSERT_TRUE(mTranslator->compile(shaderStrings, 1, SH_VARIABLES));
+
+    const std::vector<sh::InterfaceBlock> &interfaceBlocks = mTranslator->getInterfaceBlocks();
+    ASSERT_EQ(1u, interfaceBlocks.size());
+
+    const sh::InterfaceBlock &interfaceBlock = interfaceBlocks[0];
+
+    EXPECT_EQ(0, interfaceBlock.arraySize);
+    EXPECT_EQ(false, interfaceBlock.isRowMajorLayout);
+    EXPECT_EQ(sh::BLOCKLAYOUT_SHARED, interfaceBlock.layout);
+    EXPECT_EQ("b", interfaceBlock.name);
+    EXPECT_EQ(true, interfaceBlock.staticUse);
+
+    ASSERT_EQ(1, interfaceBlock.fields.size());
+
+    const sh::InterfaceBlockField &field = interfaceBlock.fields[0];
+
+    EXPECT_EQ(GL_HIGH_FLOAT, field.precision);
+    EXPECT_EQ(true, field.staticUse);
+    EXPECT_EQ(GL_FLOAT, field.type);
+    EXPECT_EQ("f", field.name);
+    EXPECT_EQ(false, field.isRowMajorMatrix);
+    EXPECT_EQ(0, field.fields.size());
+}