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 *> ¶meters,
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