blob: 0219d7a0a0e99fd6fcdf7277caf318e3480d1e9d [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
Nico Weber41b072b2018-02-09 10:01:32 -05009#include "common/angle_fallthrough.h"
Olli Etuaho95ed1942018-02-01 14:01:19 +020010#include "compiler/translator/FunctionLookup.h"
11
Olli Etuahob741c762016-06-29 15:49:22 +030012#include <memory>
13
Jamie Madill45bcc782016-11-07 13:58:48 -050014namespace sh
15{
16
Olli Etuaho853dc1a2014-11-06 17:25:48 +020017namespace
18{
19
Olli Etuahofbb1c792018-01-19 16:26:59 +020020constexpr const ImmutableString kParamXName("x");
21constexpr const ImmutableString kParamYName("y");
22constexpr const ImmutableString kAngleFrmString("angle_frm");
23constexpr const ImmutableString kAngleFrlString("angle_frl");
24
Olli Etuahob741c762016-06-29 15:49:22 +030025class RoundingHelperWriter : angle::NonCopyable
Olli Etuaho853dc1a2014-11-06 17:25:48 +020026{
Olli Etuahob741c762016-06-29 15:49:22 +030027 public:
28 static RoundingHelperWriter *createHelperWriter(const ShShaderOutput outputLanguage);
Olli Etuaho853dc1a2014-11-06 17:25:48 +020029
Olli Etuahob741c762016-06-29 15:49:22 +030030 void writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion);
31 void writeCompoundAssignmentHelper(TInfoSinkBase &sink,
32 const char *lType,
33 const char *rType,
34 const char *opStr,
35 const char *opNameStr);
Olli Etuaho853dc1a2014-11-06 17:25:48 +020036
Olli Etuahob741c762016-06-29 15:49:22 +030037 virtual ~RoundingHelperWriter() {}
Olli Etuaho853dc1a2014-11-06 17:25:48 +020038
Olli Etuahob741c762016-06-29 15:49:22 +030039 protected:
40 RoundingHelperWriter(const ShShaderOutput outputLanguage) : mOutputLanguage(outputLanguage) {}
41 RoundingHelperWriter() = delete;
42
43 const ShShaderOutput mOutputLanguage;
44
45 private:
46 virtual std::string getTypeString(const char *glslType) = 0;
47 virtual void writeFloatRoundingHelpers(TInfoSinkBase &sink) = 0;
48 virtual void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) = 0;
49 virtual void writeMatrixRoundingHelper(TInfoSinkBase &sink,
50 const unsigned int columns,
51 const unsigned int rows,
52 const char *functionName) = 0;
53};
54
55class RoundingHelperWriterGLSL : public RoundingHelperWriter
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030056{
Olli Etuahob741c762016-06-29 15:49:22 +030057 public:
58 RoundingHelperWriterGLSL(const ShShaderOutput outputLanguage)
59 : RoundingHelperWriter(outputLanguage)
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030060 {
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030061 }
62
Olli Etuahob741c762016-06-29 15:49:22 +030063 private:
64 std::string getTypeString(const char *glslType) override;
65 void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
66 void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
67 void writeMatrixRoundingHelper(TInfoSinkBase &sink,
68 const unsigned int columns,
69 const unsigned int rows,
70 const char *functionName) override;
71};
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030072
Olli Etuahob741c762016-06-29 15:49:22 +030073class RoundingHelperWriterESSL : public RoundingHelperWriterGLSL
74{
75 public:
76 RoundingHelperWriterESSL(const ShShaderOutput outputLanguage)
77 : RoundingHelperWriterGLSL(outputLanguage)
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030078 {
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030079 }
80
Olli Etuahob741c762016-06-29 15:49:22 +030081 private:
82 std::string getTypeString(const char *glslType) override;
83};
84
85class RoundingHelperWriterHLSL : public RoundingHelperWriter
86{
87 public:
88 RoundingHelperWriterHLSL(const ShShaderOutput outputLanguage)
89 : RoundingHelperWriter(outputLanguage)
90 {
91 }
92
93 private:
94 std::string getTypeString(const char *glslType) override;
95 void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
96 void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
97 void writeMatrixRoundingHelper(TInfoSinkBase &sink,
98 const unsigned int columns,
99 const unsigned int rows,
100 const char *functionName) override;
101};
102
103RoundingHelperWriter *RoundingHelperWriter::createHelperWriter(const ShShaderOutput outputLanguage)
104{
Jamie Madilld5696192016-10-06 11:09:24 -0400105 ASSERT(EmulatePrecision::SupportedInLanguage(outputLanguage));
Olli Etuahob741c762016-06-29 15:49:22 +0300106 switch (outputLanguage)
107 {
108 case SH_HLSL_4_1_OUTPUT:
109 return new RoundingHelperWriterHLSL(outputLanguage);
110 case SH_ESSL_OUTPUT:
111 return new RoundingHelperWriterESSL(outputLanguage);
112 default:
Olli Etuahob741c762016-06-29 15:49:22 +0300113 return new RoundingHelperWriterGLSL(outputLanguage);
114 }
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +0300115}
116
Olli Etuahob741c762016-06-29 15:49:22 +0300117void RoundingHelperWriter::writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200118{
119 // Write the angle_frm functions that round floating point numbers to
120 // half precision, and angle_frl functions that round them to minimum lowp
121 // precision.
122
Olli Etuahob741c762016-06-29 15:49:22 +0300123 writeFloatRoundingHelpers(sink);
124 writeVectorRoundingHelpers(sink, 2);
125 writeVectorRoundingHelpers(sink, 3);
126 writeVectorRoundingHelpers(sink, 4);
127 if (shaderVersion > 100)
128 {
129 for (unsigned int columns = 2; columns <= 4; ++columns)
130 {
131 for (unsigned int rows = 2; rows <= 4; ++rows)
132 {
133 writeMatrixRoundingHelper(sink, columns, rows, "angle_frm");
134 writeMatrixRoundingHelper(sink, columns, rows, "angle_frl");
135 }
136 }
137 }
138 else
139 {
140 for (unsigned int size = 2; size <= 4; ++size)
141 {
142 writeMatrixRoundingHelper(sink, size, size, "angle_frm");
143 writeMatrixRoundingHelper(sink, size, size, "angle_frl");
144 }
145 }
146}
147
148void RoundingHelperWriter::writeCompoundAssignmentHelper(TInfoSinkBase &sink,
149 const char *lType,
150 const char *rType,
151 const char *opStr,
152 const char *opNameStr)
153{
154 std::string lTypeStr = getTypeString(lType);
155 std::string rTypeStr = getTypeString(rType);
156
157 // Note that y should be passed through angle_frm at the function call site,
158 // but x can't be passed through angle_frm there since it is an inout parameter.
159 // So only pass x and the result through angle_frm here.
160 // clang-format off
161 sink <<
162 lTypeStr << " angle_compound_" << opNameStr << "_frm(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
163 " x = angle_frm(angle_frm(x) " << opStr << " y);\n"
164 " return x;\n"
165 "}\n";
166 sink <<
167 lTypeStr << " angle_compound_" << opNameStr << "_frl(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
Olli Etuaho19ecebe2017-05-24 16:44:42 +0300168 " x = angle_frl(angle_frl(x) " << opStr << " y);\n"
Olli Etuahob741c762016-06-29 15:49:22 +0300169 " return x;\n"
170 "}\n";
171 // clang-format on
172}
173
174std::string RoundingHelperWriterGLSL::getTypeString(const char *glslType)
175{
176 return glslType;
177}
178
179std::string RoundingHelperWriterESSL::getTypeString(const char *glslType)
180{
181 std::stringstream typeStrStr;
182 typeStrStr << "highp " << glslType;
183 return typeStrStr.str();
184}
185
186void RoundingHelperWriterGLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
187{
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200188 // Unoptimized version of angle_frm for single floats:
189 //
Olli Etuahob741c762016-06-29 15:49:22 +0300190 // int webgl_maxNormalExponent(in int exponentBits)
191 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200192 // int possibleExponents = int(exp2(float(exponentBits)));
193 // int exponentBias = possibleExponents / 2 - 1;
194 // int allExponentBitsOne = possibleExponents - 1;
195 // return (allExponentBitsOne - 1) - exponentBias;
196 // }
197 //
Olli Etuahob741c762016-06-29 15:49:22 +0300198 // float angle_frm(in float x)
199 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200200 // int mantissaBits = 10;
201 // int exponentBits = 5;
202 // float possibleMantissas = exp2(float(mantissaBits));
203 // float mantissaMax = 2.0 - 1.0 / possibleMantissas;
204 // int maxNE = webgl_maxNormalExponent(exponentBits);
205 // float max = exp2(float(maxNE)) * mantissaMax;
Olli Etuahob741c762016-06-29 15:49:22 +0300206 // if (x > max)
207 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200208 // return max;
209 // }
Olli Etuahob741c762016-06-29 15:49:22 +0300210 // if (x < -max)
211 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200212 // return -max;
213 // }
214 // float exponent = floor(log2(abs(x)));
Olli Etuahob741c762016-06-29 15:49:22 +0300215 // if (abs(x) == 0.0 || exponent < -float(maxNE))
216 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200217 // return 0.0 * sign(x)
218 // }
219 // x = x * exp2(-(exponent - float(mantissaBits)));
220 // x = sign(x) * floor(abs(x));
221 // return x * exp2(exponent - float(mantissaBits));
222 // }
223
224 // All numbers with a magnitude less than 2^-15 are subnormal, and are
225 // flushed to zero.
226
227 // Note the constant numbers below:
228 // a) 65504 is the maximum possible mantissa (1.1111111111 in binary) times
229 // 2^15, the maximum normal exponent.
230 // b) 10.0 is the number of mantissa bits.
231 // c) -25.0 is the minimum normal half-float exponent -15.0 minus the number
232 // of mantissa bits.
233 // d) + 1e-30 is to make sure the argument of log2() won't be zero. It can
234 // only affect the result of log2 on x where abs(x) < 1e-22. Since these
235 // numbers will be flushed to zero either way (2^-15 is the smallest
236 // normal positive number), this does not introduce any error.
237
Olli Etuahob741c762016-06-29 15:49:22 +0300238 std::string floatType = getTypeString("float");
239
240 // clang-format off
241 sink <<
242 floatType << " angle_frm(in " << floatType << " x) {\n"
243 " x = clamp(x, -65504.0, 65504.0);\n"
244 " " << floatType << " exponent = floor(log2(abs(x) + 1e-30)) - 10.0;\n"
245 " bool isNonZero = (exponent >= -25.0);\n"
246 " x = x * exp2(-exponent);\n"
247 " x = sign(x) * floor(abs(x));\n"
248 " return x * exp2(exponent) * float(isNonZero);\n"
249 "}\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200250
251 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300252 floatType << " angle_frl(in " << floatType << " x) {\n"
253 " x = clamp(x, -2.0, 2.0);\n"
254 " x = x * 256.0;\n"
255 " x = sign(x) * floor(abs(x));\n"
256 " return x * 0.00390625;\n"
257 "}\n";
258 // clang-format on
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300259}
260
Olli Etuahob741c762016-06-29 15:49:22 +0300261void RoundingHelperWriterGLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
262 const unsigned int size)
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300263{
Olli Etuahob741c762016-06-29 15:49:22 +0300264 std::stringstream vecTypeStrStr;
265 vecTypeStrStr << "vec" << size;
266 std::string vecType = getTypeString(vecTypeStrStr.str().c_str());
267
268 // clang-format off
269 sink <<
270 vecType << " angle_frm(in " << vecType << " v) {\n"
271 " v = clamp(v, -65504.0, 65504.0);\n"
272 " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
273 " bvec" << size << " isNonZero = greaterThanEqual(exponent, vec" << size << "(-25.0));\n"
274 " v = v * exp2(-exponent);\n"
275 " v = sign(v) * floor(abs(v));\n"
276 " return v * exp2(exponent) * vec" << size << "(isNonZero);\n"
277 "}\n";
278
279 sink <<
280 vecType << " angle_frl(in " << vecType << " v) {\n"
281 " v = clamp(v, -2.0, 2.0);\n"
282 " v = v * 256.0;\n"
283 " v = sign(v) * floor(abs(v));\n"
284 " return v * 0.00390625;\n"
285 "}\n";
286 // clang-format on
287}
288
289void RoundingHelperWriterGLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
290 const unsigned int columns,
291 const unsigned int rows,
292 const char *functionName)
293{
294 std::stringstream matTypeStrStr;
295 matTypeStrStr << "mat" << columns;
296 if (rows != columns)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200297 {
Olli Etuahob741c762016-06-29 15:49:22 +0300298 matTypeStrStr << "x" << rows;
299 }
300 std::string matType = getTypeString(matTypeStrStr.str().c_str());
301
302 sink << matType << " " << functionName << "(in " << matType << " m) {\n"
303 << " " << matType << " rounded;\n";
304
305 for (unsigned int i = 0; i < columns; ++i)
306 {
307 sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200308 }
309
Olli Etuahob741c762016-06-29 15:49:22 +0300310 sink << " return rounded;\n"
311 "}\n";
312}
313
314static const char *GetHLSLTypeStr(const char *floatTypeStr)
315{
316 if (strcmp(floatTypeStr, "float") == 0)
317 {
318 return "float";
319 }
320 if (strcmp(floatTypeStr, "vec2") == 0)
321 {
322 return "float2";
323 }
324 if (strcmp(floatTypeStr, "vec3") == 0)
325 {
326 return "float3";
327 }
328 if (strcmp(floatTypeStr, "vec4") == 0)
329 {
330 return "float4";
331 }
332 if (strcmp(floatTypeStr, "mat2") == 0)
333 {
334 return "float2x2";
335 }
336 if (strcmp(floatTypeStr, "mat3") == 0)
337 {
338 return "float3x3";
339 }
340 if (strcmp(floatTypeStr, "mat4") == 0)
341 {
342 return "float4x4";
343 }
344 if (strcmp(floatTypeStr, "mat2x3") == 0)
345 {
346 return "float2x3";
347 }
348 if (strcmp(floatTypeStr, "mat2x4") == 0)
349 {
350 return "float2x4";
351 }
352 if (strcmp(floatTypeStr, "mat3x2") == 0)
353 {
354 return "float3x2";
355 }
356 if (strcmp(floatTypeStr, "mat3x4") == 0)
357 {
358 return "float3x4";
359 }
360 if (strcmp(floatTypeStr, "mat4x2") == 0)
361 {
362 return "float4x2";
363 }
364 if (strcmp(floatTypeStr, "mat4x3") == 0)
365 {
366 return "float4x3";
367 }
368 UNREACHABLE();
369 return nullptr;
370}
371
372std::string RoundingHelperWriterHLSL::getTypeString(const char *glslType)
373{
374 return GetHLSLTypeStr(glslType);
375}
376
377void RoundingHelperWriterHLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
378{
379 // In HLSL scalars are the same as 1-vectors.
380 writeVectorRoundingHelpers(sink, 1);
381}
382
383void RoundingHelperWriterHLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
384 const unsigned int size)
385{
386 std::stringstream vecTypeStrStr;
387 vecTypeStrStr << "float" << size;
388 std::string vecType = vecTypeStrStr.str();
389
390 // clang-format off
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200391 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300392 vecType << " angle_frm(" << vecType << " v) {\n"
393 " v = clamp(v, -65504.0, 65504.0);\n"
394 " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
395 " bool" << size << " isNonZero = exponent < -25.0;\n"
396 " v = v * exp2(-exponent);\n"
397 " v = sign(v) * floor(abs(v));\n"
398 " return v * exp2(exponent) * (float" << size << ")(isNonZero);\n"
399 "}\n";
400
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200401 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300402 vecType << " angle_frl(" << vecType << " v) {\n"
403 " v = clamp(v, -2.0, 2.0);\n"
404 " v = v * 256.0;\n"
405 " v = sign(v) * floor(abs(v));\n"
406 " return v * 0.00390625;\n"
407 "}\n";
408 // clang-format on
409}
410
411void RoundingHelperWriterHLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
412 const unsigned int columns,
413 const unsigned int rows,
414 const char *functionName)
415{
416 std::stringstream matTypeStrStr;
417 matTypeStrStr << "float" << columns << "x" << rows;
418 std::string matType = matTypeStrStr.str();
419
420 sink << matType << " " << functionName << "(" << matType << " m) {\n"
421 << " " << matType << " rounded;\n";
422
423 for (unsigned int i = 0; i < columns; ++i)
424 {
425 sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
426 }
427
428 sink << " return rounded;\n"
429 "}\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200430}
431
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200432bool canRoundFloat(const TType &type)
433{
Olli Etuaho61b81ac2016-06-28 14:15:20 +0300434 return type.getBasicType() == EbtFloat && !type.isArray() &&
435 (type.getPrecision() == EbpLow || type.getPrecision() == EbpMedium);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200436}
437
Olli Etuaho7854d862017-05-09 14:29:15 +0300438bool ParentUsesResult(TIntermNode *parent, TIntermTyped *node)
Olli Etuaho1be88702015-01-19 16:56:44 +0200439{
440 if (!parent)
441 {
442 return false;
443 }
444
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100445 TIntermBlock *blockParent = parent->getAsBlock();
446 // If the parent is a block, the result is not assigned anywhere,
Olli Etuaho1be88702015-01-19 16:56:44 +0200447 // so rounding it is not needed. In particular, this can avoid a lot of
448 // unnecessary rounding of unused return values of assignment.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100449 if (blockParent)
Olli Etuaho1be88702015-01-19 16:56:44 +0200450 {
451 return false;
452 }
Olli Etuaho4db7ded2016-10-13 12:23:11 +0100453 TIntermBinary *binaryParent = parent->getAsBinaryNode();
454 if (binaryParent && binaryParent->getOp() == EOpComma && (binaryParent->getRight() != node))
Olli Etuaho1be88702015-01-19 16:56:44 +0200455 {
456 return false;
457 }
458 return true;
459}
460
Olli Etuaho7854d862017-05-09 14:29:15 +0300461bool ParentConstructorTakesCareOfRounding(TIntermNode *parent, TIntermTyped *node)
462{
463 if (!parent)
464 {
465 return false;
466 }
467 TIntermAggregate *parentConstructor = parent->getAsAggregate();
468 if (!parentConstructor || parentConstructor->getOp() != EOpConstruct)
469 {
470 return false;
471 }
472 if (parentConstructor->getPrecision() != node->getPrecision())
473 {
474 return false;
475 }
476 return canRoundFloat(parentConstructor->getType());
477}
478
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200479} // namespace anonymous
480
Olli Etuaho68981eb2018-01-23 17:46:12 +0200481EmulatePrecision::EmulatePrecision(TSymbolTable *symbolTable)
Olli Etuahofbb1c792018-01-19 16:26:59 +0200482 : TLValueTrackingTraverser(true, true, true, symbolTable), mDeclaringVariables(false)
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500483{
484}
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200485
486void EmulatePrecision::visitSymbol(TIntermSymbol *node)
487{
Olli Etuaho7854d862017-05-09 14:29:15 +0300488 TIntermNode *parent = getParentNode();
489 if (canRoundFloat(node->getType()) && ParentUsesResult(parent, node) &&
490 !ParentConstructorTakesCareOfRounding(parent, node) && !mDeclaringVariables &&
491 !isLValueRequiredHere())
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200492 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200493 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300494 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200495 }
496}
497
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200498bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node)
499{
500 bool visitChildren = true;
501
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200502 TOperator op = node->getOp();
503
504 // RHS of initialize is not being declared.
505 if (op == EOpInitialize && visit == InVisit)
506 mDeclaringVariables = false;
507
Olli Etuahob6fa0432016-09-28 16:28:05 +0100508 if ((op == EOpIndexDirectStruct) && visit == InVisit)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200509 visitChildren = false;
510
511 if (visit != PreVisit)
512 return visitChildren;
513
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500514 const TType &type = node->getType();
515 bool roundFloat = canRoundFloat(type);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200516
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500517 if (roundFloat)
518 {
519 switch (op)
520 {
521 // Math operators that can result in a float may need to apply rounding to the return
522 // value. Note that in the case of assignment, the rounding is applied to its return
523 // value here, not the value being assigned.
524 case EOpAssign:
525 case EOpAdd:
526 case EOpSub:
527 case EOpMul:
528 case EOpDiv:
529 case EOpVectorTimesScalar:
530 case EOpVectorTimesMatrix:
531 case EOpMatrixTimesVector:
532 case EOpMatrixTimesScalar:
533 case EOpMatrixTimesMatrix:
Olli Etuaho1be88702015-01-19 16:56:44 +0200534 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500535 TIntermNode *parent = getParentNode();
Olli Etuaho7854d862017-05-09 14:29:15 +0300536 if (!ParentUsesResult(parent, node) ||
537 ParentConstructorTakesCareOfRounding(parent, node))
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500538 {
539 break;
540 }
541 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300542 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho1be88702015-01-19 16:56:44 +0200543 break;
544 }
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200545
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500546 // Compound assignment cases need to replace the operator with a function call.
547 case EOpAddAssign:
548 {
549 mEmulateCompoundAdd.insert(
550 TypePair(type.getBuiltInTypeNameString(),
551 node->getRight()->getType().getBuiltInTypeNameString()));
552 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
553 node->getLeft(), node->getRight(), "add");
Olli Etuahoea39a222017-07-06 12:47:59 +0300554 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500555 break;
556 }
557 case EOpSubAssign:
558 {
559 mEmulateCompoundSub.insert(
560 TypePair(type.getBuiltInTypeNameString(),
561 node->getRight()->getType().getBuiltInTypeNameString()));
562 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
563 node->getLeft(), node->getRight(), "sub");
Olli Etuahoea39a222017-07-06 12:47:59 +0300564 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500565 break;
566 }
567 case EOpMulAssign:
568 case EOpVectorTimesMatrixAssign:
569 case EOpVectorTimesScalarAssign:
570 case EOpMatrixTimesScalarAssign:
571 case EOpMatrixTimesMatrixAssign:
572 {
573 mEmulateCompoundMul.insert(
574 TypePair(type.getBuiltInTypeNameString(),
575 node->getRight()->getType().getBuiltInTypeNameString()));
576 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
577 node->getLeft(), node->getRight(), "mul");
Olli Etuahoea39a222017-07-06 12:47:59 +0300578 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500579 break;
580 }
581 case EOpDivAssign:
582 {
583 mEmulateCompoundDiv.insert(
584 TypePair(type.getBuiltInTypeNameString(),
585 node->getRight()->getType().getBuiltInTypeNameString()));
586 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
587 node->getLeft(), node->getRight(), "div");
Olli Etuahoea39a222017-07-06 12:47:59 +0300588 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500589 break;
590 }
591 default:
592 // The rest of the binary operations should not need precision emulation.
593 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200594 }
595 }
596 return visitChildren;
597}
598
Olli Etuaho13389b62016-10-16 11:48:18 +0100599bool EmulatePrecision::visitDeclaration(Visit visit, TIntermDeclaration *node)
600{
601 // Variable or interface block declaration.
602 if (visit == PreVisit)
603 {
604 mDeclaringVariables = true;
605 }
606 else if (visit == InVisit)
607 {
608 mDeclaringVariables = true;
609 }
610 else
611 {
612 mDeclaringVariables = false;
613 }
614 return true;
615}
616
Olli Etuahobf4e1b72016-12-09 11:30:15 +0000617bool EmulatePrecision::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node)
618{
619 return false;
620}
621
Olli Etuaho16c745a2017-01-16 17:02:27 +0000622bool EmulatePrecision::visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node)
623{
624 return false;
625}
626
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200627bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
628{
Olli Etuaho7854d862017-05-09 14:29:15 +0300629 if (visit != PreVisit)
630 return true;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200631 switch (node->getOp())
632 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800633 case EOpCallInternalRawFunction:
634 case EOpCallFunctionInAST:
635 // User-defined function return values are not rounded. The calculations that produced
636 // the value inside the function definition should have been rounded.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500637 break;
Olli Etuaho8fab3202017-05-08 18:22:22 +0300638 case EOpConstruct:
639 if (node->getBasicType() == EbtStruct)
640 {
641 break;
642 }
Nico Weber41b072b2018-02-09 10:01:32 -0500643 ANGLE_FALLTHROUGH;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500644 default:
Olli Etuaho1be88702015-01-19 16:56:44 +0200645 TIntermNode *parent = getParentNode();
Olli Etuaho7854d862017-05-09 14:29:15 +0300646 if (canRoundFloat(node->getType()) && ParentUsesResult(parent, node) &&
647 !ParentConstructorTakesCareOfRounding(parent, node))
Olli Etuaho1be88702015-01-19 16:56:44 +0200648 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200649 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300650 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200651 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500652 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200653 }
Olli Etuaho7854d862017-05-09 14:29:15 +0300654 return true;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200655}
656
657bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node)
658{
659 switch (node->getOp())
660 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500661 case EOpNegative:
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500662 case EOpLogicalNot:
663 case EOpPostIncrement:
664 case EOpPostDecrement:
665 case EOpPreIncrement:
666 case EOpPreDecrement:
Olli Etuahod68924e2017-01-02 17:34:40 +0000667 case EOpLogicalNotComponentWise:
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500668 break;
669 default:
670 if (canRoundFloat(node->getType()) && visit == PreVisit)
671 {
672 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300673 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500674 }
675 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200676 }
677
678 return true;
679}
680
Olli Etuaho61b81ac2016-06-28 14:15:20 +0300681void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase &sink,
682 const int shaderVersion,
683 const ShShaderOutput outputLanguage)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200684{
Olli Etuahob741c762016-06-29 15:49:22 +0300685 std::unique_ptr<RoundingHelperWriter> roundingHelperWriter(
686 RoundingHelperWriter::createHelperWriter(outputLanguage));
687
688 roundingHelperWriter->writeCommonRoundingHelpers(sink, shaderVersion);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200689
690 EmulationSet::const_iterator it;
691 for (it = mEmulateCompoundAdd.begin(); it != mEmulateCompoundAdd.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300692 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "+", "add");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200693 for (it = mEmulateCompoundSub.begin(); it != mEmulateCompoundSub.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300694 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "-", "sub");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200695 for (it = mEmulateCompoundDiv.begin(); it != mEmulateCompoundDiv.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300696 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "/", "div");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200697 for (it = mEmulateCompoundMul.begin(); it != mEmulateCompoundMul.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300698 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "*", "mul");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200699}
700
Jamie Madilld5696192016-10-06 11:09:24 -0400701// static
702bool EmulatePrecision::SupportedInLanguage(const ShShaderOutput outputLanguage)
703{
704 switch (outputLanguage)
705 {
706 case SH_HLSL_4_1_OUTPUT:
707 case SH_ESSL_OUTPUT:
708 return true;
709 default:
710 // Other languages not yet supported
711 return (outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT ||
Jamie Madillacb4b812016-11-07 13:50:29 -0500712 sh::IsGLSL130OrNewer(outputLanguage));
Jamie Madilld5696192016-10-06 11:09:24 -0400713 }
714}
Jamie Madill45bcc782016-11-07 13:58:48 -0500715
Olli Etuahofbb1c792018-01-19 16:26:59 +0200716const TFunction *EmulatePrecision::getInternalFunction(const ImmutableString &functionName,
Olli Etuaho68981eb2018-01-23 17:46:12 +0200717 const TType &returnType,
718 TIntermSequence *arguments,
719 const TVector<TConstParameter> &parameters,
720 bool knownToNotHaveSideEffects)
Olli Etuaho0c371002017-12-13 17:00:25 +0400721{
Olli Etuahofbb1c792018-01-19 16:26:59 +0200722 ImmutableString mangledName = TFunctionLookup::GetMangledName(functionName.data(), *arguments);
Olli Etuaho0c371002017-12-13 17:00:25 +0400723 if (mInternalFunctions.find(mangledName) == mInternalFunctions.end())
724 {
Olli Etuaho68981eb2018-01-23 17:46:12 +0200725 TFunction *func = new TFunction(mSymbolTable, functionName, new TType(returnType),
726 SymbolType::AngleInternal, knownToNotHaveSideEffects);
727 ASSERT(parameters.size() == arguments->size());
728 for (size_t i = 0; i < parameters.size(); ++i)
729 {
730 func->addParameter(parameters[i]);
731 }
732 mInternalFunctions[mangledName] = func;
Olli Etuaho0c371002017-12-13 17:00:25 +0400733 }
734 return mInternalFunctions[mangledName];
735}
736
737TIntermAggregate *EmulatePrecision::createRoundingFunctionCallNode(TIntermTyped *roundedChild)
738{
Olli Etuahofbb1c792018-01-19 16:26:59 +0200739 const ImmutableString *roundFunctionName = &kAngleFrmString;
740 if (roundedChild->getPrecision() == EbpLow)
741 roundFunctionName = &kAngleFrlString;
Olli Etuaho0c371002017-12-13 17:00:25 +0400742 TIntermSequence *arguments = new TIntermSequence();
743 arguments->push_back(roundedChild);
Olli Etuaho68981eb2018-01-23 17:46:12 +0200744
745 TVector<TConstParameter> parameters;
746 TType *paramType = new TType(roundedChild->getType());
747 paramType->setPrecision(EbpHigh);
748 paramType->setQualifier(EvqIn);
Olli Etuahofbb1c792018-01-19 16:26:59 +0200749 parameters.push_back(TConstParameter(kParamXName, static_cast<const TType *>(paramType)));
Olli Etuaho68981eb2018-01-23 17:46:12 +0200750
Olli Etuaho0c371002017-12-13 17:00:25 +0400751 return TIntermAggregate::CreateRawFunctionCall(
Olli Etuahofbb1c792018-01-19 16:26:59 +0200752 *getInternalFunction(*roundFunctionName, roundedChild->getType(), arguments, parameters,
753 true),
Olli Etuaho68981eb2018-01-23 17:46:12 +0200754 arguments);
Olli Etuaho0c371002017-12-13 17:00:25 +0400755}
756
757TIntermAggregate *EmulatePrecision::createCompoundAssignmentFunctionCallNode(TIntermTyped *left,
758 TIntermTyped *right,
759 const char *opNameStr)
760{
761 std::stringstream strstr;
762 if (left->getPrecision() == EbpMedium)
763 strstr << "angle_compound_" << opNameStr << "_frm";
764 else
765 strstr << "angle_compound_" << opNameStr << "_frl";
Olli Etuahofbb1c792018-01-19 16:26:59 +0200766 ImmutableString functionName = ImmutableString(strstr.str());
Olli Etuaho0c371002017-12-13 17:00:25 +0400767 TIntermSequence *arguments = new TIntermSequence();
768 arguments->push_back(left);
769 arguments->push_back(right);
Olli Etuaho68981eb2018-01-23 17:46:12 +0200770
771 TVector<TConstParameter> parameters;
772 TType *leftParamType = new TType(left->getType());
773 leftParamType->setPrecision(EbpHigh);
774 leftParamType->setQualifier(EvqOut);
Olli Etuahofbb1c792018-01-19 16:26:59 +0200775 parameters.push_back(TConstParameter(kParamXName, static_cast<const TType *>(leftParamType)));
Olli Etuaho68981eb2018-01-23 17:46:12 +0200776 TType *rightParamType = new TType(right->getType());
777 rightParamType->setPrecision(EbpHigh);
778 rightParamType->setQualifier(EvqIn);
Olli Etuahofbb1c792018-01-19 16:26:59 +0200779 parameters.push_back(TConstParameter(kParamYName, static_cast<const TType *>(rightParamType)));
Olli Etuaho68981eb2018-01-23 17:46:12 +0200780
Olli Etuaho0c371002017-12-13 17:00:25 +0400781 return TIntermAggregate::CreateRawFunctionCall(
Olli Etuaho68981eb2018-01-23 17:46:12 +0200782 *getInternalFunction(functionName, left->getType(), arguments, parameters, false),
783 arguments);
Olli Etuaho0c371002017-12-13 17:00:25 +0400784}
785
Jamie Madill45bcc782016-11-07 13:58:48 -0500786} // namespace sh