Clean up checking variable packing limits

This encapsulates expanding struct variables inside the VariablePacker
class that packs variables according to the GLSL ES spec.

The variable expansion step is no longer run twice when checking
uniforms against the max uniforms limit.

BUG=angleproject:2068
TEST=angle_unittests

Change-Id: I012ddaa249f71c0a78d937c98007c61352e64888
Reviewed-on: https://chromium-review.googlesource.com/608367
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/VariablePacker.cpp b/src/compiler/translator/VariablePacker.cpp
index 9c4c937..1fa668b 100644
--- a/src/compiler/translator/VariablePacker.cpp
+++ b/src/compiler/translator/VariablePacker.cpp
@@ -3,6 +3,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
+// Check whether variables fit within packing limits according to the packing rules from the GLSL ES
+// 1.00.17 spec, Appendix A, section 7.
 
 #include <algorithm>
 
@@ -11,62 +13,102 @@
 #include "compiler/translator/VariablePacker.h"
 #include "common/utilities.h"
 
-int VariablePacker::GetNumComponentsPerRow(sh::GLenum type)
+namespace sh
 {
-    switch (type)
+
+namespace
+{
+
+void ExpandVariable(const ShaderVariable &variable,
+                    const std::string &name,
+                    const std::string &mappedName,
+                    bool markStaticUse,
+                    std::vector<ShaderVariable> *expanded);
+
+void ExpandUserDefinedVariable(const ShaderVariable &variable,
+                               const std::string &name,
+                               const std::string &mappedName,
+                               bool markStaticUse,
+                               std::vector<ShaderVariable> *expanded)
+{
+    ASSERT(variable.isStruct());
+
+    const std::vector<ShaderVariable> &fields = variable.fields;
+
+    for (size_t fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
     {
-        case GL_FLOAT_MAT4:
-        case GL_FLOAT_MAT2:
-        case GL_FLOAT_MAT2x4:
-        case GL_FLOAT_MAT3x4:
-        case GL_FLOAT_MAT4x2:
-        case GL_FLOAT_MAT4x3:
-        case GL_FLOAT_VEC4:
-        case GL_INT_VEC4:
-        case GL_BOOL_VEC4:
-        case GL_UNSIGNED_INT_VEC4:
-            return 4;
-        case GL_FLOAT_MAT3:
-        case GL_FLOAT_MAT2x3:
-        case GL_FLOAT_MAT3x2:
-        case GL_FLOAT_VEC3:
-        case GL_INT_VEC3:
-        case GL_BOOL_VEC3:
-        case GL_UNSIGNED_INT_VEC3:
-            return 3;
-        case GL_FLOAT_VEC2:
-        case GL_INT_VEC2:
-        case GL_BOOL_VEC2:
-        case GL_UNSIGNED_INT_VEC2:
-            return 2;
-        default:
-            ASSERT(gl::VariableComponentCount(type) == 1);
-            return 1;
+        const ShaderVariable &field = fields[fieldIndex];
+        ExpandVariable(field, name + "." + field.name, mappedName + "." + field.mappedName,
+                       markStaticUse, expanded);
     }
 }
 
-int VariablePacker::GetNumRows(sh::GLenum type)
+void ExpandVariable(const ShaderVariable &variable,
+                    const std::string &name,
+                    const std::string &mappedName,
+                    bool markStaticUse,
+                    std::vector<ShaderVariable> *expanded)
 {
-    switch (type)
+    if (variable.isStruct())
     {
-        case GL_FLOAT_MAT4:
-        case GL_FLOAT_MAT2x4:
-        case GL_FLOAT_MAT3x4:
-        case GL_FLOAT_MAT4x3:
-        case GL_FLOAT_MAT4x2:
-            return 4;
-        case GL_FLOAT_MAT3:
-        case GL_FLOAT_MAT2x3:
-        case GL_FLOAT_MAT3x2:
-            return 3;
-        case GL_FLOAT_MAT2:
-            return 2;
-        default:
-            ASSERT(gl::VariableRowCount(type) == 1);
-            return 1;
+        if (variable.isArray())
+        {
+            for (unsigned int elementIndex = 0; elementIndex < variable.elementCount();
+                 elementIndex++)
+            {
+                std::string lname       = name + ::ArrayString(elementIndex);
+                std::string lmappedName = mappedName + ::ArrayString(elementIndex);
+                ExpandUserDefinedVariable(variable, lname, lmappedName, markStaticUse, expanded);
+            }
+        }
+        else
+        {
+            ExpandUserDefinedVariable(variable, name, mappedName, markStaticUse, expanded);
+        }
+    }
+    else
+    {
+        ShaderVariable expandedVar = variable;
+
+        expandedVar.name       = name;
+        expandedVar.mappedName = mappedName;
+
+        // Mark all expanded fields as used if the parent is used
+        if (markStaticUse)
+        {
+            expandedVar.staticUse = true;
+        }
+
+        if (expandedVar.isArray())
+        {
+            expandedVar.name += "[0]";
+            expandedVar.mappedName += "[0]";
+        }
+
+        expanded->push_back(expandedVar);
     }
 }
 
+class VariablePacker
+{
+  public:
+    bool checkExpandedVariablesWithinPackingLimits(unsigned int maxVectors,
+                                                   std::vector<sh::ShaderVariable> *variables);
+
+  private:
+    static const int kNumColumns      = 4;
+    static const unsigned kColumnMask = (1 << kNumColumns) - 1;
+
+    unsigned makeColumnFlags(int column, int numComponentsPerRow);
+    void fillColumns(int topRow, int numRows, int column, int numComponentsPerRow);
+    bool searchColumn(int column, int numRows, int *destRow, int *destSize);
+
+    int topNonFullRow_;
+    int bottomNonFullRow_;
+    int maxRows_;
+    std::vector<unsigned> rows_;
+};
+
 struct TVariableInfoComparer
 {
     bool operator()(const sh::ShaderVariable &lhs, const sh::ShaderVariable &rhs) const
@@ -159,27 +201,21 @@
     return true;
 }
 
-bool VariablePacker::CheckVariablesWithinPackingLimits(
+bool VariablePacker::checkExpandedVariablesWithinPackingLimits(
     unsigned int maxVectors,
-    const std::vector<sh::ShaderVariable> &in_variables)
+    std::vector<sh::ShaderVariable> *variables)
 {
     ASSERT(maxVectors > 0);
     maxRows_          = maxVectors;
     topNonFullRow_    = 0;
     bottomNonFullRow_ = maxRows_ - 1;
-    std::vector<sh::ShaderVariable> variables;
-
-    for (const auto &variable : in_variables)
-    {
-        ExpandVariable(variable, variable.name, variable.mappedName, variable.staticUse,
-                       &variables);
-    }
 
     // Check whether each variable fits in the available vectors.
-    for (size_t i = 0; i < variables.size(); i++)
+    for (const sh::ShaderVariable &variable : *variables)
     {
-        const sh::ShaderVariable &variable = variables[i];
-        if (variable.elementCount() > maxVectors / GetNumRows(variable.type))
+        // Structs should have been expanded before reaching here.
+        ASSERT(!variable.isStruct());
+        if (variable.elementCount() > maxVectors / GetVariablePackingRows(variable.type))
         {
             return false;
         }
@@ -187,20 +223,20 @@
 
     // As per GLSL 1.017 Appendix A, Section 7 variables are packed in specific
     // order by type, then by size of array, largest first.
-    std::sort(variables.begin(), variables.end(), TVariableInfoComparer());
+    std::sort(variables->begin(), variables->end(), TVariableInfoComparer());
     rows_.clear();
     rows_.resize(maxVectors, 0);
 
     // Packs the 4 column variables.
     size_t ii = 0;
-    for (; ii < variables.size(); ++ii)
+    for (; ii < variables->size(); ++ii)
     {
-        const sh::ShaderVariable &variable = variables[ii];
-        if (GetNumComponentsPerRow(variable.type) != 4)
+        const sh::ShaderVariable &variable = (*variables)[ii];
+        if (GetVariablePackingComponentsPerRow(variable.type) != 4)
         {
             break;
         }
-        topNonFullRow_ += GetNumRows(variable.type) * variable.elementCount();
+        topNonFullRow_ += GetVariablePackingRows(variable.type) * variable.elementCount();
     }
 
     if (topNonFullRow_ > maxRows_)
@@ -210,14 +246,14 @@
 
     // Packs the 3 column variables.
     int num3ColumnRows = 0;
-    for (; ii < variables.size(); ++ii)
+    for (; ii < variables->size(); ++ii)
     {
-        const sh::ShaderVariable &variable = variables[ii];
-        if (GetNumComponentsPerRow(variable.type) != 3)
+        const sh::ShaderVariable &variable = (*variables)[ii];
+        if (GetVariablePackingComponentsPerRow(variable.type) != 3)
         {
             break;
         }
-        num3ColumnRows += GetNumRows(variable.type) * variable.elementCount();
+        num3ColumnRows += GetVariablePackingRows(variable.type) * variable.elementCount();
     }
 
     if (topNonFullRow_ + num3ColumnRows > maxRows_)
@@ -232,14 +268,14 @@
     int twoColumnRowsAvailable   = maxRows_ - top2ColumnRow;
     int rowsAvailableInColumns01 = twoColumnRowsAvailable;
     int rowsAvailableInColumns23 = twoColumnRowsAvailable;
-    for (; ii < variables.size(); ++ii)
+    for (; ii < variables->size(); ++ii)
     {
-        const sh::ShaderVariable &variable = variables[ii];
-        if (GetNumComponentsPerRow(variable.type) != 2)
+        const sh::ShaderVariable &variable = (*variables)[ii];
+        if (GetVariablePackingComponentsPerRow(variable.type) != 2)
         {
             break;
         }
-        int numRows = GetNumRows(variable.type) * variable.elementCount();
+        int numRows = GetVariablePackingRows(variable.type) * variable.elementCount();
         if (numRows <= rowsAvailableInColumns01)
         {
             rowsAvailableInColumns01 -= numRows;
@@ -260,11 +296,11 @@
     fillColumns(maxRows_ - numRowsUsedInColumns23, numRowsUsedInColumns23, 2, 2);
 
     // Packs the 1 column variables.
-    for (; ii < variables.size(); ++ii)
+    for (; ii < variables->size(); ++ii)
     {
-        const sh::ShaderVariable &variable = variables[ii];
-        ASSERT(1 == GetNumComponentsPerRow(variable.type));
-        int numRows        = GetNumRows(variable.type) * variable.elementCount();
+        const sh::ShaderVariable &variable = (*variables)[ii];
+        ASSERT(1 == GetVariablePackingComponentsPerRow(variable.type));
+        int numRows        = GetVariablePackingRows(variable.type) * variable.elementCount();
         int smallestColumn = -1;
         int smallestSize   = maxRows_ + 1;
         int topRow         = -1;
@@ -291,7 +327,86 @@
         fillColumns(topRow, numRows, smallestColumn, 1);
     }
 
-    ASSERT(variables.size() == ii);
+    ASSERT(variables->size() == ii);
 
     return true;
 }
+
+}  // anonymous namespace
+
+int GetVariablePackingComponentsPerRow(sh::GLenum type)
+{
+    switch (type)
+    {
+        case GL_FLOAT_MAT4:
+        case GL_FLOAT_MAT2:
+        case GL_FLOAT_MAT2x4:
+        case GL_FLOAT_MAT3x4:
+        case GL_FLOAT_MAT4x2:
+        case GL_FLOAT_MAT4x3:
+        case GL_FLOAT_VEC4:
+        case GL_INT_VEC4:
+        case GL_BOOL_VEC4:
+        case GL_UNSIGNED_INT_VEC4:
+            return 4;
+        case GL_FLOAT_MAT3:
+        case GL_FLOAT_MAT2x3:
+        case GL_FLOAT_MAT3x2:
+        case GL_FLOAT_VEC3:
+        case GL_INT_VEC3:
+        case GL_BOOL_VEC3:
+        case GL_UNSIGNED_INT_VEC3:
+            return 3;
+        case GL_FLOAT_VEC2:
+        case GL_INT_VEC2:
+        case GL_BOOL_VEC2:
+        case GL_UNSIGNED_INT_VEC2:
+            return 2;
+        default:
+            ASSERT(gl::VariableComponentCount(type) == 1);
+            return 1;
+    }
+}
+
+int GetVariablePackingRows(sh::GLenum type)
+{
+    switch (type)
+    {
+        case GL_FLOAT_MAT4:
+        case GL_FLOAT_MAT2x4:
+        case GL_FLOAT_MAT3x4:
+        case GL_FLOAT_MAT4x3:
+        case GL_FLOAT_MAT4x2:
+            return 4;
+        case GL_FLOAT_MAT3:
+        case GL_FLOAT_MAT2x3:
+        case GL_FLOAT_MAT3x2:
+            return 3;
+        case GL_FLOAT_MAT2:
+            return 2;
+        default:
+            ASSERT(gl::VariableRowCount(type) == 1);
+            return 1;
+    }
+}
+
+template <typename T>
+bool CheckVariablesInPackingLimits(unsigned int maxVectors, const std::vector<T> &variables)
+{
+    VariablePacker packer;
+    std::vector<sh::ShaderVariable> expandedVariables;
+    for (const ShaderVariable &variable : variables)
+    {
+        ExpandVariable(variable, variable.name, variable.mappedName, variable.staticUse,
+                       &expandedVariables);
+    }
+    return packer.checkExpandedVariablesWithinPackingLimits(maxVectors, &expandedVariables);
+}
+
+template bool CheckVariablesInPackingLimits<ShaderVariable>(
+    unsigned int maxVectors,
+    const std::vector<ShaderVariable> &variables);
+template bool CheckVariablesInPackingLimits<Uniform>(unsigned int maxVectors,
+                                                     const std::vector<Uniform> &variables);
+
+}  // namespace sh