Make output variables part of Program's shared data.

Also initialize this structure within Program instead of DynamicHLSL.
This should have benefits for other back-ends. Also these variables
weren't being serialized and de-serialized with the program binary,
which could mess up WebGL apps that use MRT.

BUG=angleproject:1123

Change-Id: Ic0dd4840f26441a1bee8527dfa178b24daf82f8a
Reviewed-on: https://chromium-review.googlesource.com/294571
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Tested-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 44e2e67..ff67dc1 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -11,6 +11,7 @@
 
 #include <algorithm>
 
+#include "common/BitSetIterator.h"
 #include "common/debug.h"
 #include "common/platform.h"
 #include "common/utilities.h"
@@ -309,8 +310,10 @@
         return Error(GL_NO_ERROR);
     }
 
-    rx::LinkResult result = mProgram->link(data, mInfoLog, mData.mAttachedFragmentShader,
-                                           mData.mAttachedVertexShader, &mOutputVariables);
+    linkOutputVariables();
+
+    rx::LinkResult result =
+        mProgram->link(data, mInfoLog, mData.mAttachedFragmentShader, mData.mAttachedVertexShader);
 
     if (result.error.isError() || !result.linkSuccess)
     {
@@ -357,6 +360,7 @@
     mData.mAttributes.clear();
     mData.mActiveAttribLocationsMask.reset();
     mData.mTransformFeedbackVaryingVars.clear();
+    mData.mOutputVariables.clear();
 
     mProgram->reset();
 
@@ -424,6 +428,17 @@
 
     stream.readInt(&mData.mTransformFeedbackBufferMode);
 
+    unsigned int outputVarCount = stream.readInt<unsigned int>();
+    for (unsigned int outputIndex = 0; outputIndex < outputVarCount; ++outputIndex)
+    {
+        int locationIndex = stream.readInt<int>();
+        VariableLocation locationData;
+        locationData.element                  = stream.readInt<unsigned int>();
+        locationData.index                    = stream.readInt<unsigned int>();
+        locationData.name                     = stream.readString();
+        mData.mOutputVariables[locationIndex] = locationData;
+    }
+
     rx::LinkResult result = mProgram->load(mInfoLog, &stream);
     if (result.error.isError() || !result.linkSuccess)
     {
@@ -464,6 +479,15 @@
 
     stream.writeInt(mData.mTransformFeedbackBufferMode);
 
+    stream.writeInt(mData.mOutputVariables.size());
+    for (const auto &outputPair : mData.mOutputVariables)
+    {
+        stream.writeInt(outputPair.first);
+        stream.writeInt(outputPair.second.element);
+        stream.writeInt(outputPair.second.index);
+        stream.writeString(outputPair.second.name);
+    }
+
     gl::Error error = mProgram->save(&stream);
     if (error.isError())
     {
@@ -692,7 +716,7 @@
 {
     std::string baseName(name);
     unsigned int arrayIndex = ParseAndStripArrayIndex(&baseName);
-    for (auto outputPair : mOutputVariables)
+    for (auto outputPair : mData.mOutputVariables)
     {
         const VariableLocation &outputVariable = outputPair.second;
         if (outputVariable.name == baseName && (arrayIndex == GL_INVALID_INDEX || arrayIndex == outputVariable.element))
@@ -1663,4 +1687,43 @@
 
     return varyings;
 }
+
+void Program::linkOutputVariables()
+{
+    const Shader *fragmentShader = mData.mAttachedFragmentShader;
+    ASSERT(fragmentShader != nullptr);
+
+    // Skip this step for GLES2 shaders.
+    if (fragmentShader->getShaderVersion() == 100)
+        return;
+
+    const std::vector<sh::Attribute> &shaderOutputVars = fragmentShader->getActiveOutputVariables();
+
+    // TODO(jmadill): any caps validation here?
+
+    for (unsigned int outputVariableIndex = 0; outputVariableIndex < shaderOutputVars.size();
+         outputVariableIndex++)
+    {
+        const sh::Attribute &outputVariable = shaderOutputVars[outputVariableIndex];
+
+        // Don't store outputs for gl_FragDepth, gl_FragColor, etc.
+        if (outputVariable.isBuiltIn())
+            continue;
+
+        // Since multiple output locations must be specified, use 0 for non-specified locations.
+        int baseLocation = (outputVariable.location == -1 ? 0 : outputVariable.location);
+
+        ASSERT(outputVariable.staticUse);
+
+        for (unsigned int elementIndex = 0; elementIndex < outputVariable.elementCount();
+             elementIndex++)
+        {
+            const int location = baseLocation + elementIndex;
+            ASSERT(mData.mOutputVariables.count(location) == 0);
+            unsigned int element = outputVariable.isArray() ? elementIndex : GL_INVALID_INDEX;
+            mData.mOutputVariables[location] =
+                VariableLocation(outputVariable.name, element, outputVariableIndex);
+        }
+    }
+}
 }