blob: d3acfbd073a982c7d6bd249ac017d0524b663dbb [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 Etuaho95ed1942018-02-01 14:01:19 +02009#include "compiler/translator/FunctionLookup.h"
10
Olli Etuahob741c762016-06-29 15:49:22 +030011#include <memory>
12
Jamie Madill45bcc782016-11-07 13:58:48 -050013namespace sh
14{
15
Olli Etuaho853dc1a2014-11-06 17:25:48 +020016namespace
17{
18
Olli Etuahofbb1c792018-01-19 16:26:59 +020019constexpr const ImmutableString kParamXName("x");
20constexpr const ImmutableString kParamYName("y");
21constexpr const ImmutableString kAngleFrmString("angle_frm");
22constexpr const ImmutableString kAngleFrlString("angle_frl");
23
Olli Etuahob741c762016-06-29 15:49:22 +030024class RoundingHelperWriter : angle::NonCopyable
Olli Etuaho853dc1a2014-11-06 17:25:48 +020025{
Olli Etuahob741c762016-06-29 15:49:22 +030026 public:
27 static RoundingHelperWriter *createHelperWriter(const ShShaderOutput outputLanguage);
Olli Etuaho853dc1a2014-11-06 17:25:48 +020028
Olli Etuahob741c762016-06-29 15:49:22 +030029 void writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion);
30 void writeCompoundAssignmentHelper(TInfoSinkBase &sink,
31 const char *lType,
32 const char *rType,
33 const char *opStr,
34 const char *opNameStr);
Olli Etuaho853dc1a2014-11-06 17:25:48 +020035
Olli Etuahob741c762016-06-29 15:49:22 +030036 virtual ~RoundingHelperWriter() {}
Olli Etuaho853dc1a2014-11-06 17:25:48 +020037
Olli Etuahob741c762016-06-29 15:49:22 +030038 protected:
39 RoundingHelperWriter(const ShShaderOutput outputLanguage) : mOutputLanguage(outputLanguage) {}
40 RoundingHelperWriter() = delete;
41
42 const ShShaderOutput mOutputLanguage;
43
44 private:
45 virtual std::string getTypeString(const char *glslType) = 0;
46 virtual void writeFloatRoundingHelpers(TInfoSinkBase &sink) = 0;
47 virtual void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) = 0;
48 virtual void writeMatrixRoundingHelper(TInfoSinkBase &sink,
49 const unsigned int columns,
50 const unsigned int rows,
51 const char *functionName) = 0;
52};
53
54class RoundingHelperWriterGLSL : public RoundingHelperWriter
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030055{
Olli Etuahob741c762016-06-29 15:49:22 +030056 public:
57 RoundingHelperWriterGLSL(const ShShaderOutput outputLanguage)
58 : RoundingHelperWriter(outputLanguage)
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030059 {
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030060 }
61
Olli Etuahob741c762016-06-29 15:49:22 +030062 private:
63 std::string getTypeString(const char *glslType) override;
64 void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
65 void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
66 void writeMatrixRoundingHelper(TInfoSinkBase &sink,
67 const unsigned int columns,
68 const unsigned int rows,
69 const char *functionName) override;
70};
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030071
Olli Etuahob741c762016-06-29 15:49:22 +030072class RoundingHelperWriterESSL : public RoundingHelperWriterGLSL
73{
74 public:
75 RoundingHelperWriterESSL(const ShShaderOutput outputLanguage)
76 : RoundingHelperWriterGLSL(outputLanguage)
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030077 {
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030078 }
79
Olli Etuahob741c762016-06-29 15:49:22 +030080 private:
81 std::string getTypeString(const char *glslType) override;
82};
83
84class RoundingHelperWriterHLSL : public RoundingHelperWriter
85{
86 public:
87 RoundingHelperWriterHLSL(const ShShaderOutput outputLanguage)
88 : RoundingHelperWriter(outputLanguage)
89 {
90 }
91
92 private:
93 std::string getTypeString(const char *glslType) override;
94 void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
95 void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
96 void writeMatrixRoundingHelper(TInfoSinkBase &sink,
97 const unsigned int columns,
98 const unsigned int rows,
99 const char *functionName) override;
100};
101
102RoundingHelperWriter *RoundingHelperWriter::createHelperWriter(const ShShaderOutput outputLanguage)
103{
Jamie Madilld5696192016-10-06 11:09:24 -0400104 ASSERT(EmulatePrecision::SupportedInLanguage(outputLanguage));
Olli Etuahob741c762016-06-29 15:49:22 +0300105 switch (outputLanguage)
106 {
107 case SH_HLSL_4_1_OUTPUT:
108 return new RoundingHelperWriterHLSL(outputLanguage);
109 case SH_ESSL_OUTPUT:
110 return new RoundingHelperWriterESSL(outputLanguage);
111 default:
Olli Etuahob741c762016-06-29 15:49:22 +0300112 return new RoundingHelperWriterGLSL(outputLanguage);
113 }
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +0300114}
115
Olli Etuahob741c762016-06-29 15:49:22 +0300116void RoundingHelperWriter::writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200117{
118 // Write the angle_frm functions that round floating point numbers to
119 // half precision, and angle_frl functions that round them to minimum lowp
120 // precision.
121
Olli Etuahob741c762016-06-29 15:49:22 +0300122 writeFloatRoundingHelpers(sink);
123 writeVectorRoundingHelpers(sink, 2);
124 writeVectorRoundingHelpers(sink, 3);
125 writeVectorRoundingHelpers(sink, 4);
126 if (shaderVersion > 100)
127 {
128 for (unsigned int columns = 2; columns <= 4; ++columns)
129 {
130 for (unsigned int rows = 2; rows <= 4; ++rows)
131 {
132 writeMatrixRoundingHelper(sink, columns, rows, "angle_frm");
133 writeMatrixRoundingHelper(sink, columns, rows, "angle_frl");
134 }
135 }
136 }
137 else
138 {
139 for (unsigned int size = 2; size <= 4; ++size)
140 {
141 writeMatrixRoundingHelper(sink, size, size, "angle_frm");
142 writeMatrixRoundingHelper(sink, size, size, "angle_frl");
143 }
144 }
145}
146
147void RoundingHelperWriter::writeCompoundAssignmentHelper(TInfoSinkBase &sink,
148 const char *lType,
149 const char *rType,
150 const char *opStr,
151 const char *opNameStr)
152{
153 std::string lTypeStr = getTypeString(lType);
154 std::string rTypeStr = getTypeString(rType);
155
156 // Note that y should be passed through angle_frm at the function call site,
157 // but x can't be passed through angle_frm there since it is an inout parameter.
158 // So only pass x and the result through angle_frm here.
159 // clang-format off
160 sink <<
161 lTypeStr << " angle_compound_" << opNameStr << "_frm(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
162 " x = angle_frm(angle_frm(x) " << opStr << " y);\n"
163 " return x;\n"
164 "}\n";
165 sink <<
166 lTypeStr << " angle_compound_" << opNameStr << "_frl(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
Olli Etuaho19ecebe2017-05-24 16:44:42 +0300167 " x = angle_frl(angle_frl(x) " << opStr << " y);\n"
Olli Etuahob741c762016-06-29 15:49:22 +0300168 " return x;\n"
169 "}\n";
170 // clang-format on
171}
172
173std::string RoundingHelperWriterGLSL::getTypeString(const char *glslType)
174{
175 return glslType;
176}
177
178std::string RoundingHelperWriterESSL::getTypeString(const char *glslType)
179{
180 std::stringstream typeStrStr;
181 typeStrStr << "highp " << glslType;
182 return typeStrStr.str();
183}
184
185void RoundingHelperWriterGLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
186{
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200187 // Unoptimized version of angle_frm for single floats:
188 //
Olli Etuahob741c762016-06-29 15:49:22 +0300189 // int webgl_maxNormalExponent(in int exponentBits)
190 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200191 // int possibleExponents = int(exp2(float(exponentBits)));
192 // int exponentBias = possibleExponents / 2 - 1;
193 // int allExponentBitsOne = possibleExponents - 1;
194 // return (allExponentBitsOne - 1) - exponentBias;
195 // }
196 //
Olli Etuahob741c762016-06-29 15:49:22 +0300197 // float angle_frm(in float x)
198 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200199 // int mantissaBits = 10;
200 // int exponentBits = 5;
201 // float possibleMantissas = exp2(float(mantissaBits));
202 // float mantissaMax = 2.0 - 1.0 / possibleMantissas;
203 // int maxNE = webgl_maxNormalExponent(exponentBits);
204 // float max = exp2(float(maxNE)) * mantissaMax;
Olli Etuahob741c762016-06-29 15:49:22 +0300205 // if (x > max)
206 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200207 // return max;
208 // }
Olli Etuahob741c762016-06-29 15:49:22 +0300209 // if (x < -max)
210 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200211 // return -max;
212 // }
213 // float exponent = floor(log2(abs(x)));
Olli Etuahob741c762016-06-29 15:49:22 +0300214 // if (abs(x) == 0.0 || exponent < -float(maxNE))
215 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200216 // return 0.0 * sign(x)
217 // }
218 // x = x * exp2(-(exponent - float(mantissaBits)));
219 // x = sign(x) * floor(abs(x));
220 // return x * exp2(exponent - float(mantissaBits));
221 // }
222
223 // All numbers with a magnitude less than 2^-15 are subnormal, and are
224 // flushed to zero.
225
226 // Note the constant numbers below:
227 // a) 65504 is the maximum possible mantissa (1.1111111111 in binary) times
228 // 2^15, the maximum normal exponent.
229 // b) 10.0 is the number of mantissa bits.
230 // c) -25.0 is the minimum normal half-float exponent -15.0 minus the number
231 // of mantissa bits.
232 // d) + 1e-30 is to make sure the argument of log2() won't be zero. It can
233 // only affect the result of log2 on x where abs(x) < 1e-22. Since these
234 // numbers will be flushed to zero either way (2^-15 is the smallest
235 // normal positive number), this does not introduce any error.
236
Olli Etuahob741c762016-06-29 15:49:22 +0300237 std::string floatType = getTypeString("float");
238
239 // clang-format off
240 sink <<
241 floatType << " angle_frm(in " << floatType << " x) {\n"
242 " x = clamp(x, -65504.0, 65504.0);\n"
243 " " << floatType << " exponent = floor(log2(abs(x) + 1e-30)) - 10.0;\n"
244 " bool isNonZero = (exponent >= -25.0);\n"
245 " x = x * exp2(-exponent);\n"
246 " x = sign(x) * floor(abs(x));\n"
247 " return x * exp2(exponent) * float(isNonZero);\n"
248 "}\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200249
250 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300251 floatType << " angle_frl(in " << floatType << " x) {\n"
252 " x = clamp(x, -2.0, 2.0);\n"
253 " x = x * 256.0;\n"
254 " x = sign(x) * floor(abs(x));\n"
255 " return x * 0.00390625;\n"
256 "}\n";
257 // clang-format on
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300258}
259
Olli Etuahob741c762016-06-29 15:49:22 +0300260void RoundingHelperWriterGLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
261 const unsigned int size)
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300262{
Olli Etuahob741c762016-06-29 15:49:22 +0300263 std::stringstream vecTypeStrStr;
264 vecTypeStrStr << "vec" << size;
265 std::string vecType = getTypeString(vecTypeStrStr.str().c_str());
266
267 // clang-format off
268 sink <<
269 vecType << " angle_frm(in " << vecType << " v) {\n"
270 " v = clamp(v, -65504.0, 65504.0);\n"
271 " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
272 " bvec" << size << " isNonZero = greaterThanEqual(exponent, vec" << size << "(-25.0));\n"
273 " v = v * exp2(-exponent);\n"
274 " v = sign(v) * floor(abs(v));\n"
275 " return v * exp2(exponent) * vec" << size << "(isNonZero);\n"
276 "}\n";
277
278 sink <<
279 vecType << " angle_frl(in " << vecType << " v) {\n"
280 " v = clamp(v, -2.0, 2.0);\n"
281 " v = v * 256.0;\n"
282 " v = sign(v) * floor(abs(v));\n"
283 " return v * 0.00390625;\n"
284 "}\n";
285 // clang-format on
286}
287
288void RoundingHelperWriterGLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
289 const unsigned int columns,
290 const unsigned int rows,
291 const char *functionName)
292{
293 std::stringstream matTypeStrStr;
294 matTypeStrStr << "mat" << columns;
295 if (rows != columns)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200296 {
Olli Etuahob741c762016-06-29 15:49:22 +0300297 matTypeStrStr << "x" << rows;
298 }
299 std::string matType = getTypeString(matTypeStrStr.str().c_str());
300
301 sink << matType << " " << functionName << "(in " << matType << " m) {\n"
302 << " " << matType << " rounded;\n";
303
304 for (unsigned int i = 0; i < columns; ++i)
305 {
306 sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200307 }
308
Olli Etuahob741c762016-06-29 15:49:22 +0300309 sink << " return rounded;\n"
310 "}\n";
311}
312
313static const char *GetHLSLTypeStr(const char *floatTypeStr)
314{
315 if (strcmp(floatTypeStr, "float") == 0)
316 {
317 return "float";
318 }
319 if (strcmp(floatTypeStr, "vec2") == 0)
320 {
321 return "float2";
322 }
323 if (strcmp(floatTypeStr, "vec3") == 0)
324 {
325 return "float3";
326 }
327 if (strcmp(floatTypeStr, "vec4") == 0)
328 {
329 return "float4";
330 }
331 if (strcmp(floatTypeStr, "mat2") == 0)
332 {
333 return "float2x2";
334 }
335 if (strcmp(floatTypeStr, "mat3") == 0)
336 {
337 return "float3x3";
338 }
339 if (strcmp(floatTypeStr, "mat4") == 0)
340 {
341 return "float4x4";
342 }
343 if (strcmp(floatTypeStr, "mat2x3") == 0)
344 {
345 return "float2x3";
346 }
347 if (strcmp(floatTypeStr, "mat2x4") == 0)
348 {
349 return "float2x4";
350 }
351 if (strcmp(floatTypeStr, "mat3x2") == 0)
352 {
353 return "float3x2";
354 }
355 if (strcmp(floatTypeStr, "mat3x4") == 0)
356 {
357 return "float3x4";
358 }
359 if (strcmp(floatTypeStr, "mat4x2") == 0)
360 {
361 return "float4x2";
362 }
363 if (strcmp(floatTypeStr, "mat4x3") == 0)
364 {
365 return "float4x3";
366 }
367 UNREACHABLE();
368 return nullptr;
369}
370
371std::string RoundingHelperWriterHLSL::getTypeString(const char *glslType)
372{
373 return GetHLSLTypeStr(glslType);
374}
375
376void RoundingHelperWriterHLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
377{
378 // In HLSL scalars are the same as 1-vectors.
379 writeVectorRoundingHelpers(sink, 1);
380}
381
382void RoundingHelperWriterHLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
383 const unsigned int size)
384{
385 std::stringstream vecTypeStrStr;
386 vecTypeStrStr << "float" << size;
387 std::string vecType = vecTypeStrStr.str();
388
389 // clang-format off
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200390 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300391 vecType << " angle_frm(" << vecType << " v) {\n"
392 " v = clamp(v, -65504.0, 65504.0);\n"
393 " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
394 " bool" << size << " isNonZero = exponent < -25.0;\n"
395 " v = v * exp2(-exponent);\n"
396 " v = sign(v) * floor(abs(v));\n"
397 " return v * exp2(exponent) * (float" << size << ")(isNonZero);\n"
398 "}\n";
399
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200400 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300401 vecType << " angle_frl(" << vecType << " v) {\n"
402 " v = clamp(v, -2.0, 2.0);\n"
403 " v = v * 256.0;\n"
404 " v = sign(v) * floor(abs(v));\n"
405 " return v * 0.00390625;\n"
406 "}\n";
407 // clang-format on
408}
409
410void RoundingHelperWriterHLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
411 const unsigned int columns,
412 const unsigned int rows,
413 const char *functionName)
414{
415 std::stringstream matTypeStrStr;
416 matTypeStrStr << "float" << columns << "x" << rows;
417 std::string matType = matTypeStrStr.str();
418
419 sink << matType << " " << functionName << "(" << matType << " m) {\n"
420 << " " << matType << " rounded;\n";
421
422 for (unsigned int i = 0; i < columns; ++i)
423 {
424 sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
425 }
426
427 sink << " return rounded;\n"
428 "}\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200429}
430
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200431bool canRoundFloat(const TType &type)
432{
Olli Etuaho61b81ac2016-06-28 14:15:20 +0300433 return type.getBasicType() == EbtFloat && !type.isArray() &&
434 (type.getPrecision() == EbpLow || type.getPrecision() == EbpMedium);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200435}
436
Olli Etuaho7854d862017-05-09 14:29:15 +0300437bool ParentUsesResult(TIntermNode *parent, TIntermTyped *node)
Olli Etuaho1be88702015-01-19 16:56:44 +0200438{
439 if (!parent)
440 {
441 return false;
442 }
443
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100444 TIntermBlock *blockParent = parent->getAsBlock();
445 // If the parent is a block, the result is not assigned anywhere,
Olli Etuaho1be88702015-01-19 16:56:44 +0200446 // so rounding it is not needed. In particular, this can avoid a lot of
447 // unnecessary rounding of unused return values of assignment.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100448 if (blockParent)
Olli Etuaho1be88702015-01-19 16:56:44 +0200449 {
450 return false;
451 }
Olli Etuaho4db7ded2016-10-13 12:23:11 +0100452 TIntermBinary *binaryParent = parent->getAsBinaryNode();
453 if (binaryParent && binaryParent->getOp() == EOpComma && (binaryParent->getRight() != node))
Olli Etuaho1be88702015-01-19 16:56:44 +0200454 {
455 return false;
456 }
457 return true;
458}
459
Olli Etuaho7854d862017-05-09 14:29:15 +0300460bool ParentConstructorTakesCareOfRounding(TIntermNode *parent, TIntermTyped *node)
461{
462 if (!parent)
463 {
464 return false;
465 }
466 TIntermAggregate *parentConstructor = parent->getAsAggregate();
467 if (!parentConstructor || parentConstructor->getOp() != EOpConstruct)
468 {
469 return false;
470 }
471 if (parentConstructor->getPrecision() != node->getPrecision())
472 {
473 return false;
474 }
475 return canRoundFloat(parentConstructor->getType());
476}
477
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200478} // namespace anonymous
479
Olli Etuaho68981eb2018-01-23 17:46:12 +0200480EmulatePrecision::EmulatePrecision(TSymbolTable *symbolTable)
Olli Etuahofbb1c792018-01-19 16:26:59 +0200481 : TLValueTrackingTraverser(true, true, true, symbolTable), mDeclaringVariables(false)
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500482{
483}
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200484
485void EmulatePrecision::visitSymbol(TIntermSymbol *node)
486{
Olli Etuaho7854d862017-05-09 14:29:15 +0300487 TIntermNode *parent = getParentNode();
488 if (canRoundFloat(node->getType()) && ParentUsesResult(parent, node) &&
489 !ParentConstructorTakesCareOfRounding(parent, node) && !mDeclaringVariables &&
490 !isLValueRequiredHere())
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200491 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200492 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300493 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200494 }
495}
496
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200497bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node)
498{
499 bool visitChildren = true;
500
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200501 TOperator op = node->getOp();
502
503 // RHS of initialize is not being declared.
504 if (op == EOpInitialize && visit == InVisit)
505 mDeclaringVariables = false;
506
Olli Etuahob6fa0432016-09-28 16:28:05 +0100507 if ((op == EOpIndexDirectStruct) && visit == InVisit)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200508 visitChildren = false;
509
510 if (visit != PreVisit)
511 return visitChildren;
512
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500513 const TType &type = node->getType();
514 bool roundFloat = canRoundFloat(type);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200515
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500516 if (roundFloat)
517 {
518 switch (op)
519 {
520 // Math operators that can result in a float may need to apply rounding to the return
521 // value. Note that in the case of assignment, the rounding is applied to its return
522 // value here, not the value being assigned.
523 case EOpAssign:
524 case EOpAdd:
525 case EOpSub:
526 case EOpMul:
527 case EOpDiv:
528 case EOpVectorTimesScalar:
529 case EOpVectorTimesMatrix:
530 case EOpMatrixTimesVector:
531 case EOpMatrixTimesScalar:
532 case EOpMatrixTimesMatrix:
Olli Etuaho1be88702015-01-19 16:56:44 +0200533 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500534 TIntermNode *parent = getParentNode();
Olli Etuaho7854d862017-05-09 14:29:15 +0300535 if (!ParentUsesResult(parent, node) ||
536 ParentConstructorTakesCareOfRounding(parent, node))
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500537 {
538 break;
539 }
540 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300541 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho1be88702015-01-19 16:56:44 +0200542 break;
543 }
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200544
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500545 // Compound assignment cases need to replace the operator with a function call.
546 case EOpAddAssign:
547 {
548 mEmulateCompoundAdd.insert(
549 TypePair(type.getBuiltInTypeNameString(),
550 node->getRight()->getType().getBuiltInTypeNameString()));
551 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
552 node->getLeft(), node->getRight(), "add");
Olli Etuahoea39a222017-07-06 12:47:59 +0300553 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500554 break;
555 }
556 case EOpSubAssign:
557 {
558 mEmulateCompoundSub.insert(
559 TypePair(type.getBuiltInTypeNameString(),
560 node->getRight()->getType().getBuiltInTypeNameString()));
561 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
562 node->getLeft(), node->getRight(), "sub");
Olli Etuahoea39a222017-07-06 12:47:59 +0300563 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500564 break;
565 }
566 case EOpMulAssign:
567 case EOpVectorTimesMatrixAssign:
568 case EOpVectorTimesScalarAssign:
569 case EOpMatrixTimesScalarAssign:
570 case EOpMatrixTimesMatrixAssign:
571 {
572 mEmulateCompoundMul.insert(
573 TypePair(type.getBuiltInTypeNameString(),
574 node->getRight()->getType().getBuiltInTypeNameString()));
575 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
576 node->getLeft(), node->getRight(), "mul");
Olli Etuahoea39a222017-07-06 12:47:59 +0300577 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500578 break;
579 }
580 case EOpDivAssign:
581 {
582 mEmulateCompoundDiv.insert(
583 TypePair(type.getBuiltInTypeNameString(),
584 node->getRight()->getType().getBuiltInTypeNameString()));
585 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
586 node->getLeft(), node->getRight(), "div");
Olli Etuahoea39a222017-07-06 12:47:59 +0300587 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500588 break;
589 }
590 default:
591 // The rest of the binary operations should not need precision emulation.
592 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200593 }
594 }
595 return visitChildren;
596}
597
Olli Etuaho13389b62016-10-16 11:48:18 +0100598bool EmulatePrecision::visitDeclaration(Visit visit, TIntermDeclaration *node)
599{
600 // Variable or interface block declaration.
601 if (visit == PreVisit)
602 {
603 mDeclaringVariables = true;
604 }
605 else if (visit == InVisit)
606 {
607 mDeclaringVariables = true;
608 }
609 else
610 {
611 mDeclaringVariables = false;
612 }
613 return true;
614}
615
Olli Etuahobf4e1b72016-12-09 11:30:15 +0000616bool EmulatePrecision::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node)
617{
618 return false;
619}
620
Olli Etuaho16c745a2017-01-16 17:02:27 +0000621bool EmulatePrecision::visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node)
622{
623 return false;
624}
625
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200626bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
627{
Olli Etuaho7854d862017-05-09 14:29:15 +0300628 if (visit != PreVisit)
629 return true;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200630 switch (node->getOp())
631 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800632 case EOpCallInternalRawFunction:
633 case EOpCallFunctionInAST:
634 // User-defined function return values are not rounded. The calculations that produced
635 // the value inside the function definition should have been rounded.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500636 break;
Olli Etuaho8fab3202017-05-08 18:22:22 +0300637 case EOpConstruct:
638 if (node->getBasicType() == EbtStruct)
639 {
640 break;
641 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500642 default:
Olli Etuaho1be88702015-01-19 16:56:44 +0200643 TIntermNode *parent = getParentNode();
Olli Etuaho7854d862017-05-09 14:29:15 +0300644 if (canRoundFloat(node->getType()) && ParentUsesResult(parent, node) &&
645 !ParentConstructorTakesCareOfRounding(parent, node))
Olli Etuaho1be88702015-01-19 16:56:44 +0200646 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200647 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300648 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200649 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500650 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200651 }
Olli Etuaho7854d862017-05-09 14:29:15 +0300652 return true;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200653}
654
655bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node)
656{
657 switch (node->getOp())
658 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500659 case EOpNegative:
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500660 case EOpLogicalNot:
661 case EOpPostIncrement:
662 case EOpPostDecrement:
663 case EOpPreIncrement:
664 case EOpPreDecrement:
Olli Etuahod68924e2017-01-02 17:34:40 +0000665 case EOpLogicalNotComponentWise:
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500666 break;
667 default:
668 if (canRoundFloat(node->getType()) && visit == PreVisit)
669 {
670 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300671 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500672 }
673 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200674 }
675
676 return true;
677}
678
Olli Etuaho61b81ac2016-06-28 14:15:20 +0300679void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase &sink,
680 const int shaderVersion,
681 const ShShaderOutput outputLanguage)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200682{
Olli Etuahob741c762016-06-29 15:49:22 +0300683 std::unique_ptr<RoundingHelperWriter> roundingHelperWriter(
684 RoundingHelperWriter::createHelperWriter(outputLanguage));
685
686 roundingHelperWriter->writeCommonRoundingHelpers(sink, shaderVersion);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200687
688 EmulationSet::const_iterator it;
689 for (it = mEmulateCompoundAdd.begin(); it != mEmulateCompoundAdd.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300690 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "+", "add");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200691 for (it = mEmulateCompoundSub.begin(); it != mEmulateCompoundSub.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300692 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "-", "sub");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200693 for (it = mEmulateCompoundDiv.begin(); it != mEmulateCompoundDiv.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300694 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "/", "div");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200695 for (it = mEmulateCompoundMul.begin(); it != mEmulateCompoundMul.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300696 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "*", "mul");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200697}
698
Jamie Madilld5696192016-10-06 11:09:24 -0400699// static
700bool EmulatePrecision::SupportedInLanguage(const ShShaderOutput outputLanguage)
701{
702 switch (outputLanguage)
703 {
704 case SH_HLSL_4_1_OUTPUT:
705 case SH_ESSL_OUTPUT:
706 return true;
707 default:
708 // Other languages not yet supported
709 return (outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT ||
Jamie Madillacb4b812016-11-07 13:50:29 -0500710 sh::IsGLSL130OrNewer(outputLanguage));
Jamie Madilld5696192016-10-06 11:09:24 -0400711 }
712}
Jamie Madill45bcc782016-11-07 13:58:48 -0500713
Olli Etuahofbb1c792018-01-19 16:26:59 +0200714const TFunction *EmulatePrecision::getInternalFunction(const ImmutableString &functionName,
Olli Etuaho68981eb2018-01-23 17:46:12 +0200715 const TType &returnType,
716 TIntermSequence *arguments,
717 const TVector<TConstParameter> &parameters,
718 bool knownToNotHaveSideEffects)
Olli Etuaho0c371002017-12-13 17:00:25 +0400719{
Olli Etuahofbb1c792018-01-19 16:26:59 +0200720 ImmutableString mangledName = TFunctionLookup::GetMangledName(functionName.data(), *arguments);
Olli Etuaho0c371002017-12-13 17:00:25 +0400721 if (mInternalFunctions.find(mangledName) == mInternalFunctions.end())
722 {
Olli Etuaho68981eb2018-01-23 17:46:12 +0200723 TFunction *func = new TFunction(mSymbolTable, functionName, new TType(returnType),
724 SymbolType::AngleInternal, knownToNotHaveSideEffects);
725 ASSERT(parameters.size() == arguments->size());
726 for (size_t i = 0; i < parameters.size(); ++i)
727 {
728 func->addParameter(parameters[i]);
729 }
730 mInternalFunctions[mangledName] = func;
Olli Etuaho0c371002017-12-13 17:00:25 +0400731 }
732 return mInternalFunctions[mangledName];
733}
734
735TIntermAggregate *EmulatePrecision::createRoundingFunctionCallNode(TIntermTyped *roundedChild)
736{
Olli Etuahofbb1c792018-01-19 16:26:59 +0200737 const ImmutableString *roundFunctionName = &kAngleFrmString;
738 if (roundedChild->getPrecision() == EbpLow)
739 roundFunctionName = &kAngleFrlString;
Olli Etuaho0c371002017-12-13 17:00:25 +0400740 TIntermSequence *arguments = new TIntermSequence();
741 arguments->push_back(roundedChild);
Olli Etuaho68981eb2018-01-23 17:46:12 +0200742
743 TVector<TConstParameter> parameters;
744 TType *paramType = new TType(roundedChild->getType());
745 paramType->setPrecision(EbpHigh);
746 paramType->setQualifier(EvqIn);
Olli Etuahofbb1c792018-01-19 16:26:59 +0200747 parameters.push_back(TConstParameter(kParamXName, static_cast<const TType *>(paramType)));
Olli Etuaho68981eb2018-01-23 17:46:12 +0200748
Olli Etuaho0c371002017-12-13 17:00:25 +0400749 return TIntermAggregate::CreateRawFunctionCall(
Olli Etuahofbb1c792018-01-19 16:26:59 +0200750 *getInternalFunction(*roundFunctionName, roundedChild->getType(), arguments, parameters,
751 true),
Olli Etuaho68981eb2018-01-23 17:46:12 +0200752 arguments);
Olli Etuaho0c371002017-12-13 17:00:25 +0400753}
754
755TIntermAggregate *EmulatePrecision::createCompoundAssignmentFunctionCallNode(TIntermTyped *left,
756 TIntermTyped *right,
757 const char *opNameStr)
758{
759 std::stringstream strstr;
760 if (left->getPrecision() == EbpMedium)
761 strstr << "angle_compound_" << opNameStr << "_frm";
762 else
763 strstr << "angle_compound_" << opNameStr << "_frl";
Olli Etuahofbb1c792018-01-19 16:26:59 +0200764 ImmutableString functionName = ImmutableString(strstr.str());
Olli Etuaho0c371002017-12-13 17:00:25 +0400765 TIntermSequence *arguments = new TIntermSequence();
766 arguments->push_back(left);
767 arguments->push_back(right);
Olli Etuaho68981eb2018-01-23 17:46:12 +0200768
769 TVector<TConstParameter> parameters;
770 TType *leftParamType = new TType(left->getType());
771 leftParamType->setPrecision(EbpHigh);
772 leftParamType->setQualifier(EvqOut);
Olli Etuahofbb1c792018-01-19 16:26:59 +0200773 parameters.push_back(TConstParameter(kParamXName, static_cast<const TType *>(leftParamType)));
Olli Etuaho68981eb2018-01-23 17:46:12 +0200774 TType *rightParamType = new TType(right->getType());
775 rightParamType->setPrecision(EbpHigh);
776 rightParamType->setQualifier(EvqIn);
Olli Etuahofbb1c792018-01-19 16:26:59 +0200777 parameters.push_back(TConstParameter(kParamYName, static_cast<const TType *>(rightParamType)));
Olli Etuaho68981eb2018-01-23 17:46:12 +0200778
Olli Etuaho0c371002017-12-13 17:00:25 +0400779 return TIntermAggregate::CreateRawFunctionCall(
Olli Etuaho68981eb2018-01-23 17:46:12 +0200780 *getInternalFunction(functionName, left->getType(), arguments, parameters, false),
781 arguments);
Olli Etuaho0c371002017-12-13 17:00:25 +0400782}
783
Jamie Madill45bcc782016-11-07 13:58:48 -0500784} // namespace sh