blob: 11fa35e87ff70be5b3b8f76b2afcab4f8fa00962 [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 {
498 TIntermNode *parent = getParentNode();
499 TIntermNode *replacement = createRoundingFunctionCallNode(node);
500 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
501 }
502}
503
504
505bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node)
506{
507 bool visitChildren = true;
508
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200509 TOperator op = node->getOp();
510
511 // RHS of initialize is not being declared.
512 if (op == EOpInitialize && visit == InVisit)
513 mDeclaringVariables = false;
514
515 if ((op == EOpIndexDirectStruct || op == EOpVectorSwizzle) && visit == InVisit)
516 visitChildren = false;
517
518 if (visit != PreVisit)
519 return visitChildren;
520
521 const TType& type = node->getType();
522 bool roundFloat = canRoundFloat(type);
523
524 if (roundFloat) {
525 switch (op) {
526 // Math operators that can result in a float may need to apply rounding to the return
527 // value. Note that in the case of assignment, the rounding is applied to its return
528 // value here, not the value being assigned.
529 case EOpAssign:
530 case EOpAdd:
531 case EOpSub:
532 case EOpMul:
533 case EOpDiv:
534 case EOpVectorTimesScalar:
535 case EOpVectorTimesMatrix:
536 case EOpMatrixTimesVector:
537 case EOpMatrixTimesScalar:
538 case EOpMatrixTimesMatrix:
539 {
540 TIntermNode *parent = getParentNode();
Olli Etuaho1be88702015-01-19 16:56:44 +0200541 if (!parentUsesResult(parent, node))
542 {
543 break;
544 }
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200545 TIntermNode *replacement = createRoundingFunctionCallNode(node);
546 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
547 break;
548 }
549
550 // Compound assignment cases need to replace the operator with a function call.
551 case EOpAddAssign:
552 {
Olli Etuahoe92507b2016-07-04 11:20:10 +0300553 mEmulateCompoundAdd.insert(
554 TypePair(type.getBuiltInTypeNameString(),
555 node->getRight()->getType().getBuiltInTypeNameString()));
556 TIntermNode *parent = getParentNode();
557 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
558 node->getLeft(), node->getRight(), "add");
559 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
560 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200561 }
562 case EOpSubAssign:
563 {
Olli Etuahoe92507b2016-07-04 11:20:10 +0300564 mEmulateCompoundSub.insert(
565 TypePair(type.getBuiltInTypeNameString(),
566 node->getRight()->getType().getBuiltInTypeNameString()));
567 TIntermNode *parent = getParentNode();
568 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
569 node->getLeft(), node->getRight(), "sub");
570 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
571 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200572 }
573 case EOpMulAssign:
574 case EOpVectorTimesMatrixAssign:
575 case EOpVectorTimesScalarAssign:
576 case EOpMatrixTimesScalarAssign:
577 case EOpMatrixTimesMatrixAssign:
578 {
Olli Etuahoe92507b2016-07-04 11:20:10 +0300579 mEmulateCompoundMul.insert(
580 TypePair(type.getBuiltInTypeNameString(),
581 node->getRight()->getType().getBuiltInTypeNameString()));
582 TIntermNode *parent = getParentNode();
583 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
584 node->getLeft(), node->getRight(), "mul");
585 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
586 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200587 }
588 case EOpDivAssign:
589 {
Olli Etuahoe92507b2016-07-04 11:20:10 +0300590 mEmulateCompoundDiv.insert(
591 TypePair(type.getBuiltInTypeNameString(),
592 node->getRight()->getType().getBuiltInTypeNameString()));
593 TIntermNode *parent = getParentNode();
594 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
595 node->getLeft(), node->getRight(), "div");
596 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
597 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200598 }
599 default:
600 // The rest of the binary operations should not need precision emulation.
601 break;
602 }
603 }
604 return visitChildren;
605}
606
607bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
608{
609 bool visitChildren = true;
610 switch (node->getOp())
611 {
612 case EOpSequence:
613 case EOpConstructStruct:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200614 case EOpFunction:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200615 break;
616 case EOpPrototype:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200617 visitChildren = false;
618 break;
619 case EOpParameters:
620 visitChildren = false;
621 break;
622 case EOpInvariantDeclaration:
623 visitChildren = false;
624 break;
625 case EOpDeclaration:
626 // Variable declaration.
627 if (visit == PreVisit)
628 {
629 mDeclaringVariables = true;
630 }
631 else if (visit == InVisit)
632 {
633 mDeclaringVariables = true;
634 }
635 else
636 {
637 mDeclaringVariables = false;
638 }
639 break;
640 case EOpFunctionCall:
641 {
642 // Function call.
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200643 if (visit == PreVisit)
644 {
Olli Etuaho1be88702015-01-19 16:56:44 +0200645 // User-defined function return values are not rounded, this relies on that
646 // calculations producing the value were rounded.
647 TIntermNode *parent = getParentNode();
Olli Etuahoa26ad582015-08-04 13:51:47 +0300648 if (canRoundFloat(node->getType()) && !isInFunctionMap(node) &&
649 parentUsesResult(parent, node))
Olli Etuaho1be88702015-01-19 16:56:44 +0200650 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200651 TIntermNode *replacement = createRoundingFunctionCallNode(node);
652 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
653 }
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200654 }
655 break;
656 }
657 default:
Olli Etuaho1be88702015-01-19 16:56:44 +0200658 TIntermNode *parent = getParentNode();
659 if (canRoundFloat(node->getType()) && visit == PreVisit && parentUsesResult(parent, node))
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200660 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200661 TIntermNode *replacement = createRoundingFunctionCallNode(node);
662 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
663 }
664 break;
665 }
666 return visitChildren;
667}
668
669bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node)
670{
671 switch (node->getOp())
672 {
673 case EOpNegative:
674 case EOpVectorLogicalNot:
675 case EOpLogicalNot:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200676 case EOpPostIncrement:
677 case EOpPostDecrement:
678 case EOpPreIncrement:
679 case EOpPreDecrement:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200680 break;
681 default:
682 if (canRoundFloat(node->getType()) && visit == PreVisit)
683 {
684 TIntermNode *parent = getParentNode();
685 TIntermNode *replacement = createRoundingFunctionCallNode(node);
686 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
687 }
688 break;
689 }
690
691 return true;
692}
693
Olli Etuaho61b81ac2016-06-28 14:15:20 +0300694void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase &sink,
695 const int shaderVersion,
696 const ShShaderOutput outputLanguage)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200697{
Olli Etuahob741c762016-06-29 15:49:22 +0300698 std::unique_ptr<RoundingHelperWriter> roundingHelperWriter(
699 RoundingHelperWriter::createHelperWriter(outputLanguage));
700
701 roundingHelperWriter->writeCommonRoundingHelpers(sink, shaderVersion);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200702
703 EmulationSet::const_iterator it;
704 for (it = mEmulateCompoundAdd.begin(); it != mEmulateCompoundAdd.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300705 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "+", "add");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200706 for (it = mEmulateCompoundSub.begin(); it != mEmulateCompoundSub.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300707 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "-", "sub");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200708 for (it = mEmulateCompoundDiv.begin(); it != mEmulateCompoundDiv.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300709 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "/", "div");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200710 for (it = mEmulateCompoundMul.begin(); it != mEmulateCompoundMul.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300711 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "*", "mul");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200712}
713