blob: 1470ace301a197e26ddc599392e0158c3ac370b8 [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
Jamie Madill45bcc782016-11-07 13:58:48 -050011namespace sh
12{
13
Olli Etuaho853dc1a2014-11-06 17:25:48 +020014namespace
15{
16
Olli Etuahob741c762016-06-29 15:49:22 +030017class RoundingHelperWriter : angle::NonCopyable
Olli Etuaho853dc1a2014-11-06 17:25:48 +020018{
Olli Etuahob741c762016-06-29 15:49:22 +030019 public:
20 static RoundingHelperWriter *createHelperWriter(const ShShaderOutput outputLanguage);
Olli Etuaho853dc1a2014-11-06 17:25:48 +020021
Olli Etuahob741c762016-06-29 15:49:22 +030022 void writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion);
23 void writeCompoundAssignmentHelper(TInfoSinkBase &sink,
24 const char *lType,
25 const char *rType,
26 const char *opStr,
27 const char *opNameStr);
Olli Etuaho853dc1a2014-11-06 17:25:48 +020028
Olli Etuahob741c762016-06-29 15:49:22 +030029 virtual ~RoundingHelperWriter() {}
Olli Etuaho853dc1a2014-11-06 17:25:48 +020030
Olli Etuahob741c762016-06-29 15:49:22 +030031 protected:
32 RoundingHelperWriter(const ShShaderOutput outputLanguage) : mOutputLanguage(outputLanguage) {}
33 RoundingHelperWriter() = delete;
34
35 const ShShaderOutput mOutputLanguage;
36
37 private:
38 virtual std::string getTypeString(const char *glslType) = 0;
39 virtual void writeFloatRoundingHelpers(TInfoSinkBase &sink) = 0;
40 virtual void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) = 0;
41 virtual void writeMatrixRoundingHelper(TInfoSinkBase &sink,
42 const unsigned int columns,
43 const unsigned int rows,
44 const char *functionName) = 0;
45};
46
47class RoundingHelperWriterGLSL : public RoundingHelperWriter
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030048{
Olli Etuahob741c762016-06-29 15:49:22 +030049 public:
50 RoundingHelperWriterGLSL(const ShShaderOutput outputLanguage)
51 : RoundingHelperWriter(outputLanguage)
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030052 {
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030053 }
54
Olli Etuahob741c762016-06-29 15:49:22 +030055 private:
56 std::string getTypeString(const char *glslType) override;
57 void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
58 void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
59 void writeMatrixRoundingHelper(TInfoSinkBase &sink,
60 const unsigned int columns,
61 const unsigned int rows,
62 const char *functionName) override;
63};
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030064
Olli Etuahob741c762016-06-29 15:49:22 +030065class RoundingHelperWriterESSL : public RoundingHelperWriterGLSL
66{
67 public:
68 RoundingHelperWriterESSL(const ShShaderOutput outputLanguage)
69 : RoundingHelperWriterGLSL(outputLanguage)
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030070 {
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030071 }
72
Olli Etuahob741c762016-06-29 15:49:22 +030073 private:
74 std::string getTypeString(const char *glslType) override;
75};
76
77class RoundingHelperWriterHLSL : public RoundingHelperWriter
78{
79 public:
80 RoundingHelperWriterHLSL(const ShShaderOutput outputLanguage)
81 : RoundingHelperWriter(outputLanguage)
82 {
83 }
84
85 private:
86 std::string getTypeString(const char *glslType) override;
87 void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
88 void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
89 void writeMatrixRoundingHelper(TInfoSinkBase &sink,
90 const unsigned int columns,
91 const unsigned int rows,
92 const char *functionName) override;
93};
94
95RoundingHelperWriter *RoundingHelperWriter::createHelperWriter(const ShShaderOutput outputLanguage)
96{
Jamie Madilld5696192016-10-06 11:09:24 -040097 ASSERT(EmulatePrecision::SupportedInLanguage(outputLanguage));
Olli Etuahob741c762016-06-29 15:49:22 +030098 switch (outputLanguage)
99 {
100 case SH_HLSL_4_1_OUTPUT:
101 return new RoundingHelperWriterHLSL(outputLanguage);
102 case SH_ESSL_OUTPUT:
103 return new RoundingHelperWriterESSL(outputLanguage);
104 default:
Olli Etuahob741c762016-06-29 15:49:22 +0300105 return new RoundingHelperWriterGLSL(outputLanguage);
106 }
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +0300107}
108
Olli Etuahob741c762016-06-29 15:49:22 +0300109void RoundingHelperWriter::writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200110{
111 // Write the angle_frm functions that round floating point numbers to
112 // half precision, and angle_frl functions that round them to minimum lowp
113 // precision.
114
Olli Etuahob741c762016-06-29 15:49:22 +0300115 writeFloatRoundingHelpers(sink);
116 writeVectorRoundingHelpers(sink, 2);
117 writeVectorRoundingHelpers(sink, 3);
118 writeVectorRoundingHelpers(sink, 4);
119 if (shaderVersion > 100)
120 {
121 for (unsigned int columns = 2; columns <= 4; ++columns)
122 {
123 for (unsigned int rows = 2; rows <= 4; ++rows)
124 {
125 writeMatrixRoundingHelper(sink, columns, rows, "angle_frm");
126 writeMatrixRoundingHelper(sink, columns, rows, "angle_frl");
127 }
128 }
129 }
130 else
131 {
132 for (unsigned int size = 2; size <= 4; ++size)
133 {
134 writeMatrixRoundingHelper(sink, size, size, "angle_frm");
135 writeMatrixRoundingHelper(sink, size, size, "angle_frl");
136 }
137 }
138}
139
140void RoundingHelperWriter::writeCompoundAssignmentHelper(TInfoSinkBase &sink,
141 const char *lType,
142 const char *rType,
143 const char *opStr,
144 const char *opNameStr)
145{
146 std::string lTypeStr = getTypeString(lType);
147 std::string rTypeStr = getTypeString(rType);
148
149 // Note that y should be passed through angle_frm at the function call site,
150 // but x can't be passed through angle_frm there since it is an inout parameter.
151 // So only pass x and the result through angle_frm here.
152 // clang-format off
153 sink <<
154 lTypeStr << " angle_compound_" << opNameStr << "_frm(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
155 " x = angle_frm(angle_frm(x) " << opStr << " y);\n"
156 " return x;\n"
157 "}\n";
158 sink <<
159 lTypeStr << " angle_compound_" << opNameStr << "_frl(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
Olli Etuaho19ecebe2017-05-24 16:44:42 +0300160 " x = angle_frl(angle_frl(x) " << opStr << " y);\n"
Olli Etuahob741c762016-06-29 15:49:22 +0300161 " return x;\n"
162 "}\n";
163 // clang-format on
164}
165
166std::string RoundingHelperWriterGLSL::getTypeString(const char *glslType)
167{
168 return glslType;
169}
170
171std::string RoundingHelperWriterESSL::getTypeString(const char *glslType)
172{
173 std::stringstream typeStrStr;
174 typeStrStr << "highp " << glslType;
175 return typeStrStr.str();
176}
177
178void RoundingHelperWriterGLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
179{
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200180 // Unoptimized version of angle_frm for single floats:
181 //
Olli Etuahob741c762016-06-29 15:49:22 +0300182 // int webgl_maxNormalExponent(in int exponentBits)
183 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200184 // int possibleExponents = int(exp2(float(exponentBits)));
185 // int exponentBias = possibleExponents / 2 - 1;
186 // int allExponentBitsOne = possibleExponents - 1;
187 // return (allExponentBitsOne - 1) - exponentBias;
188 // }
189 //
Olli Etuahob741c762016-06-29 15:49:22 +0300190 // float angle_frm(in float x)
191 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200192 // int mantissaBits = 10;
193 // int exponentBits = 5;
194 // float possibleMantissas = exp2(float(mantissaBits));
195 // float mantissaMax = 2.0 - 1.0 / possibleMantissas;
196 // int maxNE = webgl_maxNormalExponent(exponentBits);
197 // float max = exp2(float(maxNE)) * mantissaMax;
Olli Etuahob741c762016-06-29 15:49:22 +0300198 // if (x > max)
199 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200200 // return max;
201 // }
Olli Etuahob741c762016-06-29 15:49:22 +0300202 // if (x < -max)
203 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200204 // return -max;
205 // }
206 // float exponent = floor(log2(abs(x)));
Olli Etuahob741c762016-06-29 15:49:22 +0300207 // if (abs(x) == 0.0 || exponent < -float(maxNE))
208 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200209 // return 0.0 * sign(x)
210 // }
211 // x = x * exp2(-(exponent - float(mantissaBits)));
212 // x = sign(x) * floor(abs(x));
213 // return x * exp2(exponent - float(mantissaBits));
214 // }
215
216 // All numbers with a magnitude less than 2^-15 are subnormal, and are
217 // flushed to zero.
218
219 // Note the constant numbers below:
220 // a) 65504 is the maximum possible mantissa (1.1111111111 in binary) times
221 // 2^15, the maximum normal exponent.
222 // b) 10.0 is the number of mantissa bits.
223 // c) -25.0 is the minimum normal half-float exponent -15.0 minus the number
224 // of mantissa bits.
225 // d) + 1e-30 is to make sure the argument of log2() won't be zero. It can
226 // only affect the result of log2 on x where abs(x) < 1e-22. Since these
227 // numbers will be flushed to zero either way (2^-15 is the smallest
228 // normal positive number), this does not introduce any error.
229
Olli Etuahob741c762016-06-29 15:49:22 +0300230 std::string floatType = getTypeString("float");
231
232 // clang-format off
233 sink <<
234 floatType << " angle_frm(in " << floatType << " x) {\n"
235 " x = clamp(x, -65504.0, 65504.0);\n"
236 " " << floatType << " exponent = floor(log2(abs(x) + 1e-30)) - 10.0;\n"
237 " bool isNonZero = (exponent >= -25.0);\n"
238 " x = x * exp2(-exponent);\n"
239 " x = sign(x) * floor(abs(x));\n"
240 " return x * exp2(exponent) * float(isNonZero);\n"
241 "}\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200242
243 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300244 floatType << " angle_frl(in " << floatType << " x) {\n"
245 " x = clamp(x, -2.0, 2.0);\n"
246 " x = x * 256.0;\n"
247 " x = sign(x) * floor(abs(x));\n"
248 " return x * 0.00390625;\n"
249 "}\n";
250 // clang-format on
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300251}
252
Olli Etuahob741c762016-06-29 15:49:22 +0300253void RoundingHelperWriterGLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
254 const unsigned int size)
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300255{
Olli Etuahob741c762016-06-29 15:49:22 +0300256 std::stringstream vecTypeStrStr;
257 vecTypeStrStr << "vec" << size;
258 std::string vecType = getTypeString(vecTypeStrStr.str().c_str());
259
260 // clang-format off
261 sink <<
262 vecType << " angle_frm(in " << vecType << " v) {\n"
263 " v = clamp(v, -65504.0, 65504.0);\n"
264 " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
265 " bvec" << size << " isNonZero = greaterThanEqual(exponent, vec" << size << "(-25.0));\n"
266 " v = v * exp2(-exponent);\n"
267 " v = sign(v) * floor(abs(v));\n"
268 " return v * exp2(exponent) * vec" << size << "(isNonZero);\n"
269 "}\n";
270
271 sink <<
272 vecType << " angle_frl(in " << vecType << " v) {\n"
273 " v = clamp(v, -2.0, 2.0);\n"
274 " v = v * 256.0;\n"
275 " v = sign(v) * floor(abs(v));\n"
276 " return v * 0.00390625;\n"
277 "}\n";
278 // clang-format on
279}
280
281void RoundingHelperWriterGLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
282 const unsigned int columns,
283 const unsigned int rows,
284 const char *functionName)
285{
286 std::stringstream matTypeStrStr;
287 matTypeStrStr << "mat" << columns;
288 if (rows != columns)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200289 {
Olli Etuahob741c762016-06-29 15:49:22 +0300290 matTypeStrStr << "x" << rows;
291 }
292 std::string matType = getTypeString(matTypeStrStr.str().c_str());
293
294 sink << matType << " " << functionName << "(in " << matType << " m) {\n"
295 << " " << matType << " rounded;\n";
296
297 for (unsigned int i = 0; i < columns; ++i)
298 {
299 sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200300 }
301
Olli Etuahob741c762016-06-29 15:49:22 +0300302 sink << " return rounded;\n"
303 "}\n";
304}
305
306static const char *GetHLSLTypeStr(const char *floatTypeStr)
307{
308 if (strcmp(floatTypeStr, "float") == 0)
309 {
310 return "float";
311 }
312 if (strcmp(floatTypeStr, "vec2") == 0)
313 {
314 return "float2";
315 }
316 if (strcmp(floatTypeStr, "vec3") == 0)
317 {
318 return "float3";
319 }
320 if (strcmp(floatTypeStr, "vec4") == 0)
321 {
322 return "float4";
323 }
324 if (strcmp(floatTypeStr, "mat2") == 0)
325 {
326 return "float2x2";
327 }
328 if (strcmp(floatTypeStr, "mat3") == 0)
329 {
330 return "float3x3";
331 }
332 if (strcmp(floatTypeStr, "mat4") == 0)
333 {
334 return "float4x4";
335 }
336 if (strcmp(floatTypeStr, "mat2x3") == 0)
337 {
338 return "float2x3";
339 }
340 if (strcmp(floatTypeStr, "mat2x4") == 0)
341 {
342 return "float2x4";
343 }
344 if (strcmp(floatTypeStr, "mat3x2") == 0)
345 {
346 return "float3x2";
347 }
348 if (strcmp(floatTypeStr, "mat3x4") == 0)
349 {
350 return "float3x4";
351 }
352 if (strcmp(floatTypeStr, "mat4x2") == 0)
353 {
354 return "float4x2";
355 }
356 if (strcmp(floatTypeStr, "mat4x3") == 0)
357 {
358 return "float4x3";
359 }
360 UNREACHABLE();
361 return nullptr;
362}
363
364std::string RoundingHelperWriterHLSL::getTypeString(const char *glslType)
365{
366 return GetHLSLTypeStr(glslType);
367}
368
369void RoundingHelperWriterHLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
370{
371 // In HLSL scalars are the same as 1-vectors.
372 writeVectorRoundingHelpers(sink, 1);
373}
374
375void RoundingHelperWriterHLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
376 const unsigned int size)
377{
378 std::stringstream vecTypeStrStr;
379 vecTypeStrStr << "float" << size;
380 std::string vecType = vecTypeStrStr.str();
381
382 // clang-format off
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200383 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300384 vecType << " angle_frm(" << vecType << " v) {\n"
385 " v = clamp(v, -65504.0, 65504.0);\n"
386 " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
387 " bool" << size << " isNonZero = exponent < -25.0;\n"
388 " v = v * exp2(-exponent);\n"
389 " v = sign(v) * floor(abs(v));\n"
390 " return v * exp2(exponent) * (float" << size << ")(isNonZero);\n"
391 "}\n";
392
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200393 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300394 vecType << " angle_frl(" << vecType << " v) {\n"
395 " v = clamp(v, -2.0, 2.0);\n"
396 " v = v * 256.0;\n"
397 " v = sign(v) * floor(abs(v));\n"
398 " return v * 0.00390625;\n"
399 "}\n";
400 // clang-format on
401}
402
403void RoundingHelperWriterHLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
404 const unsigned int columns,
405 const unsigned int rows,
406 const char *functionName)
407{
408 std::stringstream matTypeStrStr;
409 matTypeStrStr << "float" << columns << "x" << rows;
410 std::string matType = matTypeStrStr.str();
411
412 sink << matType << " " << functionName << "(" << matType << " m) {\n"
413 << " " << matType << " rounded;\n";
414
415 for (unsigned int i = 0; i < columns; ++i)
416 {
417 sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
418 }
419
420 sink << " return rounded;\n"
421 "}\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200422}
423
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200424bool canRoundFloat(const TType &type)
425{
Olli Etuaho61b81ac2016-06-28 14:15:20 +0300426 return type.getBasicType() == EbtFloat && !type.isArray() &&
427 (type.getPrecision() == EbpLow || type.getPrecision() == EbpMedium);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200428}
429
Olli Etuaho7854d862017-05-09 14:29:15 +0300430bool ParentUsesResult(TIntermNode *parent, TIntermTyped *node)
Olli Etuaho1be88702015-01-19 16:56:44 +0200431{
432 if (!parent)
433 {
434 return false;
435 }
436
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100437 TIntermBlock *blockParent = parent->getAsBlock();
438 // If the parent is a block, the result is not assigned anywhere,
Olli Etuaho1be88702015-01-19 16:56:44 +0200439 // so rounding it is not needed. In particular, this can avoid a lot of
440 // unnecessary rounding of unused return values of assignment.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100441 if (blockParent)
Olli Etuaho1be88702015-01-19 16:56:44 +0200442 {
443 return false;
444 }
Olli Etuaho4db7ded2016-10-13 12:23:11 +0100445 TIntermBinary *binaryParent = parent->getAsBinaryNode();
446 if (binaryParent && binaryParent->getOp() == EOpComma && (binaryParent->getRight() != node))
Olli Etuaho1be88702015-01-19 16:56:44 +0200447 {
448 return false;
449 }
450 return true;
451}
452
Olli Etuaho7854d862017-05-09 14:29:15 +0300453bool ParentConstructorTakesCareOfRounding(TIntermNode *parent, TIntermTyped *node)
454{
455 if (!parent)
456 {
457 return false;
458 }
459 TIntermAggregate *parentConstructor = parent->getAsAggregate();
460 if (!parentConstructor || parentConstructor->getOp() != EOpConstruct)
461 {
462 return false;
463 }
464 if (parentConstructor->getPrecision() != node->getPrecision())
465 {
466 return false;
467 }
468 return canRoundFloat(parentConstructor->getType());
469}
470
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200471} // namespace anonymous
472
Olli Etuaho68981eb2018-01-23 17:46:12 +0200473EmulatePrecision::EmulatePrecision(TSymbolTable *symbolTable)
474 : TLValueTrackingTraverser(true, true, true, symbolTable),
475 mDeclaringVariables(false),
476 mParamXName(NewPoolTString("x")),
477 mParamYName(NewPoolTString("y"))
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500478{
479}
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200480
481void EmulatePrecision::visitSymbol(TIntermSymbol *node)
482{
Olli Etuaho7854d862017-05-09 14:29:15 +0300483 TIntermNode *parent = getParentNode();
484 if (canRoundFloat(node->getType()) && ParentUsesResult(parent, node) &&
485 !ParentConstructorTakesCareOfRounding(parent, node) && !mDeclaringVariables &&
486 !isLValueRequiredHere())
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200487 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200488 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300489 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200490 }
491}
492
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200493bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node)
494{
495 bool visitChildren = true;
496
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200497 TOperator op = node->getOp();
498
499 // RHS of initialize is not being declared.
500 if (op == EOpInitialize && visit == InVisit)
501 mDeclaringVariables = false;
502
Olli Etuahob6fa0432016-09-28 16:28:05 +0100503 if ((op == EOpIndexDirectStruct) && visit == InVisit)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200504 visitChildren = false;
505
506 if (visit != PreVisit)
507 return visitChildren;
508
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500509 const TType &type = node->getType();
510 bool roundFloat = canRoundFloat(type);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200511
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500512 if (roundFloat)
513 {
514 switch (op)
515 {
516 // Math operators that can result in a float may need to apply rounding to the return
517 // value. Note that in the case of assignment, the rounding is applied to its return
518 // value here, not the value being assigned.
519 case EOpAssign:
520 case EOpAdd:
521 case EOpSub:
522 case EOpMul:
523 case EOpDiv:
524 case EOpVectorTimesScalar:
525 case EOpVectorTimesMatrix:
526 case EOpMatrixTimesVector:
527 case EOpMatrixTimesScalar:
528 case EOpMatrixTimesMatrix:
Olli Etuaho1be88702015-01-19 16:56:44 +0200529 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500530 TIntermNode *parent = getParentNode();
Olli Etuaho7854d862017-05-09 14:29:15 +0300531 if (!ParentUsesResult(parent, node) ||
532 ParentConstructorTakesCareOfRounding(parent, node))
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500533 {
534 break;
535 }
536 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300537 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho1be88702015-01-19 16:56:44 +0200538 break;
539 }
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200540
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500541 // Compound assignment cases need to replace the operator with a function call.
542 case EOpAddAssign:
543 {
544 mEmulateCompoundAdd.insert(
545 TypePair(type.getBuiltInTypeNameString(),
546 node->getRight()->getType().getBuiltInTypeNameString()));
547 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
548 node->getLeft(), node->getRight(), "add");
Olli Etuahoea39a222017-07-06 12:47:59 +0300549 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500550 break;
551 }
552 case EOpSubAssign:
553 {
554 mEmulateCompoundSub.insert(
555 TypePair(type.getBuiltInTypeNameString(),
556 node->getRight()->getType().getBuiltInTypeNameString()));
557 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
558 node->getLeft(), node->getRight(), "sub");
Olli Etuahoea39a222017-07-06 12:47:59 +0300559 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500560 break;
561 }
562 case EOpMulAssign:
563 case EOpVectorTimesMatrixAssign:
564 case EOpVectorTimesScalarAssign:
565 case EOpMatrixTimesScalarAssign:
566 case EOpMatrixTimesMatrixAssign:
567 {
568 mEmulateCompoundMul.insert(
569 TypePair(type.getBuiltInTypeNameString(),
570 node->getRight()->getType().getBuiltInTypeNameString()));
571 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
572 node->getLeft(), node->getRight(), "mul");
Olli Etuahoea39a222017-07-06 12:47:59 +0300573 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500574 break;
575 }
576 case EOpDivAssign:
577 {
578 mEmulateCompoundDiv.insert(
579 TypePair(type.getBuiltInTypeNameString(),
580 node->getRight()->getType().getBuiltInTypeNameString()));
581 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
582 node->getLeft(), node->getRight(), "div");
Olli Etuahoea39a222017-07-06 12:47:59 +0300583 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500584 break;
585 }
586 default:
587 // The rest of the binary operations should not need precision emulation.
588 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200589 }
590 }
591 return visitChildren;
592}
593
Olli Etuaho13389b62016-10-16 11:48:18 +0100594bool EmulatePrecision::visitDeclaration(Visit visit, TIntermDeclaration *node)
595{
596 // Variable or interface block declaration.
597 if (visit == PreVisit)
598 {
599 mDeclaringVariables = true;
600 }
601 else if (visit == InVisit)
602 {
603 mDeclaringVariables = true;
604 }
605 else
606 {
607 mDeclaringVariables = false;
608 }
609 return true;
610}
611
Olli Etuahobf4e1b72016-12-09 11:30:15 +0000612bool EmulatePrecision::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node)
613{
614 return false;
615}
616
Olli Etuaho16c745a2017-01-16 17:02:27 +0000617bool EmulatePrecision::visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node)
618{
619 return false;
620}
621
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200622bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
623{
Olli Etuaho7854d862017-05-09 14:29:15 +0300624 if (visit != PreVisit)
625 return true;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200626 switch (node->getOp())
627 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800628 case EOpCallInternalRawFunction:
629 case EOpCallFunctionInAST:
630 // User-defined function return values are not rounded. The calculations that produced
631 // the value inside the function definition should have been rounded.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500632 break;
Olli Etuaho8fab3202017-05-08 18:22:22 +0300633 case EOpConstruct:
634 if (node->getBasicType() == EbtStruct)
635 {
636 break;
637 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500638 default:
Olli Etuaho1be88702015-01-19 16:56:44 +0200639 TIntermNode *parent = getParentNode();
Olli Etuaho7854d862017-05-09 14:29:15 +0300640 if (canRoundFloat(node->getType()) && ParentUsesResult(parent, node) &&
641 !ParentConstructorTakesCareOfRounding(parent, node))
Olli Etuaho1be88702015-01-19 16:56:44 +0200642 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200643 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300644 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200645 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500646 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200647 }
Olli Etuaho7854d862017-05-09 14:29:15 +0300648 return true;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200649}
650
651bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node)
652{
653 switch (node->getOp())
654 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500655 case EOpNegative:
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500656 case EOpLogicalNot:
657 case EOpPostIncrement:
658 case EOpPostDecrement:
659 case EOpPreIncrement:
660 case EOpPreDecrement:
Olli Etuahod68924e2017-01-02 17:34:40 +0000661 case EOpLogicalNotComponentWise:
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500662 break;
663 default:
664 if (canRoundFloat(node->getType()) && visit == PreVisit)
665 {
666 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300667 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500668 }
669 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200670 }
671
672 return true;
673}
674
Olli Etuaho61b81ac2016-06-28 14:15:20 +0300675void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase &sink,
676 const int shaderVersion,
677 const ShShaderOutput outputLanguage)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200678{
Olli Etuahob741c762016-06-29 15:49:22 +0300679 std::unique_ptr<RoundingHelperWriter> roundingHelperWriter(
680 RoundingHelperWriter::createHelperWriter(outputLanguage));
681
682 roundingHelperWriter->writeCommonRoundingHelpers(sink, shaderVersion);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200683
684 EmulationSet::const_iterator it;
685 for (it = mEmulateCompoundAdd.begin(); it != mEmulateCompoundAdd.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300686 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "+", "add");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200687 for (it = mEmulateCompoundSub.begin(); it != mEmulateCompoundSub.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300688 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "-", "sub");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200689 for (it = mEmulateCompoundDiv.begin(); it != mEmulateCompoundDiv.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300690 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "/", "div");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200691 for (it = mEmulateCompoundMul.begin(); it != mEmulateCompoundMul.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300692 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "*", "mul");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200693}
694
Jamie Madilld5696192016-10-06 11:09:24 -0400695// static
696bool EmulatePrecision::SupportedInLanguage(const ShShaderOutput outputLanguage)
697{
698 switch (outputLanguage)
699 {
700 case SH_HLSL_4_1_OUTPUT:
701 case SH_ESSL_OUTPUT:
702 return true;
703 default:
704 // Other languages not yet supported
705 return (outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT ||
Jamie Madillacb4b812016-11-07 13:50:29 -0500706 sh::IsGLSL130OrNewer(outputLanguage));
Jamie Madilld5696192016-10-06 11:09:24 -0400707 }
708}
Jamie Madill45bcc782016-11-07 13:58:48 -0500709
Olli Etuaho68981eb2018-01-23 17:46:12 +0200710const TFunction *EmulatePrecision::getInternalFunction(TString *functionName,
711 const TType &returnType,
712 TIntermSequence *arguments,
713 const TVector<TConstParameter> &parameters,
714 bool knownToNotHaveSideEffects)
Olli Etuaho0c371002017-12-13 17:00:25 +0400715{
716 TString mangledName = TFunction::GetMangledNameFromCall(*functionName, *arguments);
717 if (mInternalFunctions.find(mangledName) == mInternalFunctions.end())
718 {
Olli Etuaho68981eb2018-01-23 17:46:12 +0200719 TFunction *func = new TFunction(mSymbolTable, functionName, new TType(returnType),
720 SymbolType::AngleInternal, knownToNotHaveSideEffects);
721 ASSERT(parameters.size() == arguments->size());
722 for (size_t i = 0; i < parameters.size(); ++i)
723 {
724 func->addParameter(parameters[i]);
725 }
726 mInternalFunctions[mangledName] = func;
Olli Etuaho0c371002017-12-13 17:00:25 +0400727 }
728 return mInternalFunctions[mangledName];
729}
730
731TIntermAggregate *EmulatePrecision::createRoundingFunctionCallNode(TIntermTyped *roundedChild)
732{
733 const char *roundFunctionName;
734 if (roundedChild->getPrecision() == EbpMedium)
735 roundFunctionName = "angle_frm";
736 else
737 roundFunctionName = "angle_frl";
738 TString *functionName = NewPoolTString(roundFunctionName);
739 TIntermSequence *arguments = new TIntermSequence();
740 arguments->push_back(roundedChild);
Olli Etuaho68981eb2018-01-23 17:46:12 +0200741
742 TVector<TConstParameter> parameters;
743 TType *paramType = new TType(roundedChild->getType());
744 paramType->setPrecision(EbpHigh);
745 paramType->setQualifier(EvqIn);
746 parameters.push_back(TConstParameter(mParamXName, static_cast<const TType *>(paramType)));
747
Olli Etuaho0c371002017-12-13 17:00:25 +0400748 return TIntermAggregate::CreateRawFunctionCall(
Olli Etuaho68981eb2018-01-23 17:46:12 +0200749 *getInternalFunction(functionName, roundedChild->getType(), arguments, parameters, true),
750 arguments);
Olli Etuaho0c371002017-12-13 17:00:25 +0400751}
752
753TIntermAggregate *EmulatePrecision::createCompoundAssignmentFunctionCallNode(TIntermTyped *left,
754 TIntermTyped *right,
755 const char *opNameStr)
756{
757 std::stringstream strstr;
758 if (left->getPrecision() == EbpMedium)
759 strstr << "angle_compound_" << opNameStr << "_frm";
760 else
761 strstr << "angle_compound_" << opNameStr << "_frl";
762 TString *functionName = NewPoolTString(strstr.str().c_str());
763 TIntermSequence *arguments = new TIntermSequence();
764 arguments->push_back(left);
765 arguments->push_back(right);
Olli Etuaho68981eb2018-01-23 17:46:12 +0200766
767 TVector<TConstParameter> parameters;
768 TType *leftParamType = new TType(left->getType());
769 leftParamType->setPrecision(EbpHigh);
770 leftParamType->setQualifier(EvqOut);
771 parameters.push_back(TConstParameter(mParamXName, static_cast<const TType *>(leftParamType)));
772 TType *rightParamType = new TType(right->getType());
773 rightParamType->setPrecision(EbpHigh);
774 rightParamType->setQualifier(EvqIn);
775 parameters.push_back(TConstParameter(mParamYName, static_cast<const TType *>(rightParamType)));
776
Olli Etuaho0c371002017-12-13 17:00:25 +0400777 return TIntermAggregate::CreateRawFunctionCall(
Olli Etuaho68981eb2018-01-23 17:46:12 +0200778 *getInternalFunction(functionName, left->getType(), arguments, parameters, false),
779 arguments);
Olli Etuaho0c371002017-12-13 17:00:25 +0400780}
781
Jamie Madill45bcc782016-11-07 13:58:48 -0500782} // namespace sh