Implement ES2 backend for Angle translator.
With this CL, we have the option to select a code output backend: GLSL, GLSL ES, or HLSL.
Note that we always emit the highest supported float precision for fragment shader due to anglebug 168. Although this is a temporary solution, it's not against GLSL ES spec, because it's ok for implementation to upgrade precision.
Tested with WebGL conformance test suite, GLES2 conformance test suite (only failed 2/1198), and a few webgl demos, including worlds of webgl, aquarium, etc.
anglebug=81
test=translator emitting correct GLSL ES code when ES2 backend is selected.
Review URL: http://codereview.appspot.com/4550129
git-svn-id: https://angleproject.googlecode.com/svn/trunk@687 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 64157d6..7ace016 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -24,6 +24,7 @@
Kenneth Russell
Ben Vanik
Adrienne Walker
+ Zhenyao Mo
Mozilla Corp.
Vladimir Vukicevic
diff --git a/include/GLSLANG/ShaderLang.h b/include/GLSLANG/ShaderLang.h
index 6795a87..6fe688c 100644
--- a/include/GLSLANG/ShaderLang.h
+++ b/include/GLSLANG/ShaderLang.h
@@ -17,7 +17,7 @@
// Version number for shader translation API.
// It is incremented everytime the API changes.
-#define SH_VERSION 104
+#define SH_VERSION 105
//
// The names of the following enums have been derived by replacing GL prefix
@@ -36,6 +36,12 @@
} ShShaderSpec;
typedef enum {
+ SH_ESSL_OUTPUT = 0x8B45,
+ SH_GLSL_OUTPUT = 0x8B46,
+ SH_HLSL_OUTPUT = 0x8B47
+} ShShaderOutput;
+
+typedef enum {
SH_NONE = 0,
SH_INT = 0x1404,
SH_FLOAT = 0x1406,
@@ -128,13 +134,17 @@
//
// Driver calls these to create and destroy compiler objects.
//
-// Returns the handle of constructed compiler.
+// Returns the handle of constructed compiler, null if the requested compiler is
+// not supported.
// Parameters:
// type: Specifies the type of shader - SH_FRAGMENT_SHADER or SH_VERTEX_SHADER.
// spec: Specifies the language spec the compiler must conform to -
// SH_GLES2_SPEC or SH_WEBGL_SPEC.
+// output: Specifies the output code type - SH_ESSL_OUTPUT, SH_GLSL_OUTPUT,
+// or SH_HLSL_OUTPUT.
// resources: Specifies the built-in resources.
ShHandle ShConstructCompiler(ShShaderType type, ShShaderSpec spec,
+ ShShaderOutput output,
const ShBuiltInResources* resources);
void ShDestruct(ShHandle handle);
diff --git a/samples/translator/translator.cpp b/samples/translator/translator.cpp
index fb1fb49..67da02d 100644
--- a/samples/translator/translator.cpp
+++ b/samples/translator/translator.cpp
@@ -66,6 +66,7 @@
char* buffer = 0;
int bufferLen = 0;
int numAttribs = 0, numUniforms = 0;
+ ShShaderOutput output = SH_ESSL_OUTPUT;
ShInitialize();
@@ -81,6 +82,18 @@
case 'm': compileOptions |= SH_MAP_LONG_VARIABLE_NAMES; break;
case 'o': compileOptions |= SH_OBJECT_CODE; break;
case 'u': compileOptions |= SH_ATTRIBUTES_UNIFORMS; break;
+ case 'b':
+ if (argv[0][2] == '=') {
+ switch (argv[0][3]) {
+ case 'e': output = SH_ESSL_OUTPUT; break;
+ case 'g': output = SH_GLSL_OUTPUT; break;
+ case 'h': output = SH_HLSL_OUTPUT; break;
+ default: failCode = EFailUsage;
+ }
+ } else {
+ failCode = EFailUsage;
+ }
+ break;
default: failCode = EFailUsage;
}
} else {
@@ -88,12 +101,14 @@
switch (FindShaderType(argv[0])) {
case SH_VERTEX_SHADER:
if (vertexCompiler == 0)
- vertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, SH_GLES2_SPEC, &resources);
+ vertexCompiler = ShConstructCompiler(
+ SH_VERTEX_SHADER, SH_GLES2_SPEC, output, &resources);
compiler = vertexCompiler;
break;
case SH_FRAGMENT_SHADER:
if (fragmentCompiler == 0)
- fragmentCompiler = ShConstructCompiler(SH_FRAGMENT_SHADER, SH_GLES2_SPEC, &resources);
+ fragmentCompiler = ShConstructCompiler(
+ SH_FRAGMENT_SHADER, SH_GLES2_SPEC, output, &resources);
compiler = fragmentCompiler;
break;
default: break;
@@ -159,12 +174,15 @@
//
void usage()
{
- printf("Usage: translate [-i -m -o -u] file1 file2 ...\n"
- "Where: filename = filename ending in .frag or .vert\n"
- " -i = print intermediate tree\n"
- " -m = map long variable names\n"
- " -o = print translated code\n"
- " -u = print active attribs and uniforms\n");
+ printf("Usage: translate [-i -m -o -u -b=e -b=g -b=h] file1 file2 ...\n"
+ "Where: filename : filename ending in .frag or .vert\n"
+ " -i : print intermediate tree\n"
+ " -m : map long variable names\n"
+ " -o : print translated code\n"
+ " -u : print active attribs and uniforms\n"
+ " -b=e : output GLSL ES code (this is by default)\n"
+ " -b=g : output GLSL code\n"
+ " -b=h : output HLSL code\n");
}
//
diff --git a/src/build_angle.gyp b/src/build_angle.gyp
index dca692b..08727d3 100644
--- a/src/build_angle.gyp
+++ b/src/build_angle.gyp
@@ -104,8 +104,14 @@
'compiler/CodeGenGLSL.cpp',
'compiler/ForLoopUnroll.cpp',
'compiler/ForLoopUnroll.h',
+ 'compiler/OutputESSL.cpp',
+ 'compiler/OutputESSL.h',
+ 'compiler/OutputGLSLBase.cpp',
+ 'compiler/OutputGLSLBase.h',
'compiler/OutputGLSL.cpp',
'compiler/OutputGLSL.h',
+ 'compiler/TranslatorESSL.cpp',
+ 'compiler/TranslatorESSL.h',
'compiler/TranslatorGLSL.cpp',
'compiler/TranslatorGLSL.h',
'compiler/VersionGLSL.cpp',
diff --git a/src/common/version.h b/src/common/version.h
index 9462f07..f47273e 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -1,7 +1,7 @@
#define MAJOR_VERSION 0
#define MINOR_VERSION 0
#define BUILD_VERSION 0
-#define BUILD_REVISION 686
+#define BUILD_REVISION 687
#define STRINGIFY(x) #x
#define MACRO_STRINGIFY(x) STRINGIFY(x)
diff --git a/src/compiler/CodeGenGLSL.cpp b/src/compiler/CodeGenGLSL.cpp
index d140b37..226bf8f 100644
--- a/src/compiler/CodeGenGLSL.cpp
+++ b/src/compiler/CodeGenGLSL.cpp
@@ -5,15 +5,24 @@
//
#include "compiler/TranslatorGLSL.h"
+#include "compiler/TranslatorESSL.h"
//
// This function must be provided to create the actual
// compile object used by higher level code. It returns
// a subclass of TCompiler.
//
-TCompiler* ConstructCompiler(ShShaderType type, ShShaderSpec spec)
+TCompiler* ConstructCompiler(
+ ShShaderType type, ShShaderSpec spec, ShShaderOutput output)
{
- return new TranslatorGLSL(type, spec);
+ switch (output) {
+ case SH_GLSL_OUTPUT:
+ return new TranslatorGLSL(type, spec);
+ case SH_ESSL_OUTPUT:
+ return new TranslatorESSL(type, spec);
+ default:
+ return NULL;
+ }
}
//
diff --git a/src/compiler/CodeGenHLSL.cpp b/src/compiler/CodeGenHLSL.cpp
index e04e789..f46ff66 100644
--- a/src/compiler/CodeGenHLSL.cpp
+++ b/src/compiler/CodeGenHLSL.cpp
@@ -11,9 +11,15 @@
// compile object used by higher level code. It returns
// a subclass of TCompiler.
//
-TCompiler* ConstructCompiler(ShShaderType type, ShShaderSpec spec)
+TCompiler* ConstructCompiler(
+ ShShaderType type, ShShaderSpec spec, ShShaderOutput output)
{
- return new TranslatorHLSL(type, spec);
+ switch (output) {
+ case SH_HLSL_OUTPUT:
+ return new TranslatorHLSL(type, spec);
+ default:
+ return NULL;
+ }
}
//
diff --git a/src/compiler/Compiler.cpp b/src/compiler/Compiler.cpp
index 41eb0ec..655d62d 100644
--- a/src/compiler/Compiler.cpp
+++ b/src/compiler/Compiler.cpp
@@ -215,3 +215,8 @@
{
return MAX_IDENTIFIER_NAME_SIZE + 1;
}
+
+const TExtensionBehavior& TCompiler::getExtensionBehavior() const
+{
+ return extensionBehavior;
+}
diff --git a/src/compiler/ExtensionBehavior.h b/src/compiler/ExtensionBehavior.h
index da96c24..ab5a0a5 100644
--- a/src/compiler/ExtensionBehavior.h
+++ b/src/compiler/ExtensionBehavior.h
@@ -16,6 +16,22 @@
EBhDisable
} TBehavior;
+inline const char* getBehaviorString(TBehavior b)
+{
+ switch(b) {
+ case EBhRequire:
+ return "require";
+ case EBhEnable:
+ return "enable";
+ case EBhWarn:
+ return "warn";
+ case EBhDisable:
+ return "disable";
+ default:
+ return NULL;
+ }
+}
+
typedef TMap<TString, TBehavior> TExtensionBehavior;
#endif // _EXTENSION_TABLE_INCLUDED_
diff --git a/src/compiler/OutputESSL.cpp b/src/compiler/OutputESSL.cpp
new file mode 100644
index 0000000..64ee92d
--- /dev/null
+++ b/src/compiler/OutputESSL.cpp
@@ -0,0 +1,22 @@
+//
+// Copyright (c) 2002-2011 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/OutputESSL.h"
+
+TOutputESSL::TOutputESSL(TInfoSinkBase& objSink)
+ : TOutputGLSLBase(objSink)
+{
+}
+
+bool TOutputESSL::writeVariablePrecision(TPrecision precision)
+{
+ if (precision == EbpUndefined)
+ return false;
+
+ TInfoSinkBase& out = objSink();
+ out << getPrecisionString(precision);
+ return true;
+}
diff --git a/src/compiler/OutputESSL.h b/src/compiler/OutputESSL.h
new file mode 100644
index 0000000..4fa73c8
--- /dev/null
+++ b/src/compiler/OutputESSL.h
@@ -0,0 +1,21 @@
+//
+// Copyright (c) 2002-2011 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 CROSSCOMPILERGLSL_OUTPUTESSL_H_
+#define CROSSCOMPILERGLSL_OUTPUTESSL_H_
+
+#include "compiler/OutputGLSLBase.h"
+
+class TOutputESSL : public TOutputGLSLBase
+{
+public:
+ TOutputESSL(TInfoSinkBase& objSink);
+
+protected:
+ virtual bool writeVariablePrecision(TPrecision precision);
+};
+
+#endif // CROSSCOMPILERGLSL_OUTPUTESSL_H_
diff --git a/src/compiler/OutputGLSL.cpp b/src/compiler/OutputGLSL.cpp
index 3224dfd..dd31b4b 100644
--- a/src/compiler/OutputGLSL.cpp
+++ b/src/compiler/OutputGLSL.cpp
@@ -5,706 +5,13 @@
//
#include "compiler/OutputGLSL.h"
-#include "compiler/debug.h"
-
-namespace
-{
-TString getTypeName(const TType& type)
-{
- TInfoSinkBase out;
- if (type.isMatrix())
- {
- out << "mat";
- out << type.getNominalSize();
- }
- else if (type.isVector())
- {
- switch (type.getBasicType())
- {
- case EbtFloat: out << "vec"; break;
- case EbtInt: out << "ivec"; break;
- case EbtBool: out << "bvec"; break;
- default: UNREACHABLE(); break;
- }
- out << type.getNominalSize();
- }
- else
- {
- if (type.getBasicType() == EbtStruct)
- out << type.getTypeName();
- else
- out << type.getBasicString();
- }
- return TString(out.c_str());
-}
-
-TString arrayBrackets(const TType& type)
-{
- ASSERT(type.isArray());
- TInfoSinkBase out;
- out << "[" << type.getArraySize() << "]";
- return TString(out.c_str());
-}
-
-bool isSingleStatement(TIntermNode* node) {
- if (const TIntermAggregate* aggregate = node->getAsAggregate())
- {
- return (aggregate->getOp() != EOpFunction) &&
- (aggregate->getOp() != EOpSequence);
- }
- else if (const TIntermSelection* selection = node->getAsSelectionNode())
- {
- // Ternary operators are usually part of an assignment operator.
- // This handles those rare cases in which they are all by themselves.
- return selection->usesTernaryOperator();
- }
- else if (node->getAsLoopNode())
- {
- return false;
- }
- return true;
-}
-} // namespace
TOutputGLSL::TOutputGLSL(TInfoSinkBase& objSink)
- : TIntermTraverser(true, true, true),
- mObjSink(objSink),
- mDeclaringVariables(false)
+ : TOutputGLSLBase(objSink)
{
}
-void TOutputGLSL::writeTriplet(Visit visit, const char* preStr, const char* inStr, const char* postStr)
+bool TOutputGLSL::writeVariablePrecision(TPrecision)
{
- TInfoSinkBase& out = objSink();
- if (visit == PreVisit && preStr)
- {
- out << preStr;
- }
- else if (visit == InVisit && inStr)
- {
- out << inStr;
- }
- else if (visit == PostVisit && postStr)
- {
- out << postStr;
- }
-}
-
-void TOutputGLSL::writeVariableType(const TType& type)
-{
- TInfoSinkBase& out = objSink();
- TQualifier qualifier = type.getQualifier();
- // TODO(alokp): Validate qualifier for variable declarations.
- if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal))
- out << type.getQualifierString() << " ";
-
- // Declare the struct if we have not done so already.
- if ((type.getBasicType() == EbtStruct) &&
- (mDeclaredStructs.find(type.getTypeName()) == mDeclaredStructs.end()))
- {
- out << "struct " << type.getTypeName() << "{\n";
- const TTypeList* structure = type.getStruct();
- ASSERT(structure != NULL);
- for (size_t i = 0; i < structure->size(); ++i)
- {
- const TType* fieldType = (*structure)[i].type;
- ASSERT(fieldType != NULL);
- out << getTypeName(*fieldType) << " " << fieldType->getFieldName();
- if (fieldType->isArray())
- out << arrayBrackets(*fieldType);
- out << ";\n";
- }
- out << "}";
- mDeclaredStructs.insert(type.getTypeName());
- }
- else
- {
- out << getTypeName(type);
- }
-}
-
-void TOutputGLSL::writeFunctionParameters(const TIntermSequence& args)
-{
- TInfoSinkBase& out = objSink();
- for (TIntermSequence::const_iterator iter = args.begin();
- iter != args.end(); ++iter)
- {
- const TIntermSymbol* arg = (*iter)->getAsSymbolNode();
- ASSERT(arg != NULL);
-
- const TType& type = arg->getType();
- TQualifier qualifier = type.getQualifier();
- // TODO(alokp): Validate qualifier for function arguments.
- if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal))
- out << type.getQualifierString() << " ";
-
- out << getTypeName(type);
-
- const TString& name = arg->getSymbol();
- if (!name.empty())
- out << " " << name;
- if (type.isArray())
- out << arrayBrackets(type);
-
- // Put a comma if this is not the last argument.
- if (iter != args.end() - 1)
- out << ", ";
- }
-}
-
-const ConstantUnion* TOutputGLSL::writeConstantUnion(const TType& type,
- const ConstantUnion* pConstUnion)
-{
- TInfoSinkBase& out = objSink();
-
- if (type.getBasicType() == EbtStruct)
- {
- out << type.getTypeName() << "(";
- const TTypeList* structure = type.getStruct();
- ASSERT(structure != NULL);
- for (size_t i = 0; i < structure->size(); ++i)
- {
- const TType* fieldType = (*structure)[i].type;
- ASSERT(fieldType != NULL);
- pConstUnion = writeConstantUnion(*fieldType, pConstUnion);
- if (i != structure->size() - 1) out << ", ";
- }
- out << ")";
- }
- else
- {
- int size = type.getObjectSize();
- bool writeType = size > 1;
- if (writeType) out << getTypeName(type) << "(";
- for (int i = 0; i < size; ++i, ++pConstUnion)
- {
- switch (pConstUnion->getType())
- {
- case EbtFloat: out << pConstUnion->getFConst(); break;
- case EbtInt: out << pConstUnion->getIConst(); break;
- case EbtBool: out << pConstUnion->getBConst(); break;
- default: UNREACHABLE();
- }
- if (i != size - 1) out << ", ";
- }
- if (writeType) out << ")";
- }
- return pConstUnion;
-}
-
-void TOutputGLSL::visitSymbol(TIntermSymbol* node)
-{
- TInfoSinkBase& out = objSink();
- if (mLoopUnroll.NeedsToReplaceSymbolWithValue(node))
- out << mLoopUnroll.GetLoopIndexValue(node);
- else
- out << node->getSymbol();
-
- if (mDeclaringVariables && node->getType().isArray())
- out << arrayBrackets(node->getType());
-}
-
-void TOutputGLSL::visitConstantUnion(TIntermConstantUnion* node)
-{
- writeConstantUnion(node->getType(), node->getUnionArrayPointer());
-}
-
-bool TOutputGLSL::visitBinary(Visit visit, TIntermBinary* node)
-{
- bool visitChildren = true;
- TInfoSinkBase& out = objSink();
- switch (node->getOp())
- {
- case EOpInitialize:
- if (visit == InVisit)
- {
- out << " = ";
- // RHS of initialize is not being declared.
- mDeclaringVariables = false;
- }
- break;
- case EOpAssign: writeTriplet(visit, "(", " = ", ")"); break;
- case EOpAddAssign: writeTriplet(visit, "(", " += ", ")"); break;
- case EOpSubAssign: writeTriplet(visit, "(", " -= ", ")"); break;
- case EOpDivAssign: writeTriplet(visit, "(", " /= ", ")"); break;
- // Notice the fall-through.
- case EOpMulAssign:
- case EOpVectorTimesMatrixAssign:
- case EOpVectorTimesScalarAssign:
- case EOpMatrixTimesScalarAssign:
- case EOpMatrixTimesMatrixAssign:
- writeTriplet(visit, "(", " *= ", ")");
- break;
-
- case EOpIndexDirect:
- case EOpIndexIndirect:
- writeTriplet(visit, NULL, "[", "]");
- break;
- case EOpIndexDirectStruct:
- if (visit == InVisit)
- {
- out << ".";
- // TODO(alokp): ASSERT
- out << node->getType().getFieldName();
- visitChildren = false;
- }
- break;
- case EOpVectorSwizzle:
- if (visit == InVisit)
- {
- out << ".";
- TIntermAggregate* rightChild = node->getRight()->getAsAggregate();
- TIntermSequence& sequence = rightChild->getSequence();
- for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); ++sit)
- {
- TIntermConstantUnion* element = (*sit)->getAsConstantUnion();
- ASSERT(element->getBasicType() == EbtInt);
- ASSERT(element->getNominalSize() == 1);
- const ConstantUnion& data = element->getUnionArrayPointer()[0];
- ASSERT(data.getType() == EbtInt);
- switch (data.getIConst())
- {
- case 0: out << "x"; break;
- case 1: out << "y"; break;
- case 2: out << "z"; break;
- case 3: out << "w"; break;
- default: UNREACHABLE(); break;
- }
- }
- visitChildren = false;
- }
- break;
-
- case EOpAdd: writeTriplet(visit, "(", " + ", ")"); break;
- case EOpSub: writeTriplet(visit, "(", " - ", ")"); break;
- case EOpMul: writeTriplet(visit, "(", " * ", ")"); break;
- case EOpDiv: writeTriplet(visit, "(", " / ", ")"); break;
- case EOpMod: UNIMPLEMENTED(); break;
- case EOpEqual: writeTriplet(visit, "(", " == ", ")"); break;
- case EOpNotEqual: writeTriplet(visit, "(", " != ", ")"); break;
- case EOpLessThan: writeTriplet(visit, "(", " < ", ")"); break;
- case EOpGreaterThan: writeTriplet(visit, "(", " > ", ")"); break;
- case EOpLessThanEqual: writeTriplet(visit, "(", " <= ", ")"); break;
- case EOpGreaterThanEqual: writeTriplet(visit, "(", " >= ", ")"); break;
-
- // Notice the fall-through.
- case EOpVectorTimesScalar:
- case EOpVectorTimesMatrix:
- case EOpMatrixTimesVector:
- case EOpMatrixTimesScalar:
- case EOpMatrixTimesMatrix:
- writeTriplet(visit, "(", " * ", ")");
- break;
-
- case EOpLogicalOr: writeTriplet(visit, "(", " || ", ")"); break;
- case EOpLogicalXor: writeTriplet(visit, "(", " ^^ ", ")"); break;
- case EOpLogicalAnd: writeTriplet(visit, "(", " && ", ")"); break;
- default: UNREACHABLE(); break;
- }
-
- return visitChildren;
-}
-
-bool TOutputGLSL::visitUnary(Visit visit, TIntermUnary* node)
-{
- switch (node->getOp())
- {
- case EOpNegative: writeTriplet(visit, "(-", NULL, ")"); break;
- case EOpVectorLogicalNot: writeTriplet(visit, "not(", NULL, ")"); break;
- case EOpLogicalNot: writeTriplet(visit, "(!", NULL, ")"); break;
-
- case EOpPostIncrement: writeTriplet(visit, "(", NULL, "++)"); break;
- case EOpPostDecrement: writeTriplet(visit, "(", NULL, "--)"); break;
- case EOpPreIncrement: writeTriplet(visit, "(++", NULL, ")"); break;
- case EOpPreDecrement: writeTriplet(visit, "(--", NULL, ")"); break;
-
- case EOpConvIntToBool:
- case EOpConvFloatToBool:
- switch (node->getOperand()->getType().getNominalSize())
- {
- case 1: writeTriplet(visit, "bool(", NULL, ")"); break;
- case 2: writeTriplet(visit, "bvec2(", NULL, ")"); break;
- case 3: writeTriplet(visit, "bvec3(", NULL, ")"); break;
- case 4: writeTriplet(visit, "bvec4(", NULL, ")"); break;
- default: UNREACHABLE();
- }
- break;
- case EOpConvBoolToFloat:
- case EOpConvIntToFloat:
- switch (node->getOperand()->getType().getNominalSize())
- {
- case 1: writeTriplet(visit, "float(", NULL, ")"); break;
- case 2: writeTriplet(visit, "vec2(", NULL, ")"); break;
- case 3: writeTriplet(visit, "vec3(", NULL, ")"); break;
- case 4: writeTriplet(visit, "vec4(", NULL, ")"); break;
- default: UNREACHABLE();
- }
- break;
- case EOpConvFloatToInt:
- case EOpConvBoolToInt:
- switch (node->getOperand()->getType().getNominalSize())
- {
- case 1: writeTriplet(visit, "int(", NULL, ")"); break;
- case 2: writeTriplet(visit, "ivec2(", NULL, ")"); break;
- case 3: writeTriplet(visit, "ivec3(", NULL, ")"); break;
- case 4: writeTriplet(visit, "ivec4(", NULL, ")"); break;
- default: UNREACHABLE();
- }
- break;
-
- case EOpRadians: writeTriplet(visit, "radians(", NULL, ")"); break;
- case EOpDegrees: writeTriplet(visit, "degrees(", NULL, ")"); break;
- case EOpSin: writeTriplet(visit, "sin(", NULL, ")"); break;
- case EOpCos: writeTriplet(visit, "cos(", NULL, ")"); break;
- case EOpTan: writeTriplet(visit, "tan(", NULL, ")"); break;
- case EOpAsin: writeTriplet(visit, "asin(", NULL, ")"); break;
- case EOpAcos: writeTriplet(visit, "acos(", NULL, ")"); break;
- case EOpAtan: writeTriplet(visit, "atan(", NULL, ")"); break;
-
- case EOpExp: writeTriplet(visit, "exp(", NULL, ")"); break;
- case EOpLog: writeTriplet(visit, "log(", NULL, ")"); break;
- case EOpExp2: writeTriplet(visit, "exp2(", NULL, ")"); break;
- case EOpLog2: writeTriplet(visit, "log2(", NULL, ")"); break;
- case EOpSqrt: writeTriplet(visit, "sqrt(", NULL, ")"); break;
- case EOpInverseSqrt: writeTriplet(visit, "inversesqrt(", NULL, ")"); break;
-
- case EOpAbs: writeTriplet(visit, "abs(", NULL, ")"); break;
- case EOpSign: writeTriplet(visit, "sign(", NULL, ")"); break;
- case EOpFloor: writeTriplet(visit, "floor(", NULL, ")"); break;
- case EOpCeil: writeTriplet(visit, "ceil(", NULL, ")"); break;
- case EOpFract: writeTriplet(visit, "fract(", NULL, ")"); break;
-
- case EOpLength: writeTriplet(visit, "length(", NULL, ")"); break;
- case EOpNormalize: writeTriplet(visit, "normalize(", NULL, ")"); break;
-
- case EOpDFdx: writeTriplet(visit, "dFdx(", NULL, ")"); break;
- case EOpDFdy: writeTriplet(visit, "dFdy(", NULL, ")"); break;
- case EOpFwidth: writeTriplet(visit, "fwidth(", NULL, ")"); break;
-
- case EOpAny: writeTriplet(visit, "any(", NULL, ")"); break;
- case EOpAll: writeTriplet(visit, "all(", NULL, ")"); break;
-
- default: UNREACHABLE(); break;
- }
-
- return true;
-}
-
-bool TOutputGLSL::visitSelection(Visit visit, TIntermSelection* node)
-{
- TInfoSinkBase& out = objSink();
-
- if (node->usesTernaryOperator())
- {
- // Notice two brackets at the beginning and end. The outer ones
- // encapsulate the whole ternary expression. This preserves the
- // order of precedence when ternary expressions are used in a
- // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
- out << "((";
- node->getCondition()->traverse(this);
- out << ") ? (";
- node->getTrueBlock()->traverse(this);
- out << ") : (";
- node->getFalseBlock()->traverse(this);
- out << "))";
- }
- else
- {
- out << "if (";
- node->getCondition()->traverse(this);
- out << ")\n";
-
- incrementDepth();
- visitCodeBlock(node->getTrueBlock());
-
- if (node->getFalseBlock())
- {
- out << "else\n";
- visitCodeBlock(node->getFalseBlock());
- }
- decrementDepth();
- }
return false;
}
-
-bool TOutputGLSL::visitAggregate(Visit visit, TIntermAggregate* node)
-{
- bool visitChildren = true;
- TInfoSinkBase& out = objSink();
- switch (node->getOp())
- {
- case EOpSequence: {
- // Scope the sequences except when at the global scope.
- if (depth > 0) out << "{\n";
-
- incrementDepth();
- const TIntermSequence& sequence = node->getSequence();
- for (TIntermSequence::const_iterator iter = sequence.begin();
- iter != sequence.end(); ++iter)
- {
- TIntermNode* node = *iter;
- ASSERT(node != NULL);
- node->traverse(this);
-
- if (isSingleStatement(node))
- out << ";\n";
- }
- decrementDepth();
-
- // Scope the sequences except when at the global scope.
- if (depth > 0) out << "}\n";
- visitChildren = false;
- break;
- }
- case EOpPrototype: {
- // Function declaration.
- ASSERT(visit == PreVisit);
- TString returnType = getTypeName(node->getType());
- out << returnType << " " << node->getName();
-
- out << "(";
- writeFunctionParameters(node->getSequence());
- out << ")";
-
- visitChildren = false;
- break;
- }
- case EOpFunction: {
- // Function definition.
- ASSERT(visit == PreVisit);
- TString returnType = getTypeName(node->getType());
- TString functionName = TFunction::unmangleName(node->getName());
- out << returnType << " " << functionName;
-
- incrementDepth();
- // Function definition node contains one or two children nodes
- // representing function parameters and function body. The latter
- // is not present in case of empty function bodies.
- const TIntermSequence& sequence = node->getSequence();
- ASSERT((sequence.size() == 1) || (sequence.size() == 2));
- TIntermSequence::const_iterator seqIter = sequence.begin();
-
- // Traverse function parameters.
- TIntermAggregate* params = (*seqIter)->getAsAggregate();
- ASSERT(params != NULL);
- ASSERT(params->getOp() == EOpParameters);
- params->traverse(this);
-
- // Traverse function body.
- TIntermAggregate* body = ++seqIter != sequence.end() ?
- (*seqIter)->getAsAggregate() : NULL;
- visitCodeBlock(body);
- decrementDepth();
-
- // Fully processed; no need to visit children.
- visitChildren = false;
- break;
- }
- case EOpFunctionCall:
- // Function call.
- if (visit == PreVisit)
- {
- TString functionName = TFunction::unmangleName(node->getName());
- out << functionName << "(";
- }
- else if (visit == InVisit)
- {
- out << ", ";
- }
- else
- {
- out << ")";
- }
- break;
- case EOpParameters: {
- // Function parameters.
- ASSERT(visit == PreVisit);
- out << "(";
- writeFunctionParameters(node->getSequence());
- out << ")";
- visitChildren = false;
- break;
- }
- case EOpDeclaration: {
- // Variable declaration.
- if (visit == PreVisit)
- {
- const TIntermSequence& sequence = node->getSequence();
- const TIntermTyped* variable = sequence.front()->getAsTyped();
- writeVariableType(variable->getType());
- out << " ";
- mDeclaringVariables = true;
- }
- else if (visit == InVisit)
- {
- out << ", ";
- mDeclaringVariables = true;
- }
- else
- {
- mDeclaringVariables = false;
- }
- break;
- }
- case EOpConstructFloat: writeTriplet(visit, "float(", NULL, ")"); break;
- case EOpConstructVec2: writeTriplet(visit, "vec2(", ", ", ")"); break;
- case EOpConstructVec3: writeTriplet(visit, "vec3(", ", ", ")"); break;
- case EOpConstructVec4: writeTriplet(visit, "vec4(", ", ", ")"); break;
- case EOpConstructBool: writeTriplet(visit, "bool(", NULL, ")"); break;
- case EOpConstructBVec2: writeTriplet(visit, "bvec2(", ", ", ")"); break;
- case EOpConstructBVec3: writeTriplet(visit, "bvec3(", ", ", ")"); break;
- case EOpConstructBVec4: writeTriplet(visit, "bvec4(", ", ", ")"); break;
- case EOpConstructInt: writeTriplet(visit, "int(", NULL, ")"); break;
- case EOpConstructIVec2: writeTriplet(visit, "ivec2(", ", ", ")"); break;
- case EOpConstructIVec3: writeTriplet(visit, "ivec3(", ", ", ")"); break;
- case EOpConstructIVec4: writeTriplet(visit, "ivec4(", ", ", ")"); break;
- case EOpConstructMat2: writeTriplet(visit, "mat2(", ", ", ")"); break;
- case EOpConstructMat3: writeTriplet(visit, "mat3(", ", ", ")"); break;
- case EOpConstructMat4: writeTriplet(visit, "mat4(", ", ", ")"); break;
- case EOpConstructStruct:
- if (visit == PreVisit)
- {
- const TType& type = node->getType();
- ASSERT(type.getBasicType() == EbtStruct);
- out << type.getTypeName() << "(";
- }
- else if (visit == InVisit)
- {
- out << ", ";
- }
- else
- {
- out << ")";
- }
- break;
-
- case EOpLessThan: writeTriplet(visit, "lessThan(", ", ", ")"); break;
- case EOpGreaterThan: writeTriplet(visit, "greaterThan(", ", ", ")"); break;
- case EOpLessThanEqual: writeTriplet(visit, "lessThanEqual(", ", ", ")"); break;
- case EOpGreaterThanEqual: writeTriplet(visit, "greaterThanEqual(", ", ", ")"); break;
- case EOpVectorEqual: writeTriplet(visit, "equal(", ", ", ")"); break;
- case EOpVectorNotEqual: writeTriplet(visit, "notEqual(", ", ", ")"); break;
- case EOpComma: writeTriplet(visit, NULL, ", ", NULL); break;
-
- case EOpMod: writeTriplet(visit, "mod(", ", ", ")"); break;
- case EOpPow: writeTriplet(visit, "pow(", ", ", ")"); break;
- case EOpAtan: writeTriplet(visit, "atan(", ", ", ")"); break;
- case EOpMin: writeTriplet(visit, "min(", ", ", ")"); break;
- case EOpMax: writeTriplet(visit, "max(", ", ", ")"); break;
- case EOpClamp: writeTriplet(visit, "clamp(", ", ", ")"); break;
- case EOpMix: writeTriplet(visit, "mix(", ", ", ")"); break;
- case EOpStep: writeTriplet(visit, "step(", ", ", ")"); break;
- case EOpSmoothStep: writeTriplet(visit, "smoothstep(", ", ", ")"); break;
-
- case EOpDistance: writeTriplet(visit, "distance(", ", ", ")"); break;
- case EOpDot: writeTriplet(visit, "dot(", ", ", ")"); break;
- case EOpCross: writeTriplet(visit, "cross(", ", ", ")"); break;
- case EOpFaceForward: writeTriplet(visit, "faceforward(", ", ", ")"); break;
- case EOpReflect: writeTriplet(visit, "reflect(", ", ", ")"); break;
- case EOpRefract: writeTriplet(visit, "refract(", ", ", ")"); break;
- case EOpMul: writeTriplet(visit, "matrixCompMult(", ", ", ")"); break;
-
- default: UNREACHABLE(); break;
- }
- return visitChildren;
-}
-
-bool TOutputGLSL::visitLoop(Visit visit, TIntermLoop* node)
-{
- TInfoSinkBase& out = objSink();
-
- incrementDepth();
- // Loop header.
- TLoopType loopType = node->getType();
- if (loopType == ELoopFor) // for loop
- {
- if (!node->getUnrollFlag()) {
- out << "for (";
- if (node->getInit())
- node->getInit()->traverse(this);
- out << "; ";
-
- if (node->getCondition())
- node->getCondition()->traverse(this);
- out << "; ";
-
- if (node->getExpression())
- node->getExpression()->traverse(this);
- out << ")\n";
- }
- }
- else if (loopType == ELoopWhile) // while loop
- {
- out << "while (";
- ASSERT(node->getCondition() != NULL);
- node->getCondition()->traverse(this);
- out << ")\n";
- }
- else // do-while loop
- {
- ASSERT(loopType == ELoopDoWhile);
- out << "do\n";
- }
-
- // Loop body.
- if (node->getUnrollFlag())
- {
- TLoopIndexInfo indexInfo;
- mLoopUnroll.FillLoopIndexInfo(node, indexInfo);
- mLoopUnroll.Push(indexInfo);
- while (mLoopUnroll.SatisfiesLoopCondition())
- {
- visitCodeBlock(node->getBody());
- mLoopUnroll.Step();
- }
- mLoopUnroll.Pop();
- }
- else
- {
- visitCodeBlock(node->getBody());
- }
-
- // Loop footer.
- if (loopType == ELoopDoWhile) // do-while loop
- {
- out << "while (";
- ASSERT(node->getCondition() != NULL);
- node->getCondition()->traverse(this);
- out << ");\n";
- }
- decrementDepth();
-
- // No need to visit children. They have been already processed in
- // this function.
- return false;
-}
-
-bool TOutputGLSL::visitBranch(Visit visit, TIntermBranch* node)
-{
- switch (node->getFlowOp())
- {
- case EOpKill: writeTriplet(visit, "discard", NULL, NULL); break;
- case EOpBreak: writeTriplet(visit, "break", NULL, NULL); break;
- case EOpContinue: writeTriplet(visit, "continue", NULL, NULL); break;
- case EOpReturn: writeTriplet(visit, "return ", NULL, NULL); break;
- default: UNREACHABLE(); break;
- }
-
- return true;
-}
-
-void TOutputGLSL::visitCodeBlock(TIntermNode* node) {
- TInfoSinkBase &out = objSink();
- if (node != NULL)
- {
- node->traverse(this);
- // Single statements not part of a sequence need to be terminated
- // with semi-colon.
- if (isSingleStatement(node))
- out << ";\n";
- }
- else
- {
- out << "{\n}\n"; // Empty code block.
- }
-}
diff --git a/src/compiler/OutputGLSL.h b/src/compiler/OutputGLSL.h
index ace110a..0fe2356 100644
--- a/src/compiler/OutputGLSL.h
+++ b/src/compiler/OutputGLSL.h
@@ -1,5 +1,5 @@
//
-// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Copyright (c) 2002-2011 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.
//
@@ -7,46 +7,15 @@
#ifndef CROSSCOMPILERGLSL_OUTPUTGLSL_H_
#define CROSSCOMPILERGLSL_OUTPUTGLSL_H_
-#include <set>
+#include "compiler/OutputGLSLBase.h"
-#include "compiler/ForLoopUnroll.h"
-#include "compiler/intermediate.h"
-#include "compiler/ParseHelper.h"
-
-class TOutputGLSL : public TIntermTraverser
+class TOutputGLSL : public TOutputGLSLBase
{
public:
TOutputGLSL(TInfoSinkBase& objSink);
protected:
- TInfoSinkBase& objSink() { return mObjSink; }
- void writeTriplet(Visit visit, const char* preStr, const char* inStr, const char* postStr);
- void writeVariableType(const TType& type);
- void writeFunctionParameters(const TIntermSequence& args);
- const ConstantUnion* writeConstantUnion(const TType& type, const ConstantUnion* pConstUnion);
-
- virtual void visitSymbol(TIntermSymbol* node);
- virtual void visitConstantUnion(TIntermConstantUnion* node);
- virtual bool visitBinary(Visit visit, TIntermBinary* node);
- virtual bool visitUnary(Visit visit, TIntermUnary* node);
- virtual bool visitSelection(Visit visit, TIntermSelection* node);
- virtual bool visitAggregate(Visit visit, TIntermAggregate* node);
- virtual bool visitLoop(Visit visit, TIntermLoop* node);
- virtual bool visitBranch(Visit visit, TIntermBranch* node);
-
- void visitCodeBlock(TIntermNode* node);
-
-private:
- TInfoSinkBase& mObjSink;
- bool mDeclaringVariables;
-
- // Structs are declared as the tree is traversed. This set contains all
- // the structs already declared. It is maintained so that a struct is
- // declared only once.
- typedef std::set<TString> DeclaredStructs;
- DeclaredStructs mDeclaredStructs;
-
- ForLoopUnroll mLoopUnroll;
+ virtual bool writeVariablePrecision(TPrecision);
};
#endif // CROSSCOMPILERGLSL_OUTPUTGLSL_H_
diff --git a/src/compiler/OutputGLSLBase.cpp b/src/compiler/OutputGLSLBase.cpp
new file mode 100644
index 0000000..5e3857c
--- /dev/null
+++ b/src/compiler/OutputGLSLBase.cpp
@@ -0,0 +1,713 @@
+//
+// Copyright (c) 2002-2011 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/OutputGLSLBase.h"
+#include "compiler/debug.h"
+
+namespace
+{
+TString getTypeName(const TType& type)
+{
+ TInfoSinkBase out;
+ if (type.isMatrix())
+ {
+ out << "mat";
+ out << type.getNominalSize();
+ }
+ else if (type.isVector())
+ {
+ switch (type.getBasicType())
+ {
+ case EbtFloat: out << "vec"; break;
+ case EbtInt: out << "ivec"; break;
+ case EbtBool: out << "bvec"; break;
+ default: UNREACHABLE(); break;
+ }
+ out << type.getNominalSize();
+ }
+ else
+ {
+ if (type.getBasicType() == EbtStruct)
+ out << type.getTypeName();
+ else
+ out << type.getBasicString();
+ }
+ return TString(out.c_str());
+}
+
+TString arrayBrackets(const TType& type)
+{
+ ASSERT(type.isArray());
+ TInfoSinkBase out;
+ out << "[" << type.getArraySize() << "]";
+ return TString(out.c_str());
+}
+
+bool isSingleStatement(TIntermNode* node) {
+ if (const TIntermAggregate* aggregate = node->getAsAggregate())
+ {
+ return (aggregate->getOp() != EOpFunction) &&
+ (aggregate->getOp() != EOpSequence);
+ }
+ else if (const TIntermSelection* selection = node->getAsSelectionNode())
+ {
+ // Ternary operators are usually part of an assignment operator.
+ // This handles those rare cases in which they are all by themselves.
+ return selection->usesTernaryOperator();
+ }
+ else if (node->getAsLoopNode())
+ {
+ return false;
+ }
+ return true;
+}
+} // namespace
+
+TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase& objSink)
+ : TIntermTraverser(true, true, true),
+ mObjSink(objSink),
+ mDeclaringVariables(false)
+{
+}
+
+void TOutputGLSLBase::writeTriplet(Visit visit, const char* preStr, const char* inStr, const char* postStr)
+{
+ TInfoSinkBase& out = objSink();
+ if (visit == PreVisit && preStr)
+ {
+ out << preStr;
+ }
+ else if (visit == InVisit && inStr)
+ {
+ out << inStr;
+ }
+ else if (visit == PostVisit && postStr)
+ {
+ out << postStr;
+ }
+}
+
+void TOutputGLSLBase::writeVariableType(const TType& type)
+{
+ TInfoSinkBase& out = objSink();
+ TQualifier qualifier = type.getQualifier();
+ // TODO(alokp): Validate qualifier for variable declarations.
+ if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal))
+ out << type.getQualifierString() << " ";
+ // Declare the struct if we have not done so already.
+ if ((type.getBasicType() == EbtStruct) &&
+ (mDeclaredStructs.find(type.getTypeName()) == mDeclaredStructs.end()))
+ {
+ out << "struct " << type.getTypeName() << "{\n";
+ const TTypeList* structure = type.getStruct();
+ ASSERT(structure != NULL);
+ for (size_t i = 0; i < structure->size(); ++i)
+ {
+ const TType* fieldType = (*structure)[i].type;
+ ASSERT(fieldType != NULL);
+ if (writeVariablePrecision(fieldType->getPrecision()))
+ out << " ";
+ out << getTypeName(*fieldType) << " " << fieldType->getFieldName();
+ if (fieldType->isArray())
+ out << arrayBrackets(*fieldType);
+ out << ";\n";
+ }
+ out << "}";
+ mDeclaredStructs.insert(type.getTypeName());
+ }
+ else
+ {
+ if (writeVariablePrecision(type.getPrecision()))
+ out << " ";
+ out << getTypeName(type);
+ }
+}
+
+void TOutputGLSLBase::writeFunctionParameters(const TIntermSequence& args)
+{
+ TInfoSinkBase& out = objSink();
+ for (TIntermSequence::const_iterator iter = args.begin();
+ iter != args.end(); ++iter)
+ {
+ const TIntermSymbol* arg = (*iter)->getAsSymbolNode();
+ ASSERT(arg != NULL);
+
+ const TType& type = arg->getType();
+ TQualifier qualifier = type.getQualifier();
+ // TODO(alokp): Validate qualifier for function arguments.
+ if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal))
+ out << type.getQualifierString() << " ";
+
+ out << getTypeName(type);
+
+ const TString& name = arg->getSymbol();
+ if (!name.empty())
+ out << " " << name;
+ if (type.isArray())
+ out << arrayBrackets(type);
+
+ // Put a comma if this is not the last argument.
+ if (iter != args.end() - 1)
+ out << ", ";
+ }
+}
+
+const ConstantUnion* TOutputGLSLBase::writeConstantUnion(const TType& type,
+ const ConstantUnion* pConstUnion)
+{
+ TInfoSinkBase& out = objSink();
+
+ if (type.getBasicType() == EbtStruct)
+ {
+ out << type.getTypeName() << "(";
+ const TTypeList* structure = type.getStruct();
+ ASSERT(structure != NULL);
+ for (size_t i = 0; i < structure->size(); ++i)
+ {
+ const TType* fieldType = (*structure)[i].type;
+ ASSERT(fieldType != NULL);
+ pConstUnion = writeConstantUnion(*fieldType, pConstUnion);
+ if (i != structure->size() - 1) out << ", ";
+ }
+ out << ")";
+ }
+ else
+ {
+ int size = type.getObjectSize();
+ bool writeType = size > 1;
+ if (writeType) out << getTypeName(type) << "(";
+ for (int i = 0; i < size; ++i, ++pConstUnion)
+ {
+ switch (pConstUnion->getType())
+ {
+ case EbtFloat: out << pConstUnion->getFConst(); break;
+ case EbtInt: out << pConstUnion->getIConst(); break;
+ case EbtBool: out << pConstUnion->getBConst(); break;
+ default: UNREACHABLE();
+ }
+ if (i != size - 1) out << ", ";
+ }
+ if (writeType) out << ")";
+ }
+ return pConstUnion;
+}
+
+void TOutputGLSLBase::visitSymbol(TIntermSymbol* node)
+{
+ TInfoSinkBase& out = objSink();
+ if (mLoopUnroll.NeedsToReplaceSymbolWithValue(node))
+ out << mLoopUnroll.GetLoopIndexValue(node);
+ else
+ out << node->getSymbol();
+
+ if (mDeclaringVariables && node->getType().isArray())
+ out << arrayBrackets(node->getType());
+}
+
+void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion* node)
+{
+ writeConstantUnion(node->getType(), node->getUnionArrayPointer());
+}
+
+bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary* node)
+{
+ bool visitChildren = true;
+ TInfoSinkBase& out = objSink();
+ switch (node->getOp())
+ {
+ case EOpInitialize:
+ if (visit == InVisit)
+ {
+ out << " = ";
+ // RHS of initialize is not being declared.
+ mDeclaringVariables = false;
+ }
+ break;
+ case EOpAssign: writeTriplet(visit, "(", " = ", ")"); break;
+ case EOpAddAssign: writeTriplet(visit, "(", " += ", ")"); break;
+ case EOpSubAssign: writeTriplet(visit, "(", " -= ", ")"); break;
+ case EOpDivAssign: writeTriplet(visit, "(", " /= ", ")"); break;
+ // Notice the fall-through.
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ writeTriplet(visit, "(", " *= ", ")");
+ break;
+
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ writeTriplet(visit, NULL, "[", "]");
+ break;
+ case EOpIndexDirectStruct:
+ if (visit == InVisit)
+ {
+ out << ".";
+ // TODO(alokp): ASSERT
+ out << node->getType().getFieldName();
+ visitChildren = false;
+ }
+ break;
+ case EOpVectorSwizzle:
+ if (visit == InVisit)
+ {
+ out << ".";
+ TIntermAggregate* rightChild = node->getRight()->getAsAggregate();
+ TIntermSequence& sequence = rightChild->getSequence();
+ for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); ++sit)
+ {
+ TIntermConstantUnion* element = (*sit)->getAsConstantUnion();
+ ASSERT(element->getBasicType() == EbtInt);
+ ASSERT(element->getNominalSize() == 1);
+ const ConstantUnion& data = element->getUnionArrayPointer()[0];
+ ASSERT(data.getType() == EbtInt);
+ switch (data.getIConst())
+ {
+ case 0: out << "x"; break;
+ case 1: out << "y"; break;
+ case 2: out << "z"; break;
+ case 3: out << "w"; break;
+ default: UNREACHABLE(); break;
+ }
+ }
+ visitChildren = false;
+ }
+ break;
+
+ case EOpAdd: writeTriplet(visit, "(", " + ", ")"); break;
+ case EOpSub: writeTriplet(visit, "(", " - ", ")"); break;
+ case EOpMul: writeTriplet(visit, "(", " * ", ")"); break;
+ case EOpDiv: writeTriplet(visit, "(", " / ", ")"); break;
+ case EOpMod: UNIMPLEMENTED(); break;
+ case EOpEqual: writeTriplet(visit, "(", " == ", ")"); break;
+ case EOpNotEqual: writeTriplet(visit, "(", " != ", ")"); break;
+ case EOpLessThan: writeTriplet(visit, "(", " < ", ")"); break;
+ case EOpGreaterThan: writeTriplet(visit, "(", " > ", ")"); break;
+ case EOpLessThanEqual: writeTriplet(visit, "(", " <= ", ")"); break;
+ case EOpGreaterThanEqual: writeTriplet(visit, "(", " >= ", ")"); break;
+
+ // Notice the fall-through.
+ case EOpVectorTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesScalar:
+ case EOpMatrixTimesMatrix:
+ writeTriplet(visit, "(", " * ", ")");
+ break;
+
+ case EOpLogicalOr: writeTriplet(visit, "(", " || ", ")"); break;
+ case EOpLogicalXor: writeTriplet(visit, "(", " ^^ ", ")"); break;
+ case EOpLogicalAnd: writeTriplet(visit, "(", " && ", ")"); break;
+ default: UNREACHABLE(); break;
+ }
+
+ return visitChildren;
+}
+
+bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary* node)
+{
+ switch (node->getOp())
+ {
+ case EOpNegative: writeTriplet(visit, "(-", NULL, ")"); break;
+ case EOpVectorLogicalNot: writeTriplet(visit, "not(", NULL, ")"); break;
+ case EOpLogicalNot: writeTriplet(visit, "(!", NULL, ")"); break;
+
+ case EOpPostIncrement: writeTriplet(visit, "(", NULL, "++)"); break;
+ case EOpPostDecrement: writeTriplet(visit, "(", NULL, "--)"); break;
+ case EOpPreIncrement: writeTriplet(visit, "(++", NULL, ")"); break;
+ case EOpPreDecrement: writeTriplet(visit, "(--", NULL, ")"); break;
+
+ case EOpConvIntToBool:
+ case EOpConvFloatToBool:
+ switch (node->getOperand()->getType().getNominalSize())
+ {
+ case 1: writeTriplet(visit, "bool(", NULL, ")"); break;
+ case 2: writeTriplet(visit, "bvec2(", NULL, ")"); break;
+ case 3: writeTriplet(visit, "bvec3(", NULL, ")"); break;
+ case 4: writeTriplet(visit, "bvec4(", NULL, ")"); break;
+ default: UNREACHABLE();
+ }
+ break;
+ case EOpConvBoolToFloat:
+ case EOpConvIntToFloat:
+ switch (node->getOperand()->getType().getNominalSize())
+ {
+ case 1: writeTriplet(visit, "float(", NULL, ")"); break;
+ case 2: writeTriplet(visit, "vec2(", NULL, ")"); break;
+ case 3: writeTriplet(visit, "vec3(", NULL, ")"); break;
+ case 4: writeTriplet(visit, "vec4(", NULL, ")"); break;
+ default: UNREACHABLE();
+ }
+ break;
+ case EOpConvFloatToInt:
+ case EOpConvBoolToInt:
+ switch (node->getOperand()->getType().getNominalSize())
+ {
+ case 1: writeTriplet(visit, "int(", NULL, ")"); break;
+ case 2: writeTriplet(visit, "ivec2(", NULL, ")"); break;
+ case 3: writeTriplet(visit, "ivec3(", NULL, ")"); break;
+ case 4: writeTriplet(visit, "ivec4(", NULL, ")"); break;
+ default: UNREACHABLE();
+ }
+ break;
+
+ case EOpRadians: writeTriplet(visit, "radians(", NULL, ")"); break;
+ case EOpDegrees: writeTriplet(visit, "degrees(", NULL, ")"); break;
+ case EOpSin: writeTriplet(visit, "sin(", NULL, ")"); break;
+ case EOpCos: writeTriplet(visit, "cos(", NULL, ")"); break;
+ case EOpTan: writeTriplet(visit, "tan(", NULL, ")"); break;
+ case EOpAsin: writeTriplet(visit, "asin(", NULL, ")"); break;
+ case EOpAcos: writeTriplet(visit, "acos(", NULL, ")"); break;
+ case EOpAtan: writeTriplet(visit, "atan(", NULL, ")"); break;
+
+ case EOpExp: writeTriplet(visit, "exp(", NULL, ")"); break;
+ case EOpLog: writeTriplet(visit, "log(", NULL, ")"); break;
+ case EOpExp2: writeTriplet(visit, "exp2(", NULL, ")"); break;
+ case EOpLog2: writeTriplet(visit, "log2(", NULL, ")"); break;
+ case EOpSqrt: writeTriplet(visit, "sqrt(", NULL, ")"); break;
+ case EOpInverseSqrt: writeTriplet(visit, "inversesqrt(", NULL, ")"); break;
+
+ case EOpAbs: writeTriplet(visit, "abs(", NULL, ")"); break;
+ case EOpSign: writeTriplet(visit, "sign(", NULL, ")"); break;
+ case EOpFloor: writeTriplet(visit, "floor(", NULL, ")"); break;
+ case EOpCeil: writeTriplet(visit, "ceil(", NULL, ")"); break;
+ case EOpFract: writeTriplet(visit, "fract(", NULL, ")"); break;
+
+ case EOpLength: writeTriplet(visit, "length(", NULL, ")"); break;
+ case EOpNormalize: writeTriplet(visit, "normalize(", NULL, ")"); break;
+
+ case EOpDFdx: writeTriplet(visit, "dFdx(", NULL, ")"); break;
+ case EOpDFdy: writeTriplet(visit, "dFdy(", NULL, ")"); break;
+ case EOpFwidth: writeTriplet(visit, "fwidth(", NULL, ")"); break;
+
+ case EOpAny: writeTriplet(visit, "any(", NULL, ")"); break;
+ case EOpAll: writeTriplet(visit, "all(", NULL, ")"); break;
+
+ default: UNREACHABLE(); break;
+ }
+
+ return true;
+}
+
+bool TOutputGLSLBase::visitSelection(Visit visit, TIntermSelection* node)
+{
+ TInfoSinkBase& out = objSink();
+
+ if (node->usesTernaryOperator())
+ {
+ // Notice two brackets at the beginning and end. The outer ones
+ // encapsulate the whole ternary expression. This preserves the
+ // order of precedence when ternary expressions are used in a
+ // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
+ out << "((";
+ node->getCondition()->traverse(this);
+ out << ") ? (";
+ node->getTrueBlock()->traverse(this);
+ out << ") : (";
+ node->getFalseBlock()->traverse(this);
+ out << "))";
+ }
+ else
+ {
+ out << "if (";
+ node->getCondition()->traverse(this);
+ out << ")\n";
+
+ incrementDepth();
+ visitCodeBlock(node->getTrueBlock());
+
+ if (node->getFalseBlock())
+ {
+ out << "else\n";
+ visitCodeBlock(node->getFalseBlock());
+ }
+ decrementDepth();
+ }
+ return false;
+}
+
+bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate* node)
+{
+ bool visitChildren = true;
+ TInfoSinkBase& out = objSink();
+ switch (node->getOp())
+ {
+ case EOpSequence: {
+ // Scope the sequences except when at the global scope.
+ if (depth > 0) out << "{\n";
+
+ incrementDepth();
+ const TIntermSequence& sequence = node->getSequence();
+ for (TIntermSequence::const_iterator iter = sequence.begin();
+ iter != sequence.end(); ++iter)
+ {
+ TIntermNode* node = *iter;
+ ASSERT(node != NULL);
+ node->traverse(this);
+
+ if (isSingleStatement(node))
+ out << ";\n";
+ }
+ decrementDepth();
+
+ // Scope the sequences except when at the global scope.
+ if (depth > 0) out << "}\n";
+ visitChildren = false;
+ break;
+ }
+ case EOpPrototype: {
+ // Function declaration.
+ ASSERT(visit == PreVisit);
+ TString returnType = getTypeName(node->getType());
+ out << returnType << " " << node->getName();
+
+ out << "(";
+ writeFunctionParameters(node->getSequence());
+ out << ")";
+
+ visitChildren = false;
+ break;
+ }
+ case EOpFunction: {
+ // Function definition.
+ ASSERT(visit == PreVisit);
+ TString returnType = getTypeName(node->getType());
+ TString functionName = TFunction::unmangleName(node->getName());
+ out << returnType << " " << functionName;
+
+ incrementDepth();
+ // Function definition node contains one or two children nodes
+ // representing function parameters and function body. The latter
+ // is not present in case of empty function bodies.
+ const TIntermSequence& sequence = node->getSequence();
+ ASSERT((sequence.size() == 1) || (sequence.size() == 2));
+ TIntermSequence::const_iterator seqIter = sequence.begin();
+
+ // Traverse function parameters.
+ TIntermAggregate* params = (*seqIter)->getAsAggregate();
+ ASSERT(params != NULL);
+ ASSERT(params->getOp() == EOpParameters);
+ params->traverse(this);
+
+ // Traverse function body.
+ TIntermAggregate* body = ++seqIter != sequence.end() ?
+ (*seqIter)->getAsAggregate() : NULL;
+ visitCodeBlock(body);
+ decrementDepth();
+
+ // Fully processed; no need to visit children.
+ visitChildren = false;
+ break;
+ }
+ case EOpFunctionCall:
+ // Function call.
+ if (visit == PreVisit)
+ {
+ TString functionName = TFunction::unmangleName(node->getName());
+ out << functionName << "(";
+ }
+ else if (visit == InVisit)
+ {
+ out << ", ";
+ }
+ else
+ {
+ out << ")";
+ }
+ break;
+ case EOpParameters: {
+ // Function parameters.
+ ASSERT(visit == PreVisit);
+ out << "(";
+ writeFunctionParameters(node->getSequence());
+ out << ")";
+ visitChildren = false;
+ break;
+ }
+ case EOpDeclaration: {
+ // Variable declaration.
+ if (visit == PreVisit)
+ {
+ const TIntermSequence& sequence = node->getSequence();
+ const TIntermTyped* variable = sequence.front()->getAsTyped();
+ writeVariableType(variable->getType());
+ out << " ";
+ mDeclaringVariables = true;
+ }
+ else if (visit == InVisit)
+ {
+ out << ", ";
+ mDeclaringVariables = true;
+ }
+ else
+ {
+ mDeclaringVariables = false;
+ }
+ break;
+ }
+ case EOpConstructFloat: writeTriplet(visit, "float(", NULL, ")"); break;
+ case EOpConstructVec2: writeTriplet(visit, "vec2(", ", ", ")"); break;
+ case EOpConstructVec3: writeTriplet(visit, "vec3(", ", ", ")"); break;
+ case EOpConstructVec4: writeTriplet(visit, "vec4(", ", ", ")"); break;
+ case EOpConstructBool: writeTriplet(visit, "bool(", NULL, ")"); break;
+ case EOpConstructBVec2: writeTriplet(visit, "bvec2(", ", ", ")"); break;
+ case EOpConstructBVec3: writeTriplet(visit, "bvec3(", ", ", ")"); break;
+ case EOpConstructBVec4: writeTriplet(visit, "bvec4(", ", ", ")"); break;
+ case EOpConstructInt: writeTriplet(visit, "int(", NULL, ")"); break;
+ case EOpConstructIVec2: writeTriplet(visit, "ivec2(", ", ", ")"); break;
+ case EOpConstructIVec3: writeTriplet(visit, "ivec3(", ", ", ")"); break;
+ case EOpConstructIVec4: writeTriplet(visit, "ivec4(", ", ", ")"); break;
+ case EOpConstructMat2: writeTriplet(visit, "mat2(", ", ", ")"); break;
+ case EOpConstructMat3: writeTriplet(visit, "mat3(", ", ", ")"); break;
+ case EOpConstructMat4: writeTriplet(visit, "mat4(", ", ", ")"); break;
+ case EOpConstructStruct:
+ if (visit == PreVisit)
+ {
+ const TType& type = node->getType();
+ ASSERT(type.getBasicType() == EbtStruct);
+ out << type.getTypeName() << "(";
+ }
+ else if (visit == InVisit)
+ {
+ out << ", ";
+ }
+ else
+ {
+ out << ")";
+ }
+ break;
+
+ case EOpLessThan: writeTriplet(visit, "lessThan(", ", ", ")"); break;
+ case EOpGreaterThan: writeTriplet(visit, "greaterThan(", ", ", ")"); break;
+ case EOpLessThanEqual: writeTriplet(visit, "lessThanEqual(", ", ", ")"); break;
+ case EOpGreaterThanEqual: writeTriplet(visit, "greaterThanEqual(", ", ", ")"); break;
+ case EOpVectorEqual: writeTriplet(visit, "equal(", ", ", ")"); break;
+ case EOpVectorNotEqual: writeTriplet(visit, "notEqual(", ", ", ")"); break;
+ case EOpComma: writeTriplet(visit, NULL, ", ", NULL); break;
+
+ case EOpMod: writeTriplet(visit, "mod(", ", ", ")"); break;
+ case EOpPow: writeTriplet(visit, "pow(", ", ", ")"); break;
+ case EOpAtan: writeTriplet(visit, "atan(", ", ", ")"); break;
+ case EOpMin: writeTriplet(visit, "min(", ", ", ")"); break;
+ case EOpMax: writeTriplet(visit, "max(", ", ", ")"); break;
+ case EOpClamp: writeTriplet(visit, "clamp(", ", ", ")"); break;
+ case EOpMix: writeTriplet(visit, "mix(", ", ", ")"); break;
+ case EOpStep: writeTriplet(visit, "step(", ", ", ")"); break;
+ case EOpSmoothStep: writeTriplet(visit, "smoothstep(", ", ", ")"); break;
+
+ case EOpDistance: writeTriplet(visit, "distance(", ", ", ")"); break;
+ case EOpDot: writeTriplet(visit, "dot(", ", ", ")"); break;
+ case EOpCross: writeTriplet(visit, "cross(", ", ", ")"); break;
+ case EOpFaceForward: writeTriplet(visit, "faceforward(", ", ", ")"); break;
+ case EOpReflect: writeTriplet(visit, "reflect(", ", ", ")"); break;
+ case EOpRefract: writeTriplet(visit, "refract(", ", ", ")"); break;
+ case EOpMul: writeTriplet(visit, "matrixCompMult(", ", ", ")"); break;
+
+ default: UNREACHABLE(); break;
+ }
+ return visitChildren;
+}
+
+bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node)
+{
+ TInfoSinkBase& out = objSink();
+
+ incrementDepth();
+ // Loop header.
+ TLoopType loopType = node->getType();
+ if (loopType == ELoopFor) // for loop
+ {
+ if (!node->getUnrollFlag()) {
+ out << "for (";
+ if (node->getInit())
+ node->getInit()->traverse(this);
+ out << "; ";
+
+ if (node->getCondition())
+ node->getCondition()->traverse(this);
+ out << "; ";
+
+ if (node->getExpression())
+ node->getExpression()->traverse(this);
+ out << ")\n";
+ }
+ }
+ else if (loopType == ELoopWhile) // while loop
+ {
+ out << "while (";
+ ASSERT(node->getCondition() != NULL);
+ node->getCondition()->traverse(this);
+ out << ")\n";
+ }
+ else // do-while loop
+ {
+ ASSERT(loopType == ELoopDoWhile);
+ out << "do\n";
+ }
+
+ // Loop body.
+ if (node->getUnrollFlag())
+ {
+ TLoopIndexInfo indexInfo;
+ mLoopUnroll.FillLoopIndexInfo(node, indexInfo);
+ mLoopUnroll.Push(indexInfo);
+ while (mLoopUnroll.SatisfiesLoopCondition())
+ {
+ visitCodeBlock(node->getBody());
+ mLoopUnroll.Step();
+ }
+ mLoopUnroll.Pop();
+ }
+ else
+ {
+ visitCodeBlock(node->getBody());
+ }
+
+ // Loop footer.
+ if (loopType == ELoopDoWhile) // do-while loop
+ {
+ out << "while (";
+ ASSERT(node->getCondition() != NULL);
+ node->getCondition()->traverse(this);
+ out << ");\n";
+ }
+ decrementDepth();
+
+ // No need to visit children. They have been already processed in
+ // this function.
+ return false;
+}
+
+bool TOutputGLSLBase::visitBranch(Visit visit, TIntermBranch* node)
+{
+ switch (node->getFlowOp())
+ {
+ case EOpKill: writeTriplet(visit, "discard", NULL, NULL); break;
+ case EOpBreak: writeTriplet(visit, "break", NULL, NULL); break;
+ case EOpContinue: writeTriplet(visit, "continue", NULL, NULL); break;
+ case EOpReturn: writeTriplet(visit, "return ", NULL, NULL); break;
+ default: UNREACHABLE(); break;
+ }
+
+ return true;
+}
+
+void TOutputGLSLBase::visitCodeBlock(TIntermNode* node) {
+ TInfoSinkBase &out = objSink();
+ if (node != NULL)
+ {
+ node->traverse(this);
+ // Single statements not part of a sequence need to be terminated
+ // with semi-colon.
+ if (isSingleStatement(node))
+ out << ";\n";
+ }
+ else
+ {
+ out << "{\n}\n"; // Empty code block.
+ }
+}
diff --git a/src/compiler/OutputGLSLBase.h b/src/compiler/OutputGLSLBase.h
new file mode 100644
index 0000000..efd0b5f
--- /dev/null
+++ b/src/compiler/OutputGLSLBase.h
@@ -0,0 +1,53 @@
+//
+// Copyright (c) 2002-2011 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 CROSSCOMPILERGLSL_OUTPUTGLSLBASE_H_
+#define CROSSCOMPILERGLSL_OUTPUTGLSLBASE_H_
+
+#include <set>
+
+#include "compiler/ForLoopUnroll.h"
+#include "compiler/intermediate.h"
+#include "compiler/ParseHelper.h"
+
+class TOutputGLSLBase : public TIntermTraverser
+{
+public:
+ TOutputGLSLBase(TInfoSinkBase& objSink);
+
+protected:
+ TInfoSinkBase& objSink() { return mObjSink; }
+ void writeTriplet(Visit visit, const char* preStr, const char* inStr, const char* postStr);
+ void writeVariableType(const TType& type);
+ virtual bool writeVariablePrecision(TPrecision precision) = 0;
+ void writeFunctionParameters(const TIntermSequence& args);
+ const ConstantUnion* writeConstantUnion(const TType& type, const ConstantUnion* pConstUnion);
+
+ virtual void visitSymbol(TIntermSymbol* node);
+ virtual void visitConstantUnion(TIntermConstantUnion* node);
+ virtual bool visitBinary(Visit visit, TIntermBinary* node);
+ virtual bool visitUnary(Visit visit, TIntermUnary* node);
+ virtual bool visitSelection(Visit visit, TIntermSelection* node);
+ virtual bool visitAggregate(Visit visit, TIntermAggregate* node);
+ virtual bool visitLoop(Visit visit, TIntermLoop* node);
+ virtual bool visitBranch(Visit visit, TIntermBranch* node);
+
+ void visitCodeBlock(TIntermNode* node);
+
+private:
+ TInfoSinkBase& mObjSink;
+ bool mDeclaringVariables;
+
+ // Structs are declared as the tree is traversed. This set contains all
+ // the structs already declared. It is maintained so that a struct is
+ // declared only once.
+ typedef std::set<TString> DeclaredStructs;
+ DeclaredStructs mDeclaredStructs;
+
+ ForLoopUnroll mLoopUnroll;
+};
+
+#endif // CROSSCOMPILERGLSL_OUTPUTGLSLBASE_H_
diff --git a/src/compiler/ParseHelper.h b/src/compiler/ParseHelper.h
index f5a6c57..0cc0728 100644
--- a/src/compiler/ParseHelper.h
+++ b/src/compiler/ParseHelper.h
@@ -30,13 +30,13 @@
// they can be passed to the parser without needing a global.
//
struct TParseContext {
- TParseContext(TSymbolTable& symt, const TExtensionBehavior& ext, TIntermediate& interm, ShShaderType type, ShShaderSpec spec, int options, const char* sourcePath, TInfoSink& is) :
+ TParseContext(TSymbolTable& symt, TExtensionBehavior& ext, TIntermediate& interm, ShShaderType type, ShShaderSpec spec, int options, const char* sourcePath, TInfoSink& is) :
intermediate(interm), symbolTable(symt), extensionBehavior(ext), infoSink(is), shaderType(type), shaderSpec(spec), compileOptions(options), sourcePath(sourcePath), treeRoot(0),
recoveredFromError(false), numErrors(0), lexAfterType(false), loopNestingLevel(0),
inTypeParen(false), scanner(NULL), contextPragma(true, false) { }
TIntermediate& intermediate; // to hold and build a parse tree
TSymbolTable& symbolTable; // symbol table that goes with the language currently being parsed
- TExtensionBehavior extensionBehavior; // mapping between supported extensions and current behavior.
+ TExtensionBehavior& extensionBehavior; // mapping between supported extensions and current behavior.
TInfoSink& infoSink;
ShShaderType shaderType; // vertex or fragment language (future: pack or unpack)
ShShaderSpec shaderSpec; // The language specification compiler conforms to - GLES2 or WebGL.
diff --git a/src/compiler/ShHandle.h b/src/compiler/ShHandle.h
index 788ce92..bf61d29 100644
--- a/src/compiler/ShHandle.h
+++ b/src/compiler/ShHandle.h
@@ -75,6 +75,8 @@
void mapLongVariableNames(TIntermNode* root);
// Translate to object code.
virtual void translate(TIntermNode* root) = 0;
+ // Get built-in extensions with default behavior.
+ const TExtensionBehavior& getExtensionBehavior() const;
private:
ShShaderType shaderType;
@@ -104,7 +106,8 @@
// destroy the machine dependent objects, which contain the
// above machine independent information.
//
-TCompiler* ConstructCompiler(ShShaderType type, ShShaderSpec spec);
+TCompiler* ConstructCompiler(
+ ShShaderType type, ShShaderSpec spec, ShShaderOutput output);
void DeleteCompiler(TCompiler*);
#endif // _SHHANDLE_INCLUDED_
diff --git a/src/compiler/ShaderLang.cpp b/src/compiler/ShaderLang.cpp
index 051e4ec..ab8799c 100644
--- a/src/compiler/ShaderLang.cpp
+++ b/src/compiler/ShaderLang.cpp
@@ -110,12 +110,13 @@
// Driver calls these to create and destroy compiler objects.
//
ShHandle ShConstructCompiler(ShShaderType type, ShShaderSpec spec,
+ ShShaderOutput output,
const ShBuiltInResources* resources)
{
if (!InitThread())
return 0;
- TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(type, spec));
+ TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(type, spec, output));
TCompiler* compiler = base->getAsCompiler();
if (compiler == 0)
return 0;
diff --git a/src/compiler/TranslatorESSL.cpp b/src/compiler/TranslatorESSL.cpp
new file mode 100644
index 0000000..62b645f
--- /dev/null
+++ b/src/compiler/TranslatorESSL.cpp
@@ -0,0 +1,46 @@
+//
+// Copyright (c) 2002-2011 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/TranslatorESSL.h"
+
+#include "compiler/OutputESSL.h"
+
+TranslatorESSL::TranslatorESSL(ShShaderType type, ShShaderSpec spec)
+ : TCompiler(type, spec) {
+}
+
+void TranslatorESSL::translate(TIntermNode* root) {
+ TInfoSinkBase& sink = getInfoSink().obj;
+
+ // Write built-in extension behaviors.
+ writeExtensionBehavior();
+
+ // FIXME(zmo): no need to emit default precision if all variables emit
+ // their own precision.
+ // http://code.google.com/p/angleproject/issues/detail?id=168
+ if (this->getShaderType() == SH_FRAGMENT_SHADER) {
+ // Write default float precision.
+ sink << "#if defined(GL_FRAGMENT_PRECISION_HIGH)\n"
+ << "precision highp float;\n"
+ << "#else\n"
+ << "precision mediump float;\n"
+ << "#endif\n";
+ }
+
+ // Write translated shader.
+ TOutputESSL outputESSL(sink);
+ root->traverse(&outputESSL);
+}
+
+void TranslatorESSL::writeExtensionBehavior() {
+ TInfoSinkBase& sink = getInfoSink().obj;
+ const TExtensionBehavior& extensionBehavior = getExtensionBehavior();
+ for (TExtensionBehavior::const_iterator iter = extensionBehavior.begin();
+ iter != extensionBehavior.end(); ++iter) {
+ sink << "#extension " << iter->first << " : "
+ << getBehaviorString(iter->second) << "\n";
+ }
+}
diff --git a/src/compiler/TranslatorESSL.h b/src/compiler/TranslatorESSL.h
new file mode 100644
index 0000000..a1196bd
--- /dev/null
+++ b/src/compiler/TranslatorESSL.h
@@ -0,0 +1,23 @@
+//
+// Copyright (c) 2002-2011 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_TRANSLATORESSL_H_
+#define COMPILER_TRANSLATORESSL_H_
+
+#include "compiler/ShHandle.h"
+
+class TranslatorESSL : public TCompiler {
+public:
+ TranslatorESSL(ShShaderType type, ShShaderSpec spec);
+
+protected:
+ virtual void translate(TIntermNode* root);
+
+private:
+ void writeExtensionBehavior();
+};
+
+#endif // COMPILER_TRANSLATORESSL_H_
diff --git a/src/libGLESv2/Shader.cpp b/src/libGLESv2/Shader.cpp
index 21c6385..f66b2c5 100644
--- a/src/libGLESv2/Shader.cpp
+++ b/src/libGLESv2/Shader.cpp
@@ -36,7 +36,7 @@
{
ShBuiltInResources resources;
ShInitBuiltInResources(&resources);
- Context *context = getContext();
+ Context *context = getContext();
resources.MaxVertexAttribs = MAX_VERTEX_ATTRIBS;
resources.MaxVertexUniformVectors = MAX_VERTEX_UNIFORM_VECTORS;
@@ -48,8 +48,8 @@
resources.MaxDrawBuffers = MAX_DRAW_BUFFERS;
resources.OES_standard_derivatives = 1;
- mFragmentCompiler = ShConstructCompiler(SH_FRAGMENT_SHADER, SH_GLES2_SPEC, &resources);
- mVertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, SH_GLES2_SPEC, &resources);
+ mFragmentCompiler = ShConstructCompiler(SH_FRAGMENT_SHADER, SH_GLES2_SPEC, SH_HLSL_OUTPUT, &resources);
+ mVertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, SH_GLES2_SPEC, SH_HLSL_OUTPUT, &resources);
}
}