Commit 5601ea0d by zmo@google.com

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
parent db5d5f66
...@@ -24,6 +24,7 @@ Google Inc. ...@@ -24,6 +24,7 @@ Google Inc.
Kenneth Russell Kenneth Russell
Ben Vanik Ben Vanik
Adrienne Walker Adrienne Walker
Zhenyao Mo
Mozilla Corp. Mozilla Corp.
Vladimir Vukicevic Vladimir Vukicevic
......
...@@ -17,7 +17,7 @@ extern "C" { ...@@ -17,7 +17,7 @@ extern "C" {
// Version number for shader translation API. // Version number for shader translation API.
// It is incremented everytime the API changes. // 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 // The names of the following enums have been derived by replacing GL prefix
...@@ -36,6 +36,12 @@ typedef enum { ...@@ -36,6 +36,12 @@ typedef enum {
} ShShaderSpec; } ShShaderSpec;
typedef enum { typedef enum {
SH_ESSL_OUTPUT = 0x8B45,
SH_GLSL_OUTPUT = 0x8B46,
SH_HLSL_OUTPUT = 0x8B47
} ShShaderOutput;
typedef enum {
SH_NONE = 0, SH_NONE = 0,
SH_INT = 0x1404, SH_INT = 0x1404,
SH_FLOAT = 0x1406, SH_FLOAT = 0x1406,
...@@ -128,13 +134,17 @@ typedef void* ShHandle; ...@@ -128,13 +134,17 @@ typedef void* ShHandle;
// //
// Driver calls these to create and destroy compiler objects. // 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: // Parameters:
// type: Specifies the type of shader - SH_FRAGMENT_SHADER or SH_VERTEX_SHADER. // type: Specifies the type of shader - SH_FRAGMENT_SHADER or SH_VERTEX_SHADER.
// spec: Specifies the language spec the compiler must conform to - // spec: Specifies the language spec the compiler must conform to -
// SH_GLES2_SPEC or SH_WEBGL_SPEC. // 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. // resources: Specifies the built-in resources.
ShHandle ShConstructCompiler(ShShaderType type, ShShaderSpec spec, ShHandle ShConstructCompiler(ShShaderType type, ShShaderSpec spec,
ShShaderOutput output,
const ShBuiltInResources* resources); const ShBuiltInResources* resources);
void ShDestruct(ShHandle handle); void ShDestruct(ShHandle handle);
......
...@@ -66,6 +66,7 @@ int main(int argc, char* argv[]) ...@@ -66,6 +66,7 @@ int main(int argc, char* argv[])
char* buffer = 0; char* buffer = 0;
int bufferLen = 0; int bufferLen = 0;
int numAttribs = 0, numUniforms = 0; int numAttribs = 0, numUniforms = 0;
ShShaderOutput output = SH_ESSL_OUTPUT;
ShInitialize(); ShInitialize();
...@@ -81,6 +82,18 @@ int main(int argc, char* argv[]) ...@@ -81,6 +82,18 @@ int main(int argc, char* argv[])
case 'm': compileOptions |= SH_MAP_LONG_VARIABLE_NAMES; break; case 'm': compileOptions |= SH_MAP_LONG_VARIABLE_NAMES; break;
case 'o': compileOptions |= SH_OBJECT_CODE; break; case 'o': compileOptions |= SH_OBJECT_CODE; break;
case 'u': compileOptions |= SH_ATTRIBUTES_UNIFORMS; 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; default: failCode = EFailUsage;
} }
} else { } else {
...@@ -88,12 +101,14 @@ int main(int argc, char* argv[]) ...@@ -88,12 +101,14 @@ int main(int argc, char* argv[])
switch (FindShaderType(argv[0])) { switch (FindShaderType(argv[0])) {
case SH_VERTEX_SHADER: case SH_VERTEX_SHADER:
if (vertexCompiler == 0) if (vertexCompiler == 0)
vertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, SH_GLES2_SPEC, &resources); vertexCompiler = ShConstructCompiler(
SH_VERTEX_SHADER, SH_GLES2_SPEC, output, &resources);
compiler = vertexCompiler; compiler = vertexCompiler;
break; break;
case SH_FRAGMENT_SHADER: case SH_FRAGMENT_SHADER:
if (fragmentCompiler == 0) if (fragmentCompiler == 0)
fragmentCompiler = ShConstructCompiler(SH_FRAGMENT_SHADER, SH_GLES2_SPEC, &resources); fragmentCompiler = ShConstructCompiler(
SH_FRAGMENT_SHADER, SH_GLES2_SPEC, output, &resources);
compiler = fragmentCompiler; compiler = fragmentCompiler;
break; break;
default: break; default: break;
...@@ -159,12 +174,15 @@ int main(int argc, char* argv[]) ...@@ -159,12 +174,15 @@ int main(int argc, char* argv[])
// //
void usage() void usage()
{ {
printf("Usage: translate [-i -m -o -u] file1 file2 ...\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" "Where: filename : filename ending in .frag or .vert\n"
" -i = print intermediate tree\n" " -i : print intermediate tree\n"
" -m = map long variable names\n" " -m : map long variable names\n"
" -o = print translated code\n" " -o : print translated code\n"
" -u = print active attribs and uniforms\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");
} }
// //
......
...@@ -104,8 +104,14 @@ ...@@ -104,8 +104,14 @@
'compiler/CodeGenGLSL.cpp', 'compiler/CodeGenGLSL.cpp',
'compiler/ForLoopUnroll.cpp', 'compiler/ForLoopUnroll.cpp',
'compiler/ForLoopUnroll.h', 'compiler/ForLoopUnroll.h',
'compiler/OutputESSL.cpp',
'compiler/OutputESSL.h',
'compiler/OutputGLSLBase.cpp',
'compiler/OutputGLSLBase.h',
'compiler/OutputGLSL.cpp', 'compiler/OutputGLSL.cpp',
'compiler/OutputGLSL.h', 'compiler/OutputGLSL.h',
'compiler/TranslatorESSL.cpp',
'compiler/TranslatorESSL.h',
'compiler/TranslatorGLSL.cpp', 'compiler/TranslatorGLSL.cpp',
'compiler/TranslatorGLSL.h', 'compiler/TranslatorGLSL.h',
'compiler/VersionGLSL.cpp', 'compiler/VersionGLSL.cpp',
......
#define MAJOR_VERSION 0 #define MAJOR_VERSION 0
#define MINOR_VERSION 0 #define MINOR_VERSION 0
#define BUILD_VERSION 0 #define BUILD_VERSION 0
#define BUILD_REVISION 686 #define BUILD_REVISION 687
#define STRINGIFY(x) #x #define STRINGIFY(x) #x
#define MACRO_STRINGIFY(x) STRINGIFY(x) #define MACRO_STRINGIFY(x) STRINGIFY(x)
......
...@@ -5,15 +5,24 @@ ...@@ -5,15 +5,24 @@
// //
#include "compiler/TranslatorGLSL.h" #include "compiler/TranslatorGLSL.h"
#include "compiler/TranslatorESSL.h"
// //
// This function must be provided to create the actual // This function must be provided to create the actual
// compile object used by higher level code. It returns // compile object used by higher level code. It returns
// a subclass of TCompiler. // 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;
}
} }
// //
......
...@@ -11,9 +11,15 @@ ...@@ -11,9 +11,15 @@
// compile object used by higher level code. It returns // compile object used by higher level code. It returns
// a subclass of TCompiler. // 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;
}
} }
// //
......
...@@ -215,3 +215,8 @@ int TCompiler::getMappedNameMaxLength() const ...@@ -215,3 +215,8 @@ int TCompiler::getMappedNameMaxLength() const
{ {
return MAX_IDENTIFIER_NAME_SIZE + 1; return MAX_IDENTIFIER_NAME_SIZE + 1;
} }
const TExtensionBehavior& TCompiler::getExtensionBehavior() const
{
return extensionBehavior;
}
...@@ -16,6 +16,22 @@ typedef enum { ...@@ -16,6 +16,22 @@ typedef enum {
EBhDisable EBhDisable
} TBehavior; } 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; typedef TMap<TString, TBehavior> TExtensionBehavior;
#endif // _EXTENSION_TABLE_INCLUDED_ #endif // _EXTENSION_TABLE_INCLUDED_
//
// 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;
}
//
// 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_
...@@ -5,706 +5,13 @@ ...@@ -5,706 +5,13 @@
// //
#include "compiler/OutputGLSL.h" #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) TOutputGLSL::TOutputGLSL(TInfoSinkBase& objSink)
: TIntermTraverser(true, true, true), : TOutputGLSLBase(objSink)
mObjSink(objSink),
mDeclaringVariables(false)
{
}
void TOutputGLSL::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 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, bool TOutputGLSL::writeVariablePrecision(TPrecision)
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; 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.
}
}
// //
// 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 // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// //
...@@ -7,46 +7,15 @@ ...@@ -7,46 +7,15 @@
#ifndef CROSSCOMPILERGLSL_OUTPUTGLSL_H_ #ifndef CROSSCOMPILERGLSL_OUTPUTGLSL_H_
#define CROSSCOMPILERGLSL_OUTPUTGLSL_H_ #define CROSSCOMPILERGLSL_OUTPUTGLSL_H_
#include <set> #include "compiler/OutputGLSLBase.h"
#include "compiler/ForLoopUnroll.h" class TOutputGLSL : public TOutputGLSLBase
#include "compiler/intermediate.h"
#include "compiler/ParseHelper.h"
class TOutputGLSL : public TIntermTraverser
{ {
public: public:
TOutputGLSL(TInfoSinkBase& objSink); TOutputGLSL(TInfoSinkBase& objSink);
protected: protected:
TInfoSinkBase& objSink() { return mObjSink; } virtual bool writeVariablePrecision(TPrecision);
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;
}; };
#endif // CROSSCOMPILERGLSL_OUTPUTGLSL_H_ #endif // CROSSCOMPILERGLSL_OUTPUTGLSL_H_
//
// 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.
}
}
//
// 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_
...@@ -30,13 +30,13 @@ struct TPragma { ...@@ -30,13 +30,13 @@ struct TPragma {
// they can be passed to the parser without needing a global. // they can be passed to the parser without needing a global.
// //
struct TParseContext { 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), 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), recoveredFromError(false), numErrors(0), lexAfterType(false), loopNestingLevel(0),
inTypeParen(false), scanner(NULL), contextPragma(true, false) { } inTypeParen(false), scanner(NULL), contextPragma(true, false) { }
TIntermediate& intermediate; // to hold and build a parse tree TIntermediate& intermediate; // to hold and build a parse tree
TSymbolTable& symbolTable; // symbol table that goes with the language currently being parsed 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; TInfoSink& infoSink;
ShShaderType shaderType; // vertex or fragment language (future: pack or unpack) ShShaderType shaderType; // vertex or fragment language (future: pack or unpack)
ShShaderSpec shaderSpec; // The language specification compiler conforms to - GLES2 or WebGL. ShShaderSpec shaderSpec; // The language specification compiler conforms to - GLES2 or WebGL.
......
...@@ -75,6 +75,8 @@ protected: ...@@ -75,6 +75,8 @@ protected:
void mapLongVariableNames(TIntermNode* root); void mapLongVariableNames(TIntermNode* root);
// Translate to object code. // Translate to object code.
virtual void translate(TIntermNode* root) = 0; virtual void translate(TIntermNode* root) = 0;
// Get built-in extensions with default behavior.
const TExtensionBehavior& getExtensionBehavior() const;
private: private:
ShShaderType shaderType; ShShaderType shaderType;
...@@ -104,7 +106,8 @@ private: ...@@ -104,7 +106,8 @@ private:
// destroy the machine dependent objects, which contain the // destroy the machine dependent objects, which contain the
// above machine independent information. // above machine independent information.
// //
TCompiler* ConstructCompiler(ShShaderType type, ShShaderSpec spec); TCompiler* ConstructCompiler(
ShShaderType type, ShShaderSpec spec, ShShaderOutput output);
void DeleteCompiler(TCompiler*); void DeleteCompiler(TCompiler*);
#endif // _SHHANDLE_INCLUDED_ #endif // _SHHANDLE_INCLUDED_
...@@ -110,12 +110,13 @@ void ShInitBuiltInResources(ShBuiltInResources* resources) ...@@ -110,12 +110,13 @@ void ShInitBuiltInResources(ShBuiltInResources* resources)
// Driver calls these to create and destroy compiler objects. // Driver calls these to create and destroy compiler objects.
// //
ShHandle ShConstructCompiler(ShShaderType type, ShShaderSpec spec, ShHandle ShConstructCompiler(ShShaderType type, ShShaderSpec spec,
ShShaderOutput output,
const ShBuiltInResources* resources) const ShBuiltInResources* resources)
{ {
if (!InitThread()) if (!InitThread())
return 0; return 0;
TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(type, spec)); TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(type, spec, output));
TCompiler* compiler = base->getAsCompiler(); TCompiler* compiler = base->getAsCompiler();
if (compiler == 0) if (compiler == 0)
return 0; return 0;
......
//
// 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";
}
}
//
// 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_
...@@ -36,7 +36,7 @@ Shader::Shader(ResourceManager *manager, GLuint handle) : mHandle(handle), mReso ...@@ -36,7 +36,7 @@ Shader::Shader(ResourceManager *manager, GLuint handle) : mHandle(handle), mReso
{ {
ShBuiltInResources resources; ShBuiltInResources resources;
ShInitBuiltInResources(&resources); ShInitBuiltInResources(&resources);
Context *context = getContext(); Context *context = getContext();
resources.MaxVertexAttribs = MAX_VERTEX_ATTRIBS; resources.MaxVertexAttribs = MAX_VERTEX_ATTRIBS;
resources.MaxVertexUniformVectors = MAX_VERTEX_UNIFORM_VECTORS; resources.MaxVertexUniformVectors = MAX_VERTEX_UNIFORM_VECTORS;
...@@ -48,8 +48,8 @@ Shader::Shader(ResourceManager *manager, GLuint handle) : mHandle(handle), mReso ...@@ -48,8 +48,8 @@ Shader::Shader(ResourceManager *manager, GLuint handle) : mHandle(handle), mReso
resources.MaxDrawBuffers = MAX_DRAW_BUFFERS; resources.MaxDrawBuffers = MAX_DRAW_BUFFERS;
resources.OES_standard_derivatives = 1; resources.OES_standard_derivatives = 1;
mFragmentCompiler = ShConstructCompiler(SH_FRAGMENT_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, &resources); mVertexCompiler = ShConstructCompiler(SH_VERTEX_SHADER, SH_GLES2_SPEC, SH_HLSL_OUTPUT, &resources);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment