Implemented varying packing
TRAC #11736
The OpenGL ES Shading Language 1.00 rev. 17 appendix A section 7 page 111, details how varyings should be packed into generic varying registers. To implement this the HLSL main() function is now generated and appended to the code during link time, where the packing and mapping can happen.
Signed-off-by: Shannon Woods
Signed-off-by: Daniel Koch

Author:    Nicolas Capens

git-svn-id: http://angleproject.googlecode.com/svn/trunk@282 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/compiler/OutputHLSL.cpp b/src/compiler/OutputHLSL.cpp
index 78b736d..d2e3081 100644
--- a/src/compiler/OutputHLSL.cpp
+++ b/src/compiler/OutputHLSL.cpp
@@ -35,6 +35,10 @@
     mUsesTextureCube = false;
     mUsesTextureCube_bias = false;
     mUsesDepthRange = false;
+    mUsesFragCoord = false;
+    mUsesPointCoord = false;
+    mUsesFrontFacing = false;
+    mUsesPointSize = false;
     mUsesXor = false;
     mUsesMod1 = false;
     mUsesMod2 = false;
@@ -68,13 +72,11 @@
 
 void OutputHLSL::output()
 {
-    mContext.treeRoot->traverse(this);   // Output the body first to determine what has to go in the header and footer
+    mContext.treeRoot->traverse(this);   // Output the body first to determine what has to go in the header
     header();
-    footer();
 
     mContext.infoSink.obj << mHeader.c_str();
     mContext.infoSink.obj << mBody.c_str();
-    mContext.infoSink.obj << mFooter.c_str();
 }
 
 TInfoSinkBase &OutputHLSL::getBodyStream()
@@ -259,8 +261,7 @@
     if (language == EShLangFragment)
     {
         TString uniforms;
-        TString varyingInput;
-        TString varyingGlobals;
+        TString varyings;
 
         TSymbolTableLevel *symbols = mContext.symbolTable.getGlobalLevel();
         int semanticIndex = 0;
@@ -288,8 +289,7 @@
                     if (mReferencedVaryings.find(name.c_str()) != mReferencedVaryings.end())
                     {
                         // Program linking depends on this exact format
-                        varyingInput += "    " + typeString(type) + " " + decorate(name) + arrayString(type) + " : TEXCOORD" + str(semanticIndex) + ";\n";
-                        varyingGlobals += "static " + typeString(type) + " " + decorate(name) + arrayString(type) + " = " + initializer(type) + ";\n";
+                        varyings += "static " + typeString(type) + " " + decorate(name) + arrayString(type) + " = " + initializer(type) + ";\n";
 
                         semanticIndex += type.isArray() ? type.getArraySize() : 1;
                     }
@@ -306,32 +306,43 @@
             }
         }
 
-        out << "uniform float4 dx_Window;\n"
-               "uniform float2 dx_Depth;\n"
-               "uniform bool dx_PointsOrLines;\n"
-               "uniform bool dx_FrontCCW;\n"
-               "\n";
+        out << "// Varyings\n";
+        out <<  varyings;
+        out << "\n"
+               "static float4 gl_Color[1] = {float4(0, 0, 0, 0)};\n";
+
+        if (mUsesFragCoord)
+        {
+            out << "static float4 gl_FragCoord = float4(0, 0, 0, 0);\n";
+        }
+
+        if (mUsesPointCoord)
+        {
+            out << "static float2 gl_PointCoord = float2(0.5, 0.5);\n";
+        }
+
+        if (mUsesFrontFacing)
+        {
+            out << "static bool gl_FrontFacing = false;\n";
+        }
+
+        out << "\n";
+
+        if (mUsesFragCoord)
+        {
+            out << "uniform float4 dx_Window;\n"
+                   "uniform float2 dx_Depth;\n";
+        }
+
+        if (mUsesFrontFacing)
+        {
+            out << "uniform bool dx_PointsOrLines;\n"
+                   "uniform bool dx_FrontCCW;\n";
+        }
+        
+        out << "\n";
         out <<  uniforms;
-        out << "\n"
-               "struct PS_INPUT\n"
-               "{\n";
-        out <<      varyingInput;
-        out << "    float4 gl_FragCoord : TEXCOORD" << semanticIndex << ";\n";
-        out << "    float vFace : VFACE;\n"
-               "};\n"
-               "\n";
-        out <<    varyingGlobals;
-        out << "\n"
-               "struct PS_OUTPUT\n"
-               "{\n"
-               "    float4 gl_Color[1] : COLOR;\n"
-               "};\n"
-               "\n"
-               "static float4 gl_Color[1] = {float4(0, 0, 0, 0)};\n"
-               "static float4 gl_FragCoord = float4(0, 0, 0, 0);\n"
-               "static float2 gl_PointCoord = float2(0.5, 0.5);\n"
-               "static bool gl_FrontFacing = false;\n"
-               "\n";
+        out << "\n";
 
         if (mUsesTexture2D)
         {
@@ -400,13 +411,10 @@
     else   // Vertex shader
     {
         TString uniforms;
-        TString attributeInput;
-        TString attributeGlobals;
-        TString varyingOutput;
-        TString varyingGlobals;
+        TString attributes;
+        TString varyings;
 
         TSymbolTableLevel *symbols = mContext.symbolTable.getGlobalLevel();
-        int semanticIndex = 0;
 
         for (TSymbolTableLevel::const_iterator namedSymbol = symbols->begin(); namedSymbol != symbols->end(); namedSymbol++)
         {
@@ -430,10 +438,7 @@
                 {
                     if (mReferencedAttributes.find(name.c_str()) != mReferencedAttributes.end())
                     {
-                        attributeInput += "    " + typeString(type) + " " + decorate(name) + arrayString(type) + " : TEXCOORD" + str(semanticIndex) + ";\n";
-                        attributeGlobals += "static " + typeString(type) + " " + decorate(name) + arrayString(type) + " = " + initializer(type) + ";\n";
-
-                        semanticIndex += vectorSize(type);
+                        attributes += "static " + typeString(type) + " " + decorate(name) + arrayString(type) + " = " + initializer(type) + ";\n";
                     }
                 }
                 else if (qualifier == EvqVaryingOut || qualifier == EvqInvariantVaryingOut)
@@ -441,8 +446,7 @@
                     if (mReferencedVaryings.find(name.c_str()) != mReferencedVaryings.end())
                     {
                         // Program linking depends on this exact format
-                        varyingOutput += "    " + typeString(type) + " " + decorate(name) + arrayString(type) + " : TEXCOORD0;\n";   // Actual semantic index assigned during link
-                        varyingGlobals += "static " + typeString(type) + " " + decorate(name) + arrayString(type) + " = " + initializer(type) + ";\n";
+                        varyings += "static " + typeString(type) + " " + decorate(name) + arrayString(type) + " = " + initializer(type) + ";\n";
                     }
                 }
                 else if (qualifier == EvqGlobal || qualifier == EvqTemporary)
@@ -457,31 +461,46 @@
             }
         }
 
