GLSL backend now emits "#version 120" to legally access invariant keyword and gl_PointCoord built-in variable.
BUG=35
Review URL: http://codereview.appspot.com/2341043

git-svn-id: https://angleproject.googlecode.com/svn/trunk@448 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/compiler/ShHandle.h b/src/compiler/ShHandle.h
index e23f1a3..385259e 100644
--- a/src/compiler/ShHandle.h
+++ b/src/compiler/ShHandle.h
@@ -54,6 +54,8 @@
     const TVariableInfoList& getUniforms() const { return uniforms; }
 
 protected:
+    ShShaderType getShaderType() const { return shaderType; }
+    ShShaderSpec getShaderSpec() const { return shaderSpec; }
     // Initialize symbol-table with built-in symbols.
     bool InitBuiltInSymbolTable(const ShBuiltInResources& resources);
     // Clears the results from the previous compilation.
diff --git a/src/compiler/TranslatorGLSL.cpp b/src/compiler/TranslatorGLSL.cpp
index 2453334..7a63ae1 100644
--- a/src/compiler/TranslatorGLSL.cpp
+++ b/src/compiler/TranslatorGLSL.cpp
@@ -7,12 +7,31 @@
 #include "compiler/TranslatorGLSL.h"
 
 #include "compiler/OutputGLSL.h"
+#include "compiler/VersionGLSL.h"
+
+static void writeVersion(ShShaderType type, TIntermNode* root,
+                         TInfoSinkBase& sink) {
+    TVersionGLSL versionGLSL(type);
+    root->traverse(&versionGLSL);
+    int version = versionGLSL.getVersion();
+    // We need to write version directive only if it is greater than 110.
+    // If there is no version directive in the shader, 110 is implied.
+    if (version > 110) {
+        sink << "#version " << version << "\n";
+    }
+}
 
 TranslatorGLSL::TranslatorGLSL(ShShaderType type, ShShaderSpec spec)
     : TCompiler(type, spec) {
 }
 
 void TranslatorGLSL::translate(TIntermNode* root) {
-    TOutputGLSL outputGLSL(getInfoSink().obj);
+    TInfoSinkBase& sink = getInfoSink().obj;
+
+    // Write GLSL version.
+    writeVersion(getShaderType(), root, sink);
+
+    // Write translated shader.
+    TOutputGLSL outputGLSL(sink);
     root->traverse(&outputGLSL);
 }
