summaryrefslogtreecommitdiffstats
path: root/gfx/angle/src/compiler/translator/EmulatePrecision.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/src/compiler/translator/EmulatePrecision.cpp')
-rwxr-xr-xgfx/angle/src/compiler/translator/EmulatePrecision.cpp726
1 files changed, 726 insertions, 0 deletions
diff --git a/gfx/angle/src/compiler/translator/EmulatePrecision.cpp b/gfx/angle/src/compiler/translator/EmulatePrecision.cpp
new file mode 100755
index 000000000..c3c49021c
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/EmulatePrecision.cpp
@@ -0,0 +1,726 @@
+//
+// Copyright (c) 2002-2014 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/translator/EmulatePrecision.h"
+
+#include <memory>
+
+namespace sh
+{
+
+namespace
+{
+
+class RoundingHelperWriter : angle::NonCopyable
+{
+ public:
+ static RoundingHelperWriter *createHelperWriter(const ShShaderOutput outputLanguage);
+
+ void writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion);
+ void writeCompoundAssignmentHelper(TInfoSinkBase &sink,
+ const char *lType,
+ const char *rType,
+ const char *opStr,
+ const char *opNameStr);
+
+ virtual ~RoundingHelperWriter() {}
+
+ protected:
+ RoundingHelperWriter(const ShShaderOutput outputLanguage) : mOutputLanguage(outputLanguage) {}
+ RoundingHelperWriter() = delete;
+
+ const ShShaderOutput mOutputLanguage;
+
+ private:
+ virtual std::string getTypeString(const char *glslType) = 0;
+ virtual void writeFloatRoundingHelpers(TInfoSinkBase &sink) = 0;
+ virtual void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) = 0;
+ virtual void writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName) = 0;
+};
+
+class RoundingHelperWriterGLSL : public RoundingHelperWriter
+{
+ public:
+ RoundingHelperWriterGLSL(const ShShaderOutput outputLanguage)
+ : RoundingHelperWriter(outputLanguage)
+ {
+ }
+
+ private:
+ std::string getTypeString(const char *glslType) override;
+ void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
+ void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
+ void writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName) override;
+};
+
+class RoundingHelperWriterESSL : public RoundingHelperWriterGLSL
+{
+ public:
+ RoundingHelperWriterESSL(const ShShaderOutput outputLanguage)
+ : RoundingHelperWriterGLSL(outputLanguage)
+ {
+ }
+
+ private:
+ std::string getTypeString(const char *glslType) override;
+};
+
+class RoundingHelperWriterHLSL : public RoundingHelperWriter
+{
+ public:
+ RoundingHelperWriterHLSL(const ShShaderOutput outputLanguage)
+ : RoundingHelperWriter(outputLanguage)
+ {
+ }
+
+ private:
+ std::string getTypeString(const char *glslType) override;
+ void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
+ void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
+ void writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName) override;
+};
+
+RoundingHelperWriter *RoundingHelperWriter::createHelperWriter(const ShShaderOutput outputLanguage)
+{
+ ASSERT(EmulatePrecision::SupportedInLanguage(outputLanguage));
+ switch (outputLanguage)
+ {
+ case SH_HLSL_4_1_OUTPUT:
+ return new RoundingHelperWriterHLSL(outputLanguage);
+ case SH_ESSL_OUTPUT:
+ return new RoundingHelperWriterESSL(outputLanguage);
+ default:
+ return new RoundingHelperWriterGLSL(outputLanguage);
+ }
+}
+
+void RoundingHelperWriter::writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion)
+{
+ // Write the angle_frm functions that round floating point numbers to
+ // half precision, and angle_frl functions that round them to minimum lowp
+ // precision.
+
+ writeFloatRoundingHelpers(sink);
+ writeVectorRoundingHelpers(sink, 2);
+ writeVectorRoundingHelpers(sink, 3);
+ writeVectorRoundingHelpers(sink, 4);
+ if (shaderVersion > 100)
+ {
+ for (unsigned int columns = 2; columns <= 4; ++columns)
+ {
+ for (unsigned int rows = 2; rows <= 4; ++rows)
+ {
+ writeMatrixRoundingHelper(sink, columns, rows, "angle_frm");
+ writeMatrixRoundingHelper(sink, columns, rows, "angle_frl");
+ }
+ }
+ }
+ else
+ {
+ for (unsigned int size = 2; size <= 4; ++size)
+ {
+ writeMatrixRoundingHelper(sink, size, size, "angle_frm");
+ writeMatrixRoundingHelper(sink, size, size, "angle_frl");
+ }
+ }
+}
+
+void RoundingHelperWriter::writeCompoundAssignmentHelper(TInfoSinkBase &sink,
+ const char *lType,
+ const char *rType,
+ const char *opStr,
+ const char *opNameStr)
+{
+ std::string lTypeStr = getTypeString(lType);
+ std::string rTypeStr = getTypeString(rType);
+
+ // Note that y should be passed through angle_frm at the function call site,
+ // but x can't be passed through angle_frm there since it is an inout parameter.
+ // So only pass x and the result through angle_frm here.
+ // clang-format off
+ sink <<
+ lTypeStr << " angle_compound_" << opNameStr << "_frm(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
+ " x = angle_frm(angle_frm(x) " << opStr << " y);\n"
+ " return x;\n"
+ "}\n";
+ sink <<
+ lTypeStr << " angle_compound_" << opNameStr << "_frl(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
+ " x = angle_frl(angle_frm(x) " << opStr << " y);\n"
+ " return x;\n"
+ "}\n";
+ // clang-format on
+}
+
+std::string RoundingHelperWriterGLSL::getTypeString(const char *glslType)
+{
+ return glslType;
+}
+
+std::string RoundingHelperWriterESSL::getTypeString(const char *glslType)
+{
+ std::stringstream typeStrStr;
+ typeStrStr << "highp " << glslType;
+ return typeStrStr.str();
+}
+
+void RoundingHelperWriterGLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
+{
+ // Unoptimized version of angle_frm for single floats:
+ //
+ // int webgl_maxNormalExponent(in int exponentBits)
+ // {
+ // int possibleExponents = int(exp2(float(exponentBits)));
+ // int exponentBias = possibleExponents / 2 - 1;
+ // int allExponentBitsOne = possibleExponents - 1;
+ // return (allExponentBitsOne - 1) - exponentBias;
+ // }
+ //
+ // float angle_frm(in float x)
+ // {
+ // int mantissaBits = 10;
+ // int exponentBits = 5;
+ // float possibleMantissas = exp2(float(mantissaBits));
+ // float mantissaMax = 2.0 - 1.0 / possibleMantissas;
+ // int maxNE = webgl_maxNormalExponent(exponentBits);
+ // float max = exp2(float(maxNE)) * mantissaMax;
+ // if (x > max)
+ // {
+ // return max;
+ // }
+ // if (x < -max)
+ // {
+ // return -max;
+ // }
+ // float exponent = floor(log2(abs(x)));
+ // if (abs(x) == 0.0 || exponent < -float(maxNE))
+ // {
+ // return 0.0 * sign(x)
+ // }
+ // x = x * exp2(-(exponent - float(mantissaBits)));
+ // x = sign(x) * floor(abs(x));
+ // return x * exp2(exponent - float(mantissaBits));
+ // }
+
+ // All numbers with a magnitude less than 2^-15 are subnormal, and are
+ // flushed to zero.
+
+ // Note the constant numbers below:
+ // a) 65504 is the maximum possible mantissa (1.1111111111 in binary) times
+ // 2^15, the maximum normal exponent.
+ // b) 10.0 is the number of mantissa bits.
+ // c) -25.0 is the minimum normal half-float exponent -15.0 minus the number
+ // of mantissa bits.
+ // d) + 1e-30 is to make sure the argument of log2() won't be zero. It can
+ // only affect the result of log2 on x where abs(x) < 1e-22. Since these
+ // numbers will be flushed to zero either way (2^-15 is the smallest
+ // normal positive number), this does not introduce any error.
+
+ std::string floatType = getTypeString("float");
+
+ // clang-format off
+ sink <<
+ floatType << " angle_frm(in " << floatType << " x) {\n"
+ " x = clamp(x, -65504.0, 65504.0);\n"
+ " " << floatType << " exponent = floor(log2(abs(x) + 1e-30)) - 10.0;\n"
+ " bool isNonZero = (exponent >= -25.0);\n"
+ " x = x * exp2(-exponent);\n"
+ " x = sign(x) * floor(abs(x));\n"
+ " return x * exp2(exponent) * float(isNonZero);\n"
+ "}\n";
+
+ sink <<
+ floatType << " angle_frl(in " << floatType << " x) {\n"
+ " x = clamp(x, -2.0, 2.0);\n"
+ " x = x * 256.0;\n"
+ " x = sign(x) * floor(abs(x));\n"
+ " return x * 0.00390625;\n"
+ "}\n";
+ // clang-format on
+}
+
+void RoundingHelperWriterGLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
+ const unsigned int size)
+{
+ std::stringstream vecTypeStrStr;
+ vecTypeStrStr << "vec" << size;
+ std::string vecType = getTypeString(vecTypeStrStr.str().c_str());
+
+ // clang-format off
+ sink <<
+ vecType << " angle_frm(in " << vecType << " v) {\n"
+ " v = clamp(v, -65504.0, 65504.0);\n"
+ " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
+ " bvec" << size << " isNonZero = greaterThanEqual(exponent, vec" << size << "(-25.0));\n"
+ " v = v * exp2(-exponent);\n"
+ " v = sign(v) * floor(abs(v));\n"
+ " return v * exp2(exponent) * vec" << size << "(isNonZero);\n"
+ "}\n";
+
+ sink <<
+ vecType << " angle_frl(in " << vecType << " v) {\n"
+ " v = clamp(v, -2.0, 2.0);\n"
+ " v = v * 256.0;\n"
+ " v = sign(v) * floor(abs(v));\n"
+ " return v * 0.00390625;\n"
+ "}\n";
+ // clang-format on
+}
+
+void RoundingHelperWriterGLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName)
+{
+ std::stringstream matTypeStrStr;
+ matTypeStrStr << "mat" << columns;
+ if (rows != columns)
+ {
+ matTypeStrStr << "x" << rows;
+ }
+ std::string matType = getTypeString(matTypeStrStr.str().c_str());
+
+ sink << matType << " " << functionName << "(in " << matType << " m) {\n"
+ << " " << matType << " rounded;\n";
+
+ for (unsigned int i = 0; i < columns; ++i)
+ {
+ sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
+ }
+
+ sink << " return rounded;\n"
+ "}\n";
+}
+
+static const char *GetHLSLTypeStr(const char *floatTypeStr)
+{
+ if (strcmp(floatTypeStr, "float") == 0)
+ {
+ return "float";
+ }
+ if (strcmp(floatTypeStr, "vec2") == 0)
+ {
+ return "float2";
+ }
+ if (strcmp(floatTypeStr, "vec3") == 0)
+ {
+ return "float3";
+ }
+ if (strcmp(floatTypeStr, "vec4") == 0)
+ {
+ return "float4";
+ }
+ if (strcmp(floatTypeStr, "mat2") == 0)
+ {
+ return "float2x2";
+ }
+ if (strcmp(floatTypeStr, "mat3") == 0)
+ {
+ return "float3x3";
+ }
+ if (strcmp(floatTypeStr, "mat4") == 0)
+ {
+ return "float4x4";
+ }
+ if (strcmp(floatTypeStr, "mat2x3") == 0)
+ {
+ return "float2x3";
+ }
+ if (strcmp(floatTypeStr, "mat2x4") == 0)
+ {
+ return "float2x4";
+ }
+ if (strcmp(floatTypeStr, "mat3x2") == 0)
+ {
+ return "float3x2";
+ }
+ if (strcmp(floatTypeStr, "mat3x4") == 0)
+ {
+ return "float3x4";
+ }
+ if (strcmp(floatTypeStr, "mat4x2") == 0)
+ {
+ return "float4x2";
+ }
+ if (strcmp(floatTypeStr, "mat4x3") == 0)
+ {
+ return "float4x3";
+ }
+ UNREACHABLE();
+ return nullptr;
+}
+
+std::string RoundingHelperWriterHLSL::getTypeString(const char *glslType)
+{
+ return GetHLSLTypeStr(glslType);
+}
+
+void RoundingHelperWriterHLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
+{
+ // In HLSL scalars are the same as 1-vectors.
+ writeVectorRoundingHelpers(sink, 1);
+}
+
+void RoundingHelperWriterHLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
+ const unsigned int size)
+{
+ std::stringstream vecTypeStrStr;
+ vecTypeStrStr << "float" << size;
+ std::string vecType = vecTypeStrStr.str();
+
+ // clang-format off
+ sink <<
+ vecType << " angle_frm(" << vecType << " v) {\n"
+ " v = clamp(v, -65504.0, 65504.0);\n"
+ " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
+ " bool" << size << " isNonZero = exponent < -25.0;\n"
+ " v = v * exp2(-exponent);\n"
+ " v = sign(v) * floor(abs(v));\n"
+ " return v * exp2(exponent) * (float" << size << ")(isNonZero);\n"
+ "}\n";
+
+ sink <<
+ vecType << " angle_frl(" << vecType << " v) {\n"
+ " v = clamp(v, -2.0, 2.0);\n"
+ " v = v * 256.0;\n"
+ " v = sign(v) * floor(abs(v));\n"
+ " return v * 0.00390625;\n"
+ "}\n";
+ // clang-format on
+}
+
+void RoundingHelperWriterHLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName)
+{
+ std::stringstream matTypeStrStr;
+ matTypeStrStr << "float" << columns << "x" << rows;
+ std::string matType = matTypeStrStr.str();
+
+ sink << matType << " " << functionName << "(" << matType << " m) {\n"
+ << " " << matType << " rounded;\n";
+
+ for (unsigned int i = 0; i < columns; ++i)
+ {
+ sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
+ }
+
+ sink << " return rounded;\n"
+ "}\n";
+}
+
+bool canRoundFloat(const TType &type)
+{
+ return type.getBasicType() == EbtFloat && !type.isArray() &&
+ (type.getPrecision() == EbpLow || type.getPrecision() == EbpMedium);
+}
+
+TIntermAggregate *createInternalFunctionCallNode(TString name, TIntermNode *child)
+{
+ TIntermAggregate *callNode = new TIntermAggregate();
+ callNode->setOp(EOpFunctionCall);
+ TName nameObj(TFunction::mangleName(name));
+ nameObj.setInternal(true);
+ callNode->getFunctionSymbolInfo()->setNameObj(nameObj);
+ callNode->getSequence()->push_back(child);
+ return callNode;
+}
+
+TIntermAggregate *createRoundingFunctionCallNode(TIntermTyped *roundedChild)
+{
+ TString roundFunctionName;
+ if (roundedChild->getPrecision() == EbpMedium)
+ roundFunctionName = "angle_frm";
+ else
+ roundFunctionName = "angle_frl";
+ TIntermAggregate *callNode = createInternalFunctionCallNode(roundFunctionName, roundedChild);
+ callNode->setType(roundedChild->getType());
+ return callNode;
+}
+
+TIntermAggregate *createCompoundAssignmentFunctionCallNode(TIntermTyped *left, TIntermTyped *right, const char *opNameStr)
+{
+ std::stringstream strstr;
+ if (left->getPrecision() == EbpMedium)
+ strstr << "angle_compound_" << opNameStr << "_frm";
+ else
+ strstr << "angle_compound_" << opNameStr << "_frl";
+ TString functionName = strstr.str().c_str();
+ TIntermAggregate *callNode = createInternalFunctionCallNode(functionName, left);
+ callNode->getSequence()->push_back(right);
+ return callNode;
+}
+
+bool parentUsesResult(TIntermNode* parent, TIntermNode* node)
+{
+ if (!parent)
+ {
+ return false;
+ }
+
+ TIntermBlock *blockParent = parent->getAsBlock();
+ // If the parent is a block, the result is not assigned anywhere,
+ // so rounding it is not needed. In particular, this can avoid a lot of
+ // unnecessary rounding of unused return values of assignment.
+ if (blockParent)
+ {
+ return false;
+ }
+ TIntermBinary *binaryParent = parent->getAsBinaryNode();
+ if (binaryParent && binaryParent->getOp() == EOpComma && (binaryParent->getRight() != node))
+ {
+ return false;
+ }
+ return true;
+}
+
+} // namespace anonymous
+
+EmulatePrecision::EmulatePrecision(const TSymbolTable &symbolTable, int shaderVersion)
+ : TLValueTrackingTraverser(true, true, true, symbolTable, shaderVersion),
+ mDeclaringVariables(false)
+{}
+
+void EmulatePrecision::visitSymbol(TIntermSymbol *node)
+{
+ if (canRoundFloat(node->getType()) && !mDeclaringVariables && !isLValueRequiredHere())
+ {
+ TIntermNode *replacement = createRoundingFunctionCallNode(node);
+ queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
+ }
+}
+
+
+bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node)
+{
+ bool visitChildren = true;
+
+ TOperator op = node->getOp();
+
+ // RHS of initialize is not being declared.
+ if (op == EOpInitialize && visit == InVisit)
+ mDeclaringVariables = false;
+
+ if ((op == EOpIndexDirectStruct) && visit == InVisit)
+ visitChildren = false;
+
+ if (visit != PreVisit)
+ return visitChildren;
+
+ const TType& type = node->getType();
+ bool roundFloat = canRoundFloat(type);
+
+ if (roundFloat) {
+ switch (op) {
+ // Math operators that can result in a float may need to apply rounding to the return
+ // value. Note that in the case of assignment, the rounding is applied to its return
+ // value here, not the value being assigned.
+ case EOpAssign:
+ case EOpAdd:
+ case EOpSub:
+ case EOpMul:
+ case EOpDiv:
+ case EOpVectorTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesScalar:
+ case EOpMatrixTimesMatrix:
+ {
+ TIntermNode *parent = getParentNode();
+ if (!parentUsesResult(parent, node))
+ {
+ break;
+ }
+ TIntermNode *replacement = createRoundingFunctionCallNode(node);
+ queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
+ break;
+ }
+
+ // Compound assignment cases need to replace the operator with a function call.
+ case EOpAddAssign:
+ {
+ mEmulateCompoundAdd.insert(
+ TypePair(type.getBuiltInTypeNameString(),
+ node->getRight()->getType().getBuiltInTypeNameString()));
+ TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
+ node->getLeft(), node->getRight(), "add");
+ queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
+ break;
+ }
+ case EOpSubAssign:
+ {
+ mEmulateCompoundSub.insert(
+ TypePair(type.getBuiltInTypeNameString(),
+ node->getRight()->getType().getBuiltInTypeNameString()));
+ TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
+ node->getLeft(), node->getRight(), "sub");
+ queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
+ break;
+ }
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ {
+ mEmulateCompoundMul.insert(
+ TypePair(type.getBuiltInTypeNameString(),
+ node->getRight()->getType().getBuiltInTypeNameString()));
+ TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
+ node->getLeft(), node->getRight(), "mul");
+ queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
+ break;
+ }
+ case EOpDivAssign:
+ {
+ mEmulateCompoundDiv.insert(
+ TypePair(type.getBuiltInTypeNameString(),
+ node->getRight()->getType().getBuiltInTypeNameString()));
+ TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
+ node->getLeft(), node->getRight(), "div");
+ queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
+ break;
+ }
+ default:
+ // The rest of the binary operations should not need precision emulation.
+ break;
+ }
+ }
+ return visitChildren;
+}
+
+bool EmulatePrecision::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+ // Variable or interface block declaration.
+ if (visit == PreVisit)
+ {
+ mDeclaringVariables = true;
+ }
+ else if (visit == InVisit)
+ {
+ mDeclaringVariables = true;
+ }
+ else
+ {
+ mDeclaringVariables = false;
+ }
+ return true;
+}
+
+bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ bool visitChildren = true;
+ switch (node->getOp())
+ {
+ case EOpConstructStruct:
+ break;
+ case EOpPrototype:
+ visitChildren = false;
+ break;
+ case EOpParameters:
+ visitChildren = false;
+ break;
+ case EOpInvariantDeclaration:
+ visitChildren = false;
+ break;
+ case EOpFunctionCall:
+ {
+ // Function call.
+ if (visit == PreVisit)
+ {
+ // User-defined function return values are not rounded, this relies on that
+ // calculations producing the value were rounded.
+ TIntermNode *parent = getParentNode();
+ if (canRoundFloat(node->getType()) && !isInFunctionMap(node) &&
+ parentUsesResult(parent, node))
+ {
+ TIntermNode *replacement = createRoundingFunctionCallNode(node);
+ queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
+ }
+ }
+ break;
+ }
+ default:
+ TIntermNode *parent = getParentNode();
+ if (canRoundFloat(node->getType()) && visit == PreVisit && parentUsesResult(parent, node))
+ {
+ TIntermNode *replacement = createRoundingFunctionCallNode(node);
+ queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
+ }
+ break;
+ }
+ return visitChildren;
+}
+
+bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node)
+{
+ switch (node->getOp())
+ {
+ case EOpNegative:
+ case EOpVectorLogicalNot:
+ case EOpLogicalNot:
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ case EOpPreIncrement:
+ case EOpPreDecrement:
+ break;
+ default:
+ if (canRoundFloat(node->getType()) && visit == PreVisit)
+ {
+ TIntermNode *replacement = createRoundingFunctionCallNode(node);
+ queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
+ }
+ break;
+ }
+
+ return true;
+}
+
+void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase &sink,
+ const int shaderVersion,
+ const ShShaderOutput outputLanguage)
+{
+ std::unique_ptr<RoundingHelperWriter> roundingHelperWriter(
+ RoundingHelperWriter::createHelperWriter(outputLanguage));
+
+ roundingHelperWriter->writeCommonRoundingHelpers(sink, shaderVersion);
+
+ EmulationSet::const_iterator it;
+ for (it = mEmulateCompoundAdd.begin(); it != mEmulateCompoundAdd.end(); it++)
+ roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "+", "add");
+ for (it = mEmulateCompoundSub.begin(); it != mEmulateCompoundSub.end(); it++)
+ roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "-", "sub");
+ for (it = mEmulateCompoundDiv.begin(); it != mEmulateCompoundDiv.end(); it++)
+ roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "/", "div");
+ for (it = mEmulateCompoundMul.begin(); it != mEmulateCompoundMul.end(); it++)
+ roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "*", "mul");
+}
+
+// static
+bool EmulatePrecision::SupportedInLanguage(const ShShaderOutput outputLanguage)
+{
+ switch (outputLanguage)
+ {
+ case SH_HLSL_4_1_OUTPUT:
+ case SH_ESSL_OUTPUT:
+ return true;
+ default:
+ // Other languages not yet supported
+ return (outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT ||
+ sh::IsGLSL130OrNewer(outputLanguage));
+ }
+}
+
+} // namespace sh