-        out << "uniform float2 dx_HalfPixelSize;\n"
+        out << "// Attributes\n";
+        out <<  attributes;
+        out << "\n"
+               "static float4 gl_Position = float4(0, 0, 0, 0);\n";
+        
+        if (mUsesPointSize)
+        {
+            out << "static float gl_PointSize = float(1);\n";
+        }
+
+        out << "\n"
+               "// Varyings\n";
+        out <<  varyings;
+        out << "\n"
+               "uniform float2 dx_HalfPixelSize;\n"
                "\n";
         out <<  uniforms;
-        out << "\n"
-               "struct VS_INPUT\n"
-               "{\n";
-        out <<        attributeInput;
-        out << "};\n"
-               "\n";
-        out <<  attributeGlobals;
-        out << "\n"
-               "struct VS_OUTPUT\n"
-               "{\n"
-               "    float4 gl_Position : POSITION;\n"
-               "    float gl_PointSize : PSIZE;\n"
-               "    float4 gl_FragCoord : TEXCOORD0;\n";   // Actual semantic index assigned during link
-        out <<      varyingOutput;
-        out << "};\n"
-               "\n"
-               "static float4 gl_Position = float4(0, 0, 0, 0);\n"
-               "static float gl_PointSize = float(1);\n";
-        out <<  varyingGlobals;
         out << "\n";
     }
 
+    if (mUsesFragCoord)
+    {
+        out << "#define GL_USES_FRAG_COORD\n";
+    }
+
+    if (mUsesPointCoord)
+    {
+        out << "#define GL_USES_POINT_COORD\n";
+    }
+
+    if (mUsesFrontFacing)
+    {
+        out << "#define GL_USES_FRONT_FACING\n";
+    }
+
+    if (mUsesPointSize)
+    {
+        out << "#define GL_USES_POINT_SIZE\n";
+    }
+
     if (mUsesDepthRange)
     {
         out << "struct gl_DepthRangeParameters\n"
@@ -716,117 +735,6 @@
     }
 }
 
-void OutputHLSL::footer()
-{
-    EShLanguage language = mContext.language;
-    TInfoSinkBase &out = mFooter;
-    TSymbolTableLevel *symbols = mContext.symbolTable.getGlobalLevel();
-
-    if (language == EShLangFragment)
-    {
-        out << "PS_OUTPUT main(PS_INPUT input)\n"
-               "{\n"
-               "    float rhw = 1.0 / input.gl_FragCoord.w;\n"
-               "    gl_FragCoord.x = (input.gl_FragCoord.x * rhw) * dx_Window.x + dx_Window.z;\n"
-               "    gl_FragCoord.y = (input.gl_FragCoord.y * rhw) * dx_Window.y + dx_Window.w;\n"
-               "    gl_FragCoord.z = (input.gl_FragCoord.z * rhw) * dx_Depth.x + dx_Depth.y;\n"
-               "    gl_FragCoord.w = rhw;\n"
-               "    gl_FrontFacing = dx_PointsOrLines || (dx_FrontCCW ? (input.vFace >= 0.0) : (input.vFace <= 0.0));\n";
-
-        for (TSymbolTableLevel::const_iterator namedSymbol = symbols->begin(); namedSymbol != symbols->end(); namedSymbol++)
-        {
-            const TSymbol *symbol = (*namedSymbol).second;
-            const TString &name = symbol->getName();
-
-            if (symbol->isVariable())
-            {
-                const TVariable *variable = static_cast<const TVariable*>(symbol);
-                const TType &type = variable->getType();
-                TQualifier qualifier = type.getQualifier();
-
-                if (qualifier == EvqVaryingIn || qualifier == EvqInvariantVaryingIn)
-                {
-                    if (mReferencedVaryings.find(name.c_str()) != mReferencedVaryings.end())
-                    {
-                        out << "    " + decorate(name) + " = input." + decorate(name) + ";\n";
-                    }
-                }
-            }
-        }
-
-        out << "\n"
-               "    gl_main();\n"
-               "\n"
-               "    PS_OUTPUT output;\n"                 
-               "    output.gl_Color[0] = gl_Color[0];\n";
-    }
-    else   // Vertex shader
-    {
-        out << "VS_OUTPUT main(VS_INPUT input)\n"
-               "{\n";
-
-        for (TSymbolTableLevel::const_iterator namedSymbol = symbols->begin(); namedSymbol != symbols->end(); namedSymbol++)
-        {
-            const TSymbol *symbol = (*namedSymbol).second;
-            const TString &name = symbol->getName();
-
-            if (symbol->isVariable())
-            {
-                const TVariable *variable = static_cast<const TVariable*>(symbol);
-                const TType &type = variable->getType();
-                TQualifier qualifier = type.getQualifier();
-
-                if (qualifier == EvqAttribute)
-                {
-                    if (mReferencedAttributes.find(name.c_str()) != mReferencedAttributes.end())
-                    {
-                        const char *transpose = type.isMatrix() ? "transpose" : "";
-
-                        out << "    " + decorate(name) + " = " + transpose + "(input." + decorate(name) + ");\n";
-                    }
-                }
-            }
-        }
-
-        out << "\n"
-               "    gl_main();\n"
-               "\n"
-               "    VS_OUTPUT output;\n"
-               "    output.gl_Position.x = gl_Position.x - dx_HalfPixelSize.x * gl_Position.w;\n"
-               "    output.gl_Position.y = -(gl_Position.y - dx_HalfPixelSize.y * gl_Position.w);\n"
-               "    output.gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n"
-               "    output.gl_Position.w = gl_Position.w;\n"
-               "    output.gl_PointSize = gl_PointSize;\n"
-               "    output.gl_FragCoord = gl_Position;\n";
-
-        TSymbolTableLevel *symbols = mContext.symbolTable.getGlobalLevel();
-
-        for (TSymbolTableLevel::const_iterator namedSymbol = symbols->begin(); namedSymbol != symbols->end(); namedSymbol++)
-        {
-            const TSymbol *symbol = (*namedSymbol).second;
-            const TString &name = symbol->getName();
-
-            if (symbol->isVariable())
-            {
-                const TVariable *variable = static_cast<const TVariable*>(symbol);
-                TQualifier qualifier = variable->getType().getQualifier();
-
-                if (qualifier == EvqVaryingOut || qualifier == EvqInvariantVaryingOut)
-                {
-                    if (mReferencedVaryings.find(name.c_str()) != mReferencedVaryings.end())
-                    {
-                        // Program linking depends on this exact format
-                        out << "    output." + decorate(name) + " = " + decorate(name) + ";\n";
-                    }
-                }
-            }
-        }
-    }
-
-    out << "    return output;\n"
-           "}\n";
-}
-
 void OutputHLSL::visitSymbol(TIntermSymbol *node)
 {
     TInfoSinkBase &out = mBody;
@@ -846,6 +754,26 @@
         mUsesDepthRange = true;
         out << name;
     }
