Compiler - implement proper varying linking
TRAC #11716
Signed-off-by: Shannon Woods
Signed-off-by: Daniel Koch

Author:    Nicolas Capens

git-svn-id: https://angleproject.googlecode.com/svn/trunk@97 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/compiler/OutputHLSL.cpp b/src/compiler/OutputHLSL.cpp
index 785e18d..4a8ad7f 100644
--- a/src/compiler/OutputHLSL.cpp
+++ b/src/compiler/OutputHLSL.cpp
@@ -50,6 +50,7 @@
                     sprintf(semantic, " : TEXCOORD%d", semanticIndex);
                     semanticIndex += type.isArray() ? type.getArraySize() : 1;
 
+                    // Program linking depends on this exact format
                     varyingInput += "    " + typeString(type) + " " + name + arrayString(type) + semantic + ";\n";
                     varyingGlobals += "static " + typeString(type) + " " + name + arrayString(type) + " = " + initializer(type) + ";\n";
                 }
@@ -74,7 +75,7 @@
                "struct PS_INPUT\n"   // FIXME: Prevent name clashes
                "{\n";
         out <<      varyingInput;
-        out << "    float4 gl_FragCoord : TEXCOORD" << HLSL_FRAG_COORD_SEMANTIC << ";\n";
+        out << "    float4 gl_FragCoord : TEXCOORD" << semanticIndex << ";\n";
         out << "    float __vFace : VFACE;\n"
                "};\n"
                "\n";
@@ -147,6 +148,7 @@
                 }
                 else if (qualifier == EvqVaryingOut || qualifier == EvqInvariantVaryingOut)
                 {
+                    // Program linking depends on this exact format
                     varyingOutput += "    " + typeString(type) + " " + name + arrayString(type) + " : TEXCOORD0;\n";   // Actual semantic index assigned during link
                     varyingGlobals += "static " + typeString(type) + " " + name + arrayString(type) + " = " + initializer(type) + ";\n";
                 }
@@ -177,7 +179,7 @@
                "{\n"
                "    float4 gl_Position : POSITION;\n"
                "    float gl_PointSize : PSIZE;\n"
-               "    float4 gl_FragCoord : TEXCOORD" << HLSL_FRAG_COORD_SEMANTIC << ";\n";
+               "    float4 gl_FragCoord : TEXCOORD0;\n";   // Actual semantic index assigned during link
         out <<      varyingOutput;
         out << "};\n"
                "\n"
@@ -501,6 +503,7 @@
 
                 if (qualifier == EvqVaryingOut || qualifier == EvqInvariantVaryingOut)
                 {
+                    // Program linking depends on this exact format
                     out << "    output." + name + " = " + name + ";\n";   // FIXME: Prevent name clashes
                 }
             }
diff --git a/src/compiler/OutputHLSL.h b/src/compiler/OutputHLSL.h
index 667d9e8..2be9a86 100644
--- a/src/compiler/OutputHLSL.h
+++ b/src/compiler/OutputHLSL.h
@@ -12,11 +12,6 @@
 
 namespace sh
 {
-enum
-{
-    HLSL_FRAG_COORD_SEMANTIC = 15   // Semantic index assigned to the gl_FragCoord varying
-};
-
 class OutputHLSL : public TIntermTraverser
 {
   public:
diff --git a/src/libGLESv2/Program.cpp b/src/libGLESv2/Program.cpp
index 3030ac1..252c5a4 100644
--- a/src/libGLESv2/Program.cpp
+++ b/src/libGLESv2/Program.cpp
@@ -41,6 +41,8 @@
         mAttributeName[index] = NULL;
     }
 
+    mPixelHLSL = NULL;
+    mVertexHLSL = NULL;
     mInfoLog = NULL;
 
     unlink();
@@ -398,6 +400,92 @@
     return NULL;
 }
 
+void Program::parseVaryings(const char *structure, char *hlsl, VaryingArray &varyings)
+{
+    char *input = strstr(hlsl, structure);
+    input += strlen(structure);
+
+    while (input && *input != '}')
+    {
+        char varyingType[256];
+        char varyingName[256];
+        unsigned int semanticIndex;
+        int matches = sscanf(input, "    %s %s : TEXCOORD%d;", varyingType, varyingName, &semanticIndex);
+
+        if (matches == 3)
+        {
+            ASSERT(semanticIndex <= 9);   // Single character
+
+            varyings.push_back(Varying(varyingName, input));
+        }
+
+        input = strstr(input, ";");
+        input += 2;
+    }
+}
+
+bool Program::linkVaryings()
+{
+    if (!mPixelHLSL || !mVertexHLSL)
+    {
+        return false;
+    }
+
+    VaryingArray vertexVaryings;
+    VaryingArray pixelVaryings;
+
+    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++)
+    {
+        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;
+
+                break;
+            }
+        }
+
+        if (in != pixelVaryings.size())
+        {
+            // 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)
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
 // Links the HLSL code of the vertex and pixel shader by matching up their varyings,
 // compiling them into binaries, determining the attribute mappings, and collecting
 // a list of uniforms
@@ -410,9 +498,6 @@
 
     unlink();
 
-    delete[] mInfoLog;
-    mInfoLog = NULL;
-
     if (!mFragmentShader || !mFragmentShader->isCompiled())
     {
         return;
@@ -427,10 +512,21 @@
     const char *vertexProfile = context->getVertexShaderProfile();
     const char *pixelProfile = context->getPixelShaderProfile();
 
-    const char *pixelHLSL = mFragmentShader->linkHLSL();
-    const char *vertexHLSL = mVertexShader->linkHLSL(pixelHLSL);
-    ID3DXBuffer *vertexBinary = compileToBinary(vertexHLSL, vertexProfile, &mConstantTableVS);
-    ID3DXBuffer *pixelBinary = compileToBinary(pixelHLSL, pixelProfile, &mConstantTablePS);
+    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);
+
+    if (!linkVaryings())
+    {
+        return;
+    }
+
+    ID3DXBuffer *vertexBinary = compileToBinary(mVertexHLSL, vertexProfile, &mConstantTableVS);
+    ID3DXBuffer *pixelBinary = compileToBinary(mPixelHLSL, pixelProfile, &mConstantTablePS);
 
     if (vertexBinary && pixelBinary)
     {
@@ -968,9 +1064,6 @@
             delete[] mAttributeName[index];
             mAttributeName[index] = NULL;
         }
-
-        delete[] mInfoLog;
-        mInfoLog = NULL;
     }
 
     if (mPixelExecutable)
