blob: 0f101bd21f4332fe16253f6a90bf2a14098ce530 [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 Etuahoa42e8b22016-06-29 15:49:22 +03009#include <memory>
10
Olli Etuaho853dc1a2014-11-06 17:25:48 +020011namespace
12{
13
Olli Etuahoa42e8b22016-06-29 15:49:22 +030014class RoundingHelperWriter
Olli Etuaho853dc1a2014-11-06 17:25:48 +020015{
Olli Etuahoa42e8b22016-06-29 15:49:22 +030016 public:
17 static RoundingHelperWriter *createHelperWriter(const ShShaderOutput outputLanguage);
Olli Etuaho853dc1a2014-11-06 17:25:48 +020018
Olli Etuahoa42e8b22016-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 Etuahoa42e8b22016-06-29 15:49:22 +030026 protected:
27 RoundingHelperWriter(const ShShaderOutput outputLanguage) : mOutputLanguage(outputLanguage) {}
28 RoundingHelperWriter() = delete;
Olli Etuaho853dc1a2014-11-06 17:25:48 +020029
Olli Etuahoa42e8b22016-06-29 15:49:22 +030030 const ShShaderOutput mOutputLanguage;
31
32 private:
33 virtual std::string getTypeString(const char *glslType) = 0;
34 virtual void writeFloatRoundingHelpers(TInfoSinkBase &sink) = 0;
35 virtual void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) = 0;
36 virtual void writeMatrixRoundingHelper(TInfoSinkBase &sink,
37 const unsigned int columns,
38 const unsigned int rows,
39 const char *functionName) = 0;
40};
41
42class RoundingHelperWriterGLSL : public RoundingHelperWriter
Olli Etuaho853dc1a2014-11-06 17:25:48 +020043{
Olli Etuahoa42e8b22016-06-29 15:49:22 +030044 public:
45 RoundingHelperWriterGLSL(const ShShaderOutput outputLanguage)
46 : RoundingHelperWriter(outputLanguage)
Olli Etuaho61b81ac2016-06-28 14:15:20 +030047 {
Olli Etuaho61b81ac2016-06-28 14:15:20 +030048 }
49
Olli Etuahoa42e8b22016-06-29 15:49:22 +030050 private:
51 std::string getTypeString(const char *glslType) override;
52 void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
53 void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
54 void writeMatrixRoundingHelper(TInfoSinkBase &sink,
55 const unsigned int columns,
56 const unsigned int rows,
57 const char *functionName) override;
58};
Olli Etuaho853dc1a2014-11-06 17:25:48 +020059
Olli Etuahoa42e8b22016-06-29 15:49:22 +030060class RoundingHelperWriterESSL : public RoundingHelperWriterGLSL
61{
62 public:
63 RoundingHelperWriterESSL(const ShShaderOutput outputLanguage)
64 : RoundingHelperWriterGLSL(outputLanguage)
Olli Etuaho853dc1a2014-11-06 17:25:48 +020065 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +020066 }
67
Olli Etuahoa42e8b22016-06-29 15:49:22 +030068 private:
69 std::string getTypeString(const char *glslType) override;
70};
71
72class RoundingHelperWriterHLSL : public RoundingHelperWriter
73{
74 public:
75 RoundingHelperWriterHLSL(const ShShaderOutput outputLanguage)
76 : RoundingHelperWriter(outputLanguage)
77 {
78 }
79
80 private:
81 std::string getTypeString(const char *glslType) override;
82 void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
83 void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
84 void writeMatrixRoundingHelper(TInfoSinkBase &sink,
85 const unsigned int columns,
86 const unsigned int rows,
87 const char *functionName) override;
88};
89
90RoundingHelperWriter *RoundingHelperWriter::createHelperWriter(const ShShaderOutput outputLanguage)
91{
92 switch (outputLanguage)
93 {
94 case SH_HLSL_4_1_OUTPUT:
95 return new RoundingHelperWriterHLSL(outputLanguage);
96 case SH_ESSL_OUTPUT:
97 return new RoundingHelperWriterESSL(outputLanguage);
98 default:
99 // Other languages not yet supported
100 ASSERT(outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT ||
101 IsGLSL130OrNewer(outputLanguage));
102 return new RoundingHelperWriterGLSL(outputLanguage);
103 }
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200104}
105
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300106void RoundingHelperWriter::writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200107{
108 // Write the angle_frm functions that round floating point numbers to
109 // half precision, and angle_frl functions that round them to minimum lowp
110 // precision.
111
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300112 writeFloatRoundingHelpers(sink);
113 writeVectorRoundingHelpers(sink, 2);
114 writeVectorRoundingHelpers(sink, 3);
115 writeVectorRoundingHelpers(sink, 4);
116 if (shaderVersion > 100)
117 {
118 for (unsigned int columns = 2; columns <= 4; ++columns)
119 {
120 for (unsigned int rows = 2; rows <= 4; ++rows)
121 {
122 writeMatrixRoundingHelper(sink, columns, rows, "angle_frm");
123 writeMatrixRoundingHelper(sink, columns, rows, "angle_frl");
124 }
125 }
126 }
127 else
128 {
129 for (unsigned int size = 2; size <= 4; ++size)
130 {
131 writeMatrixRoundingHelper(sink, size, size, "angle_frm");
132 writeMatrixRoundingHelper(sink, size, size, "angle_frl");
133 }
134 }
135}
136
137void RoundingHelperWriter::writeCompoundAssignmentHelper(TInfoSinkBase &sink,
138 const char *lType,
139 const char *rType,
140 const char *opStr,
141 const char *opNameStr)
142{
143 std::string lTypeStr = getTypeString(lType);
144 std::string rTypeStr = getTypeString(rType);
145
146 // Note that y should be passed through angle_frm at the function call site,
147 // but x can't be passed through angle_frm there since it is an inout parameter.
148 // So only pass x and the result through angle_frm here.
149 // clang-format off
150 sink <<
151 lTypeStr << " angle_compound_" << opNameStr << "_frm(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
152 " x = angle_frm(angle_frm(x) " << opStr << " y);\n"
153 " return x;\n"
154 "}\n";
155 sink <<
156 lTypeStr << " angle_compound_" << opNameStr << "_frl(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
157 " x = angle_frl(angle_frm(x) " << opStr << " y);\n"
158 " return x;\n"
159 "}\n";
160 // clang-format on
161}
162
163std::string RoundingHelperWriterGLSL::getTypeString(const char *glslType)
164{
165 return glslType;
166}
167
168std::string RoundingHelperWriterESSL::getTypeString(const char *glslType)
169{
170 std::stringstream typeStrStr;
171 typeStrStr << "highp " << glslType;
172 return typeStrStr.str();
173}
174
175void RoundingHelperWriterGLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
176{
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200177 // Unoptimized version of angle_frm for single floats:
178 //
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300179 // int webgl_maxNormalExponent(in int exponentBits)
180 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200181 // int possibleExponents = int(exp2(float(exponentBits)));
182 // int exponentBias = possibleExponents / 2 - 1;
183 // int allExponentBitsOne = possibleExponents - 1;
184 // return (allExponentBitsOne - 1) - exponentBias;
185 // }
186 //
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300187 // float angle_frm(in float x)
188 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200189 // int mantissaBits = 10;
190 // int exponentBits = 5;
191 // float possibleMantissas = exp2(float(mantissaBits));
192 // float mantissaMax = 2.0 - 1.0 / possibleMantissas;
193 // int maxNE = webgl_maxNormalExponent(exponentBits);
194 // float max = exp2(float(maxNE)) * mantissaMax;
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300195 // if (x > max)
196 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200197 // return max;
198 // }
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300199 // if (x < -max)
200 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200201 // return -max;
202 // }
203 // float exponent = floor(log2(abs(x)));
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300204 // if (abs(x) == 0.0 || exponent < -float(maxNE))
205 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200206 // return 0.0 * sign(x)
207 // }
208 // x = x * exp2(-(exponent - float(mantissaBits)));
209 // x = sign(x) * floor(abs(x));
210 // return x * exp2(exponent - float(mantissaBits));
211 // }
212
213 // All numbers with a magnitude less than 2^-15 are subnormal, and are
214 // flushed to zero.
215
216 // Note the constant numbers below:
217 // a) 65504 is the maximum possible mantissa (1.1111111111 in binary) times
218 // 2^15, the maximum normal exponent.
219 // b) 10.0 is the number of mantissa bits.
220 // c) -25.0 is the minimum normal half-float exponent -15.0 minus the number
221 // of mantissa bits.
222 // d) + 1e-30 is to make sure the argument of log2() won't be zero. It can
223 // only affect the result of log2 on x where abs(x) < 1e-22. Since these
224 // numbers will be flushed to zero either way (2^-15 is the smallest
225 // normal positive number), this does not introduce any error.
226
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300227 std::string floatType = getTypeString("float");
228
229 // clang-format off
230 sink <<
231 floatType << " angle_frm(in " << floatType << " x) {\n"
232 " x = clamp(x, -65504.0, 65504.0);\n"
233 " " << floatType << " exponent = floor(log2(abs(x) + 1e-30)) - 10.0;\n"
234 " bool isNonZero = (exponent >= -25.0);\n"
235 " x = x * exp2(-exponent);\n"
236 " x = sign(x) * floor(abs(x));\n"
237 " return x * exp2(exponent) * float(isNonZero);\n"
238 "}\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200239
240 sink <<
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300241 floatType << " angle_frl(in " << floatType << " x) {\n"
242 " x = clamp(x, -2.0, 2.0);\n"
243 " x = x * 256.0;\n"
244 " x = sign(x) * floor(abs(x));\n"
245 " return x * 0.00390625;\n"
246 "}\n";
247 // clang-format on
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200248}
249
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300250void RoundingHelperWriterGLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
251 const unsigned int size)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200252{
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300253 std::stringstream vecTypeStrStr;
254 vecTypeStrStr << "vec" << size;
255 std::string vecType = getTypeString(vecTypeStrStr.str().c_str());
256
257 // clang-format off
258 sink <<
259 vecType << " angle_frm(in " << vecType << " v) {\n"
260 " v = clamp(v, -65504.0, 65504.0);\n"
261 " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
262 " bvec" << size << " isNonZero = greaterThanEqual(exponent, vec" << size << "(-25.0));\n"
263 " v = v * exp2(-exponent);\n"
264 " v = sign(v) * floor(abs(v));\n"
265 " return v * exp2(exponent) * vec" << size << "(isNonZero);\n"
266 "}\n";
267
268 sink <<
269 vecType << " angle_frl(in " << vecType << " v) {\n"
270 " v = clamp(v, -2.0, 2.0);\n"
271 " v = v * 256.0;\n"
272 " v = sign(v) * floor(abs(v));\n"
273 " return v * 0.00390625;\n"
274 "}\n";
275 // clang-format on
276}
277
278void RoundingHelperWriterGLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
279 const unsigned int columns,
280 const unsigned int rows,
281 const char *functionName)
282{
283 std::stringstream matTypeStrStr;
284 matTypeStrStr << "mat" << columns;
285 if (rows != columns)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200286 {
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300287 matTypeStrStr << "x" << rows;
288 }
289 std::string matType = getTypeString(matTypeStrStr.str().c_str());
290
291 sink << matType << " " << functionName << "(in " << matType << " m) {\n"
292 << " " << matType << " rounded;\n";
293
294 for (unsigned int i = 0; i < columns; ++i)
295 {
296 sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200297 }
298
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300299 sink << " return rounded;\n"
300 "}\n";
301}
302
303static const char *GetHLSLTypeStr(const char *floatTypeStr)
304{
305 if (strcmp(floatTypeStr, "float") == 0)
306 {
307 return "float";
308 }
309 if (strcmp(floatTypeStr, "vec2") == 0)
310 {
311 return "float2";
312 }
313 if (strcmp(floatTypeStr, "vec3") == 0)
314 {
315 return "float3";
316 }
317 if (strcmp(floatTypeStr, "vec4") == 0)
318 {
319 return "float4";
320 }
321 if (strcmp(floatTypeStr, "mat2") == 0)
322 {
323 return "float2x2";
324 }
325 if (strcmp(floatTypeStr, "mat3") == 0)
326 {
327 return "float3x3";
328 }
329 if (strcmp(floatTypeStr, "mat4") == 0)
330 {
331 return "float4x4";
332 }
333 if (strcmp(floatTypeStr, "mat2x3") == 0)
334 {
335 return "float2x3";
336 }
337 if (strcmp(floatTypeStr, "mat2x4") == 0)
338 {
339 return "float2x4";
340 }
341 if (strcmp(floatTypeStr, "mat3x2") == 0)
342 {
343 return "float3x2";
344 }
345 if (strcmp(floatTypeStr, "mat3x4") == 0)
346 {
347 return "float3x4";
348 }
349 if (strcmp(floatTypeStr, "mat4x2") == 0)
350 {
351 return "float4x2";
352 }
353 if (strcmp(floatTypeStr, "mat4x3") == 0)
354 {
355 return "float4x3";
356 }
357 UNREACHABLE();
358 return nullptr;
359}
360
361std::string RoundingHelperWriterHLSL::getTypeString(const char *glslType)
362{
363 return GetHLSLTypeStr(glslType);
364}
365
366void RoundingHelperWriterHLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
367{
368 // In HLSL scalars are the same as 1-vectors.
369 writeVectorRoundingHelpers(sink, 1);
370}
371
372void RoundingHelperWriterHLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
373 const unsigned int size)
374{
375 std::stringstream vecTypeStrStr;
376 vecTypeStrStr << "float" << size;
377 std::string vecType = vecTypeStrStr.str();
378
379 // clang-format off
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200380 sink <<
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300381 vecType << " angle_frm(" << vecType << " v) {\n"
382 " v = clamp(v, -65504.0, 65504.0);\n"
383 " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
384 " bool" << size << " isNonZero = exponent < -25.0;\n"
385 " v = v * exp2(-exponent);\n"
386 " v = sign(v) * floor(abs(v));\n"
387 " return v * exp2(exponent) * (float" << size << ")(isNonZero);\n"
388 "}\n";
389
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200390 sink <<
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300391 vecType << " angle_frl(" << vecType << " v) {\n"
392 " v = clamp(v, -2.0, 2.0);\n"
393 " v = v * 256.0;\n"
394 " v = sign(v) * floor(abs(v));\n"
395 " return v * 0.00390625;\n"
396 "}\n";
397 // clang-format on
398}
399
400void RoundingHelperWriterHLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
401 const unsigned int columns,
402 const unsigned int rows,
403 const char *functionName)
404{
405 std::stringstream matTypeStrStr;
406 matTypeStrStr << "float" << columns << "x" << rows;
407 std::string matType = matTypeStrStr.str();
408
409 sink << matType << " " << functionName << "(" << matType << " m) {\n"
410 << " " << matType << " rounded;\n";
411
412 for (unsigned int i = 0; i < columns; ++i)
413 {
414 sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
415 }
416
417 sink << " return rounded;\n"
418 "}\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200419}
420
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200421bool canRoundFloat(const TType &type)
422{
Olli Etuaho61b81ac2016-06-28 14:15:20 +0300423 return type.getBasicType() == EbtFloat && !type.isArray() &&
424 (type.getPrecision() == EbpLow || type.getPrecision() == EbpMedium);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200425}
426
427TIntermAggregate *createInternalFunctionCallNode(TString name, TIntermNode *child)
428{
429 TIntermAggregate *callNode = new TIntermAggregate();
Olli Etuaho59f9a642015-08-06 20:38:26 +0300430 callNode->setOp(EOpFunctionCall);
431 TName nameObj(TFunction::mangleName(name));
432 nameObj.setInternal(true);
433 callNode->setNameObj(nameObj);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200434 callNode->getSequence()->push_back(child);
435 return callNode;
436}
437
438TIntermAggregate *createRoundingFunctionCallNode(TIntermTyped *roundedChild)
439{
440 TString roundFunctionName;
441 if (roundedChild->getPrecision() == EbpMedium)
442 roundFunctionName = "angle_frm";
443 else
444 roundFunctionName = "angle_frl";
Olli Etuaho3820e9c2016-07-04 16:01:15 +0300445 TIntermAggregate *callNode = createInternalFunctionCallNode(roundFunctionName, roundedChild);
446 callNode->setType(roundedChild->getType());
447 return callNode;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200448}
449
450TIntermAggregate *createCompoundAssignmentFunctionCallNode(TIntermTyped *left, TIntermTyped *right, const char *opNameStr)
451{
452 std::stringstream strstr;
453 if (left->getPrecision() == EbpMedium)
454 strstr << "angle_compound_" << opNameStr << "_frm";
455 else
456 strstr << "angle_compound_" << opNameStr << "_frl";
457 TString functionName = strstr.str().c_str();
458 TIntermAggregate *callNode = createInternalFunctionCallNode(functionName, left);
459 callNode->getSequence()->push_back(right);
460 return callNode;
461}
462
Olli Etuaho1be88702015-01-19 16:56:44 +0200463bool parentUsesResult(TIntermNode* parent, TIntermNode* node)
464{
465 if (!parent)
466 {
467 return false;
468 }
469
470 TIntermAggregate *aggParent = parent->getAsAggregate();
471 // If the parent's op is EOpSequence, the result is not assigned anywhere,
472 // so rounding it is not needed. In particular, this can avoid a lot of
473 // unnecessary rounding of unused return values of assignment.
474 if (aggParent && aggParent->getOp() == EOpSequence)
475 {
476 return false;
477 }
478 if (aggParent && aggParent->getOp() == EOpComma && (aggParent->getSequence()->back() != node))
479 {
480 return false;
481 }
482 return true;
483}
484
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200485} // namespace anonymous
486
Olli Etuaho217fe6e2015-08-05 13:25:08 +0300487EmulatePrecision::EmulatePrecision(const TSymbolTable &symbolTable, int shaderVersion)
488 : TLValueTrackingTraverser(true, true, true, symbolTable, shaderVersion),
489 mDeclaringVariables(false)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200490{}
491
492void EmulatePrecision::visitSymbol(TIntermSymbol *node)
493{
Olli Etuahoa26ad582015-08-04 13:51:47 +0300494 if (canRoundFloat(node->getType()) && !mDeclaringVariables && !isLValueRequiredHere())
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200495 {
496 TIntermNode *parent = getParentNode();
497 TIntermNode *replacement = createRoundingFunctionCallNode(node);
498 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
499 }
500}
501
502
503bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node)
504{
505 bool visitChildren = true;
506
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200507 TOperator op = node->getOp();
508
509 // RHS of initialize is not being declared.
510 if (op == EOpInitialize && visit == InVisit)
511 mDeclaringVariables = false;
512
513 if ((op == EOpIndexDirectStruct || op == EOpVectorSwizzle) && visit == InVisit)
514 visitChildren = false;
515
516 if (visit != PreVisit)
517 return visitChildren;
518
519 const TType& type = node->getType();
520 bool roundFloat = canRoundFloat(type);
521
522 if (roundFloat) {
523 switch (op) {
524 // Math operators that can result in a float may need to apply rounding to the return
525 // value. Note that in the case of assignment, the rounding is applied to its return
526 // value here, not the value being assigned.
527 case EOpAssign:
528 case EOpAdd:
529 case EOpSub:
530 case EOpMul:
531 case EOpDiv:
532 case EOpVectorTimesScalar:
533 case EOpVectorTimesMatrix:
534 case EOpMatrixTimesVector:
535 case EOpMatrixTimesScalar:
536 case EOpMatrixTimesMatrix:
537 {
538 TIntermNode *parent = getParentNode();
Olli Etuaho1be88702015-01-19 16:56:44 +0200539 if (!parentUsesResult(parent, node))
540 {
541 break;
542 }
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200543 TIntermNode *replacement = createRoundingFunctionCallNode(node);
544 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
545 break;
546 }
547
548 // Compound assignment cases need to replace the operator with a function call.
549 case EOpAddAssign:
550 {
Olli Etuahoe92507b2016-07-04 11:20:10 +0300551 mEmulateCompoundAdd.insert(
552 TypePair(type.getBuiltInTypeNameString(),
553 node->getRight()->getType().getBuiltInTypeNameString()));
554 TIntermNode *parent = getParentNode();
555 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
556 node->getLeft(), node->getRight(), "add");
557 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
558 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()));
565 TIntermNode *parent = getParentNode();
566 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
567 node->getLeft(), node->getRight(), "sub");
568 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
569 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200570 }
571 case EOpMulAssign:
572 case EOpVectorTimesMatrixAssign:
573 case EOpVectorTimesScalarAssign:
574 case EOpMatrixTimesScalarAssign:
575 case EOpMatrixTimesMatrixAssign:
576 {
Olli Etuahoe92507b2016-07-04 11:20:10 +0300577 mEmulateCompoundMul.insert(
578 TypePair(type.getBuiltInTypeNameString(),
579 node->getRight()->getType().getBuiltInTypeNameString()));
580 TIntermNode *parent = getParentNode();
581 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
582 node->getLeft(), node->getRight(), "mul");
583 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
584 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200585 }
586 case EOpDivAssign:
587 {
Olli Etuahoe92507b2016-07-04 11:20:10 +0300588 mEmulateCompoundDiv.insert(
589 TypePair(type.getBuiltInTypeNameString(),
590 node->getRight()->getType().getBuiltInTypeNameString()));
591 TIntermNode *parent = getParentNode();
592 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
593 node->getLeft(), node->getRight(), "div");
594 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
595 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200596 }
597 default:
598 // The rest of the binary operations should not need precision emulation.
599 break;
600 }
601 }
602 return visitChildren;
603}
604
605bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
606{
607 bool visitChildren = true;
608 switch (node->getOp())
609 {
610 case EOpSequence:
611 case EOpConstructStruct:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200612 case EOpFunction:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200613 break;
614 case EOpPrototype:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200615 visitChildren = false;
616 break;
617 case EOpParameters:
618 visitChildren = false;
619 break;
620 case EOpInvariantDeclaration:
621 visitChildren = false;
622 break;
623 case EOpDeclaration:
624 // Variable declaration.
625 if (visit == PreVisit)
626 {
627 mDeclaringVariables = true;
628 }
629 else if (visit == InVisit)
630 {
631 mDeclaringVariables = true;
632 }
633 else
634 {
635 mDeclaringVariables = false;
636 }
637 break;
638 case EOpFunctionCall:
639 {
640 // Function call.
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200641 if (visit == PreVisit)
642 {
Olli Etuaho1be88702015-01-19 16:56:44 +0200643 // User-defined function return values are not rounded, this relies on that
644 // calculations producing the value were rounded.
645 TIntermNode *parent = getParentNode();
Olli Etuahoa26ad582015-08-04 13:51:47 +0300646 if (canRoundFloat(node->getType()) && !isInFunctionMap(node) &&
647 parentUsesResult(parent, node))
Olli Etuaho1be88702015-01-19 16:56:44 +0200648 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200649 TIntermNode *replacement = createRoundingFunctionCallNode(node);
650 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
651 }
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200652 }
653 break;
654 }
655 default:
Olli Etuaho1be88702015-01-19 16:56:44 +0200656 TIntermNode *parent = getParentNode();
657 if (canRoundFloat(node->getType()) && visit == PreVisit && parentUsesResult(parent, node))
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200658 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200659 TIntermNode *replacement = createRoundingFunctionCallNode(node);
660 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
661 }
662 break;
663 }
664 return visitChildren;
665}
666
667bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node)
668{
669 switch (node->getOp())
670 {
671 case EOpNegative:
672 case EOpVectorLogicalNot:
673 case EOpLogicalNot:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200674 case EOpPostIncrement:
675 case EOpPostDecrement:
676 case EOpPreIncrement:
677 case EOpPreDecrement:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200678 break;
679 default:
680 if (canRoundFloat(node->getType()) && visit == PreVisit)
681 {
682 TIntermNode *parent = getParentNode();
683 TIntermNode *replacement = createRoundingFunctionCallNode(node);
684 mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
685 }
686 break;
687 }
688
689 return true;
690}
691
Olli Etuaho61b81ac2016-06-28 14:15:20 +0300692void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase &sink,
693 const int shaderVersion,
694 const ShShaderOutput outputLanguage)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200695{
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300696 std::unique_ptr<RoundingHelperWriter> roundingHelperWriter(
697 RoundingHelperWriter::createHelperWriter(outputLanguage));
698
699 roundingHelperWriter->writeCommonRoundingHelpers(sink, shaderVersion);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200700
701 EmulationSet::const_iterator it;
702 for (it = mEmulateCompoundAdd.begin(); it != mEmulateCompoundAdd.end(); it++)
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300703 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "+", "add");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200704 for (it = mEmulateCompoundSub.begin(); it != mEmulateCompoundSub.end(); it++)
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300705 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "-", "sub");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200706 for (it = mEmulateCompoundDiv.begin(); it != mEmulateCompoundDiv.end(); it++)
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300707 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "/", "div");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200708 for (it = mEmulateCompoundMul.begin(); it != mEmulateCompoundMul.end(); it++)
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300709 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "*", "mul");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200710}
711