Add GLSL/ESSL validator/translator support for GL_EXT_shader_framebuffer_fetch.

BUG=angle:834

Change-Id: I2d4e25909a8e1266b9bb7f1d4421324143157c8a
Reviewed-on: https://chromium-review.googlesource.com/231032
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Tested-by: Erik Dahlström <ed@opera.com>
diff --git a/src/compiler/translator/BaseTypes.h b/src/compiler/translator/BaseTypes.h
index 71221c9..87bf4ab 100644
--- a/src/compiler/translator/BaseTypes.h
+++ b/src/compiler/translator/BaseTypes.h
@@ -307,6 +307,10 @@
     EvqFragData,
     EvqFragDepth,
 
+    // built-ins written by the shader_framebuffer_fetch extension(s)
+    EvqLastFragColor,
+    EvqLastFragData,
+
     // GLSL ES 3.0 vertex output and fragment input
     EvqSmooth,        // Incomplete qualifier, smooth is the default
     EvqFlat,          // Incomplete qualifier
@@ -396,6 +400,8 @@
     case EvqSmoothIn:       return "smooth in";      break;
     case EvqCentroidIn:     return "centroid in";    break;
     case EvqFlatIn:         return "flat in";        break;
+    case EvqLastFragColor:  return "LastFragColor";  break;
+    case EvqLastFragData:   return "LastFragData";   break;
     default:                return "unknown qualifier";
     }
 }
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index ab1c9dd..9b273e8 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -392,6 +392,9 @@
               << ":MaxCallStackDepth:" << compileResources.MaxCallStackDepth
               << ":EXT_frag_depth:" << compileResources.EXT_frag_depth
               << ":EXT_shader_texture_lod:" << compileResources.EXT_shader_texture_lod
+              << ":EXT_shader_framebuffer_fetch:" << compileResources.EXT_shader_framebuffer_fetch
+              << ":NV_shader_framebuffer_fetch:" << compileResources.NV_shader_framebuffer_fetch
+              << ":ARM_shader_framebuffer_fetch:" << compileResources.ARM_shader_framebuffer_fetch
               << ":MaxVertexOutputVectors:" << compileResources.MaxVertexOutputVectors
               << ":MaxFragmentInputVectors:" << compileResources.MaxFragmentInputVectors
               << ":MinProgramTexelOffset:" << compileResources.MinProgramTexelOffset
diff --git a/src/compiler/translator/Initialize.cpp b/src/compiler/translator/Initialize.cpp
index 10b21e6..043a251 100644
--- a/src/compiler/translator/Initialize.cpp
+++ b/src/compiler/translator/Initialize.cpp
@@ -620,6 +620,19 @@
                 symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragDepthEXT"), TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium, EvqFragDepth, 1)));
                 symbolTable.relateToExtension(ESSL1_BUILTINS, "gl_FragDepthEXT", "GL_EXT_frag_depth");
             }
+            if (resources.EXT_shader_framebuffer_fetch)
+            {
+                symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_LastFragData[gl_MaxDrawBuffers]"), TType(EbtFloat, EbpMedium, EvqLastFragData,   4)));
+            }
+            else if (resources.NV_shader_framebuffer_fetch)
+            {
+                symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_LastFragColor"), TType(EbtFloat, EbpMedium, EvqLastFragColor,   4)));
+                symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_LastFragData[gl_MaxDrawBuffers]"), TType(EbtFloat, EbpMedium, EvqLastFragData,   4)));
+            }
+            else if (resources.ARM_shader_framebuffer_fetch)
+            {
+                symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_LastFragColorARM"), TType(EbtFloat, EbpMedium, EvqLastFragColor,   4)));
+            }
         } else {
             symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("css_MixColor"), TType(EbtFloat, EbpMedium, EvqGlobal,      4)));
             symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("css_ColorMatrix"), TType(EbtFloat, EbpMedium, EvqGlobal,      4, 4)));
