1 //
2 // Copyright 2002 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 #include "compiler/translator/tree_ops/EmulatePrecision.h"
8 
9 #include "compiler/translator/FunctionLookup.h"
10 
11 #include <memory>
12 
13 namespace sh
14 {
15 
16 namespace
17 {
18 
19 constexpr const ImmutableString kParamXName("x");
20 constexpr const ImmutableString kParamYName("y");
21 constexpr const ImmutableString kAngleFrmString("angle_frm");
22 constexpr const ImmutableString kAngleFrlString("angle_frl");
23 
24 class RoundingHelperWriter : angle::NonCopyable
25 {
26   public:
27     static RoundingHelperWriter *createHelperWriter(const ShShaderOutput outputLanguage);
28 
29     void writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion);
30     void writeCompoundAssignmentHelper(TInfoSinkBase &sink,
31                                        const char *lType,
32                                        const char *rType,
33                                        const char *opStr,
34                                        const char *opNameStr);
35 
~RoundingHelperWriter()36     virtual ~RoundingHelperWriter() {}
37 
38   protected:
RoundingHelperWriter(const ShShaderOutput outputLanguage)39     RoundingHelperWriter(const ShShaderOutput outputLanguage) : mOutputLanguage(outputLanguage) {}
40     RoundingHelperWriter() = delete;
41 
42     const ShShaderOutput mOutputLanguage;
43 
44   private:
45     virtual std::string getTypeString(const char *glslType)                               = 0;
46     virtual void writeFloatRoundingHelpers(TInfoSinkBase &sink)                           = 0;
47     virtual void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) = 0;
48     virtual void writeMatrixRoundingHelper(TInfoSinkBase &sink,
49                                            const unsigned int columns,
50                                            const unsigned int rows,
51                                            const char *functionName)                      = 0;
52 };
53 
54 class RoundingHelperWriterGLSL : public RoundingHelperWriter
55 {
56   public:
RoundingHelperWriterGLSL(const ShShaderOutput outputLanguage)57     RoundingHelperWriterGLSL(const ShShaderOutput outputLanguage)
58         : RoundingHelperWriter(outputLanguage)
59     {}
60 
61   private:
62     std::string getTypeString(const char *glslType) override;
63     void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
64     void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
65     void writeMatrixRoundingHelper(TInfoSinkBase &sink,
66                                    const unsigned int columns,
67                                    const unsigned int rows,
68                                    const char *functionName) override;
69 };
70 
71 class RoundingHelperWriterESSL : public RoundingHelperWriterGLSL
72 {
73   public:
RoundingHelperWriterESSL(const ShShaderOutput outputLanguage)74     RoundingHelperWriterESSL(const ShShaderOutput outputLanguage)
75         : RoundingHelperWriterGLSL(outputLanguage)
76     {}
77 
78   private:
79     std::string getTypeString(const char *glslType) override;
80 };
81 
82 class RoundingHelperWriterHLSL : public RoundingHelperWriter
83 {
84   public:
RoundingHelperWriterHLSL(const ShShaderOutput outputLanguage)85     RoundingHelperWriterHLSL(const ShShaderOutput outputLanguage)
86         : RoundingHelperWriter(outputLanguage)
87     {}
88 
89   private:
90     std::string getTypeString(const char *glslType) override;
91     void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
92     void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
93     void writeMatrixRoundingHelper(TInfoSinkBase &sink,
94                                    const unsigned int columns,
95                                    const unsigned int rows,
96                                    const char *functionName) override;
97 };
98 
createHelperWriter(const ShShaderOutput outputLanguage)99 RoundingHelperWriter *RoundingHelperWriter::createHelperWriter(const ShShaderOutput outputLanguage)
100 {
101     ASSERT(EmulatePrecision::SupportedInLanguage(outputLanguage));
102     switch (outputLanguage)
103     {
104         case SH_HLSL_4_1_OUTPUT:
105             return new RoundingHelperWriterHLSL(outputLanguage);
106         case SH_ESSL_OUTPUT:
107             return new RoundingHelperWriterESSL(outputLanguage);
108         default:
109             return new RoundingHelperWriterGLSL(outputLanguage);
110     }
111 }
112 
writeCommonRoundingHelpers(TInfoSinkBase & sink,const int shaderVersion)113 void RoundingHelperWriter::writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion)
114 {
115     // Write the angle_frm functions that round floating point numbers to
116     // half precision, and angle_frl functions that round them to minimum lowp
117     // precision.
118 
119     writeFloatRoundingHelpers(sink);
120     writeVectorRoundingHelpers(sink, 2);
121     writeVectorRoundingHelpers(sink, 3);
122     writeVectorRoundingHelpers(sink, 4);
123     if (shaderVersion > 100)
124     {
125         for (unsigned int columns = 2; columns <= 4; ++columns)
126         {
127             for (unsigned int rows = 2; rows <= 4; ++rows)
128             {
129                 writeMatrixRoundingHelper(sink, columns, rows, "angle_frm");
130                 writeMatrixRoundingHelper(sink, columns, rows, "angle_frl");
131             }
132         }
133     }
134     else
135     {
136         for (unsigned int size = 2; size <= 4; ++size)
137         {
138             writeMatrixRoundingHelper(sink, size, size, "angle_frm");
139             writeMatrixRoundingHelper(sink, size, size, "angle_frl");
140         }
141     }
142 }
143 
writeCompoundAssignmentHelper(TInfoSinkBase & sink,const char * lType,const char * rType,const char * opStr,const char * opNameStr)144 void RoundingHelperWriter::writeCompoundAssignmentHelper(TInfoSinkBase &sink,
145                                                          const char *lType,
146                                                          const char *rType,
147                                                          const char *opStr,
148                                                          const char *opNameStr)
149 {
150     std::string lTypeStr = getTypeString(lType);
151     std::string rTypeStr = getTypeString(rType);
152 
153     // Note that y should be passed through angle_frm at the function call site,
154     // but x can't be passed through angle_frm there since it is an inout parameter.
155     // So only pass x and the result through angle_frm here.
156     // clang-format off
157     sink <<
158         lTypeStr << " angle_compound_" << opNameStr << "_frm(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
159         "    x = angle_frm(angle_frm(x) " << opStr << " y);\n"
160         "    return x;\n"
161         "}\n";
162     sink <<
163         lTypeStr << " angle_compound_" << opNameStr << "_frl(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
164         "    x = angle_frl(angle_frl(x) " << opStr << " y);\n"
165         "    return x;\n"
166         "}\n";
167     // clang-format on
168 }
169 
getTypeString(const char * glslType)170 std::string RoundingHelperWriterGLSL::getTypeString(const char *glslType)
171 {
172     return glslType;
173 }
174 
getTypeString(const char * glslType)175 std::string RoundingHelperWriterESSL::getTypeString(const char *glslType)
176 {
177     std::stringstream typeStrStr = sh::InitializeStream<std::stringstream>();
178     typeStrStr << "highp " << glslType;
179     return typeStrStr.str();
180 }
181 
writeFloatRoundingHelpers(TInfoSinkBase & sink)182 void RoundingHelperWriterGLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
183 {
184     // Unoptimized version of angle_frm for single floats:
185     //
186     // int webgl_maxNormalExponent(in int exponentBits)
187     // {
188     //     int possibleExponents = int(exp2(float(exponentBits)));
189     //     int exponentBias = possibleExponents / 2 - 1;
190     //     int allExponentBitsOne = possibleExponents - 1;
191     //     return (allExponentBitsOne - 1) - exponentBias;
192     // }
193     //
194     // float angle_frm(in float x)
195     // {
196     //     int mantissaBits = 10;
197     //     int exponentBits = 5;
198     //     float possibleMantissas = exp2(float(mantissaBits));
199     //     float mantissaMax = 2.0 - 1.0 / possibleMantissas;
200     //     int maxNE = webgl_maxNormalExponent(exponentBits);
201     //     float max = exp2(float(maxNE)) * mantissaMax;
202     //     if (x > max)
203     //     {
204     //         return max;
205     //     }
206     //     if (x < -max)
207     //     {
208     //         return -max;
209     //     }
210     //     float exponent = floor(log2(abs(x)));
211     //     if (abs(x) == 0.0 || exponent < -float(maxNE))
212     //     {
213     //         return 0.0 * sign(x)
214     //     }
215     //     x = x * exp2(-(exponent - float(mantissaBits)));
216     //     x = sign(x) * floor(abs(x));
217     //     return x * exp2(exponent - float(mantissaBits));
218     // }
219 
220     // All numbers with a magnitude less than 2^-15 are subnormal, and are
221     // flushed to zero.
222 
223     // Note the constant numbers below:
224     // a) 65504 is the maximum possible mantissa (1.1111111111 in binary) times
225     //    2^15, the maximum normal exponent.
226     // b) 10.0 is the number of mantissa bits.
227     // c) -25.0 is the minimum normal half-float exponent -15.0 minus the number
228     //    of mantissa bits.
229     // d) + 1e-30 is to make sure the argument of log2() won't be zero. It can
230     //    only affect the result of log2 on x where abs(x) < 1e-22. Since these
231     //    numbers will be flushed to zero either way (2^-15 is the smallest
232     //    normal positive number), this does not introduce any error.
233 
234     std::string floatType = getTypeString("float");
235 
236     // clang-format off
237     sink <<
238         floatType << " angle_frm(in " << floatType << " x) {\n"
239         "    x = clamp(x, -65504.0, 65504.0);\n"
240         "    " << floatType << " exponent = floor(log2(abs(x) + 1e-30)) - 10.0;\n"
241         "    bool isNonZero = (exponent >= -25.0);\n"
242         "    x = x * exp2(-exponent);\n"
243         "    x = sign(x) * floor(abs(x));\n"
244         "    return x * exp2(exponent) * float(isNonZero);\n"
245         "}\n";
246 
247     sink <<
248         floatType << " angle_frl(in " << floatType << " x) {\n"
249         "    x = clamp(x, -2.0, 2.0);\n"
250         "    x = x * 256.0;\n"
251         "    x = sign(x) * floor(abs(x));\n"
252         "    return x * 0.00390625;\n"
253         "}\n";
254     // clang-format on
255 }
256 
writeVectorRoundingHelpers(TInfoSinkBase & sink,const unsigned int size)257 void RoundingHelperWriterGLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
258                                                           const unsigned int size)
259 {
260     std::stringstream vecTypeStrStr = sh::InitializeStream<std::stringstream>();
261     vecTypeStrStr << "vec" << size;
262     std::string vecType = getTypeString(vecTypeStrStr.str().c_str());
263 
264     // clang-format off
265     sink <<
266         vecType << " angle_frm(in " << vecType << " v) {\n"
267         "    v = clamp(v, -65504.0, 65504.0);\n"
268         "    " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
269         "    bvec" << size << " isNonZero = greaterThanEqual(exponent, vec" << size << "(-25.0));\n"
270         "    v = v * exp2(-exponent);\n"
271         "    v = sign(v) * floor(abs(v));\n"
272         "    return v * exp2(exponent) * vec" << size << "(isNonZero);\n"
273         "}\n";
274 
275     sink <<
276         vecType << " angle_frl(in " << vecType << " v) {\n"
277         "    v = clamp(v, -2.0, 2.0);\n"
278         "    v = v * 256.0;\n"
279         "    v = sign(v) * floor(abs(v));\n"
280         "    return v * 0.00390625;\n"
281         "}\n";
282     // clang-format on
283 }
284 
writeMatrixRoundingHelper(TInfoSinkBase & sink,const unsigned int columns,const unsigned int rows,const char * functionName)285 void RoundingHelperWriterGLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
286                                                          const unsigned int columns,
287                                                          const unsigned int rows,
288                                                          const char *functionName)
289 {
290     std::stringstream matTypeStrStr = sh::InitializeStream<std::stringstream>();
291     matTypeStrStr << "mat" << columns;
292     if (rows != columns)
293     {
294         matTypeStrStr << "x" << rows;
295     }
296     std::string matType = getTypeString(matTypeStrStr.str().c_str());
297 
298     sink << matType << " " << functionName << "(in " << matType << " m) {\n"
299          << "    " << matType << " rounded;\n";
300 
301     for (unsigned int i = 0; i < columns; ++i)
302     {
303         sink << "    rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
304     }
305 
306     sink << "    return rounded;\n"
307             "}\n";
308 }
309 
GetHLSLTypeStr(const char * floatTypeStr)310 static const char *GetHLSLTypeStr(const char *floatTypeStr)
311 {
312     if (strcmp(floatTypeStr, "float") == 0)
313     {
314         return "float";
315     }
316     if (strcmp(floatTypeStr, "vec2") == 0)
317     {
318         return "float2";
319     }
320     if (strcmp(floatTypeStr, "vec3") == 0)
321     {
322         return "float3";
323     }
324     if (strcmp(floatTypeStr, "vec4") == 0)
325     {
326         return "float4";
327     }
328     if (strcmp(floatTypeStr, "mat2") == 0)
329     {
330         return "float2x2";
331     }
332     if (strcmp(floatTypeStr, "mat3") == 0)
333     {
334         return "float3x3";
335     }
336     if (strcmp(floatTypeStr, "mat4") == 0)
337     {
338         return "float4x4";
339     }
340     if (strcmp(floatTypeStr, "mat2x3") == 0)
341     {
342         return "float2x3";
343     }
344     if (strcmp(floatTypeStr, "mat2x4") == 0)
345     {
346         return "float2x4";
347     }
348     if (strcmp(floatTypeStr, "mat3x2") == 0)
349     {
350         return "float3x2";
351     }
352     if (strcmp(floatTypeStr, "mat3x4") == 0)
353     {
354         return "float3x4";
355     }
356     if (strcmp(floatTypeStr, "mat4x2") == 0)
357     {
358         return "float4x2";
359     }
360     if (strcmp(floatTypeStr, "mat4x3") == 0)
361     {
362         return "float4x3";
363     }
364     UNREACHABLE();
365     return nullptr;
366 }
367 
getTypeString(const char * glslType)368 std::string RoundingHelperWriterHLSL::getTypeString(const char *glslType)
369 {
370     return GetHLSLTypeStr(glslType);
371 }
372 
writeFloatRoundingHelpers(TInfoSinkBase & sink)373 void RoundingHelperWriterHLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
374 {
375     // In HLSL scalars are the same as 1-vectors.
376     writeVectorRoundingHelpers(sink, 1);
377 }
378 
writeVectorRoundingHelpers(TInfoSinkBase & sink,const unsigned int size)379 void RoundingHelperWriterHLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
380                                                           const unsigned int size)
381 {
382     std::stringstream vecTypeStrStr = sh::InitializeStream<std::stringstream>();
383     vecTypeStrStr << "float" << size;
384     std::string vecType = vecTypeStrStr.str();
385 
386     // clang-format off
387     sink <<
388         vecType << " angle_frm(" << vecType << " v) {\n"
389         "    v = clamp(v, -65504.0, 65504.0);\n"
390         "    " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
391         "    bool" << size << " isNonZero = exponent < -25.0;\n"
392         "    v = v * exp2(-exponent);\n"
393         "    v = sign(v) * floor(abs(v));\n"
394         "    return v * exp2(exponent) * (float" << size << ")(isNonZero);\n"
395         "}\n";
396 
397     sink <<
398         vecType << " angle_frl(" << vecType << " v) {\n"
399         "    v = clamp(v, -2.0, 2.0);\n"
400         "    v = v * 256.0;\n"
401         "    v = sign(v) * floor(abs(v));\n"
402         "    return v * 0.00390625;\n"
403         "}\n";
404     // clang-format on
405 }
406 
writeMatrixRoundingHelper(TInfoSinkBase & sink,const unsigned int columns,const unsigned int rows,const char * functionName)407 void RoundingHelperWriterHLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
408                                                          const unsigned int columns,
409                                                          const unsigned int rows,
410                                                          const char *functionName)
411 {
412     std::stringstream matTypeStrStr = sh::InitializeStream<std::stringstream>();
413     matTypeStrStr << "float" << columns << "x" << rows;
414     std::string matType = matTypeStrStr.str();
415 
416     sink << matType << " " << functionName << "(" << matType << " m) {\n"
417          << "    " << matType << " rounded;\n";
418 
419     for (unsigned int i = 0; i < columns; ++i)
420     {
421         sink << "    rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
422     }
423 
424     sink << "    return rounded;\n"
425             "}\n";
426 }
427 
canRoundFloat(const TType & type)428 bool canRoundFloat(const TType &type)
429 {
430     return type.getBasicType() == EbtFloat && !type.isArray() &&
431            (type.getPrecision() == EbpLow || type.getPrecision() == EbpMedium);
432 }
433 
ParentUsesResult(TIntermNode * parent,TIntermTyped * node)434 bool ParentUsesResult(TIntermNode *parent, TIntermTyped *node)
435 {
436     if (!parent)
437     {
438         return false;
439     }
440 
441     TIntermBlock *blockParent = parent->getAsBlock();
442     // If the parent is a block, the result is not assigned anywhere,
443     // so rounding it is not needed. In particular, this can avoid a lot of
444     // unnecessary rounding of unused return values of assignment.
445     if (blockParent)
446     {
447         return false;
448     }
449     TIntermBinary *binaryParent = parent->getAsBinaryNode();
450     if (binaryParent && binaryParent->getOp() == EOpComma && (binaryParent->getRight() != node))
451     {
452         return false;
453     }
454     return true;
455 }
456 
ParentConstructorTakesCareOfRounding(TIntermNode * parent,TIntermTyped * node)457 bool ParentConstructorTakesCareOfRounding(TIntermNode *parent, TIntermTyped *node)
458 {
459     if (!parent)
460     {
461         return false;
462     }
463     TIntermAggregate *parentConstructor = parent->getAsAggregate();
464     if (!parentConstructor || parentConstructor->getOp() != EOpConstruct)
465     {
466         return false;
467     }
468     if (parentConstructor->getPrecision() != node->getPrecision())
469     {
470         return false;
471     }
472     return canRoundFloat(parentConstructor->getType());
473 }
474 
475 }  // namespace
476 
EmulatePrecision(TSymbolTable * symbolTable)477 EmulatePrecision::EmulatePrecision(TSymbolTable *symbolTable)
478     : TLValueTrackingTraverser(true, true, true, symbolTable), mDeclaringVariables(false)
479 {}
480 
visitSymbol(TIntermSymbol * node)481 void EmulatePrecision::visitSymbol(TIntermSymbol *node)
482 {
483     TIntermNode *parent = getParentNode();
484     if (canRoundFloat(node->getType()) && ParentUsesResult(parent, node) &&
485         !ParentConstructorTakesCareOfRounding(parent, node) && !mDeclaringVariables &&
486         !isLValueRequiredHere())
487     {
488         TIntermNode *replacement = createRoundingFunctionCallNode(node);
489         queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
490     }
491 }
492 
visitBinary(Visit visit,TIntermBinary * node)493 bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node)
494 {
495     bool visitChildren = true;
496 
497     TOperator op = node->getOp();
498 
499     // RHS of initialize is not being declared.
500     if (op == EOpInitialize && visit == InVisit)
501         mDeclaringVariables = false;
502 
503     if ((op == EOpIndexDirectStruct) && visit == InVisit)
504         visitChildren = false;
505 
506     if (visit != PreVisit)
507         return visitChildren;
508 
509     const TType &type = node->getType();
510     bool roundFloat   = canRoundFloat(type);
511 
512     if (roundFloat)
513     {
514         switch (op)
515         {
516             // Math operators that can result in a float may need to apply rounding to the return
517             // value. Note that in the case of assignment, the rounding is applied to its return
518             // value here, not the value being assigned.
519             case EOpAssign:
520             case EOpAdd:
521             case EOpSub:
522             case EOpMul:
523             case EOpDiv:
524             case EOpVectorTimesScalar:
525             case EOpVectorTimesMatrix:
526             case EOpMatrixTimesVector:
527             case EOpMatrixTimesScalar:
528             case EOpMatrixTimesMatrix:
529             {
530                 TIntermNode *parent = getParentNode();
531                 if (!ParentUsesResult(parent, node) ||
532                     ParentConstructorTakesCareOfRounding(parent, node))
533                 {
534                     break;
535                 }
536                 TIntermNode *replacement = createRoundingFunctionCallNode(node);
537                 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
538                 break;
539             }
540 
541             // Compound assignment cases need to replace the operator with a function call.
542             case EOpAddAssign:
543             {
544                 mEmulateCompoundAdd.insert(
545                     TypePair(type.getBuiltInTypeNameString(),
546                              node->getRight()->getType().getBuiltInTypeNameString()));
547                 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
548                     node->getLeft(), node->getRight(), "add");
549                 queueReplacement(replacement, OriginalNode::IS_DROPPED);
550                 break;
551             }
552             case EOpSubAssign:
553             {
554                 mEmulateCompoundSub.insert(
555                     TypePair(type.getBuiltInTypeNameString(),
556                              node->getRight()->getType().getBuiltInTypeNameString()));
557                 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
558                     node->getLeft(), node->getRight(), "sub");
559                 queueReplacement(replacement, OriginalNode::IS_DROPPED);
560                 break;
561             }
562             case EOpMulAssign:
563             case EOpVectorTimesMatrixAssign:
564             case EOpVectorTimesScalarAssign:
565             case EOpMatrixTimesScalarAssign:
566             case EOpMatrixTimesMatrixAssign:
567             {
568                 mEmulateCompoundMul.insert(
569                     TypePair(type.getBuiltInTypeNameString(),
570                              node->getRight()->getType().getBuiltInTypeNameString()));
571                 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
572                     node->getLeft(), node->getRight(), "mul");
573                 queueReplacement(replacement, OriginalNode::IS_DROPPED);
574                 break;
575             }
576             case EOpDivAssign:
577             {
578                 mEmulateCompoundDiv.insert(
579                     TypePair(type.getBuiltInTypeNameString(),
580                              node->getRight()->getType().getBuiltInTypeNameString()));
581                 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
582                     node->getLeft(), node->getRight(), "div");
583                 queueReplacement(replacement, OriginalNode::IS_DROPPED);
584                 break;
585             }
586             default:
587                 // The rest of the binary operations should not need precision emulation.
588                 break;
589         }
590     }
591     return visitChildren;
592 }
593 
visitDeclaration(Visit visit,TIntermDeclaration * node)594 bool EmulatePrecision::visitDeclaration(Visit visit, TIntermDeclaration *node)
595 {
596     // Variable or interface block declaration.
597     if (visit == PreVisit)
598     {
599         mDeclaringVariables = true;
600     }
601     else if (visit == InVisit)
602     {
603         mDeclaringVariables = true;
604     }
605     else
606     {
607         mDeclaringVariables = false;
608     }
609     return true;
610 }
611 
visitGlobalQualifierDeclaration(Visit visit,TIntermGlobalQualifierDeclaration * node)612 bool EmulatePrecision::visitGlobalQualifierDeclaration(Visit visit,
613                                                        TIntermGlobalQualifierDeclaration *node)
614 {
615     return false;
616 }
617 
visitAggregate(Visit visit,TIntermAggregate * node)618 bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
619 {
620     if (visit != PreVisit)
621         return true;
622 
623     // User-defined function return values are not rounded. The calculations that produced
624     // the value inside the function definition should have been rounded.
625     TOperator op = node->getOp();
626     if (op == EOpCallInternalRawFunction || op == EOpCallFunctionInAST ||
627         (op == EOpConstruct && node->getBasicType() == EbtStruct))
628     {
629         return true;
630     }
631 
632     TIntermNode *parent = getParentNode();
633     if (canRoundFloat(node->getType()) && ParentUsesResult(parent, node) &&
634         !ParentConstructorTakesCareOfRounding(parent, node))
635     {
636         TIntermNode *replacement = createRoundingFunctionCallNode(node);
637         queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
638     }
639     return true;
640 }
641 
visitUnary(Visit visit,TIntermUnary * node)642 bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node)
643 {
644     switch (node->getOp())
645     {
646         case EOpNegative:
647         case EOpLogicalNot:
648         case EOpPostIncrement:
649         case EOpPostDecrement:
650         case EOpPreIncrement:
651         case EOpPreDecrement:
652         case EOpNotComponentWise:
653             break;
654         default:
655             if (canRoundFloat(node->getType()) && visit == PreVisit)
656             {
657                 TIntermNode *replacement = createRoundingFunctionCallNode(node);
658                 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
659             }
660             break;
661     }
662 
663     return true;
664 }
665 
writeEmulationHelpers(TInfoSinkBase & sink,const int shaderVersion,const ShShaderOutput outputLanguage)666 void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase &sink,
667                                              const int shaderVersion,
668                                              const ShShaderOutput outputLanguage)
669 {
670     std::unique_ptr<RoundingHelperWriter> roundingHelperWriter(
671         RoundingHelperWriter::createHelperWriter(outputLanguage));
672 
673     roundingHelperWriter->writeCommonRoundingHelpers(sink, shaderVersion);
674 
675     EmulationSet::const_iterator it;
676     for (it = mEmulateCompoundAdd.begin(); it != mEmulateCompoundAdd.end(); it++)
677         roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "+", "add");
678     for (it = mEmulateCompoundSub.begin(); it != mEmulateCompoundSub.end(); it++)
679         roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "-", "sub");
680     for (it = mEmulateCompoundDiv.begin(); it != mEmulateCompoundDiv.end(); it++)
681         roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "/", "div");
682     for (it = mEmulateCompoundMul.begin(); it != mEmulateCompoundMul.end(); it++)
683         roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "*", "mul");
684 }
685 
686 // static
SupportedInLanguage(const ShShaderOutput outputLanguage)687 bool EmulatePrecision::SupportedInLanguage(const ShShaderOutput outputLanguage)
688 {
689     switch (outputLanguage)
690     {
691         case SH_HLSL_4_1_OUTPUT:
692         case SH_ESSL_OUTPUT:
693             return true;
694         default:
695             // Other languages not yet supported
696             return (outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT ||
697                     sh::IsGLSL130OrNewer(outputLanguage));
698     }
699 }
700 
getInternalFunction(const ImmutableString & functionName,const TType & returnType,TIntermSequence * arguments,const TVector<const TVariable * > & parameters,bool knownToNotHaveSideEffects)701 const TFunction *EmulatePrecision::getInternalFunction(const ImmutableString &functionName,
702                                                        const TType &returnType,
703                                                        TIntermSequence *arguments,
704                                                        const TVector<const TVariable *> &parameters,
705                                                        bool knownToNotHaveSideEffects)
706 {
707     ImmutableString mangledName = TFunctionLookup::GetMangledName(functionName.data(), *arguments);
708     if (mInternalFunctions.find(mangledName) == mInternalFunctions.end())
709     {
710         TFunction *func = new TFunction(mSymbolTable, functionName, SymbolType::AngleInternal,
711                                         new TType(returnType), knownToNotHaveSideEffects);
712         ASSERT(parameters.size() == arguments->size());
713         for (size_t i = 0; i < parameters.size(); ++i)
714         {
715             func->addParameter(parameters[i]);
716         }
717         mInternalFunctions[mangledName] = func;
718     }
719     return mInternalFunctions[mangledName];
720 }
721 
createRoundingFunctionCallNode(TIntermTyped * roundedChild)722 TIntermAggregate *EmulatePrecision::createRoundingFunctionCallNode(TIntermTyped *roundedChild)
723 {
724     const ImmutableString *roundFunctionName = &kAngleFrmString;
725     if (roundedChild->getPrecision() == EbpLow)
726         roundFunctionName = &kAngleFrlString;
727     TIntermSequence arguments;
728     arguments.push_back(roundedChild);
729 
730     TVector<const TVariable *> parameters;
731     TType *paramType = new TType(roundedChild->getType());
732     paramType->setPrecision(EbpHigh);
733     paramType->setQualifier(EvqIn);
734     parameters.push_back(new TVariable(mSymbolTable, kParamXName,
735                                        static_cast<const TType *>(paramType),
736                                        SymbolType::AngleInternal));
737 
738     return TIntermAggregate::CreateRawFunctionCall(
739         *getInternalFunction(*roundFunctionName, roundedChild->getType(), &arguments, parameters,
740                              true),
741         &arguments);
742 }
743 
createCompoundAssignmentFunctionCallNode(TIntermTyped * left,TIntermTyped * right,const char * opNameStr)744 TIntermAggregate *EmulatePrecision::createCompoundAssignmentFunctionCallNode(TIntermTyped *left,
745                                                                              TIntermTyped *right,
746                                                                              const char *opNameStr)
747 {
748     std::stringstream strstr = sh::InitializeStream<std::stringstream>();
749     if (left->getPrecision() == EbpMedium)
750         strstr << "angle_compound_" << opNameStr << "_frm";
751     else
752         strstr << "angle_compound_" << opNameStr << "_frl";
753     ImmutableString functionName = ImmutableString(strstr.str());
754     TIntermSequence arguments;
755     arguments.push_back(left);
756     arguments.push_back(right);
757 
758     TVector<const TVariable *> parameters;
759     TType *leftParamType = new TType(left->getType());
760     leftParamType->setPrecision(EbpHigh);
761     leftParamType->setQualifier(EvqOut);
762     parameters.push_back(new TVariable(mSymbolTable, kParamXName,
763                                        static_cast<const TType *>(leftParamType),
764                                        SymbolType::AngleInternal));
765     TType *rightParamType = new TType(right->getType());
766     rightParamType->setPrecision(EbpHigh);
767     rightParamType->setQualifier(EvqIn);
768     parameters.push_back(new TVariable(mSymbolTable, kParamYName,
769                                        static_cast<const TType *>(rightParamType),
770                                        SymbolType::AngleInternal));
771 
772     return TIntermAggregate::CreateRawFunctionCall(
773         *getInternalFunction(functionName, left->getType(), &arguments, parameters, false),
774         &arguments);
775 }
776 
777 }  // namespace sh
778