@@ -1013,6 +1106,15 @@
         mUniforms.pop_back();
     }
 
+    delete[] mPixelHLSL;
+    mPixelHLSL = NULL;
+
+    delete[] mVertexHLSL;
+    mVertexHLSL = NULL;
+
+    delete[] mInfoLog;
+    mInfoLog = NULL;
+
     mLinked = false;
 }
 
diff --git a/src/libGLESv2/Program.h b/src/libGLESv2/Program.h
index 9502ca0..bf60515 100644
--- a/src/libGLESv2/Program.h
+++ b/src/libGLESv2/Program.h
@@ -83,7 +83,25 @@
     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);
+    bool linkVaryings();
+
     bool linkAttributes();
+
     bool linkUniforms(ID3DXConstantTable *constantTable);
     bool defineUniform(const D3DXHANDLE &constantHandle, const D3DXCONSTANT_DESC &constantDescription, std::string name = "");
     bool defineUniform(const D3DXCONSTANT_DESC &constantDescription, std::string &name);
@@ -102,6 +120,9 @@
     FragmentShader *mFragmentShader;
     VertexShader *mVertexShader;
 
+    char *mPixelHLSL;
+    char *mVertexHLSL;
+
     IDirect3DPixelShader9 *mPixelExecutable;
     IDirect3DVertexShader9 *mVertexExecutable;
     ID3DXConstantTable *mConstantTablePS;
diff --git a/src/libGLESv2/Shader.cpp b/src/libGLESv2/Shader.cpp
index 90ce10f..7144a8f 100644
--- a/src/libGLESv2/Shader.cpp
+++ b/src/libGLESv2/Shader.cpp
@@ -171,7 +171,7 @@
     return mHlsl != NULL;
 }
 
-const char *Shader::linkHLSL()
+const char *Shader::getHLSL()
 {
     return mHlsl;
 }
@@ -306,45 +306,6 @@
     parseAttributes();
 }
 
-const char *VertexShader::linkHLSL(const char *pixelHLSL)
-{
-    if (mHlsl && pixelHLSL)
-    {
-        const char *input = strstr(pixelHLSL, "struct PS_INPUT");
-        char *output = strstr(mHlsl, "struct VS_OUTPUT");
-
-        while (*input != '}' && output)
-        {
-            char varyingName[100];
-            unsigned int semanticIndex;
-            int matches = sscanf(input, "%s : TEXCOORD%d;", varyingName, &semanticIndex);
-
-            if (matches == 2 && semanticIndex != sh::HLSL_FRAG_COORD_SEMANTIC)
-            {
-                ASSERT(semanticIndex < MAX_VARYING_VECTORS);
-                char *varying = strstr(output, varyingName);
-
-                if (varying)
-                {
-                    ASSERT(semanticIndex <= 9);   // Single character
-                    varying = strstr(varying, " : TEXCOORD0;");
-                    varying[11] = '0' + semanticIndex;
-                }
-                else
-                {
-                    return NULL;
-                }
-
-                input = strstr(input, ";");
-            }
-
-            input++;
-        }
-    }
-
-    return mHlsl;
-}
-
 const char *VertexShader::getAttributeName(unsigned int attributeIndex)
 {
     if (attributeIndex < MAX_VERTEX_ATTRIBS)
diff --git a/src/libGLESv2/Shader.h b/src/libGLESv2/Shader.h
index 6bc9cf5..5f5a327 100644
--- a/src/libGLESv2/Shader.h
+++ b/src/libGLESv2/Shader.h
@@ -39,7 +39,7 @@
 
     virtual void compile() = 0;
     bool isCompiled();
-    const char *linkHLSL();
+    const char *getHLSL();
 
     void attach();
     void detach();
@@ -90,7 +90,6 @@
 
     GLenum getType();
     void compile();
-    const char *linkHLSL(const char *pixelHLSL);
     const char *getAttributeName(unsigned int attributeIndex);
     bool isActiveAttribute(const char *attributeName);
     int getInputMapping(const char *attributeName);