@@ -714,6 +727,14 @@
             symbolTable.relateToExtension(ESSL1_BUILTINS, "texture2DProjLodEXT", "GL_EXT_shader_texture_lod");
             symbolTable.relateToExtension(ESSL1_BUILTINS, "textureCubeLodEXT", "GL_EXT_shader_texture_lod");
         }
+        if (resources.NV_shader_framebuffer_fetch)
+        {
+            symbolTable.relateToExtension(ESSL1_BUILTINS, "gl_LastFragColor", "GL_NV_shader_framebuffer_fetch");
+        }
+        else if (resources.ARM_shader_framebuffer_fetch)
+        {
+            symbolTable.relateToExtension(ESSL1_BUILTINS, "gl_LastFragColorARM", "GL_ARM_shader_framebuffer_fetch");
+        }
         break;
     default: break;
     }
@@ -737,6 +758,22 @@
             TType fragData(EbtFloat, EbpMedium, EvqFragData, 4, 1, true);
             fragData.setArraySize(resources.MaxDrawBuffers);
             symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragData"), fragData));
+
+            if (resources.EXT_shader_framebuffer_fetch || resources.NV_shader_framebuffer_fetch) {
+                // Set up gl_LastFragData.  The array size.
+                TType lastFragData(EbtFloat, EbpMedium, EvqLastFragData, 4, 1, true);
+                lastFragData.setArraySize(resources.MaxDrawBuffers);
+                symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_LastFragData"), lastFragData));
+
+                if (resources.EXT_shader_framebuffer_fetch)
+                {
+                    symbolTable.relateToExtension(ESSL1_BUILTINS, "gl_LastFragData", "GL_EXT_shader_framebuffer_fetch");
+                }
+                else if (resources.NV_shader_framebuffer_fetch)
+                {
+                    symbolTable.relateToExtension(ESSL1_BUILTINS, "gl_LastFragData", "GL_NV_shader_framebuffer_fetch");
+                }
+            }
         }
         break;
     default: break;
@@ -758,4 +795,10 @@
         extBehavior["GL_EXT_frag_depth"] = EBhUndefined;
     if (resources.EXT_shader_texture_lod)
         extBehavior["GL_EXT_shader_texture_lod"] = EBhUndefined;
