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