Edit

kc3-lang/angle/src/compiler/OutputGLSL.cpp

Branch :

  • Show log

    Commit

  • Author : alokp@chromium.org
    Date : 2010-03-31 20:25:53
    Hash : 376e1064
    Message : Added support for loops. Review URL: http://codereview.appspot.com/802047 git-svn-id: https://angleproject.googlecode.com/svn/trunk@91 736b8ea6-26fd-11df-bfd4-992fa37f6226

  • src/compiler/OutputGLSL.cpp
  • //
    // Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    #include "OutputGLSL.h"
    #include "common/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 getIndentationString(int depth)
    {
        TString indentation(depth, ' ');
        return indentation;
    }
    }  // namespace
    
    TOutputGLSL::TOutputGLSL(TParseContext &context)
        : TIntermTraverser(true, true, true),
          writeFullSymbol(false),
          parseContext(context)
    {
    }
    
    // Header declares user-defined structs.
    void TOutputGLSL::header()
    {
        TInfoSinkBase& out = objSink();
    
        TSymbolTableLevel* symbols = parseContext.symbolTable.getGlobalLevel();
        for (TSymbolTableLevel::const_iterator symbolIter = symbols->begin(); symbolIter != symbols->end(); ++symbolIter)
        {
            const TSymbol* symbol = symbolIter->second;
            if (!symbol->isVariable())
                continue;
    
            const TVariable* variable = static_cast<const TVariable*>(symbol);
            if (!variable->isUserType())
                continue;
    
            const TType& type = variable->getType();
            ASSERT(type.getQualifier() == EvqTemporary);
            ASSERT(type.getBasicType() == EbtStruct);
    
            out << "struct " << variable->getName() << "{\n";
            const TTypeList* structure = type.getStruct();
            ASSERT(structure != NULL);
            incrementDepth();
            for (size_t i = 0; i < structure->size(); ++i) {
                const TType* fieldType = (*structure)[i].type;
                ASSERT(fieldType != NULL);
                out << getIndentationString(depth);
                out << getTypeName(*fieldType) << " " << fieldType->getFieldName() << ";\n";
            }
            decrementDepth();
            out << "};\n";
        }
    }
    
    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::visitSymbol(TIntermSymbol* node)
    {
        TInfoSinkBase& out = objSink();
        if (writeFullSymbol)
        {
            TQualifier qualifier = node->getQualifier();
            if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal))
                out << node->getQualifierString() << " ";
     
            out << getTypeName(node->getType()) << " ";
        }
        out << node->getSymbol();
        if (writeFullSymbol && node->getType().isArray())
        {
            out << "[" << node->getType().getArraySize() << "]";
        }
    }
    
    void TOutputGLSL::visitConstantUnion(TIntermConstantUnion* node)
    {
        TInfoSinkBase& out = objSink();
    
        TType type = node->getType();
        int size = type.getObjectSize();
        if (size > 1)
            out << getTypeName(type) << "(";
        for (int i = 0; i < size; ++i) {
            const constUnion& data = node->getUnionArrayPointer()[i];
            switch (data.getType())
            {
                case EbtFloat: out << data.getFConst(); break;
                case EbtInt: out << data.getIConst(); break;
                case EbtBool: out << data.getBConst(); break;
                default: UNREACHABLE(); break;
            }
            if (i != size - 1)
                out << ", ";
        }
        if (size > 1)
            out << ")";
    }
    
    bool TOutputGLSL::visitBinary(Visit visit, TIntermBinary* node)
    {
        bool visitChildren = true;
        TInfoSinkBase& out = objSink();
        switch (node->getOp())
        {
            case EOpAssign: writeTriplet(visit, NULL, " = ", NULL); break;
            case EOpInitialize:
                if (visit == InVisit) {
                    out << " = ";
                    writeFullSymbol= false;
                }
                break;
            case EOpAddAssign: writeTriplet(visit, NULL, " += ", NULL); break;
            case EOpSubAssign: UNIMPLEMENTED(); break;
            case EOpMulAssign: writeTriplet(visit, NULL, " *= ", NULL); break;
            case EOpVectorTimesMatrixAssign: UNIMPLEMENTED(); break;
            case EOpVectorTimesScalarAssign: UNIMPLEMENTED(); break;
            case EOpMatrixTimesScalarAssign: UNIMPLEMENTED(); break;
            case EOpMatrixTimesMatrixAssign: UNIMPLEMENTED(); break;
            case EOpDivAssign: UNIMPLEMENTED(); break;
    
            case EOpIndexDirect: writeTriplet(visit, NULL, "[", "]"); break;
            case EOpIndexIndirect: UNIMPLEMENTED(); 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 constUnion& 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: UNIMPLEMENTED(); break;
            case EOpLessThan: writeTriplet(visit, "(", " < ", ")"); break;
            case EOpGreaterThan: writeTriplet(visit, "(", " > ", ")"); break;
            case EOpLessThanEqual: UNIMPLEMENTED(); break;
            case EOpGreaterThanEqual: UNIMPLEMENTED(); 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: UNIMPLEMENTED(); break;
            case EOpLogicalAnd: UNIMPLEMENTED(); break;
            default: UNREACHABLE(); break;
        }
    
        return visitChildren;
    }
    
    bool TOutputGLSL::visitUnary(Visit visit, TIntermUnary* node)
    {
        TInfoSinkBase& out = objSink();
    
        switch (node->getOp())
        {
            case EOpNegative: writeTriplet(visit, "(-", NULL, ")"); break;
            case EOpVectorLogicalNot: UNIMPLEMENTED(); break;
            case EOpLogicalNot: UNIMPLEMENTED(); break;
    
            case EOpPostIncrement: UNIMPLEMENTED(); break;
            case EOpPostDecrement: UNIMPLEMENTED(); break;
            case EOpPreIncrement: UNIMPLEMENTED(); break;
            case EOpPreDecrement: UNIMPLEMENTED(); break;
    
            case EOpConvIntToBool: UNIMPLEMENTED(); break;
            case EOpConvFloatToBool: UNIMPLEMENTED(); break;
            case EOpConvBoolToFloat: UNIMPLEMENTED(); break;
            case EOpConvIntToFloat: writeTriplet(visit, "float(", NULL, ")"); break;
            case EOpConvFloatToInt: writeTriplet(visit, "int(", NULL, ")"); break;
            case EOpConvBoolToInt: UNIMPLEMENTED(); break;
    
            case EOpRadians: UNIMPLEMENTED(); break;
            case EOpDegrees: UNIMPLEMENTED(); break;
            case EOpSin: writeTriplet(visit, "sin(", NULL, ")"); break;
            case EOpCos: writeTriplet(visit, "cos(", NULL, ")"); break;
            case EOpTan: UNIMPLEMENTED(); break;
            case EOpAsin: UNIMPLEMENTED(); break;
            case EOpAcos: writeTriplet(visit, "acos(", NULL, ")"); break;
            case EOpAtan: UNIMPLEMENTED(); break;
    
            case EOpExp: UNIMPLEMENTED(); break;
            case EOpLog: UNIMPLEMENTED(); break;
            case EOpExp2: UNIMPLEMENTED(); break;
            case EOpLog2: UNIMPLEMENTED(); break;
            case EOpSqrt: UNIMPLEMENTED(); break;
            case EOpInverseSqrt: UNIMPLEMENTED(); break;
    
            case EOpAbs: writeTriplet(visit, "abs(", NULL, ")"); break;
            case EOpSign: UNIMPLEMENTED(); break;
            case EOpFloor: writeTriplet(visit, "floor(", NULL, ")"); break;
            case EOpCeil: UNIMPLEMENTED(); break;
            case EOpFract: UNIMPLEMENTED(); break;
    
            case EOpLength: UNIMPLEMENTED(); break;
            case EOpNormalize: writeTriplet(visit, "normalize(", NULL, ")"); break;
    
            case EOpAny: UNIMPLEMENTED(); break;
            case EOpAll: UNIMPLEMENTED(); break;
    
            default: UNREACHABLE(); break;
        }
    
        return true;
    }
    
    bool TOutputGLSL::visitSelection(Visit visit, TIntermSelection* node)
    {
        TInfoSinkBase& out = objSink();
    
        if (node->usesTernaryOperator())
        {
            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();
            node->getTrueBlock()->traverse(this);
            out << getIndentationString(depth - 2) << "}";
    
            if (node->getFalseBlock())
            {
                out << " else {\n";
                node->getFalseBlock()->traverse(this);
                out << getIndentationString(depth - 2) << "}";
            }
            decrementDepth();
            out << "\n";
        }
        return false;
    }
    
    bool TOutputGLSL::visitAggregate(Visit visit, TIntermAggregate* node)
    {
        TInfoSinkBase& out = objSink();
        switch (node->getOp())
        {
            case EOpSequence:
                if (visit == PreVisit)
                {
                    out << getIndentationString(depth);
                }
                else if (visit == InVisit)
                {
                    out << ";\n";
                    out << getIndentationString(depth - 1);
                }
                else
                {
                    out << ";\n";
                }
                break;
            case EOpComma:
                UNIMPLEMENTED();
                return true;
            case EOpFunction:
                if (visit == PreVisit)
                {
                    TString returnType = getTypeName(node->getType());
                    TString functionName = TFunction::unmangleName(node->getName());
                    out << returnType << " " << functionName;
                }
                else if (visit == InVisit)
                {
                    // Called after traversing function arguments (EOpParameters)
                    // but before traversing function body (EOpSequence).
                    out << "{\n";
                }
                else if (visit == PostVisit)
                {
                    // Called after traversing function body (EOpSequence).
                    out << "}\n";
                }
                break;
            case EOpFunctionCall:
                if (visit == PreVisit)
                {
                    TString functionName = TFunction::unmangleName(node->getName());
                    out << functionName << "(";
                }
                else if (visit == InVisit)
                {
                    out << ", ";
                }
                else
                {
                    out << ")";
                }
                break;
            case EOpParameters:
                if (visit == PreVisit)
                {
                    out << "(";
                    writeFullSymbol = true;
                }
                else if (visit == InVisit)
                {
                    out << ", ";
                }
                else
                {
                    out << ")";
                    writeFullSymbol = false;
                }
                break;
            case EOpDeclaration:
                if (visit == PreVisit)
                {
                    writeFullSymbol = true;
                }
                else if (visit == InVisit)
                {
                    out << ", ";
                    writeFullSymbol = false;
                }
                else
                {
                    writeFullSymbol = false;
                }
                break;
    
            case EOpConstructFloat: UNIMPLEMENTED(); break;
    	case EOpConstructVec2: writeTriplet(visit, "vec2(", ", ", ")"); break;
    	case EOpConstructVec3: writeTriplet(visit, "vec3(", ", ", ")"); break;
    	case EOpConstructVec4: writeTriplet(visit, "vec4(", ", ", ")"); break;
    	case EOpConstructBool: UNIMPLEMENTED(); break;
    	case EOpConstructBVec2: UNIMPLEMENTED(); break;
    	case EOpConstructBVec3: UNIMPLEMENTED(); break;
    	case EOpConstructBVec4: UNIMPLEMENTED(); break;
    	case EOpConstructInt: UNIMPLEMENTED(); break;
    	case EOpConstructIVec2: UNIMPLEMENTED(); break;
    	case EOpConstructIVec3: UNIMPLEMENTED(); break;
    	case EOpConstructIVec4: UNIMPLEMENTED(); break;
    	case EOpConstructMat2: UNIMPLEMENTED(); break;
    	case EOpConstructMat3: UNIMPLEMENTED(); break;
    	case EOpConstructMat4: writeTriplet(visit, "mat4(", ", ", ")"); break;
    	case EOpConstructStruct: UNIMPLEMENTED(); break;
    
    	case EOpLessThan: UNIMPLEMENTED(); break;
    	case EOpGreaterThan: UNIMPLEMENTED(); break;
    	case EOpLessThanEqual: UNIMPLEMENTED(); break;
    	case EOpGreaterThanEqual: UNIMPLEMENTED(); break;
    	case EOpVectorEqual: UNIMPLEMENTED(); break;
    	case EOpVectorNotEqual: UNIMPLEMENTED(); break;
    
    	case EOpMod: writeTriplet(visit, "mod(", ", ", ")"); break;
    	case EOpPow: writeTriplet(visit, "pow(", ", ", ")"); break;
    
    	case EOpAtan: UNIMPLEMENTED(); 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: UNIMPLEMENTED(); break;
    	case EOpSmoothStep: UNIMPLEMENTED(); break;
    
    	case EOpDistance: UNIMPLEMENTED(); break;
    	case EOpDot: writeTriplet(visit, "dot(", ", ", ")"); break;
    	case EOpCross: UNIMPLEMENTED(); break;
    	case EOpFaceForward: UNIMPLEMENTED(); break;
    	case EOpReflect: writeTriplet(visit, "reflect(", ", ", ")"); break;
    	case EOpRefract: UNIMPLEMENTED(); break;
    	case EOpMul: UNIMPLEMENTED(); break;
    
            default: UNREACHABLE(); break;
        }
        return true;
    }
    
    bool TOutputGLSL::visitLoop(Visit visit, TIntermLoop* node)
    {
        TInfoSinkBase& out = objSink();
    
        // Loop header.
        if (node->testFirst())  // for loop
        {
            out << "for (";
            if (node->getInit())
                node->getInit()->traverse(this);
            out << "; ";
    
            ASSERT(node->getTest() != NULL);
            node->getTest()->traverse(this);
            out << "; ";
    
            if (node->getTerminal())
                node->getTerminal()->traverse(this);
            out << ") {\n";
        }
        else  // do-while loop
        {
            out << "do {\n";
        }
    
        // Loop body.
        if (node->getBody())
            node->getBody()->traverse(this);
    
        // Loop footer.
        if (node->testFirst())  // for loop
        {
            out << "}\n";
        }
        else  // do-while loop
        {
            out << "} while (";
            ASSERT(node->getTest() != NULL);
            node->getTest()->traverse(this);
            out << ");\n";
        }
    
        // No need to visit children. They have been already processed in
        // this function.
        return false;
    }
    
    bool TOutputGLSL::visitBranch(Visit visit, TIntermBranch* node)
    {
        TInfoSinkBase &out = objSink();
    
        switch (node->getFlowOp())
        {
            case EOpKill: UNIMPLEMENTED(); break;
            case EOpBreak: UNIMPLEMENTED(); break;
            case EOpContinue: UNIMPLEMENTED(); break;
            case EOpReturn:
                if (visit == PreVisit)
                    out << "return ";
                break;
            default: UNREACHABLE(); break;
        }
    
        return true;
    }