blob: da3ff5a89082f6d537aa639661a22d72edeb1ce6 [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 Etuahob741c762016-06-29 15:49:22 +030019class RoundingHelperWriter : angle::NonCopyable
Olli Etuaho853dc1a2014-11-06 17:25:48 +020020{
Olli Etuahob741c762016-06-29 15:49:22 +030021 public:
22 static RoundingHelperWriter *createHelperWriter(const ShShaderOutput outputLanguage);
Olli Etuaho853dc1a2014-11-06 17:25:48 +020023
Olli Etuahob741c762016-06-29 15:49:22 +030024 void writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion);
25 void writeCompoundAssignmentHelper(TInfoSinkBase &sink,
26 const char *lType,
27 const char *rType,
28 const char *opStr,
29 const char *opNameStr);
Olli Etuaho853dc1a2014-11-06 17:25:48 +020030
Olli Etuahob741c762016-06-29 15:49:22 +030031 virtual ~RoundingHelperWriter() {}
Olli Etuaho853dc1a2014-11-06 17:25:48 +020032
Olli Etuahob741c762016-06-29 15:49:22 +030033 protected:
34 RoundingHelperWriter(const ShShaderOutput outputLanguage) : mOutputLanguage(outputLanguage) {}
35 RoundingHelperWriter() = delete;
36
37 const ShShaderOutput mOutputLanguage;
38
39 private:
40 virtual std::string getTypeString(const char *glslType) = 0;
41 virtual void writeFloatRoundingHelpers(TInfoSinkBase &sink) = 0;
42 virtual void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) = 0;
43 virtual void writeMatrixRoundingHelper(TInfoSinkBase &sink,
44 const unsigned int columns,
45 const unsigned int rows,
46 const char *functionName) = 0;
47};
48
49class RoundingHelperWriterGLSL : public RoundingHelperWriter
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030050{
Olli Etuahob741c762016-06-29 15:49:22 +030051 public:
52 RoundingHelperWriterGLSL(const ShShaderOutput outputLanguage)
53 : RoundingHelperWriter(outputLanguage)
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030054 {
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030055 }
56
Olli Etuahob741c762016-06-29 15:49:22 +030057 private:
58 std::string getTypeString(const char *glslType) override;
59 void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
60 void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
61 void writeMatrixRoundingHelper(TInfoSinkBase &sink,
62 const unsigned int columns,
63 const unsigned int rows,
64 const char *functionName) override;
65};
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030066
Olli Etuahob741c762016-06-29 15:49:22 +030067class RoundingHelperWriterESSL : public RoundingHelperWriterGLSL
68{
69 public:
70 RoundingHelperWriterESSL(const ShShaderOutput outputLanguage)
71 : RoundingHelperWriterGLSL(outputLanguage)
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030072 {
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +030073 }
74
Olli Etuahob741c762016-06-29 15:49:22 +030075 private:
76 std::string getTypeString(const char *glslType) override;
77};
78
79class RoundingHelperWriterHLSL : public RoundingHelperWriter
80{
81 public:
82 RoundingHelperWriterHLSL(const ShShaderOutput outputLanguage)
83 : RoundingHelperWriter(outputLanguage)
84 {
85 }
86
87 private:
88 std::string getTypeString(const char *glslType) override;
89 void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
90 void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
91 void writeMatrixRoundingHelper(TInfoSinkBase &sink,
92 const unsigned int columns,
93 const unsigned int rows,
94 const char *functionName) override;
95};
96
97RoundingHelperWriter *RoundingHelperWriter::createHelperWriter(const ShShaderOutput outputLanguage)
98{
Jamie Madilld5696192016-10-06 11:09:24 -040099 ASSERT(EmulatePrecision::SupportedInLanguage(outputLanguage));
Olli Etuahob741c762016-06-29 15:49:22 +0300100 switch (outputLanguage)
101 {
102 case SH_HLSL_4_1_OUTPUT:
103 return new RoundingHelperWriterHLSL(outputLanguage);
104 case SH_ESSL_OUTPUT:
105 return new RoundingHelperWriterESSL(outputLanguage);
106 default:
Olli Etuahob741c762016-06-29 15:49:22 +0300107 return new RoundingHelperWriterGLSL(outputLanguage);
108 }
Olli Etuaho3fdaf6f2016-07-13 15:07:41 +0300109}
110
Olli Etuahob741c762016-06-29 15:49:22 +0300111void RoundingHelperWriter::writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200112{
113 // Write the angle_frm functions that round floating point numbers to
114 // half precision, and angle_frl functions that round them to minimum lowp
115 // precision.
116
Olli Etuahob741c762016-06-29 15:49:22 +0300117 writeFloatRoundingHelpers(sink);
118 writeVectorRoundingHelpers(sink, 2);
119 writeVectorRoundingHelpers(sink, 3);
120 writeVectorRoundingHelpers(sink, 4);
121 if (shaderVersion > 100)
122 {
123 for (unsigned int columns = 2; columns <= 4; ++columns)
124 {
125 for (unsigned int rows = 2; rows <= 4; ++rows)
126 {
127 writeMatrixRoundingHelper(sink, columns, rows, "angle_frm");
128 writeMatrixRoundingHelper(sink, columns, rows, "angle_frl");
129 }
130 }
131 }
132 else
133 {
134 for (unsigned int size = 2; size <= 4; ++size)
135 {
136 writeMatrixRoundingHelper(sink, size, size, "angle_frm");
137 writeMatrixRoundingHelper(sink, size, size, "angle_frl");
138 }
139 }
140}
141
142void RoundingHelperWriter::writeCompoundAssignmentHelper(TInfoSinkBase &sink,
143 const char *lType,
144 const char *rType,
145 const char *opStr,
146 const char *opNameStr)
147{
148 std::string lTypeStr = getTypeString(lType);
149 std::string rTypeStr = getTypeString(rType);
150
151 // Note that y should be passed through angle_frm at the function call site,
152 // but x can't be passed through angle_frm there since it is an inout parameter.
153 // So only pass x and the result through angle_frm here.
154 // clang-format off
155 sink <<
156 lTypeStr << " angle_compound_" << opNameStr << "_frm(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
157 " x = angle_frm(angle_frm(x) " << opStr << " y);\n"
158 " return x;\n"
159 "}\n";
160 sink <<
161 lTypeStr << " angle_compound_" << opNameStr << "_frl(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
Olli Etuaho19ecebe2017-05-24 16:44:42 +0300162 " x = angle_frl(angle_frl(x) " << opStr << " y);\n"
Olli Etuahob741c762016-06-29 15:49:22 +0300163 " return x;\n"
164 "}\n";
165 // clang-format on
166}
167
168std::string RoundingHelperWriterGLSL::getTypeString(const char *glslType)
169{
170 return glslType;
171}
172
173std::string RoundingHelperWriterESSL::getTypeString(const char *glslType)
174{
175 std::stringstream typeStrStr;
176 typeStrStr << "highp " << glslType;
177 return typeStrStr.str();
178}
179
180void RoundingHelperWriterGLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
181{
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200182 // Unoptimized version of angle_frm for single floats:
183 //
Olli Etuahob741c762016-06-29 15:49:22 +0300184 // int webgl_maxNormalExponent(in int exponentBits)
185 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200186 // int possibleExponents = int(exp2(float(exponentBits)));
187 // int exponentBias = possibleExponents / 2 - 1;
188 // int allExponentBitsOne = possibleExponents - 1;
189 // return (allExponentBitsOne - 1) - exponentBias;
190 // }
191 //
Olli Etuahob741c762016-06-29 15:49:22 +0300192 // float angle_frm(in float x)
193 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200194 // int mantissaBits = 10;
195 // int exponentBits = 5;
196 // float possibleMantissas = exp2(float(mantissaBits));
197 // float mantissaMax = 2.0 - 1.0 / possibleMantissas;
198 // int maxNE = webgl_maxNormalExponent(exponentBits);
199 // float max = exp2(float(maxNE)) * mantissaMax;
Olli Etuahob741c762016-06-29 15:49:22 +0300200 // if (x > max)
201 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200202 // return max;
203 // }
Olli Etuahob741c762016-06-29 15:49:22 +0300204 // if (x < -max)
205 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200206 // return -max;
207 // }
208 // float exponent = floor(log2(abs(x)));
Olli Etuahob741c762016-06-29 15:49:22 +0300209 // if (abs(x) == 0.0 || exponent < -float(maxNE))
210 // {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200211 // return 0.0 * sign(x)
212 // }
213 // x = x * exp2(-(exponent - float(mantissaBits)));
214 // x = sign(x) * floor(abs(x));
215 // return x * exp2(exponent - float(mantissaBits));
216 // }
217
218 // All numbers with a magnitude less than 2^-15 are subnormal, and are
219 // flushed to zero.
220
221 // Note the constant numbers below:
222 // a) 65504 is the maximum possible mantissa (1.1111111111 in binary) times
223 // 2^15, the maximum normal exponent.
224 // b) 10.0 is the number of mantissa bits.
225 // c) -25.0 is the minimum normal half-float exponent -15.0 minus the number
226 // of mantissa bits.
227 // d) + 1e-30 is to make sure the argument of log2() won't be zero. It can
228 // only affect the result of log2 on x where abs(x) < 1e-22. Since these
229 // numbers will be flushed to zero either way (2^-15 is the smallest
230 // normal positive number), this does not introduce any error.
231
Olli Etuahob741c762016-06-29 15:49:22 +0300232 std::string floatType = getTypeString("float");
233
234 // clang-format off
235 sink <<
236 floatType << " angle_frm(in " << floatType << " x) {\n"
237 " x = clamp(x, -65504.0, 65504.0);\n"
238 " " << floatType << " exponent = floor(log2(abs(x) + 1e-30)) - 10.0;\n"
239 " bool isNonZero = (exponent >= -25.0);\n"
240 " x = x * exp2(-exponent);\n"
241 " x = sign(x) * floor(abs(x));\n"
242 " return x * exp2(exponent) * float(isNonZero);\n"
243 "}\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200244
245 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300246 floatType << " angle_frl(in " << floatType << " x) {\n"
247 " x = clamp(x, -2.0, 2.0);\n"
248 " x = x * 256.0;\n"
249 " x = sign(x) * floor(abs(x));\n"
250 " return x * 0.00390625;\n"
251 "}\n";
252 // clang-format on
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300253}
254
Olli Etuahob741c762016-06-29 15:49:22 +0300255void RoundingHelperWriterGLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
256 const unsigned int size)
Olli Etuahoa42e8b22016-06-29 15:49:22 +0300257{
Olli Etuahob741c762016-06-29 15:49:22 +0300258 std::stringstream vecTypeStrStr;
259 vecTypeStrStr << "vec" << size;
260 std::string vecType = getTypeString(vecTypeStrStr.str().c_str());
261
262 // clang-format off
263 sink <<
264 vecType << " angle_frm(in " << vecType << " v) {\n"
265 " v = clamp(v, -65504.0, 65504.0);\n"
266 " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
267 " bvec" << size << " isNonZero = greaterThanEqual(exponent, vec" << size << "(-25.0));\n"
268 " v = v * exp2(-exponent);\n"
269 " v = sign(v) * floor(abs(v));\n"
270 " return v * exp2(exponent) * vec" << size << "(isNonZero);\n"
271 "}\n";
272
273 sink <<
274 vecType << " angle_frl(in " << vecType << " v) {\n"
275 " v = clamp(v, -2.0, 2.0);\n"
276 " v = v * 256.0;\n"
277 " v = sign(v) * floor(abs(v));\n"
278 " return v * 0.00390625;\n"
279 "}\n";
280 // clang-format on
281}
282
283void RoundingHelperWriterGLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
284 const unsigned int columns,
285 const unsigned int rows,
286 const char *functionName)
287{
288 std::stringstream matTypeStrStr;
289 matTypeStrStr << "mat" << columns;
290 if (rows != columns)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200291 {
Olli Etuahob741c762016-06-29 15:49:22 +0300292 matTypeStrStr << "x" << rows;
293 }
294 std::string matType = getTypeString(matTypeStrStr.str().c_str());
295
296 sink << matType << " " << functionName << "(in " << matType << " m) {\n"
297 << " " << matType << " rounded;\n";
298
299 for (unsigned int i = 0; i < columns; ++i)
300 {
301 sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200302 }
303
Olli Etuahob741c762016-06-29 15:49:22 +0300304 sink << " return rounded;\n"
305 "}\n";
306}
307
308static const char *GetHLSLTypeStr(const char *floatTypeStr)
309{
310 if (strcmp(floatTypeStr, "float") == 0)
311 {
312 return "float";
313 }
314 if (strcmp(floatTypeStr, "vec2") == 0)
315 {
316 return "float2";
317 }
318 if (strcmp(floatTypeStr, "vec3") == 0)
319 {
320 return "float3";
321 }
322 if (strcmp(floatTypeStr, "vec4") == 0)
323 {
324 return "float4";
325 }
326 if (strcmp(floatTypeStr, "mat2") == 0)
327 {
328 return "float2x2";
329 }
330 if (strcmp(floatTypeStr, "mat3") == 0)
331 {
332 return "float3x3";
333 }
334 if (strcmp(floatTypeStr, "mat4") == 0)
335 {
336 return "float4x4";
337 }
338 if (strcmp(floatTypeStr, "mat2x3") == 0)
339 {
340 return "float2x3";
341 }
342 if (strcmp(floatTypeStr, "mat2x4") == 0)
343 {
344 return "float2x4";
345 }
346 if (strcmp(floatTypeStr, "mat3x2") == 0)
347 {
348 return "float3x2";
349 }
350 if (strcmp(floatTypeStr, "mat3x4") == 0)
351 {
352 return "float3x4";
353 }
354 if (strcmp(floatTypeStr, "mat4x2") == 0)
355 {
356 return "float4x2";
357 }
358 if (strcmp(floatTypeStr, "mat4x3") == 0)
359 {
360 return "float4x3";
361 }
362 UNREACHABLE();
363 return nullptr;
364}
365
366std::string RoundingHelperWriterHLSL::getTypeString(const char *glslType)
367{
368 return GetHLSLTypeStr(glslType);
369}
370
371void RoundingHelperWriterHLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
372{
373 // In HLSL scalars are the same as 1-vectors.
374 writeVectorRoundingHelpers(sink, 1);
375}
376
377void RoundingHelperWriterHLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
378 const unsigned int size)
379{
380 std::stringstream vecTypeStrStr;
381 vecTypeStrStr << "float" << size;
382 std::string vecType = vecTypeStrStr.str();
383
384 // clang-format off
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200385 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300386 vecType << " angle_frm(" << vecType << " v) {\n"
387 " v = clamp(v, -65504.0, 65504.0);\n"
388 " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
389 " bool" << size << " isNonZero = exponent < -25.0;\n"
390 " v = v * exp2(-exponent);\n"
391 " v = sign(v) * floor(abs(v));\n"
392 " return v * exp2(exponent) * (float" << size << ")(isNonZero);\n"
393 "}\n";
394
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200395 sink <<
Olli Etuahob741c762016-06-29 15:49:22 +0300396 vecType << " angle_frl(" << vecType << " v) {\n"
397 " v = clamp(v, -2.0, 2.0);\n"
398 " v = v * 256.0;\n"
399 " v = sign(v) * floor(abs(v));\n"
400 " return v * 0.00390625;\n"
401 "}\n";
402 // clang-format on
403}
404
405void RoundingHelperWriterHLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
406 const unsigned int columns,
407 const unsigned int rows,
408 const char *functionName)
409{
410 std::stringstream matTypeStrStr;
411 matTypeStrStr << "float" << columns << "x" << rows;
412 std::string matType = matTypeStrStr.str();
413
414 sink << matType << " " << functionName << "(" << matType << " m) {\n"
415 << " " << matType << " rounded;\n";
416
417 for (unsigned int i = 0; i < columns; ++i)
418 {
419 sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
420 }
421
422 sink << " return rounded;\n"
423 "}\n";
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200424}
425
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200426bool canRoundFloat(const TType &type)
427{
Olli Etuaho61b81ac2016-06-28 14:15:20 +0300428 return type.getBasicType() == EbtFloat && !type.isArray() &&
429 (type.getPrecision() == EbpLow || type.getPrecision() == EbpMedium);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200430}
431
Olli Etuaho7854d862017-05-09 14:29:15 +0300432bool ParentUsesResult(TIntermNode *parent, TIntermTyped *node)
Olli Etuaho1be88702015-01-19 16:56:44 +0200433{
434 if (!parent)
435 {
436 return false;
437 }
438
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100439 TIntermBlock *blockParent = parent->getAsBlock();
440 // If the parent is a block, the result is not assigned anywhere,
Olli Etuaho1be88702015-01-19 16:56:44 +0200441 // so rounding it is not needed. In particular, this can avoid a lot of
442 // unnecessary rounding of unused return values of assignment.
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100443 if (blockParent)
Olli Etuaho1be88702015-01-19 16:56:44 +0200444 {
445 return false;
446 }
Olli Etuaho4db7ded2016-10-13 12:23:11 +0100447 TIntermBinary *binaryParent = parent->getAsBinaryNode();
448 if (binaryParent && binaryParent->getOp() == EOpComma && (binaryParent->getRight() != node))
Olli Etuaho1be88702015-01-19 16:56:44 +0200449 {
450 return false;
451 }
452 return true;
453}
454
Olli Etuaho7854d862017-05-09 14:29:15 +0300455bool ParentConstructorTakesCareOfRounding(TIntermNode *parent, TIntermTyped *node)
456{
457 if (!parent)
458 {
459 return false;
460 }
461 TIntermAggregate *parentConstructor = parent->getAsAggregate();
462 if (!parentConstructor || parentConstructor->getOp() != EOpConstruct)
463 {
464 return false;
465 }
466 if (parentConstructor->getPrecision() != node->getPrecision())
467 {
468 return false;
469 }
470 return canRoundFloat(parentConstructor->getType());
471}
472
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200473} // namespace anonymous
474
Olli Etuaho68981eb2018-01-23 17:46:12 +0200475EmulatePrecision::EmulatePrecision(TSymbolTable *symbolTable)
476 : TLValueTrackingTraverser(true, true, true, symbolTable),
477 mDeclaringVariables(false),
478 mParamXName(NewPoolTString("x")),
479 mParamYName(NewPoolTString("y"))
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500480{
481}
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200482
483void EmulatePrecision::visitSymbol(TIntermSymbol *node)
484{
Olli Etuaho7854d862017-05-09 14:29:15 +0300485 TIntermNode *parent = getParentNode();
486 if (canRoundFloat(node->getType()) && ParentUsesResult(parent, node) &&
487 !ParentConstructorTakesCareOfRounding(parent, node) && !mDeclaringVariables &&
488 !isLValueRequiredHere())
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200489 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200490 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300491 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200492 }
493}
494
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200495bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node)
496{
497 bool visitChildren = true;
498
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200499 TOperator op = node->getOp();
500
501 // RHS of initialize is not being declared.
502 if (op == EOpInitialize && visit == InVisit)
503 mDeclaringVariables = false;
504
Olli Etuahob6fa0432016-09-28 16:28:05 +0100505 if ((op == EOpIndexDirectStruct) && visit == InVisit)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200506 visitChildren = false;
507
508 if (visit != PreVisit)
509 return visitChildren;
510
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500511 const TType &type = node->getType();
512 bool roundFloat = canRoundFloat(type);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200513
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500514 if (roundFloat)
515 {
516 switch (op)
517 {
518 // Math operators that can result in a float may need to apply rounding to the return
519 // value. Note that in the case of assignment, the rounding is applied to its return
520 // value here, not the value being assigned.
521 case EOpAssign:
522 case EOpAdd:
523 case EOpSub:
524 case EOpMul:
525 case EOpDiv:
526 case EOpVectorTimesScalar:
527 case EOpVectorTimesMatrix:
528 case EOpMatrixTimesVector:
529 case EOpMatrixTimesScalar:
530 case EOpMatrixTimesMatrix:
Olli Etuaho1be88702015-01-19 16:56:44 +0200531 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500532 TIntermNode *parent = getParentNode();
Olli Etuaho7854d862017-05-09 14:29:15 +0300533 if (!ParentUsesResult(parent, node) ||
534 ParentConstructorTakesCareOfRounding(parent, node))
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500535 {
536 break;
537 }
538 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300539 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho1be88702015-01-19 16:56:44 +0200540 break;
541 }
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200542
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500543 // Compound assignment cases need to replace the operator with a function call.
544 case EOpAddAssign:
545 {
546 mEmulateCompoundAdd.insert(
547 TypePair(type.getBuiltInTypeNameString(),
548 node->getRight()->getType().getBuiltInTypeNameString()));
549 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
550 node->getLeft(), node->getRight(), "add");
Olli Etuahoea39a222017-07-06 12:47:59 +0300551 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500552 break;
553 }
554 case EOpSubAssign:
555 {
556 mEmulateCompoundSub.insert(
557 TypePair(type.getBuiltInTypeNameString(),
558 node->getRight()->getType().getBuiltInTypeNameString()));
559 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
560 node->getLeft(), node->getRight(), "sub");
Olli Etuahoea39a222017-07-06 12:47:59 +0300561 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500562 break;
563 }
564 case EOpMulAssign:
565 case EOpVectorTimesMatrixAssign:
566 case EOpVectorTimesScalarAssign:
567 case EOpMatrixTimesScalarAssign:
568 case EOpMatrixTimesMatrixAssign:
569 {
570 mEmulateCompoundMul.insert(
571 TypePair(type.getBuiltInTypeNameString(),
572 node->getRight()->getType().getBuiltInTypeNameString()));
573 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
574 node->getLeft(), node->getRight(), "mul");
Olli Etuahoea39a222017-07-06 12:47:59 +0300575 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500576 break;
577 }
578 case EOpDivAssign:
579 {
580 mEmulateCompoundDiv.insert(
581 TypePair(type.getBuiltInTypeNameString(),
582 node->getRight()->getType().getBuiltInTypeNameString()));
583 TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
584 node->getLeft(), node->getRight(), "div");
Olli Etuahoea39a222017-07-06 12:47:59 +0300585 queueReplacement(replacement, OriginalNode::IS_DROPPED);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500586 break;
587 }
588 default:
589 // The rest of the binary operations should not need precision emulation.
590 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200591 }
592 }
593 return visitChildren;
594}
595
Olli Etuaho13389b62016-10-16 11:48:18 +0100596bool EmulatePrecision::visitDeclaration(Visit visit, TIntermDeclaration *node)
597{
598 // Variable or interface block declaration.
599 if (visit == PreVisit)
600 {
601 mDeclaringVariables = true;
602 }
603 else if (visit == InVisit)
604 {
605 mDeclaringVariables = true;
606 }
607 else
608 {
609 mDeclaringVariables = false;
610 }
611 return true;
612}
613
Olli Etuahobf4e1b72016-12-09 11:30:15 +0000614bool EmulatePrecision::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node)
615{
616 return false;
617}
618
Olli Etuaho16c745a2017-01-16 17:02:27 +0000619bool EmulatePrecision::visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node)
620{
621 return false;
622}
623
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200624bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
625{
Olli Etuaho7854d862017-05-09 14:29:15 +0300626 if (visit != PreVisit)
627 return true;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200628 switch (node->getOp())
629 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800630 case EOpCallInternalRawFunction:
631 case EOpCallFunctionInAST:
632 // User-defined function return values are not rounded. The calculations that produced
633 // the value inside the function definition should have been rounded.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500634 break;
Olli Etuaho8fab3202017-05-08 18:22:22 +0300635 case EOpConstruct:
636 if (node->getBasicType() == EbtStruct)
637 {
638 break;
639 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500640 default:
Olli Etuaho1be88702015-01-19 16:56:44 +0200641 TIntermNode *parent = getParentNode();
Olli Etuaho7854d862017-05-09 14:29:15 +0300642 if (canRoundFloat(node->getType()) && ParentUsesResult(parent, node) &&
643 !ParentConstructorTakesCareOfRounding(parent, node))
Olli Etuaho1be88702015-01-19 16:56:44 +0200644 {
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200645 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300646 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200647 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500648 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200649 }
Olli Etuaho7854d862017-05-09 14:29:15 +0300650 return true;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200651}
652
653bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node)
654{
655 switch (node->getOp())
656 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500657 case EOpNegative:
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500658 case EOpLogicalNot:
659 case EOpPostIncrement:
660 case EOpPostDecrement:
661 case EOpPreIncrement:
662 case EOpPreDecrement:
Olli Etuahod68924e2017-01-02 17:34:40 +0000663 case EOpLogicalNotComponentWise:
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500664 break;
665 default:
666 if (canRoundFloat(node->getType()) && visit == PreVisit)
667 {
668 TIntermNode *replacement = createRoundingFunctionCallNode(node);
Olli Etuahoea39a222017-07-06 12:47:59 +0300669 queueReplacement(replacement, OriginalNode::BECOMES_CHILD);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500670 }
671 break;
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200672 }
673
674 return true;
675}
676
Olli Etuaho61b81ac2016-06-28 14:15:20 +0300677void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase &sink,
678 const int shaderVersion,
679 const ShShaderOutput outputLanguage)
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200680{
Olli Etuahob741c762016-06-29 15:49:22 +0300681 std::unique_ptr<RoundingHelperWriter> roundingHelperWriter(
682 RoundingHelperWriter::createHelperWriter(outputLanguage));
683
684 roundingHelperWriter->writeCommonRoundingHelpers(sink, shaderVersion);
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200685
686 EmulationSet::const_iterator it;
687 for (it = mEmulateCompoundAdd.begin(); it != mEmulateCompoundAdd.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300688 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "+", "add");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200689 for (it = mEmulateCompoundSub.begin(); it != mEmulateCompoundSub.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300690 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "-", "sub");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200691 for (it = mEmulateCompoundDiv.begin(); it != mEmulateCompoundDiv.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300692 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "/", "div");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200693 for (it = mEmulateCompoundMul.begin(); it != mEmulateCompoundMul.end(); it++)
Olli Etuahob741c762016-06-29 15:49:22 +0300694 roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "*", "mul");
Olli Etuaho853dc1a2014-11-06 17:25:48 +0200695}
696
Jamie Madilld5696192016-10-06 11:09:24 -0400697// static
698bool EmulatePrecision::SupportedInLanguage(const ShShaderOutput outputLanguage)
699{
700 switch (outputLanguage)
701 {
702 case SH_HLSL_4_1_OUTPUT:
703 case SH_ESSL_OUTPUT:
704 return true;
705 default:
706 // Other languages not yet supported
707 return (outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT ||
Jamie Madillacb4b812016-11-07 13:50:29 -0500708 sh::IsGLSL130OrNewer(outputLanguage));
Jamie Madilld5696192016-10-06 11:09:24 -0400709 }
710}
Jamie Madill45bcc782016-11-07 13:58:48 -0500711
Olli Etuaho68981eb2018-01-23 17:46:12 +0200712const TFunction *EmulatePrecision::getInternalFunction(TString *functionName,
713 const TType &returnType,
714 TIntermSequence *arguments,
715 const TVector<TConstParameter> &parameters,
716 bool knownToNotHaveSideEffects)
Olli Etuaho0c371002017-12-13 17:00:25 +0400717{
Olli Etuaho95ed1942018-02-01 14:01:19 +0200718 TString mangledName = TFunctionLookup::GetMangledName(*functionName, *arguments);
Olli Etuaho0c371002017-12-13 17:00:25 +0400719 if (mInternalFunctions.find(mangledName) == mInternalFunctions.end())
720 {
Olli Etuaho68981eb2018-01-23 17:46:12 +0200721 TFunction *func = new TFunction(mSymbolTable, functionName, new TType(returnType),
722 SymbolType::AngleInternal, knownToNotHaveSideEffects);
723 ASSERT(parameters.size() == arguments->size());
724 for (size_t i = 0; i < parameters.size(); ++i)
725 {
726 func->addParameter(parameters[i]);
727 }
728 mInternalFunctions[mangledName] = func;
Olli Etuaho0c371002017-12-13 17:00:25 +0400729 }
730 return mInternalFunctions[mangledName];
731}
732
733TIntermAggregate *EmulatePrecision::createRoundingFunctionCallNode(TIntermTyped *roundedChild)
734{
735 const char *roundFunctionName;
736 if (roundedChild->getPrecision() == EbpMedium)
737 roundFunctionName = "angle_frm";
738 else
739 roundFunctionName = "angle_frl";
740 TString *functionName = NewPoolTString(roundFunctionName);
741 TIntermSequence *arguments = new TIntermSequence();
742 arguments->push_back(roundedChild);
Olli Etuaho68981eb2018-01-23 17:46:12 +0200743
744 TVector<TConstParameter> parameters;
745 TType *paramType = new TType(roundedChild->getType());
746 paramType->setPrecision(EbpHigh);
747 paramType->setQualifier(EvqIn);
748 parameters.push_back(TConstParameter(mParamXName, static_cast<const TType *>(paramType)));
749
Olli Etuaho0c371002017-12-13 17:00:25 +0400750 return TIntermAggregate::CreateRawFunctionCall(
Olli Etuaho68981eb2018-01-23 17:46:12 +0200751 *getInternalFunction(functionName, roundedChild->getType(), arguments, parameters, true),
752 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";
764 TString *functionName = NewPoolTString(strstr.str().c_str());
765 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);
773 parameters.push_back(TConstParameter(mParamXName, static_cast<const TType *>(leftParamType)));
774 TType *rightParamType = new TType(right->getType());
775 rightParamType->setPrecision(EbpHigh);
776 rightParamType->setQualifier(EvqIn);
777 parameters.push_back(TConstParameter(mParamYName, static_cast<const TType *>(rightParamType)));
778
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