+    if (resources.EXT_shader_framebuffer_fetch)
+        extBehavior["GL_EXT_shader_framebuffer_fetch"] = EBhUndefined;
+    if (resources.NV_shader_framebuffer_fetch)
+        extBehavior["GL_NV_shader_framebuffer_fetch"] = EBhUndefined;
+    if (resources.ARM_shader_framebuffer_fetch)
+        extBehavior["GL_ARM_shader_framebuffer_fetch"] = EBhUndefined;
 }
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index 8d21467..89ab9a5 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -796,9 +796,24 @@
     bool sameScope = false;
     TSymbol* symbol = symbolTable.find(identifier, 0, &builtIn, &sameScope);
     if (symbol == 0 || !sameScope) {
-        if (reservedErrorCheck(line, identifier))
-            return true;
-        
+        bool needsReservedErrorCheck = true;
+
+        // gl_LastFragData may be redeclared with a new precision qualifier
+        if (identifier.compare(0, 15, "gl_LastFragData") == 0) {
+            if (type.arraySize == static_cast<const TVariable*>(symbolTable.findBuiltIn("gl_MaxDrawBuffers", shaderVersion))->getConstPointer()->getIConst()) {
+                if (TSymbol* builtInSymbol = symbolTable.findBuiltIn(identifier, shaderVersion)) {
+                    needsReservedErrorCheck = extensionErrorCheck(line, builtInSymbol->getExtension());
+                }
+            } else {
+                error(line, "redeclaration of array with size != gl_MaxDrawBuffers", identifier.c_str());
+                return true;
+            }
+        }
+
+        if (needsReservedErrorCheck)
+            if (reservedErrorCheck(line, identifier))
+                return true;
+
         variable = new TVariable(&identifier, TType(type));
 
         if (type.arraySize)
diff --git a/src/compiler/translator/ShaderLang.cpp b/src/compiler/translator/ShaderLang.cpp
index ebbd68a..03a9aef 100644
--- a/src/compiler/translator/ShaderLang.cpp
+++ b/src/compiler/translator/ShaderLang.cpp
@@ -154,6 +154,9 @@
     resources->EXT_frag_depth = 0;
     resources->EXT_shader_texture_lod = 0;
     resources->WEBGL_debug_shader_precision = 0;
+    resources->EXT_shader_framebuffer_fetch = 0;
+    resources->NV_shader_framebuffer_fetch = 0;
+    resources->ARM_shader_framebuffer_fetch = 0;
 
     resources->NV_draw_buffers = 0;
 
diff --git a/src/compiler/translator/TranslatorESSL.cpp b/src/compiler/translator/TranslatorESSL.cpp
index e75eb90..3b50f7d 100644
--- a/src/compiler/translator/TranslatorESSL.cpp
+++ b/src/compiler/translator/TranslatorESSL.cpp
@@ -50,7 +50,10 @@
     for (TExtensionBehavior::const_iterator iter = extensionBehavior.begin();
          iter != extensionBehavior.end(); ++iter) {
         if (iter->second != EBhUndefined) {
-            if (getResources().NV_draw_buffers && iter->first == "GL_EXT_draw_buffers") {
+            if (getResources().NV_shader_framebuffer_fetch && iter->first == "GL_EXT_shader_framebuffer_fetch") {
+                sink << "#extension GL_NV_shader_framebuffer_fetch : "
+                     << getBehaviorString(iter->second) << "\n";
+            } else if (getResources().NV_draw_buffers && iter->first == "GL_EXT_draw_buffers") {
                 sink << "#extension GL_NV_draw_buffers : "
                      << getBehaviorString(iter->second) << "\n";
             } else {
diff --git a/src/compiler/translator/VariableInfo.cpp b/src/compiler/translator/VariableInfo.cpp
index d8e1378..67b7c8c 100644
--- a/src/compiler/translator/VariableInfo.cpp
+++ b/src/compiler/translator/VariableInfo.cpp
@@ -144,6 +144,7 @@
       mFragCoordAdded(false),
       mPositionAdded(false),
       mPointSizeAdded(false),
+      mLastFragDataAdded(false),
       mHashFunction(hashFunction),
       mSymbolTable(symbolTable)
 {
@@ -281,6 +282,22 @@
                 mPointSizeAdded = true;
             }
             return;
+          case EvqLastFragData:
+            if (!mLastFragDataAdded)
+            {
+                Varying info;
+                const char kName[] = "gl_LastFragData";
+                info.name = kName;
+                info.mappedName = kName;
+                info.type = GL_FLOAT_VEC4;
+                info.arraySize = static_cast<const TVariable*>(mSymbolTable.findBuiltIn("gl_MaxDrawBuffers", 100))->getConstPointer()->getIConst();
+                info.precision = GL_MEDIUM_FLOAT;  // Defined by spec.
+                info.staticUse = true;
+                info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
+                mVaryings->push_back(info);
+                mLastFragDataAdded = true;
+            }
+            return;
           default:
             break;
         }
diff --git a/src/compiler/translator/VariableInfo.h b/src/compiler/translator/VariableInfo.h
index 30c2a61..b0e0455 100644
--- a/src/compiler/translator/VariableInfo.h
+++ b/src/compiler/translator/VariableInfo.h
@@ -53,6 +53,7 @@
 
     bool mPositionAdded;
     bool mPointSizeAdded;
+    bool mLastFragDataAdded;
 
     ShHashFunction64 mHashFunction;