blob: 5f1454b57602c9c1873c91c05255bab1349bf2c6 [file] [log] [blame]
Olli Etuaho853dc1a2014-11-06 17:25:48 +02001//
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
Olli Etuahob741c762016-06-29 15:49:22 +03009#include <memory>
10
Olli Etuaho853dc1a2014-11-06 17:25:48 +020011namespace
12{
13
Olli Etuahob741c762016-06-29 15:49:22 +030014class RoundingHelperWriter : angle::NonCopyable
Olli Etuaho853dc1a2014-11-06 17:25:48 +020015{
Olli Etuahob741c762016-06-29 15:49:22 +030016 public:
17 static RoundingHelperWriter *createHelperWriter(const ShShaderOutput outputLanguage);
Olli Etuaho853dc1a2014-11-06 17:25:48 +020018
Olli Etuahob741c762016-06-29 15:49:22 +030019 void writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion);
20 void writeCompoundAssignmentHelper(TInfoSinkBase &sink,
21 const char *lType,
22 const char *rType,
23 const char *opStr,
24 const char *opNameStr);
Olli Etuaho853dc1a2014-11-06 17:25:48 +020025
Olli Etuahob741c762016-06-29 15:49:22 +030026 virtual ~RoundingHelperWriter() {}
Olli Etuaho853dc1a2014-11-06 17:25:48 +020027
Olli Etuahob741c762016-06-29 15:49:22 +030028 protected:
29 RoundingHelperWriter(const ShShaderOutput outputLanguage) : mOutputLanguage(outputLanguage) {}
30 RoundingHelperWriter() = delete;
31
32 const ShShaderOutput mOutputLanguage;
33
34 private:
35 virtual std::string getTypeString(const char *glslType) = 0;
36 virtual void writeFloatRoundingHelpers(TInfoSinkBase &sink) = 0;
37 virtual void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) = 0;
38 virtual void writeMatrixRoundingHelper(TInfoSinkBase &sink,
39 const unsigned int columns,
40 const unsigned int rows,
41 const char *functionName) = 0;
42};
43
44class RoundingHelperWriterGLSL : public RoundingHelperWriter
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030045{
Olli Etuahob741c762016-06-29 15:49:22 +030046 public:
47 RoundingHelperWriterGLSL(const ShShaderOutput outputLanguage)
48 : RoundingHelperWriter(outputLanguage)
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030049 {
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030050 }
51
Olli Etuahob741c762016-06-29 15:49:22 +030052 private:
53 std::string getTypeString(const char *glslType) override;
54 void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
55 void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
56 void writeMatrixRoundingHelper(TInfoSinkBase &sink,
57 const unsigned int columns,
58 const unsigned int rows,
59 const char *functionName) override;
60};
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030061
Olli Etuahob741c762016-06-29 15:49:22 +030062class RoundingHelperWriterESSL : public RoundingHelperWriterGLSL
63{
64 public:
65 RoundingHelperWriterESSL(const ShShaderOutput outputLanguage)
66 : RoundingHelperWriterGLSL(outputLanguage)
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030067 {
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030068 }
69
Olli Etuahob741c762016-06-29 15:49:22 +030070 private:
71 std::string getTypeString(const char *glslType) override;
72};
73
74class RoundingHelperWriterHLSL : public RoundingHelperWriter
75{
76 public:
77 RoundingHelperWriterHLSL(const ShShaderOutput outputLanguage)
78 : RoundingHelperWriter(outputLanguage)
79 {
80 }
81
82 private:
83 std::string getTypeString(const char *glslType) override;
84 void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
85 void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
86 void writeMatrixRoundingHelper(TInfoSinkBase &sink,
87 const unsigned int columns,
88 const unsigned int rows,
89 const char *functionName) override;
90};
91
92RoundingHelperWriter *RoundingHelperWriter::createHelperWriter(const ShShaderOutput outputLanguage)
93{
94 switch (outputLanguage)
95 {
96 case SH_HLSL_4_1_OUTPUT:
97 return new RoundingHelperWriterHLSL(outputLanguage);
98 case SH_ESSL_OUTPUT:
99 return new RoundingHelperWriterESSL(outputLanguage);
100 default:
101 // Other languages not yet supported
102 ASSERT(outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT ||
103 IsGLSL130OrNewer(outputLanguage));
104 return new RoundingHelperWriterGLSL(outputLanguage);
105 }
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +0300106}
107
Olli Etuahob741c762016-06-29 15:49:22 +0300108void RoundingHelperWriter::writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200109{
110 // Write the angle_frm functions that round floating point numbers to
111 // half precision, and angle_frl functions that round them to minimum lowp
112 // precision.
113
Olli Etuahob741c762016-06-29 15:49:22 +0300114 writeFloatRoundingHelpers(sink);
115 writeVectorRoundingHelpers(sink, 2);
116 writeVectorRoundingHelpers(sink, 3);
117 writeVectorRoundingHelpers(sink, 4);
118 if (shaderVersion > 100)
119 {
120 for (unsigned int columns = 2; columns <= 4; ++columns)
121 {
122 for (unsigned int rows = 2; rows <= 4; ++rows)
123 {
124 writeMatrixRoundingHelper(sink, columns, rows, "angle_frm");
125 writeMatrixRoundingHelper(sink, columns, rows, "angle_frl");
126 }
127 }
128 }
129 else
130 {
131 for (unsigned int size = 2; size <= 4; ++size)
132 {
133 writeMatrixRoundingHelper(sink, size, size, "angle_frm");
134 writeMatrixRoundingHelper(sink, size, size, "angle_frl");
135 }
136 }
137}
138
139void RoundingHelperWriter::writeCompoundAssignmentHelper(TInfoSinkBase &sink,
140 const char *lType,
141 const char *rType,
142 const char *opStr,
143 const char *opNameStr)
144{
145 std::string lTypeStr = getTypeString(lType);
146 std::string rTypeStr = getTypeString(rType);
147
148 // Note that y should be passed through angle_frm at the function call site,
149 // but x can't be passed through angle_frm there since it is an inout parameter.
150 // So only pass x and the result through angle_frm here.
151 // clang-format off
152 sink <<
153 lTypeStr << " angle_compound_" << opNameStr << "_frm(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
154 " x = angle_frm(angle_frm(x) " << opStr << " y);\n"
155 " return x;\n"
156 "}\n";
157 sink <<
158 lTypeStr << " angle_compound_" << opNameStr << "_frl(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
159 " x = angle_frl(angle_frm(x) " << opStr << " y);\n"
160 " return x;\n"
161 "}\n";
162 // clang-format on
163}
164
165std::string RoundingHelperWriterGLSL::getTypeString(const char *glslType)
166{
167 return glslType;
168}
169
170std::string RoundingHelperWriterESSL::getTypeString(const char *glslType)
171{
172 std::stringstream typeStrStr;
173 typeStrStr << "highp " << glslType;
174 return typeStrStr.str();
175}
176
177void RoundingHelperWriterGLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
178{
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200179 // Unoptimized version of angle_frm for single floats:
180 //
Olli Etuahob741c762016-06-29 15:49:22 +0300181 // int webgl_maxNormalExponent(in int exponentBits)
182 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200183 // int possibleExponents = int(exp2(float(exponentBits)));
184 // int exponentBias = possibleExponents / 2 - 1;
185 // int allExponentBitsOne = possibleExponents - 1;
186 // return (allExponentBitsOne - 1) - exponentBias;
187 // }
188 //
Olli Etuahob741c762016-06-29 15:49:22 +0300189 // float angle_frm(in float x)
190 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200191 // int mantissaBits = 10;
192 // int exponentBits = 5;
193 // float possibleMantissas = exp2(float(mantissaBits));
194 // float mantissaMax = 2.0 - 1.0 / possibleMantissas;
195 // int maxNE = webgl_maxNormalExponent(exponentBits);
196 // float max = exp2(float(maxNE)) * mantissaMax;
Olli Etuahob741c762016-06-29 15:49:22 +0300197 // if (x > max)
198 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200199 // return max;
200 // }
Olli Etuahob741c762016-06-29 15:49:22 +0300201 // if (x < -max)
202 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200203 // return -max;
204 // }
205 // float exponent = floor(log2(abs(x)));
Olli Etuahob741c762016-06-29 15:49:22 +0300206 // if (abs(x) == 0.0 || exponent < -float(maxNE))
207 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200208 // return 0.0 * sign(x)
209 // }
210 // x = x * exp2(-(exponent - float(mantissaBits)));
211 // x = sign(x) * floor(abs(x));
212 // return x * exp2(exponent - float(mantissaBits));
213 // }
214
215 // All numbers with a magnitude less than 2^-15 are subnormal, and are
216 // flushed to zero.
217
218 // Note the constant numbers below:
219 // a) 65504 is the maximum possible mantissa (1.1111111111 in binary) times
220 // 2^15, the maximum normal exponent.
221 // b) 10.0 is the number of mantissa bits.
222 // c) -25.0 is the minimum normal half-float exponent -15.0 minus the number
223 // of mantissa bits.
224 // d) + 1e-30 is to make sure the argument of log2() won't be zero. It can
225 // only affect the result of log2 on x where abs(x) < 1e-22. Since these
226 // numbers will be flushed to zero either way (2^-15 is the smallest
227 // normal positive number), this does not introduce any error.
228
Olli Etuahob741c762016-06-29 15:49:22 +0300229 std::string floatType = getTypeString("float");
230
231 // clang-format off
232 sink <<
233 floatType << " angle_frm(in " << floatType << " x) {\n"
234 " x = clamp(x, -65504.0, 65504.0);\n"
235 " " << floatType << " exponent = floor(log2(abs(x) + 1e-30)) - 10.0;\n"
236 " bool isNonZero = (exponent >= -25.0);\n"
237 " x = x * exp2(-exponent);\n"
238 " x = sign(x) * floor(abs(x));\n"
239 " return x * exp2(exponent) * float(isNonZero);\n"
240 "}\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200241
242 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300243 floatType << " angle_frl(in " << floatType << " x) {\n"
244 " x = clamp(x, -2.0, 2.0);\n"
245 " x = x * 256.0;\n"
246 " x = sign(x) * floor(abs(x));\n"
247 " return x * 0.00390625;\n"
248 "}\n";
249 // clang-format on
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300250}
251
Olli Etuahob741c762016-06-29 15:49:22 +0300252void RoundingHelperWriterGLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
253 const unsigned int size)
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300254{
Olli Etuahob741c762016-06-29 15:49:22 +0300255 std::stringstream vecTypeStrStr;
256 vecTypeStrStr << "vec" << size;
257 std::string vecType = getTypeString(vecTypeStrStr.str().c_str());
258
259 // clang-format off
260 sink <<
261 vecType << " angle_frm(in " << vecType << " v) {\n"
262 " v = clamp(v, -65504.0, 65504.0);\n"
263 " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
264 " bvec" << size << " isNonZero = greaterThanEqual(exponent, vec" << size << "(-25.0));\n"
265 " v = v * exp2(-exponent);\n"
266 " v = sign(v) * floor(abs(v));\n"
267 " return v * exp2(exponent) * vec" << size << "(isNonZero);\n"
268 "}\n";
269
270 sink <<
271 vecType << " angle_frl(in " << vecType << " v) {\n"
272 " v = clamp(v, -2.0, 2.0);\n"
273 " v = v * 256.0;\n"
274 " v = sign(v) * floor(abs(v));\n"
275 " return v * 0.00390625;\n"
276 "}\n";
277 // clang-format on
278}
279
280void RoundingHelperWriterGLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
281 const unsigned int columns,
282 const unsigned int rows,
283 const char *functionName)
284{
285 std::stringstream matTypeStrStr;
286 matTypeStrStr << "mat" << columns;
287 if (rows != columns)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200288 {
Olli Etuahob741c762016-06-29 15:49:22 +0300289 matTypeStrStr << "x" << rows;
290 }
291 std::string matType = getTypeString(matTypeStrStr.str().c_str());
292
293 sink << matType << " " << functionName << "(in " << matType << " m) {\n"
294 << " " << matType << " rounded;\n";
295
296 for (unsigned int i = 0; i < columns; ++i)
297 {
298 sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200299 }
300
Olli Etuahob741c762016-06-29 15:49:22 +0300301 sink << " return rounded;\n"
302 "}\n";
303}
304
305static const char *GetHLSLTypeStr(const char *floatTypeStr)
306{
307 if (strcmp(floatTypeStr, "float") == 0)
308 {
309 return "float";
310 }
311 if (strcmp(floatTypeStr, "vec2") == 0)
312 {
313 return "float2";
314 }
315 if (strcmp(floatTypeStr, "vec3") == 0)
316 {
317 return "float3";
318 }
319 if (strcmp(floatTypeStr, "vec4") == 0)
320 {
321 return "float4";
322 }
323 if (strcmp(floatTypeStr, "mat2") == 0)
324 {
325 return "float2x2";
326 }
327 if (strcmp(floatTypeStr, "mat3") == 0)
328 {
329 return "float3x3";
330 }
331 if (strcmp(floatTypeStr, "mat4") == 0)
332 {
333 return "float4x4";
334 }
335 if (strcmp(floatTypeStr, "mat2x3") == 0)
336 {
337 return "float2x3";
338 }
339 if (strcmp(floatTypeStr, "mat2x4") == 0)
340 {
341 return "float2x4";
342 }
343 if (strcmp(floatTypeStr, "mat3x2") == 0)
344 {
345 return "float3x2";
346 }
347 if (strcmp(floatTypeStr, "mat3x4") == 0)
348 {
349 return "float3x4";
350 }
351 if (strcmp(floatTypeStr, "mat4x2") == 0)
352 {
353 return "float4x2";
354 }
355 if (strcmp(floatTypeStr, "mat4x3") == 0)
356 {
357 return "float4x3";
358 }
359 UNREACHABLE();
360 return nullptr;
361}
362
363std::string RoundingHelperWriterHLSL::getTypeString(const char *glslType)
364{
365 return GetHLSLTypeStr(glslType);
366}
367
368void RoundingHelperWriterHLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
369{
370 // In HLSL scalars are the same as 1-vectors.
371 writeVectorRoundingHelpers(sink, 1);
372}
373
374void RoundingHelperWriterHLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
375 const unsigned int size)
376{
377 std::stringstream vecTypeStrStr;
378 vecTypeStrStr << "float" << size;
379 std::string vecType = vecTypeStrStr.str();
380
381 // clang-format off
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200382 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300383 vecType << " angle_frm(" << vecType << " v) {\n"
384 " v = clamp(v, -65504.0, 65504.0);\n"
385 " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
386 " bool" << size << " isNonZero = exponent < -25.0;\n"
387 " v = v * exp2(-exponent);\n"
388 " v = sign(v) * floor(abs(v));\n"
389 " return v * exp2(exponent) * (float" << size << ")(isNonZero);\n"
390 "}\n";
391
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200392 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300393 vecType << " angle_frl(" << vecType << " v) {\n"
394 " v = clamp(v, -2.0, 2.0);\n"
395 " v = v * 256.0;\n"
396 " v = sign(v) * floor(abs(v));\n"
397 " return v * 0.00390625;\n"
398 "}\n";
399 // clang-format on
400}
401
402void RoundingHelperWriterHLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
403 const unsigned int columns,
404 const unsigned int rows,
405 const char *functionName)
406{
407 std::stringstream matTypeStrStr;
408 matTypeStrStr << "float" << columns << "x" << rows;
409 std::string matType = matTypeStrStr.str();
410
411 sink << matType << " " << functionName << "(" << matType << " m) {\n"
412 << " " << matType << " rounded;\n";
413
414 for (unsigned int i = 0; i < columns; ++i)
415 {
416 sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
417 }
418
419 sink << " return rounded;\n"
420 "}\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200421}
422
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200423bool canRoundFloat(const TType &type)
424{
Olli Etuaho61b81ac2016-06-28 14:15:20 +0300425 return type.getBasicType() == EbtFloat && !type.isArray() &&
426 (type.getPrecision() == EbpLow || type.getPrecision() == EbpMedium);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200427}
428
429TIntermAggregate *createInternalFunctionCallNode(TString name, TIntermNode *child)
430{
431 TIntermAggregate *callNode = new TIntermAggregate();
Olli Etuaho59f9a642015-08-06 20:38:26 +0300432 callNode->setOp(EOpFunctionCall);
433 TName nameObj(TFunction::mangleName(name));
434 nameObj.setInternal(true);
435 callNode->setNameObj(nameObj);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200436 callNode->getSequence()->push_back(child);
437 return callNode;
438}
439
440TIntermAggregate *createRoundingFunctionCallNode(TIntermTyped *roundedChild)
441{
442 TString roundFunctionName;
443 if (roundedChild->getPrecision() == EbpMedium)
444 roundFunctionName = "angle_frm";
445 else
446 roundFunctionName = "angle_frl";
Olli Etuaho3820e9c2016-07-04 16:01:15 +0300447 TIntermAggregate *callNode = createInternalFunctionCallNode(roundFunctionName, roundedChild);
448 callNode->setType(roundedChild->getType());
449 return callNode;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200450}
451
452TIntermAggregate *createCompoundAssignmentFunctionCallNode(TIntermTyped *left, TIntermTyped *right, const char *opNameStr)
453{
454 std::stringstream strstr;
455 if (left->getPrecision() == EbpMedium)
456 strstr << "angle_compound_" << opNameStr << "_frm";
457 else
458 strstr << "angle_compound_" << opNameStr << "_frl";
459 TString functionName = strstr.str().c_str();
460 TIntermAggregate *callNode = createInternalFunctionCallNode(functionName, left);
461 callNode->getSequence()->push_back(right);
462 return callNode;
463}
464
Olli Etuaho1be88702015-01-19 16:56:44 +0200465bool parentUsesResult(TIntermNode* parent, TIntermNode* node)
466{
467 if (!parent)
468 {
469 return false;
470 }
471
472 TIntermAggregate *aggParent = parent->getAsAggregate();
473 // If the parent's op is EOpSequence, the result is not assigned anywhere,
474 // so rounding it is not needed. In particular, this can avoid a lot of
475 // unnecessary rounding of unused return values of assignment.
476 if (aggParent && aggParent->getOp() == EOpSequence)
477 {
478 return false;
479 }
480 if (aggParent && aggParent->getOp() == EOpComma && (aggParent->getSequence()->back() != node))
481 {
482 return false;
483 }
484 return true;
485}
486
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200487} // namespace anonymous
488
Olli Etuaho217fe6e2015-08-05 13:25:08 +0300489EmulatePrecision::EmulatePrecision(const TSymbolTable &symbolTable, int shaderVersion)
490 : TLValueTrackingTraverser(true, true, true, symbolTable, shaderVersion),
491 mDeclaringVariables(false)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200492{}
493
494void EmulatePrecision::visitSymbol(TIntermSymbol *node)
495{
Olli Etuahoa26ad582015-08-04 13:51:47 +0300496 if (canRoundFloat(node->getType()) && !mDeclaringVariables && !isLValueRequiredHere())
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200497 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200498 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Jamie Madill03d863c2016-07-27 18:15:53 -0400499 queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200500 }
501}
502
503
504bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node)
505{
506 bool visitChildren = true;
507
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200508 TOperator op = node->getOp();
509
510 // RHS of initialize is not being declared.
511 if (op == EOpInitialize && visit == InVisit)
512 mDeclaringVariables = false;
513
514 if ((op == EOpIndexDirectStruct || op == EOpVectorSwizzle) && visit == InVisit)
515 visitChildren = false;
516
517 if (visit != PreVisit)
518 return visitChildren;
519
520 const TType& type = node->getType();
521 bool roundFloat = canRoundFloat(type);
522
523 if (roundFloat) {
524 switch (op) {
525 // Math operators that can result in a float may need to apply rounding to the return
526 // value. Note that in the case of assignment, the rounding is applied to its return
527 // value here, not the value being assigned.
528 case EOpAssign:
529 case EOpAdd:
530 case EOpSub:
531 case EOpMul:
532 case EOpDiv:
533 case EOpVectorTimesScalar:
534 case EOpVectorTimesMatrix:
535 case EOpMatrixTimesVector:
536 case EOpMatrixTimesScalar:
537 case EOpMatrixTimesMatrix:
538 {
539 TIntermNode *parent = getParentNode();
Olli Etuaho1be88702015-01-19 16:56:44 +0200540 if (!parentUsesResult(parent, node))
541 {
542 break;
543 }
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200544 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Jamie Madill03d863c2016-07-27 18:15:53 -0400545 queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200546 break;
547 }
548
549 // Compound assignment cases need to replace the operator with a function call.
550 case EOpAddAssign:
551 {
Olli Etuahoe92507b2016-07-04 11:20:10 +0300552 mEmulateCompoundAdd.insert(
553 TypePair(type.getBuiltInTypeNameString(),
554 node->getRight()->getType().getBuiltInTypeNameString()));
Olli Etuahoe92507b2016-07-04 11:20:10 +0300555 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
556 node->getLeft(), node->getRight(), "add");
Jamie Madill03d863c2016-07-27 18:15:53 -0400557 queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
Olli Etuahoe92507b2016-07-04 11:20:10 +0300558 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200559 }
560 case EOpSubAssign:
561 {
Olli Etuahoe92507b2016-07-04 11:20:10 +0300562 mEmulateCompoundSub.insert(
563 TypePair(type.getBuiltInTypeNameString(),
564 node->getRight()->getType().getBuiltInTypeNameString()));
Olli Etuahoe92507b2016-07-04 11:20:10 +0300565 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
566 node->getLeft(), node->getRight(), "sub");
Jamie Madill03d863c2016-07-27 18:15:53 -0400567 queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
Olli Etuahoe92507b2016-07-04 11:20:10 +0300568 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200569 }
570 case EOpMulAssign:
571 case EOpVectorTimesMatrixAssign:
572 case EOpVectorTimesScalarAssign:
573 case EOpMatrixTimesScalarAssign:
574 case EOpMatrixTimesMatrixAssign:
575 {
Olli Etuahoe92507b2016-07-04 11:20:10 +0300576 mEmulateCompoundMul.insert(
577 TypePair(type.getBuiltInTypeNameString(),
578 node->getRight()->getType().getBuiltInTypeNameString()));
Olli Etuahoe92507b2016-07-04 11:20:10 +0300579 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
580 node->getLeft(), node->getRight(), "mul");
Jamie Madill03d863c2016-07-27 18:15:53 -0400581 queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
Olli Etuahoe92507b2016-07-04 11:20:10 +0300582 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200583 }
584 case EOpDivAssign:
585 {
Olli Etuahoe92507b2016-07-04 11:20:10 +0300586 mEmulateCompoundDiv.insert(
587 TypePair(type.getBuiltInTypeNameString(),
588 node->getRight()->getType().getBuiltInTypeNameString()));
Olli Etuahoe92507b2016-07-04 11:20:10 +0300589 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
590 node->getLeft(), node->getRight(), "div");
Jamie Madill03d863c2016-07-27 18:15:53 -0400591 queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
Olli Etuahoe92507b2016-07-04 11:20:10 +0300592 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200593 }
594 default:
595 // The rest of the binary operations should not need precision emulation.
596 break;
597 }
598 }
599 return visitChildren;
600}
601
602bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
603{
604 bool visitChildren = true;
605 switch (node->getOp())
606 {
607 case EOpSequence:
608 case EOpConstructStruct:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200609 case EOpFunction:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200610 break;
611 case EOpPrototype:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200612 visitChildren = false;
613 break;
614 case EOpParameters:
615 visitChildren = false;
616 break;
617 case EOpInvariantDeclaration:
618 visitChildren = false;
619 break;
620 case EOpDeclaration:
621 // Variable declaration.
622 if (visit == PreVisit)
623 {
624 mDeclaringVariables = true;
625 }
626 else if (visit == InVisit)
627 {
628 mDeclaringVariables = true;
629 }
630 else
631 {
632 mDeclaringVariables = false;
633 }
634 break;
635 case EOpFunctionCall:
636 {
637 // Function call.
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200638 if (visit == PreVisit)
639 {
Olli Etuaho1be88702015-01-19 16:56:44 +0200640 // User-defined function return values are not rounded, this relies on that
641 // calculations producing the value were rounded.
642 TIntermNode *parent = getParentNode();
Olli Etuahoa26ad582015-08-04 13:51:47 +0300643 if (canRoundFloat(node->getType()) && !isInFunctionMap(node) &&
644 parentUsesResult(parent, node))
Olli Etuaho1be88702015-01-19 16:56:44 +0200645 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200646 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Jamie Madill03d863c2016-07-27 18:15:53 -0400647 queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200648 }
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200649 }
650 break;
651 }
652 default:
Olli Etuaho1be88702015-01-19 16:56:44 +0200653 TIntermNode *parent = getParentNode();
654 if (canRoundFloat(node->getType()) && visit == PreVisit && parentUsesResult(parent, node))
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200655 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200656 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Jamie Madill03d863c2016-07-27 18:15:53 -0400657 queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200658 }
659 break;
660 }
661 return visitChildren;
662}
663
664bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node)
665{
666 switch (node->getOp())
667 {
668 case EOpNegative:
669 case EOpVectorLogicalNot:
670 case EOpLogicalNot:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200671 case EOpPostIncrement:
672 case EOpPostDecrement:
673 case EOpPreIncrement:
674 case EOpPreDecrement:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200675 break;
676 default:
677 if (canRoundFloat(node->getType()) && visit == PreVisit)
678 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200679 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Jamie Madill03d863c2016-07-27 18:15:53 -0400680 queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200681 }
682 break;
683 }
684
685 return true;
686}
687
Olli Etuaho61b81ac2016-06-28 14:15:20 +0300688void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase &sink,
689 const int shaderVersion,
690 const ShShaderOutput outputLanguage)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200691{
Olli Etuahob741c762016-06-29 15:49:22 +0300692 std::unique_ptr<RoundingHelperWriter> roundingHelperWriter(
693 RoundingHelperWriter::createHelperWriter(outputLanguage));
694
695 roundingHelperWriter->writeCommonRoundingHelpers(sink, shaderVersion);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200696
697 EmulationSet::const_iterator it;
698 for (it = mEmulateCompoundAdd.begin(); it != mEmulateCompoundAdd.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300699 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "+", "add");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200700 for (it = mEmulateCompoundSub.begin(); it != mEmulateCompoundSub.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300701 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "-", "sub");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200702 for (it = mEmulateCompoundDiv.begin(); it != mEmulateCompoundDiv.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300703 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "/", "div");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200704 for (it = mEmulateCompoundMul.begin(); it != mEmulateCompoundMul.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300705 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "*", "mul");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200706}
707