Implement ES 2,3 parts of EXT_blend_func_extended for shader translation

Exposes gl_SecondaryFragColor, glSecondaryFragData[] and
gl_MaxDualSourceDrawBuffers to GLES SL 1.0.

Relaxes rules for undefined output locations for GLES SL 3.0
and exposes gl_MaxDualSourceDrawBuffers.

If the output GL context is GL ES 2.0 or 3.0:
The emulation layer is expected to turn on EXT_blend_func_extended
if the output GL context supports it.

If the output GL context is GL:
The emulation layer is expected to turn on EXT_blend_func_extended
if the output GL context supports ARB_blend_func_extended or if GL
context is 3.2 or later.

If the source shader spec is GLES SL 2.0: The emulation layer is
expected to inspect the shader compilation output variables upon
linking. If output target is GL SL, the emulation layer should bind
color location 0, index 1 to "angle_SecondaryFragColor" if variable
"gl_SecondaryFragColorEXT" is used. Alternatively, emulation layer
should bind "angle_SecondaryFragData" to locations 0,1,2,3,..., all
color index 1, if "gl_SecondaryFragData" array is used.
(The caller can not bind the locations or specify output variables.)

If the source shader spec is GLES SL 3.0:
The emulation layer is expected to do location auto-resolve of the
the output variables that have undefined output locations that have
not been bound by the caller.
(The caller can not use gl_ built-ins, so nothing to do with those.)

BUG=angleproject:1085
TEST=angle_unittest

Change-Id: I5cafe205b0c29478b0dcd24aa89a7b0000f5d046
Reviewed-on: https://chromium-review.googlesource.com/287580
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Tested-by: Kimmo Kinnunen <kkinnunen@nvidia.com>
diff --git a/src/compiler/translator/ValidateOutputs.cpp b/src/compiler/translator/ValidateOutputs.cpp
index 6976309..cd37aea 100644
--- a/src/compiler/translator/ValidateOutputs.cpp
+++ b/src/compiler/translator/ValidateOutputs.cpp
@@ -9,12 +9,23 @@
 #include "compiler/translator/InitializeParseContext.h"
 #include "compiler/translator/ParseContext.h"
 
-ValidateOutputs::ValidateOutputs(TInfoSinkBase& sink, int maxDrawBuffers)
+namespace
+{
+void error(int *errorCount, TInfoSinkBase &sink, const TIntermSymbol &symbol, const char *reason)
+{
+    sink.prefix(EPrefixError);
+    sink.location(symbol.getLine());
+    sink << "'" << symbol.getSymbol() << "' : " << reason << "\n";
+    (*errorCount)++;
+}
+
+}  // namespace
+
+ValidateOutputs::ValidateOutputs(const TExtensionBehavior &extBehavior, int maxDrawBuffers)
     : TIntermTraverser(true, false, false),
-      mSink(sink),
       mMaxDrawBuffers(maxDrawBuffers),
-      mNumErrors(0),
-      mHasUnspecifiedOutputLocation(false)
+      mAllowUnspecifiedOutputLocationResolution(
+          IsExtensionEnabled(extBehavior, "GL_EXT_blend_func_extended"))
 {
 }
 
@@ -30,51 +41,68 @@
 
     if (qualifier == EvqFragmentOut)
     {
-        const TType &type = symbol->getType();
-        const int location = type.getLayoutQualifier().location;
-        const bool isUnspecifiedOutputLocation = location == -1;
-
-        if (mHasUnspecifiedOutputLocation || (isUnspecifiedOutputLocation && !mOutputMap.empty()))
+        if (symbol->getType().getLayoutQualifier().location == -1)
         {
-            error(symbol->getLine(), "must explicitly specify all locations when using multiple fragment outputs", name.c_str());
-        }
-        else if (isUnspecifiedOutputLocation)
-        {
-            mHasUnspecifiedOutputLocation = true;
+            mUnspecifiedLocationOutputs.push_back(symbol);
         }
         else
         {
-            OutputMap::iterator mapEntry = mOutputMap.find(location);
-            if (mapEntry == mOutputMap.end())
-            {
-                const int elementCount = type.isArray() ? type.getArraySize() : 1;
-                if (location + elementCount > mMaxDrawBuffers)
-                {
-                    error(symbol->getLine(), "output location must be < MAX_DRAW_BUFFERS", name.c_str());
-                }
-
-                for (int elementIndex = 0; elementIndex < elementCount; elementIndex++)
-                {
-                    const int offsetLocation = location + elementIndex;
-                    mOutputMap[offsetLocation] = symbol;
-                }
-            }
-            else
-            {
-                std::stringstream strstr;
-                strstr << "conflicting output locations with previously defined output '"
-                       << mapEntry->second->getSymbol() << "'";
-
-                error(symbol->getLine(), strstr.str().c_str(), name.c_str());
-            }
+            mOutputs.push_back(symbol);
         }
     }
 }
 
-void ValidateOutputs::error(TSourceLoc loc, const char *reason, const char* token)
+int ValidateOutputs::validateAndCountErrors(TInfoSinkBase &sink) const
 {
-    mSink.prefix(EPrefixError);
-    mSink.location(loc);
-    mSink << "'" << token << "' : " << reason << "\n";
-    mNumErrors++;
+    OutputVector validOutputs(mMaxDrawBuffers);
+    int errorCount = 0;
+
+    for (const auto &symbol : mOutputs)
+    {
+        const TType &type         = symbol->getType();
+        const size_t elementCount = static_cast<size_t>(type.isArray() ? type.getArraySize() : 1);
+        const size_t location     = static_cast<size_t>(type.getLayoutQualifier().location);
+
+        ASSERT(type.getLayoutQualifier().location != -1);
+
+        if (location + elementCount <= validOutputs.size())
+        {
+            for (size_t elementIndex = 0; elementIndex < elementCount; elementIndex++)
+            {
+                const size_t offsetLocation = location + elementIndex;
+                if (validOutputs[offsetLocation])
+                {
+                    std::stringstream strstr;
+                    strstr << "conflicting output locations with previously defined output '"
+                           << validOutputs[offsetLocation]->getSymbol() << "'";
+                    error(&errorCount, sink, *symbol, strstr.str().c_str());
+                }
+                else
+                {
+                    validOutputs[offsetLocation] = symbol;
+                }
+            }
+        }
+        else
+        {
+            if (elementCount > 0)
+            {
+                error(&errorCount, sink, *symbol,
+                      elementCount > 1 ? "output array locations would exceed MAX_DRAW_BUFFERS"
+                                       : "output location must be < MAX_DRAW_BUFFERS");
+            }
+        }
+    }
+
+    if (!mAllowUnspecifiedOutputLocationResolution &&
+        ((!mOutputs.empty() && !mUnspecifiedLocationOutputs.empty()) ||
+         mUnspecifiedLocationOutputs.size() > 1))
+    {
+        for (const auto &symbol : mUnspecifiedLocationOutputs)
+        {
+            error(&errorCount, sink, *symbol,
+                  "must explicitly specify all locations when using multiple fragment outputs");
+        }
+    }
+    return errorCount;
 }