diff --git a/src/compiler/VersionGLSL.cpp b/src/compiler/VersionGLSL.cpp
new file mode 100644
index 0000000..3f87820
--- /dev/null
+++ b/src/compiler/VersionGLSL.cpp
@@ -0,0 +1,108 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/VersionGLSL.h"
+
+static const int GLSL_VERSION_110 = 110;
+static const int GLSL_VERSION_120 = 120;
+
+// We need to scan for two things:
+// 1. "invariant" keyword: This can occur in both - vertex and fragment shaders
+//    but only at the global scope.
+// 2. "gl_PointCoord" built-in variable: This can only occur in fragment shader
+//    but inside any scope.
+// So we need to scan the entire fragment shader but only the global scope
+// of vertex shader.
+//
+// TODO(alokp): The following two cases of invariant decalaration get lost
+// during parsing - they do not get carried over to the intermediate tree.
+// Handle these cases:
+// 1. When a pragma is used to force all output variables to be invariant:
+//    - #pragma STDGL invariant(all)
+// 2. When a previously decalared or built-in variable is marked invariant:
+//    - invariant gl_Position;
+//    - varying vec3 color; invariant color;
+//
+TVersionGLSL::TVersionGLSL(ShShaderType type)
+    : mShaderType(type),
+      mVersion(GLSL_VERSION_110)
+{
+}
+
+void TVersionGLSL::visitSymbol(TIntermSymbol* node)
+{
+    ASSERT(mShaderType == SH_FRAGMENT_SHADER);
+
+    if (node->getSymbol() == "gl_PointCoord")
+        updateVersion(GLSL_VERSION_120);
+}
+
+void TVersionGLSL::visitConstantUnion(TIntermConstantUnion*)
+{
+    ASSERT(mShaderType == SH_FRAGMENT_SHADER);
+}
+
+bool TVersionGLSL::visitBinary(Visit, TIntermBinary*)
+{
+    ASSERT(mShaderType == SH_FRAGMENT_SHADER);
+    return true;
+}
+
+bool TVersionGLSL::visitUnary(Visit, TIntermUnary*)
+{
+    ASSERT(mShaderType == SH_FRAGMENT_SHADER);
+    return true;
+}
+
+bool TVersionGLSL::visitSelection(Visit, TIntermSelection*)
+{
+    ASSERT(mShaderType == SH_FRAGMENT_SHADER);
+    return true;
+}
+
+bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate* node)
+{
+    // We need to scan the entire fragment shader but only the global scope
+    // of vertex shader.
+    bool visitChildren = mShaderType == SH_FRAGMENT_SHADER ? true : false;
+
+    switch (node->getOp()) {
+      case EOpSequence:
+        // We need to visit sequence children to get to global or inner scope.
+        visitChildren = true;
+        break;
+      case EOpDeclaration: {
+        const TIntermSequence& sequence = node->getSequence();
+        TQualifier qualifier = sequence.front()->getAsTyped()->getQualifier();
+        if ((qualifier == EvqInvariantVaryingIn) ||
+            (qualifier == EvqInvariantVaryingOut)) {
+            updateVersion(GLSL_VERSION_120);
+        }
+        break;
+      }
+      default: break;
+    }
+
+    return visitChildren;
+}
+
+bool TVersionGLSL::visitLoop(Visit, TIntermLoop*)
+{
+    ASSERT(mShaderType == SH_FRAGMENT_SHADER);
+    return true;
+}
+
+bool TVersionGLSL::visitBranch(Visit, TIntermBranch*)
+{
+    ASSERT(mShaderType == SH_FRAGMENT_SHADER);
+    return true;
+}
+
+void TVersionGLSL::updateVersion(int version)
+{
+    mVersion = std::max(version, mVersion);
+}
+
diff --git a/src/compiler/VersionGLSL.h b/src/compiler/VersionGLSL.h
new file mode 100644
index 0000000..64d002b
--- /dev/null
+++ b/src/compiler/VersionGLSL.h
@@ -0,0 +1,50 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_VERSIONGLSL_H_
+#define COMPILER_VERSIONGLSL_H_
+
+#include "GLSLANG/ShaderLang.h"
+#include "compiler/intermediate.h"
+
+// Traverses the intermediate tree to return the minimum GLSL version
+// required to legally access all built-in features used in the shader.
+// GLSL 1.1 which is mandated by OpenGL 2.0 provides:
+//   - #version and #extension to declare version and extensions.
+//   - built-in functions refract, exp, and log.
+//   - updated step() to compare x < edge instead of x <= edge.
+// GLSL 1.2 which is mandated by OpenGL 2.1 provides:
+//   - many changes to reduce differences when compared to the ES specification.
+//   - invariant keyword and its support.
+//   - c++ style name hiding rules.
+//   - built-in variable gl_PointCoord for fragment shaders.
+//
+class TVersionGLSL : public TIntermTraverser {
+public:
+    TVersionGLSL(ShShaderType type);
+
+    // Returns 120 if "invariant" keyword or "gl_PointCoord" is used
+    // in the shader. Else 110 is returned.
+    int getVersion() { return mVersion; }
+
+    virtual void visitSymbol(TIntermSymbol*);
+    virtual void visitConstantUnion(TIntermConstantUnion*);
+    virtual bool visitBinary(Visit, TIntermBinary*);
+    virtual bool visitUnary(Visit, TIntermUnary*);
+    virtual bool visitSelection(Visit, TIntermSelection*);
+    virtual bool visitAggregate(Visit, TIntermAggregate*);
+    virtual bool visitLoop(Visit, TIntermLoop*);
+    virtual bool visitBranch(Visit, TIntermBranch*);
+
+protected:
+    void updateVersion(int version);
+
+private:
+    ShShaderType mShaderType;
+    int mVersion;
+};
+
+#endif  // COMPILER_VERSIONGLSL_H_