+    else if (name == "gl_FragCoord")
+    {
+        mUsesFragCoord = true;
+        out << name;
+    }
+    else if (name == "gl_PointCoord")
+    {
+        mUsesPointCoord = true;
+        out << name;
+    }
+    else if (name == "gl_FrontFacing")
+    {
+        mUsesFrontFacing = true;
+        out << name;
+    }
+    else if (name == "gl_PointSize")
+    {
+        mUsesPointSize = true;
+        out << name;
+    }
     else
     {
         TQualifier qualifier = node->getQualifier();
diff --git a/src/compiler/OutputHLSL.h b/src/compiler/OutputHLSL.h
index 9d64d3c..90f172d 100644
--- a/src/compiler/OutputHLSL.h
+++ b/src/compiler/OutputHLSL.h
@@ -32,7 +32,6 @@
 
   protected:
     void header();
-    void footer();
 
     // Visit AST nodes and output their code to the body stream
     void visitSymbol(TIntermSymbol*);
@@ -74,6 +73,10 @@
     bool mUsesTextureCube;
     bool mUsesTextureCube_bias;
     bool mUsesDepthRange;
+    bool mUsesFragCoord;
+    bool mUsesPointCoord;
+    bool mUsesFrontFacing;
+    bool mUsesPointSize;
     bool mUsesXor;
     bool mUsesMod1;
     bool mUsesMod2;
diff --git a/src/libGLESv2/Program.cpp b/src/libGLESv2/Program.cpp
index ca33c57..b709dbc 100644
--- a/src/libGLESv2/Program.cpp
+++ b/src/libGLESv2/Program.cpp
@@ -19,6 +19,13 @@
 {
 unsigned int Program::mCurrentSerial = 1;
 
+std::string str(int i)
+{
+    char buffer[20];
+    sprintf(buffer, "%d", i);
+    return buffer;
+}
+
 Uniform::Uniform(GLenum type, const std::string &name, unsigned int arraySize) : type(type), name(name), arraySize(arraySize)
 {
     int bytes = UniformTypeSize(type) * arraySize;
@@ -47,8 +54,6 @@
     mConstantTablePS = NULL;
     mConstantTableVS = NULL;
 
-    mPixelHLSL = NULL;
-    mVertexHLSL = NULL;
     mInfoLog = NULL;
     mValidated = false;
 
@@ -983,99 +988,438 @@
     return NULL;
 }
 
-void Program::parseVaryings(const char *structure, char *hlsl, VaryingArray &varyings)
+// Packs varyings into generic varying registers, using the algorithm from [OpenGL ES Shading Language 1.00 rev. 17] appendix A section 7 page 111
+// Returns the number of used varying registers, or -1 if unsuccesful
+int Program::packVaryings(const Varying *packing[][4])
 {
-    char *input = strstr(hlsl, structure);
-    input += strlen(structure);
-
-    while (input && *input != '}')
+    for (VaryingList::iterator varying = mFragmentShader->varyings.begin(); varying != mFragmentShader->varyings.end(); varying++)
     {
-        char varyingType[256];
-        char varyingName[256];
-        unsigned int semanticIndex;
+        int n = VariableRowCount(varying->type) * varying->size;
+        int m = VariableColumnCount(varying->type);
+        bool success = false;
 
-        int matches = sscanf(input, "    %s %s : TEXCOORD%d;", varyingType, varyingName, &semanticIndex);
-
-        if (matches == 3)
+        if (m == 2 || m == 3 || m == 4)
         {
-            ASSERT(semanticIndex <= 9);   // Single character
-
-            char *array = strstr(varyingName, "[");
-
-            if (array)
+            for (int r = 0; r <= MAX_VARYING_VECTORS - n && !success; r++)
             {
-                *array = '\0';
+                bool available = true;
+
+                for (int y = 0; y < n && available; y++)
+                {
+                    for (int x = 0; x < m && available; x++)
+                    {
+                        if (packing[r + y][x])
+                        {
+                            available = false;
+                        }
+                    }
+                }
+
+                if (available)
+                {
+                    varying->reg = r;
+                    varying->col = 0;
+
+                    for (int y = 0; y < n; y++)
+                    {
+                        for (int x = 0; x < m; x++)
+                        {
+                            packing[r + y][x] = &*varying;
+                        }
+                    }
+
+                    success = true;
+                }
             }
 
-            varyings.push_back(Varying(varyingName, input));
-        }
+            if (!success && m == 2)
+            {
+                for (int r = MAX_VARYING_VECTORS - n; r >= 0 && !success; r--)
+                {
+                    bool available = true;
 
-        input = strstr(input, ";");
-        input += 2;
+                    for (int y = 0; y < n && available; y++)
+                    {
+                        for (int x = 2; x < 4 && available; x++)
+                        {
+                            if (packing[r + y][x])
+                            {
+                                available = false;
+                            }
+                        }
+                    }
+
+                    if (available)
+                    {
+                        varying->reg = r;
+                        varying->col = 2;
+
+                        for (int y = 0; y < n; y++)
+                        {
+                            for (int x = 2; x < 4; x++)
+                            {
+                                packing[r + y][x] = &*varying;
+                            }
+                        }
+
+                        success = true;
+                    }
+                }
+            }
+        }
+        else if (m == 1)
+        {
+            int space[4] = {0};
+
+            for (int y = 0; y < MAX_VARYING_VECTORS; y++)
+            {
+                for (int x = 0; x < 4; x++)
+                {
+                    space[x] += packing[y][x] ? 0 : 1;
+                }
+            }
+
+            int column = 0;
+
+            for (int x = 0; x < 4; x++)
+            {
+                if (space[x] > n && space[x] < space[column])
+                {
+                    column = x;
+                }
+            }
+
+            if (space[column] > n)
+            {
+                for (int r = 0; r < MAX_VARYING_VECTORS; r++)
+                {
+                    if (!packing[r][column])
+                    {
+                        varying->reg = r;
+
+                        for (int y = r; y < r + n; y++)
+                        {
+                            packing[y][column] = &*varying;
+                        }
+
+                        break;
+                    }
+                }
+
+                varying->col = column;
+
+                success = true;
+            }
+        }
+        else UNREACHABLE();
+
+        if (!success)
+        {
+            appendToInfoLog("Could not pack varying %s", varying->name.c_str());
+
+            return -1;
+        }
     }
+
+    // Return the number of used registers
+    int registers = 0;
+
+    for (int r = 0; r < MAX_VARYING_VECTORS; r++)
+    {
+        if (packing[r][0] || packing[r][1] || packing[r][2] || packing[r][3])
+        {
+            registers++;
+        }
+    }
+
+    return registers;
 }
 
 bool Program::linkVaryings()
 {
-    if (!mPixelHLSL || !mVertexHLSL)
+    if (mPixelHLSL.empty() || mVertexHLSL.empty())
     {
         return false;
     }
 
-    VaryingArray vertexVaryings;
-    VaryingArray pixelVaryings;
+    const Varying *packing[MAX_VARYING_VECTORS][4] = {NULL};
+    int registers = packVaryings(packing);
 
-    parseVaryings("struct VS_OUTPUT\n{\n", mVertexHLSL, vertexVaryings);
-    parseVaryings("struct PS_INPUT\n{\n", mPixelHLSL, pixelVaryings);
-
-    for (unsigned int out = 0; out < vertexVaryings.size(); out++)
+    if (registers < 0)
     {
-        unsigned int in;
-        for (in = 0; in < pixelVaryings.size(); in++)
-        {
-            if (vertexVaryings[out].name == pixelVaryings[in].name)
-            {
-                pixelVaryings[in].link = out;
-                vertexVaryings[out].link = in;
+        return false;
+    }
 
+    if (registers == MAX_VARYING_VECTORS && mFragmentShader->mUsesFragCoord)
+    {
+        appendToInfoLog("No varying registers left to support gl_FragCoord");
+
+        return false;
+    }
+
+    for (VaryingList::iterator input = mFragmentShader->varyings.begin(); input != mFragmentShader->varyings.end(); input++)
+    {
+        bool matched = false;
+
+        for (VaryingList::iterator output = mVertexShader->varyings.begin(); output != mVertexShader->varyings.end(); output++)
+        {
+            if (output->name == input->name)
+            {
+                if (output->type != input->type || output->size != input->size)
+                {
+                    appendToInfoLog("Type of vertex varying %s does not match that of the fragment varying", output->name.c_str());
+
+                    return false;
+                }
+
+                output->reg = input->reg;
+                output->col = input->col;
+
+                matched = true;
                 break;
             }
         }
 
-        if (in != pixelVaryings.size())
+        if (!matched)
         {
-            // FIXME: Verify matching type and qualifiers
-
-            char *outputSemantic = strstr(vertexVaryings[out].declaration, " : TEXCOORD");
-            char *inputSemantic = strstr(pixelVaryings[in].declaration, " : TEXCOORD");
-            outputSemantic[11] = inputSemantic[11];
-        }
-        else
-        {
-            // Comment out the declaration and output assignment
-            vertexVaryings[out].declaration[0] = '/';
-            vertexVaryings[out].declaration[1] = '/';
-
-            char outputString[256];
-            sprintf(outputString, "    output.%s = ", vertexVaryings[out].name.c_str());
-            char *varyingOutput = strstr(mVertexHLSL, outputString);
-
-            varyingOutput[0] = '/';
-            varyingOutput[1] = '/';
-        }
-    }
-
-    // Verify that each pixel varying has been linked to a vertex varying
-    for (unsigned int in = 0; in < pixelVaryings.size(); in++)
-    {
-        if (pixelVaryings[in].link < 0)
-        {
-            appendToInfoLog("Fragment varying (%s) does not match any vertex varying", pixelVaryings[in].name.c_str());
+            appendToInfoLog("Fragment varying varying %s does not match any vertex varying", input->name.c_str());
 
             return false;
         }
     }
 
+    mVertexHLSL += "struct VS_INPUT\n"
+                   "{\n";
+
+    int semanticIndex = 0;
+    for (AttributeArray::iterator attribute = mVertexShader->mAttributes.begin(); attribute != mVertexShader->mAttributes.end(); attribute++)
+    {
+        switch (attribute->type)
+        {
+          case GL_FLOAT:      mVertexHLSL += "    float ";    break;
+          case GL_FLOAT_VEC2: mVertexHLSL += "    float2 ";   break;
+          case GL_FLOAT_VEC3: mVertexHLSL += "    float3 ";   break;
+          case GL_FLOAT_VEC4: mVertexHLSL += "    float4 ";   break;
+          case GL_FLOAT_MAT2: mVertexHLSL += "    float2x2 "; break;
+          case GL_FLOAT_MAT3: mVertexHLSL += "    float3x3 "; break;
+          case GL_FLOAT_MAT4: mVertexHLSL += "    float4x4 "; break;
+          default:  UNREACHABLE();
+        }
+
+        mVertexHLSL += decorate(attribute->name) + " : TEXCOORD" + str(semanticIndex) + ";\n";
+
+        semanticIndex += VariableRowCount(attribute->type);
+    }
+
+    mVertexHLSL += "};\n"
+                   "\n"
+                   "struct VS_OUTPUT\n"
+                   "{\n"
+                   "    float4 gl_Position : POSITION;\n";
+
+    for (int r = 0; r < registers; r++)
+    {
+        int registerSize = packing[r][3] ? 4 : (packing[r][2] ? 3 : (packing[r][1] ? 2 : 1));
+
+        mVertexHLSL += "    float" + str(registerSize) + " v" + str(r) + " : TEXCOORD" + str(r) + ";\n";
+    }
+
+    if (mFragmentShader->mUsesFragCoord)
+    {
+        mVertexHLSL += "    float4 gl_FragCoord : TEXCOORD" + str(registers) + ";\n";
+    }
+
+    mVertexHLSL += "};\n"
+                   "\n"
+                   "VS_OUTPUT main(VS_INPUT input)\n"
+                   "{\n";
+
+    for (AttributeArray::iterator attribute = mVertexShader->mAttributes.begin(); attribute != mVertexShader->mAttributes.end(); attribute++)
+    {
+        mVertexHLSL += "    " + decorate(attribute->name) + " = ";
+
+        if (VariableRowCount(attribute->type) > 1)   // Matrix
+        {
+            mVertexHLSL += "transpose";
+        }
+
+        mVertexHLSL += "(input." + decorate(attribute->name) + ");\n";
+    }
+
+    mVertexHLSL += "\n"
+                   "    gl_main();\n"
+                   "\n"
+                   "    VS_OUTPUT output;\n"
+                   "    output.gl_Position.x = gl_Position.x - dx_HalfPixelSize.x * gl_Position.w;\n"
+                   "    output.gl_Position.y = -(gl_Position.y - dx_HalfPixelSize.y * gl_Position.w);\n"
+                   "    output.gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;\n"
+                   "    output.gl_Position.w = gl_Position.w;\n";
+
+    if (mFragmentShader->mUsesFragCoord)
+    {
+        mVertexHLSL += "    output.gl_FragCoord = gl_Position;\n";
+    }
+
+    for (VaryingList::iterator varying = mVertexShader->varyings.begin(); varying != mVertexShader->varyings.end(); varying++)
+    {
+        if (varying->reg >= 0)
+        {
+            for (int i = 0; i < varying->size; i++)
+            {
+                int rows = VariableRowCount(varying->type);
+
+                for (int j = 0; j < rows; j++)
+                {
+                    int r = varying->reg + i * rows + j;
+                    mVertexHLSL += "    output.v" + str(r);
+
+                    bool sharedRegister = false;   // Register used by multiple varyings
+                    
+                    for (int x = 0; x < 4; x++)
+                    {
+                        if (packing[r][x] && packing[r][x] != packing[r][0])
+                        {
+                            sharedRegister = true;
+                            break;
+                        }
+                    }
+
+                    if(sharedRegister)
+                    {
+                        mVertexHLSL += ".";
+
+                        for (int x = 0; x < 4; x++)
+                        {
+                            if (packing[r][x] == &*varying)
+                            {
+                                switch(x)
+                                {
+                                  case 0: mVertexHLSL += "x"; break;
+                                  case 1: mVertexHLSL += "y"; break;
+                                  case 2: mVertexHLSL += "z"; break;
+                                  case 3: mVertexHLSL += "w"; break;
+                                }
+                            }
+                        }
+                    }
+
+                    mVertexHLSL += " = " + varying->name;
+                    
+                    if (varying->array)
+                    {
+                        mVertexHLSL += "[" + str(i) + "]";
+                    }
+
+                    if (rows > 1)
+                    {
+                        mVertexHLSL += "[" + str(j) + "]";
+                    }
+                    
+                    mVertexHLSL += ";\n";
+                }
+            }
+        }
+    }
+
+    mVertexHLSL += "\n"
+                   "    return output;\n"
+                   "}\n";
+
+    mPixelHLSL += "struct PS_INPUT\n"
+                  "{\n";
+    
+    for (VaryingList::iterator varying = mFragmentShader->varyings.begin(); varying != mFragmentShader->varyings.end(); varying++)
+    {
+        if (varying->reg >= 0)
+        {
+            for (int i = 0; i < varying->size; i++)
+            {
+                int rows = VariableRowCount(varying->type);
+                for (int j = 0; j < rows; j++)
+                {
+                    std::string n = str(varying->reg + i * rows + j);
+                    mPixelHLSL += "    float4 v" + n + " : TEXCOORD" + n + ";\n";
+                }
+            }
+        }
+        else UNREACHABLE();
+    }
+
+    if (mFragmentShader->mUsesFragCoord)
+    {
+        mPixelHLSL += "    float4 gl_FragCoord : TEXCOORD" + str(registers) + ";\n";
+    }
+        
+    if (mFragmentShader->mUsesFrontFacing)
+    {
+        mPixelHLSL += "    float vFace : VFACE;\n";
+    }
+
+    mPixelHLSL += "};\n"
+                  "\n"
+                  "struct PS_OUTPUT\n"
+                  "{\n"
+                  "    float4 gl_Color[1] : COLOR;\n"
+                  "};\n"
+                  "\n"
+                  "PS_OUTPUT main(PS_INPUT input)\n"
+                  "{\n";
+
+    if (mFragmentShader->mUsesFragCoord)
+    {
+        mPixelHLSL += "    float rhw = 1.0 / input.gl_FragCoord.w;\n"
+                      "    gl_FragCoord.x = (input.gl_FragCoord.x * rhw) * dx_Window.x + dx_Window.z;\n"
+                      "    gl_FragCoord.y = (input.gl_FragCoord.y * rhw) * dx_Window.y + dx_Window.w;\n"
+                      "    gl_FragCoord.z = (input.gl_FragCoord.z * rhw) * dx_Depth.x + dx_Depth.y;\n"
+                      "    gl_FragCoord.w = rhw;\n";
+    }
+
+    if (mFragmentShader->mUsesFrontFacing)
+    {
+        mPixelHLSL += "    gl_FrontFacing = dx_PointsOrLines || (dx_FrontCCW ? (input.vFace >= 0.0) : (input.vFace <= 0.0));\n";
+    }
+
+    for (VaryingList::iterator varying = mFragmentShader->varyings.begin(); varying != mFragmentShader->varyings.end(); varying++)
+    {
+        if (varying->reg >= 0)
+        {
+            for (int i = 0; i < varying->size; i++)
+            {
+                int rows = VariableRowCount(varying->type);
+                for (int j = 0; j < rows; j++)
+                {
+                    std::string n = str(varying->reg + i * rows + j);
+                    mPixelHLSL += "    " + varying->name;
+
+                    if (varying->array)
+                    {
+                        mPixelHLSL += "[" + str(i) + "]";
+                    }
+
+                    if (rows > 1)
+                    {
+                        mPixelHLSL += "[" + str(j) + "]";
+                    }
+
+                    mPixelHLSL += " = input.v" + n + ";\n";
+                }
+            }
+        }
+        else UNREACHABLE();
+    }
+
+    mPixelHLSL += "\n"
+                  "    gl_main();\n"
+                  "\n"
+                  "    PS_OUTPUT output;\n"                 
+                  "    output.gl_Color[0] = gl_Color[0];\n"
+                  "\n"
+                  "    return output;\n"
+                  "}\n";
+
+    TRACE("\n%s", mPixelHLSL.c_str());
+    TRACE("\n%s", mVertexHLSL.c_str());
+
     return true;
 }
 
@@ -1100,21 +1444,16 @@
     const char *vertexProfile = context->getVertexShaderProfile();
     const char *pixelProfile = context->getPixelShaderProfile();
 
-    const char *ps = mFragmentShader->getHLSL();
-    const char *vs = mVertexShader->getHLSL();
-
-    mPixelHLSL = new char[strlen(ps) + 1];
-    strcpy(mPixelHLSL, ps);
-    mVertexHLSL = new char[strlen(vs) + 1];
-    strcpy(mVertexHLSL, vs);
+    mPixelHLSL = mFragmentShader->getHLSL();
+    mVertexHLSL = mVertexShader->getHLSL();
 
     if (!linkVaryings())
     {
         return;
     }
 
-    ID3DXBuffer *vertexBinary = compileToBinary(mVertexHLSL, vertexProfile, &mConstantTableVS);
-    ID3DXBuffer *pixelBinary = compileToBinary(mPixelHLSL, pixelProfile, &mConstantTablePS);
+    ID3DXBuffer *vertexBinary = compileToBinary(mVertexHLSL.c_str(), vertexProfile, &mConstantTableVS);
+    ID3DXBuffer *pixelBinary = compileToBinary(mPixelHLSL.c_str(), pixelProfile, &mConstantTablePS);
 
     if (vertexBinary && pixelBinary)
     {
@@ -1162,10 +1501,9 @@
     unsigned int usedLocations = 0;
 
     // Link attributes that have a binding location
-    for (int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; attributeIndex++)
+    for (AttributeArray::iterator attribute = mVertexShader->mAttributes.begin(); attribute != mVertexShader->mAttributes.end(); attribute++)
     {
-        const Attribute &attribute = mVertexShader->getAttribute(attributeIndex);
-        int location = getAttributeBinding(attribute.name);
+        int location = getAttributeBinding(attribute->name);
 
         if (location != -1)   // Set by glBindAttribLocation
         {
@@ -1174,18 +1512,18 @@
                 // Multiple active attributes bound to the same location; not an error
             }
 
-            mLinkedAttribute[location] = attribute;
+            mLinkedAttribute[location] = *attribute;
 
-            int size = AttributeVectorCount(attribute.type);
+            int rows = VariableRowCount(attribute->type);
 
-            if (size + location > MAX_VERTEX_ATTRIBS)
+            if (rows + location > MAX_VERTEX_ATTRIBS)
             {
-                appendToInfoLog("Active attribute (%s) at location %d is too big to fit", attribute.name.c_str(), location);
+                appendToInfoLog("Active attribute (%s) at location %d is too big to fit", attribute->name.c_str(), location);
 
                 return false;
             }
 
-            for (int i = 0; i < size; i++)
+            for (int i = 0; i < rows; i++)
             {
                 usedLocations |= 1 << (location + i);
             }
@@ -1193,43 +1531,34 @@
     }
 
     // Link attributes that don't have a binding location
-    for (int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS + 1; attributeIndex++)
+    for (AttributeArray::iterator attribute = mVertexShader->mAttributes.begin(); attribute != mVertexShader->mAttributes.end(); attribute++)
     {
-        const Attribute &attribute = mVertexShader->getAttribute(attributeIndex);
-        int location = getAttributeBinding(attribute.name);
+        int location = getAttributeBinding(attribute->name);
 
-        if (!attribute.name.empty() && location == -1)   // Not set by glBindAttribLocation
+        if (location == -1)   // Not set by glBindAttribLocation
         {
-            int size = AttributeVectorCount(attribute.type);
-            int availableIndex = AllocateFirstFreeBits(&usedLocations, size, MAX_VERTEX_ATTRIBS);
+            int rows = VariableRowCount(attribute->type);
+            int availableIndex = AllocateFirstFreeBits(&usedLocations, rows, MAX_VERTEX_ATTRIBS);
 
-            if (availableIndex == -1 || availableIndex + size > MAX_VERTEX_ATTRIBS)
+            if (availableIndex == -1 || availableIndex + rows > MAX_VERTEX_ATTRIBS)
             {
-                appendToInfoLog("Too many active attributes (%s)", attribute.name.c_str());
+                appendToInfoLog("Too many active attributes (%s)", attribute->name.c_str());
 
                 return false;   // Fail to link
             }
 
-            mLinkedAttribute[availableIndex] = attribute;
+            mLinkedAttribute[availableIndex] = *attribute;
         }
     }
 
     for (int attributeIndex = 0; attributeIndex < MAX_VERTEX_ATTRIBS; )
     {
         int index = mVertexShader->getSemanticIndex(mLinkedAttribute[attributeIndex].name);
+        int rows = std::max(VariableRowCount(mLinkedAttribute[attributeIndex].type), 1);
 
-        if (index == -1)
+        for (int r = 0; r < rows; r++)
         {
-            mSemanticIndex[attributeIndex++] = -1;
-        }
-        else
-        {
-            int size = AttributeVectorCount(mVertexShader->getAttribute(index).type);
-
-            for (int i = 0; i < size; i++)
-            {
-                mSemanticIndex[attributeIndex++] = index++;
-            }
+            mSemanticIndex[attributeIndex++] = index++;
         }
     }
 
@@ -2047,11 +2376,8 @@
 
     mUniformIndex.clear();
 
-    delete[] mPixelHLSL;
-    mPixelHLSL = NULL;
-
-    delete[] mVertexHLSL;
-    mVertexHLSL = NULL;
+    mPixelHLSL.clear();
+    mVertexHLSL.clear();
 
     delete[] mInfoLog;
     mInfoLog = NULL;
diff --git a/src/libGLESv2/Program.h b/src/libGLESv2/Program.h
index 11aa2ca..27a260a 100644
--- a/src/libGLESv2/Program.h
+++ b/src/libGLESv2/Program.h
@@ -121,21 +121,7 @@
     ID3DXBuffer *compileToBinary(const char *hlsl, const char *profile, ID3DXConstantTable **constantTable);
     void unlink(bool destroy = false);
 
-    struct Varying
-    {
-        Varying(const std::string &name, char *declaration) : name(name), declaration(declaration)
-        {
-            link = -1;
-        }
-
-        int link;
-        std::string name;
-        char *declaration;
-    };
-
-    typedef std::vector<Varying> VaryingArray;
-
-    void parseVaryings(const char *structure, char *hlsl, VaryingArray &varyings);
+    int packVaryings(const Varying *packing[][4]);
     bool linkVaryings();
 
     bool linkAttributes();
@@ -172,8 +158,8 @@
     FragmentShader *mFragmentShader;
     VertexShader *mVertexShader;
 
-    char *mPixelHLSL;
-    char *mVertexHLSL;
+    std::string mPixelHLSL;
+    std::string mVertexHLSL;
 
     IDirect3DPixelShader9 *mPixelExecutable;
     IDirect3DVertexShader9 *mVertexExecutable;
diff --git a/src/libGLESv2/Shader.cpp b/src/libGLESv2/Shader.cpp
index 1f3da76..75eba4a 100644
--- a/src/libGLESv2/Shader.cpp
+++ b/src/libGLESv2/Shader.cpp
@@ -15,6 +15,7 @@
 #include "common/debug.h"
 
 #include "libGLESv2/main.h"
+#include "libGLESv2/utilities.h"
 
 namespace gl
 {
@@ -216,39 +217,41 @@
     mVertexCompiler = NULL;
 }
 
-GLenum Shader::parseAttributeType(const std::string &type)
+void Shader::parseVaryings()
 {
-    if (type == "float")
+    if (mHlsl)
     {
-        return GL_FLOAT;
-    }
-    else if (type == "float2")
-    {
-        return GL_FLOAT_VEC2;
-    }
-    else if (type == "float3")
-    {
-        return GL_FLOAT_VEC3;
-    }
-    else if (type == "float4")
-    {
-        return GL_FLOAT_VEC4;
-    }
-    else if (type == "float2x2")
-    {
-        return GL_FLOAT_MAT2;
-    }
-    else if (type == "float3x3")
-    {
-        return GL_FLOAT_MAT3;
-    }
-    else if (type == "float4x4")
-    {
-        return GL_FLOAT_MAT4;
-    }
-    else UNREACHABLE();
+        const char *input = strstr(mHlsl, "// Varyings") + 12;
 
-    return GL_NONE;
+        while(true)
+        {
+            char varyingType[256];
+            char varyingName[256];
+
+            int matches = sscanf(input, "static %s %s", varyingType, varyingName);
+
+            if (matches != 2)
+            {
+                break;
+            }
+
+            char *array = strstr(varyingName, "[");
+            int size = 1;
+
+            if (array)
+            {
+                size = atoi(array + 1);
+                *array = '\0';
+            }
+
+            varyings.push_back(Varying(parseType(varyingType), varyingName, size, array != NULL));
+
+            input = strstr(input, ";") + 2;
+        }
+
+        mUsesFragCoord = strstr(mHlsl, "GL_USES_FRAG_COORD") != NULL;
+        mUsesFrontFacing = strstr(mHlsl, "GL_USES_FRONT_FACING") != NULL;
+    }
 }
 
 void Shader::compileToHLSL(void *compiler)
@@ -294,6 +297,124 @@
     }
 }
 
+GLenum Shader::parseType(const std::string &type)
+{
+    if (type == "float")
+    {
+        return GL_FLOAT;
+    }
+    else if (type == "float2")
+    {
+        return GL_FLOAT_VEC2;
+    }
+    else if (type == "float3")
+    {
+        return GL_FLOAT_VEC3;
+    }
+    else if (type == "float4")
+    {
+        return GL_FLOAT_VEC4;
+    }
+    else if (type == "float2x2")
+    {
+        return GL_FLOAT_MAT2;
+    }
+    else if (type == "float3x3")
+    {
+        return GL_FLOAT_MAT3;
+    }
+    else if (type == "float4x4")
+    {
+        return GL_FLOAT_MAT4;
+    }
+    else UNREACHABLE();
+
+    return GL_NONE;
+}
+
+// true if varying x has a higher priority in packing than y
+bool Shader::compareVarying(const Varying &x, const Varying &y)
+{
+    if(x.type == y.type)
+    {
+        return x.size > y.size;
+    }
+
+    switch (x.type)
+    {
+      case GL_FLOAT_MAT4: return true;
+      case GL_FLOAT_MAT2:
+        switch(y.type)
+        {
+          case GL_FLOAT_MAT4: return false;
+          case GL_FLOAT_MAT2: return true;
+          case GL_FLOAT_VEC4: return true;
+          case GL_FLOAT_MAT3: return true;
+          case GL_FLOAT_VEC3: return true;
+          case GL_FLOAT_VEC2: return true;
+          case GL_FLOAT:      return true;
+          default: UNREACHABLE();
+        }
+        break;
+      case GL_FLOAT_VEC4:
+        switch(y.type)
+        {
+          case GL_FLOAT_MAT4: return false;
+          case GL_FLOAT_MAT2: return false;
+          case GL_FLOAT_VEC4: return true;
+          case GL_FLOAT_MAT3: return true;
+          case GL_FLOAT_VEC3: return true;
+          case GL_FLOAT_VEC2: return true;
+          case GL_FLOAT:      return true;
+          default: UNREACHABLE();
+        }
+        break;
+      case GL_FLOAT_MAT3:
+        switch(y.type)
+        {
+          case GL_FLOAT_MAT4: return false;
+          case GL_FLOAT_MAT2: return false;
+          case GL_FLOAT_VEC4: return false;
+          case GL_FLOAT_MAT3: return true;
+          case GL_FLOAT_VEC3: return true;
+          case GL_FLOAT_VEC2: return true;
+          case GL_FLOAT:      return true;
+          default: UNREACHABLE();
+        }
+        break;
+      case GL_FLOAT_VEC3:
+        switch(y.type)
+        {
+          case GL_FLOAT_MAT4: return false;
+          case GL_FLOAT_MAT2: return false;
+          case GL_FLOAT_VEC4: return false;
+          case GL_FLOAT_MAT3: return false;
+          case GL_FLOAT_VEC3: return true;
+          case GL_FLOAT_VEC2: return true;
+          case GL_FLOAT:      return true;
+          default: UNREACHABLE();
+        }
+        break;
+      case GL_FLOAT_VEC2:
+        switch(y.type)
+        {
+          case GL_FLOAT_MAT4: return false;
+          case GL_FLOAT_MAT2: return false;
+          case GL_FLOAT_VEC4: return false;
+          case GL_FLOAT_MAT3: return false;
+          case GL_FLOAT_VEC3: return false;
+          case GL_FLOAT_VEC2: return true;
+          case GL_FLOAT:      return true;
+          default: UNREACHABLE();
+        }
+        break;
+      case GL_FLOAT: return false;
+      default: UNREACHABLE();
+    }
+
+    return false;
+}
+
 VertexShader::VertexShader(Context *context, GLuint handle) : Shader(context, handle)
 {
 }
@@ -311,25 +432,22 @@
 {
     compileToHLSL(mVertexCompiler);
     parseAttributes();
-}
-
-const Attribute &VertexShader::getAttribute(unsigned int semanticIndex)
-{
-    ASSERT(semanticIndex < MAX_VERTEX_ATTRIBS + 1);
-
-    return mAttribute[semanticIndex];
+    parseVaryings();
 }
 
 int VertexShader::getSemanticIndex(const std::string &attributeName)
 {
     if (!attributeName.empty())
     {
-        for (int semanticIndex = 0; semanticIndex < MAX_VERTEX_ATTRIBS; semanticIndex++)
+        int semanticIndex = 0;
+        for (AttributeArray::iterator attribute = mAttributes.begin(); attribute != mAttributes.end(); attribute++)
         {
-            if (mAttribute[semanticIndex].name == attributeName)
+            if (attribute->name == attributeName)
             {
                 return semanticIndex;
             }
+
+            semanticIndex += VariableRowCount(attribute->type);
         }
     }
 
@@ -340,30 +458,23 @@
 {
     if (mHlsl)
     {
-        const char *input = strstr(mHlsl, "struct VS_INPUT");
+        const char *input = strstr(mHlsl, "// Attributes") + 14;
 
-        for (int attributeIndex = 0; *input != '}'; input++)
+        while(true)
         {
-            char attributeType[100];
-            char attributeName[100];
-            int semanticIndex;
+            char attributeType[256];
+            char attributeName[256];
 
-            int matches = sscanf(input, "%s _%s : TEXCOORD%d;", attributeType, attributeName, &semanticIndex);
+            int matches = sscanf(input, "static %s _%s", attributeType, attributeName);
 
-            if (matches == 3)
+            if (matches != 2)
             {
-                if (semanticIndex < MAX_VERTEX_ATTRIBS + 1)
-                {
-                    mAttribute[semanticIndex].type = parseAttributeType(attributeType);
-                    mAttribute[semanticIndex].name = attributeName;
-                }
-                else
-                {
-                    break;
-                }
-
-                input = strstr(input, ";");
+                break;
             }
+
+            mAttributes.push_back(Attribute(parseType(attributeType), attributeName));
+
+            input = strstr(input, ";") + 2;
         }
     }
 }
@@ -384,5 +495,7 @@
 void FragmentShader::compile()
 {
     compileToHLSL(mFragmentCompiler);
+    parseVaryings();
+    varyings.sort(compareVarying);
 }
 }
diff --git a/src/libGLESv2/Shader.h b/src/libGLESv2/Shader.h
index a02fd20..777c236 100644
--- a/src/libGLESv2/Shader.h
+++ b/src/libGLESv2/Shader.h
@@ -15,13 +15,35 @@
 #define GL_APICALL
 #include <GLES2/gl2.h>
 #include <d3dx9.h>
+#include <list>
+#include <vector>
 
 #include "libGLESv2/Context.h"
 
 namespace gl
 {
+struct Varying
+{
+    Varying(GLenum type, const std::string &name, int size, bool array)
+        : type(type), name(name), size(size), array(array), reg(-1), col(-1)
+    {
+    }
+
+    GLenum type;
+    std::string name;
+    int size;   // Number of 'type' elements
+    bool array;
+
+    int reg;    // First varying register, assigned during link
+    int col;    // First register element, assigned during link
+};
+
+typedef std::list<Varying> VaryingList;
+
 class Shader
 {
+    friend Program;
+
   public:
     Shader(Context *context, GLuint handle);
 
@@ -48,13 +70,17 @@
     void flagForDeletion();
 
     static void releaseCompiler();
-    static GLenum parseAttributeType(const std::string &type);
 
   protected:
     DISALLOW_COPY_AND_ASSIGN(Shader);
 
+    void parseVaryings();
+
     void compileToHLSL(void *compiler);
 
+    static GLenum parseType(const std::string &type);
+    static bool compareVarying(const Varying &x, const Varying &y);
+
     const GLuint mHandle;
     int mAttachCount;     // Number of program objects this shader is attached to
     bool mDeleteStatus;   // Flag to indicate that the shader can be deleted when no longer in use
@@ -63,6 +89,11 @@
     char *mHlsl;
     char *mInfoLog;
 
+    VaryingList varyings;
+
+    bool mUsesFragCoord;
+    bool mUsesFrontFacing;
+
     Context *mContext;
 
     static void *mFragmentCompiler;
@@ -71,12 +102,24 @@
 
 struct Attribute
 {
+    Attribute() : type(GL_NONE), name("")
+    {
+    }
+
+    Attribute(GLenum type, const std::string &name) : type(type), name(name)
+    {
+    }
+
     GLenum type;
     std::string name;
 };
 
+typedef std::vector<Attribute> AttributeArray;
+
 class VertexShader : public Shader
 {
+    friend Program;
+
   public:
     VertexShader(Context *context, GLuint handle);
 
@@ -84,7 +127,6 @@
 
     GLenum getType();
     void compile();
-    const Attribute &getAttribute(unsigned int attributeIndex);
     int getSemanticIndex(const std::string &attributeName);
 
   private:
@@ -92,7 +134,7 @@
 
     void parseAttributes();
 
-    Attribute mAttribute[MAX_VERTEX_ATTRIBS + 1];   // One extra to report link error
+    AttributeArray mAttributes;
 };
 
 class FragmentShader : public Shader
diff --git a/src/libGLESv2/utilities.cpp b/src/libGLESv2/utilities.cpp
index ff7f090..f584588 100644
--- a/src/libGLESv2/utilities.cpp
+++ b/src/libGLESv2/utilities.cpp
@@ -91,10 +91,12 @@
     return UniformTypeSize(UniformComponentType(type)) * UniformComponentCount(type);
 }
 
-int AttributeVectorCount(GLenum type)
+int VariableRowCount(GLenum type)
 {
     switch (type)
     {
+      case GL_NONE:
+        return 0;
       case GL_BOOL:
       case GL_FLOAT:
       case GL_INT:
@@ -108,20 +110,49 @@
       case GL_FLOAT_VEC4:
       case GL_INT_VEC4:
         return 1;
-
       case GL_FLOAT_MAT2:
         return 2;
-
       case GL_FLOAT_MAT3:
         return 3;
-
       case GL_FLOAT_MAT4:
         return 4;
-
       default:
         UNREACHABLE();
-        return 0;
     }
+
+    return 0;
+}
+
+int VariableColumnCount(GLenum type)
+{
+    switch (type)
+    {
+      case GL_NONE:
+        return 0;
+      case GL_BOOL:
+      case GL_FLOAT:
+      case GL_INT:
+        return 1;
+      case GL_BOOL_VEC2:
+      case GL_FLOAT_VEC2:
+      case GL_INT_VEC2:
+      case GL_FLOAT_MAT2:
+        return 2;
+      case GL_INT_VEC3:
+      case GL_FLOAT_VEC3:
+      case GL_BOOL_VEC3:
+      case GL_FLOAT_MAT3:
+        return 3;
+      case GL_BOOL_VEC4:
+      case GL_FLOAT_VEC4:
+      case GL_INT_VEC4:
+      case GL_FLOAT_MAT4:
+        return 4;
+      default:
+        UNREACHABLE();
+    }
+
+    return 0;
 }
 
 int AllocateFirstFreeBits(unsigned int *bits, unsigned int allocationSize, unsigned int bitsSize)
diff --git a/src/libGLESv2/utilities.h b/src/libGLESv2/utilities.h
index f7ad715..6e6c140 100644
--- a/src/libGLESv2/utilities.h
+++ b/src/libGLESv2/utilities.h
@@ -21,7 +21,8 @@
 int UniformComponentCount(GLenum type);
 GLenum UniformComponentType(GLenum type);
 size_t UniformTypeSize(GLenum type);
-int AttributeVectorCount(GLenum type);
+int VariableRowCount(GLenum type);
+int VariableColumnCount(GLenum type);
 
 int AllocateFirstFreeBits(unsigned int *bits, unsigned int allocationSize, unsigned int bitsSize);