blob: cc80717b1c819afacde716e376048c7368b39404 [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{
Jamie Madilld5696192016-10-06 11:09:24 -040094 ASSERT(EmulatePrecision::SupportedInLanguage(outputLanguage));
Olli Etuahob741c762016-06-29 15:49:22 +030095 switch (outputLanguage)
96 {
97 case SH_HLSL_4_1_OUTPUT:
98 return new RoundingHelperWriterHLSL(outputLanguage);
99 case SH_ESSL_OUTPUT:
100 return new RoundingHelperWriterESSL(outputLanguage);
101 default:
Olli Etuahob741c762016-06-29 15:49:22 +0300102 return new RoundingHelperWriterGLSL(outputLanguage);
103 }
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +0300104}
105
Olli Etuahob741c762016-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 Etuahob741c762016-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 Etuahob741c762016-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 Etuahob741c762016-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 Etuahob741c762016-06-29 15:49:22 +0300195 // if (x > max)
196 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200197 // return max;
198 // }
Olli Etuahob741c762016-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 Etuahob741c762016-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 Etuahob741c762016-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 Etuahob741c762016-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 Etuahoa42e8b22016-06-29 15:49:22 +0300248}
249
Olli Etuahob741c762016-06-29 15:49:22 +0300250void RoundingHelperWriterGLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
251 const unsigned int size)
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300252{
Olli Etuahob741c762016-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 Etuahob741c762016-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 Etuahob741c762016-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 Etuahob741c762016-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 Etuahob741c762016-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
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100470 TIntermBlock *blockParent = parent->getAsBlock();
471 // If the parent is a block, the result is not assigned anywhere,
Olli Etuaho1be88702015-01-19 16:56:44 +0200472 // so rounding it is not needed. In particular, this can avoid a lot of
473 // unnecessary rounding of unused return values of assignment.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100474 if (blockParent)
Olli Etuaho1be88702015-01-19 16:56:44 +0200475 {
476 return false;
477 }
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100478 TIntermAggregate *aggParent = parent->getAsAggregate();
Olli Etuaho1be88702015-01-19 16:56:44 +0200479 if (aggParent && aggParent->getOp() == EOpComma && (aggParent->getSequence()->back() != node))
480 {
481 return false;
482 }
483 return true;
484}
485
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200486} // namespace anonymous
487
Olli Etuaho217fe6e2015-08-05 13:25:08 +0300488EmulatePrecision::EmulatePrecision(const TSymbolTable &symbolTable, int shaderVersion)
489 : TLValueTrackingTraverser(true, true, true, symbolTable, shaderVersion),
490 mDeclaringVariables(false)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200491{}
492
493void EmulatePrecision::visitSymbol(TIntermSymbol *node)
494{
Olli Etuahoa26ad582015-08-04 13:51:47 +0300495 if (canRoundFloat(node->getType()) && !mDeclaringVariables && !isLValueRequiredHere())
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200496 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200497 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Jamie Madill03d863c2016-07-27 18:15:53 -0400498 queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200499 }
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
Olli Etuahob6fa0432016-09-28 16:28:05 +0100513 if ((op == EOpIndexDirectStruct) && visit == InVisit)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200514 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);
Jamie Madill03d863c2016-07-27 18:15:53 -0400544 queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200545 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()));
Olli Etuahoe92507b2016-07-04 11:20:10 +0300554 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
555 node->getLeft(), node->getRight(), "add");
Jamie Madill03d863c2016-07-27 18:15:53 -0400556 queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
Olli Etuahoe92507b2016-07-04 11:20:10 +0300557 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200558 }
559 case EOpSubAssign:
560 {
Olli Etuahoe92507b2016-07-04 11:20:10 +0300561 mEmulateCompoundSub.insert(
562 TypePair(type.getBuiltInTypeNameString(),
563 node->getRight()->getType().getBuiltInTypeNameString()));
Olli Etuahoe92507b2016-07-04 11:20:10 +0300564 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
565 node->getLeft(), node->getRight(), "sub");
Jamie Madill03d863c2016-07-27 18:15:53 -0400566 queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
Olli Etuahoe92507b2016-07-04 11:20:10 +0300567 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200568 }
569 case EOpMulAssign:
570 case EOpVectorTimesMatrixAssign:
571 case EOpVectorTimesScalarAssign:
572 case EOpMatrixTimesScalarAssign:
573 case EOpMatrixTimesMatrixAssign:
574 {
Olli Etuahoe92507b2016-07-04 11:20:10 +0300575 mEmulateCompoundMul.insert(
576 TypePair(type.getBuiltInTypeNameString(),
577 node->getRight()->getType().getBuiltInTypeNameString()));
Olli Etuahoe92507b2016-07-04 11:20:10 +0300578 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
579 node->getLeft(), node->getRight(), "mul");
Jamie Madill03d863c2016-07-27 18:15:53 -0400580 queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
Olli Etuahoe92507b2016-07-04 11:20:10 +0300581 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200582 }
583 case EOpDivAssign:
584 {
Olli Etuahoe92507b2016-07-04 11:20:10 +0300585 mEmulateCompoundDiv.insert(
586 TypePair(type.getBuiltInTypeNameString(),
587 node->getRight()->getType().getBuiltInTypeNameString()));
Olli Etuahoe92507b2016-07-04 11:20:10 +0300588 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
589 node->getLeft(), node->getRight(), "div");
Jamie Madill03d863c2016-07-27 18:15:53 -0400590 queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
Olli Etuahoe92507b2016-07-04 11:20:10 +0300591 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200592 }
593 default:
594 // The rest of the binary operations should not need precision emulation.
595 break;
596 }
597 }
598 return visitChildren;
599}
600
601bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
602{
603 bool visitChildren = true;
604 switch (node->getOp())
605 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200606 case EOpConstructStruct:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200607 case EOpFunction:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200608 break;
609 case EOpPrototype:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200610 visitChildren = false;
611 break;
612 case EOpParameters:
613 visitChildren = false;
614 break;
615 case EOpInvariantDeclaration:
616 visitChildren = false;
617 break;
618 case EOpDeclaration:
619 // Variable declaration.
620 if (visit == PreVisit)
621 {
622 mDeclaringVariables = true;
623 }
624 else if (visit == InVisit)
625 {
626 mDeclaringVariables = true;
627 }
628 else
629 {
630 mDeclaringVariables = false;
631 }
632 break;
633 case EOpFunctionCall:
634 {
635 // Function call.
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200636 if (visit == PreVisit)
637 {
Olli Etuaho1be88702015-01-19 16:56:44 +0200638 // User-defined function return values are not rounded, this relies on that
639 // calculations producing the value were rounded.
640 TIntermNode *parent = getParentNode();
Olli Etuahoa26ad582015-08-04 13:51:47 +0300641 if (canRoundFloat(node->getType()) && !isInFunctionMap(node) &&
642 parentUsesResult(parent, node))
Olli Etuaho1be88702015-01-19 16:56:44 +0200643 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200644 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Jamie Madill03d863c2016-07-27 18:15:53 -0400645 queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200646 }
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200647 }
648 break;
649 }
650 default:
Olli Etuaho1be88702015-01-19 16:56:44 +0200651 TIntermNode *parent = getParentNode();
652 if (canRoundFloat(node->getType()) && visit == PreVisit && parentUsesResult(parent, node))
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200653 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200654 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Jamie Madill03d863c2016-07-27 18:15:53 -0400655 queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200656 }
657 break;
658 }
659 return visitChildren;
660}
661
662bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node)
663{
664 switch (node->getOp())
665 {
666 case EOpNegative:
667 case EOpVectorLogicalNot:
668 case EOpLogicalNot:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200669 case EOpPostIncrement:
670 case EOpPostDecrement:
671 case EOpPreIncrement:
672 case EOpPreDecrement:
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200673 break;
674 default:
675 if (canRoundFloat(node->getType()) && visit == PreVisit)
676 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200677 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Jamie Madill03d863c2016-07-27 18:15:53 -0400678 queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200679 }
680 break;
681 }
682
683 return true;
684}
685
Olli Etuaho61b81ac2016-06-28 14:15:20 +0300686void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase &sink,
687 const int shaderVersion,
688 const ShShaderOutput outputLanguage)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200689{
Olli Etuahob741c762016-06-29 15:49:22 +0300690 std::unique_ptr<RoundingHelperWriter> roundingHelperWriter(
691 RoundingHelperWriter::createHelperWriter(outputLanguage));
692
693 roundingHelperWriter->writeCommonRoundingHelpers(sink, shaderVersion);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200694
695 EmulationSet::const_iterator it;
696 for (it = mEmulateCompoundAdd.begin(); it != mEmulateCompoundAdd.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300697 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "+", "add");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200698 for (it = mEmulateCompoundSub.begin(); it != mEmulateCompoundSub.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300699 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "-", "sub");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200700 for (it = mEmulateCompoundDiv.begin(); it != mEmulateCompoundDiv.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300701 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "/", "div");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200702 for (it = mEmulateCompoundMul.begin(); it != mEmulateCompoundMul.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300703 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "*", "mul");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200704}
705
Jamie Madilld5696192016-10-06 11:09:24 -0400706// static
707bool EmulatePrecision::SupportedInLanguage(const ShShaderOutput outputLanguage)
708{
709 switch (outputLanguage)
710 {
711 case SH_HLSL_4_1_OUTPUT:
712 case SH_ESSL_OUTPUT:
713 return true;
714 default:
715 // Other languages not yet supported
716 return (outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT ||
717 IsGLSL130OrNewer(outputLanguage));
718 }
719}