blob: afbc16926ffdf70ef68062a4a38cfbf651646be6 [file] [log] [blame]
zmo@google.com32e97312011-08-24 01:03:11 +00001//
2// Copyright (c) 2002-2011 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
Geoff Lang17732822013-08-29 13:46:49 -04007#include "compiler/translator/BuiltInFunctionEmulator.h"
zmo@google.com32e97312011-08-24 01:03:11 +00008
Geoff Lang17732822013-08-29 13:46:49 -04009#include "compiler/translator/SymbolTable.h"
zmo@google.com32e97312011-08-24 01:03:11 +000010
11namespace {
12
zmo@google.coma3b4ab42011-09-16 00:53:26 +000013// we use macros here instead of function definitions to work around more GLSL
14// compiler bugs, in particular on NVIDIA hardware on Mac OSX. Macros are
15// problematic because if the argument has side-effects they will be repeatedly
16// evaluated. This is unlikely to show up in real shaders, but is something to
17// consider.
18const char* kFunctionEmulationVertexSource[] = {
zmo@google.coma3b4ab42011-09-16 00:53:26 +000019 "#error no emulation for cos(float)",
20 "#error no emulation for cos(vec2)",
21 "#error no emulation for cos(vec3)",
22 "#error no emulation for cos(vec4)",
zmo@google.comf420c422011-09-12 18:27:59 +000023
zmo@google.coma3b4ab42011-09-16 00:53:26 +000024 "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))",
25 "#error no emulation for distance(vec2, vec2)",
26 "#error no emulation for distance(vec3, vec3)",
27 "#error no emulation for distance(vec4, vec4)",
zmo@google.comf420c422011-09-12 18:27:59 +000028
zmo@google.coma3b4ab42011-09-16 00:53:26 +000029 "#define webgl_dot_emu(x, y) ((x) * (y))",
30 "#error no emulation for dot(vec2, vec2)",
31 "#error no emulation for dot(vec3, vec3)",
32 "#error no emulation for dot(vec4, vec4)",
zmo@google.comf420c422011-09-12 18:27:59 +000033
zmo@google.coma3b4ab42011-09-16 00:53:26 +000034 "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))",
35 "#error no emulation for length(vec2)",
36 "#error no emulation for length(vec3)",
37 "#error no emulation for length(vec4)",
38
zmo@google.coma3b4ab42011-09-16 00:53:26 +000039 "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))",
40 "#error no emulation for normalize(vec2)",
41 "#error no emulation for normalize(vec3)",
42 "#error no emulation for normalize(vec4)",
43
44 "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))",
45 "#error no emulation for reflect(vec2, vec2)",
46 "#error no emulation for reflect(vec3, vec3)",
47 "#error no emulation for reflect(vec4, vec4)"
48};
49
50const char* kFunctionEmulationFragmentSource[] = {
zmo@google.coma3b4ab42011-09-16 00:53:26 +000051 "webgl_emu_precision float webgl_cos_emu(webgl_emu_precision float a) { return cos(a); }",
52 "webgl_emu_precision vec2 webgl_cos_emu(webgl_emu_precision vec2 a) { return cos(a); }",
53 "webgl_emu_precision vec3 webgl_cos_emu(webgl_emu_precision vec3 a) { return cos(a); }",
54 "webgl_emu_precision vec4 webgl_cos_emu(webgl_emu_precision vec4 a) { return cos(a); }",
55
zmo@google.com57442b12012-04-12 23:09:59 +000056 "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))",
zmo@google.coma3b4ab42011-09-16 00:53:26 +000057 "#error no emulation for distance(vec2, vec2)",
58 "#error no emulation for distance(vec3, vec3)",
59 "#error no emulation for distance(vec4, vec4)",
60
zmo@google.com57442b12012-04-12 23:09:59 +000061 "#define webgl_dot_emu(x, y) ((x) * (y))",
zmo@google.coma3b4ab42011-09-16 00:53:26 +000062 "#error no emulation for dot(vec2, vec2)",
63 "#error no emulation for dot(vec3, vec3)",
64 "#error no emulation for dot(vec4, vec4)",
65
zmo@google.com57442b12012-04-12 23:09:59 +000066 "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))",
zmo@google.coma3b4ab42011-09-16 00:53:26 +000067 "#error no emulation for length(vec2)",
68 "#error no emulation for length(vec3)",
69 "#error no emulation for length(vec4)",
70
zmo@google.com57442b12012-04-12 23:09:59 +000071 "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))",
zmo@google.coma3b4ab42011-09-16 00:53:26 +000072 "#error no emulation for normalize(vec2)",
73 "#error no emulation for normalize(vec3)",
74 "#error no emulation for normalize(vec4)",
75
zmo@google.com57442b12012-04-12 23:09:59 +000076 "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))",
zmo@google.coma3b4ab42011-09-16 00:53:26 +000077 "#error no emulation for reflect(vec2, vec2)",
78 "#error no emulation for reflect(vec3, vec3)",
79 "#error no emulation for reflect(vec4, vec4)"
zmo@google.com32e97312011-08-24 01:03:11 +000080};
81
zmo@google.comf420c422011-09-12 18:27:59 +000082const bool kFunctionEmulationVertexMask[] = {
zmo@google.coma3b4ab42011-09-16 00:53:26 +000083#if defined(__APPLE__)
84 // Work around ATI driver bugs in Mac.
zmo@google.coma3b4ab42011-09-16 00:53:26 +000085 false, // TFunctionCos1
86 false, // TFunctionCos2
87 false, // TFunctionCos3
88 false, // TFunctionCos4
89 true, // TFunctionDistance1_1
90 false, // TFunctionDistance2_2
91 false, // TFunctionDistance3_3
92 false, // TFunctionDistance4_4
93 true, // TFunctionDot1_1
94 false, // TFunctionDot2_2
95 false, // TFunctionDot3_3
96 false, // TFunctionDot4_4
97 true, // TFunctionLength1
98 false, // TFunctionLength2
99 false, // TFunctionLength3
100 false, // TFunctionLength4
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000101 true, // TFunctionNormalize1
102 false, // TFunctionNormalize2
103 false, // TFunctionNormalize3
104 false, // TFunctionNormalize4
105 true, // TFunctionReflect1_1
106 false, // TFunctionReflect2_2
107 false, // TFunctionReflect3_3
108 false, // TFunctionReflect4_4
109#else
110 // Work around D3D driver bug in Win.
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000111 false, // TFunctionCos1
112 false, // TFunctionCos2
113 false, // TFunctionCos3
114 false, // TFunctionCos4
115 false, // TFunctionDistance1_1
116 false, // TFunctionDistance2_2
117 false, // TFunctionDistance3_3
118 false, // TFunctionDistance4_4
119 false, // TFunctionDot1_1
120 false, // TFunctionDot2_2
121 false, // TFunctionDot3_3
122 false, // TFunctionDot4_4
123 false, // TFunctionLength1
124 false, // TFunctionLength2
125 false, // TFunctionLength3
126 false, // TFunctionLength4
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000127 false, // TFunctionNormalize1
128 false, // TFunctionNormalize2
129 false, // TFunctionNormalize3
130 false, // TFunctionNormalize4
131 false, // TFunctionReflect1_1
132 false, // TFunctionReflect2_2
133 false, // TFunctionReflect3_3
134 false, // TFunctionReflect4_4
135#endif
136 false // TFunctionUnknown
137};
zmo@google.comf420c422011-09-12 18:27:59 +0000138
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000139const bool kFunctionEmulationFragmentMask[] = {
zmo@google.comf420c422011-09-12 18:27:59 +0000140#if defined(__APPLE__)
zmo@google.com57442b12012-04-12 23:09:59 +0000141 // Work around ATI driver bugs in Mac.
zmo@google.comf420c422011-09-12 18:27:59 +0000142 true, // TFunctionCos1
143 true, // TFunctionCos2
144 true, // TFunctionCos3
145 true, // TFunctionCos4
zmo@google.com57442b12012-04-12 23:09:59 +0000146 true, // TFunctionDistance1_1
147 false, // TFunctionDistance2_2
148 false, // TFunctionDistance3_3
149 false, // TFunctionDistance4_4
150 true, // TFunctionDot1_1
151 false, // TFunctionDot2_2
152 false, // TFunctionDot3_3
153 false, // TFunctionDot4_4
154 true, // TFunctionLength1
155 false, // TFunctionLength2
156 false, // TFunctionLength3
157 false, // TFunctionLength4
158 true, // TFunctionNormalize1
159 false, // TFunctionNormalize2
160 false, // TFunctionNormalize3
161 false, // TFunctionNormalize4
162 true, // TFunctionReflect1_1
163 false, // TFunctionReflect2_2
164 false, // TFunctionReflect3_3
165 false, // TFunctionReflect4_4
zmo@google.comf420c422011-09-12 18:27:59 +0000166#else
zmo@google.com57442b12012-04-12 23:09:59 +0000167 // Work around D3D driver bug in Win.
zmo@google.comf420c422011-09-12 18:27:59 +0000168 false, // TFunctionCos1
169 false, // TFunctionCos2
170 false, // TFunctionCos3
171 false, // TFunctionCos4
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000172 false, // TFunctionDistance1_1
173 false, // TFunctionDistance2_2
174 false, // TFunctionDistance3_3
175 false, // TFunctionDistance4_4
176 false, // TFunctionDot1_1
177 false, // TFunctionDot2_2
178 false, // TFunctionDot3_3
179 false, // TFunctionDot4_4
180 false, // TFunctionLength1
181 false, // TFunctionLength2
182 false, // TFunctionLength3
183 false, // TFunctionLength4
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000184 false, // TFunctionNormalize1
185 false, // TFunctionNormalize2
186 false, // TFunctionNormalize3
187 false, // TFunctionNormalize4
188 false, // TFunctionReflect1_1
189 false, // TFunctionReflect2_2
190 false, // TFunctionReflect3_3
191 false, // TFunctionReflect4_4
zmo@google.com57442b12012-04-12 23:09:59 +0000192#endif
zmo@google.comf420c422011-09-12 18:27:59 +0000193 false // TFunctionUnknown
194};
195
zmo@google.com32e97312011-08-24 01:03:11 +0000196class BuiltInFunctionEmulationMarker : public TIntermTraverser {
197public:
198 BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator& emulator)
199 : mEmulator(emulator)
200 {
201 }
202
203 virtual bool visitUnary(Visit visit, TIntermUnary* node)
204 {
205 if (visit == PreVisit) {
206 bool needToEmulate = mEmulator.SetFunctionCalled(
207 node->getOp(), node->getOperand()->getType());
208 if (needToEmulate)
209 node->setUseEmulatedFunction();
210 }
211 return true;
212 }
213
zmo@google.comf420c422011-09-12 18:27:59 +0000214 virtual bool visitAggregate(Visit visit, TIntermAggregate* node)
215 {
216 if (visit == PreVisit) {
217 // Here we handle all the built-in functions instead of the ones we
218 // currently identified as problematic.
219 switch (node->getOp()) {
220 case EOpLessThan:
221 case EOpGreaterThan:
222 case EOpLessThanEqual:
223 case EOpGreaterThanEqual:
224 case EOpVectorEqual:
225 case EOpVectorNotEqual:
226 case EOpMod:
227 case EOpPow:
228 case EOpAtan:
229 case EOpMin:
230 case EOpMax:
231 case EOpClamp:
232 case EOpMix:
233 case EOpStep:
234 case EOpSmoothStep:
235 case EOpDistance:
236 case EOpDot:
237 case EOpCross:
238 case EOpFaceForward:
239 case EOpReflect:
240 case EOpRefract:
241 case EOpMul:
242 break;
243 default:
244 return true;
245 };
246 const TIntermSequence& sequence = node->getSequence();
247 // Right now we only handle built-in functions with two parameters.
248 if (sequence.size() != 2)
249 return true;
250 TIntermTyped* param1 = sequence[0]->getAsTyped();
251 TIntermTyped* param2 = sequence[1]->getAsTyped();
252 if (!param1 || !param2)
253 return true;
254 bool needToEmulate = mEmulator.SetFunctionCalled(
255 node->getOp(), param1->getType(), param2->getType());
256 if (needToEmulate)
257 node->setUseEmulatedFunction();
258 }
259 return true;
260 }
261
zmo@google.com32e97312011-08-24 01:03:11 +0000262private:
263 BuiltInFunctionEmulator& mEmulator;
264};
265
266} // anonymous namepsace
267
zmo@google.comf420c422011-09-12 18:27:59 +0000268BuiltInFunctionEmulator::BuiltInFunctionEmulator(ShShaderType shaderType)
zmo@google.com32e97312011-08-24 01:03:11 +0000269{
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000270 if (shaderType == SH_FRAGMENT_SHADER) {
zmo@google.comf420c422011-09-12 18:27:59 +0000271 mFunctionMask = kFunctionEmulationFragmentMask;
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000272 mFunctionSource = kFunctionEmulationFragmentSource;
273 } else {
zmo@google.comf420c422011-09-12 18:27:59 +0000274 mFunctionMask = kFunctionEmulationVertexMask;
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000275 mFunctionSource = kFunctionEmulationVertexSource;
276 }
zmo@google.com32e97312011-08-24 01:03:11 +0000277}
278
279bool BuiltInFunctionEmulator::SetFunctionCalled(
zmo@google.comf420c422011-09-12 18:27:59 +0000280 TOperator op, const TType& param)
zmo@google.com32e97312011-08-24 01:03:11 +0000281{
zmo@google.comf420c422011-09-12 18:27:59 +0000282 TBuiltInFunction function = IdentifyFunction(op, param);
283 return SetFunctionCalled(function);
284}
285
286bool BuiltInFunctionEmulator::SetFunctionCalled(
287 TOperator op, const TType& param1, const TType& param2)
288{
289 TBuiltInFunction function = IdentifyFunction(op, param1, param2);
290 return SetFunctionCalled(function);
291}
292
293bool BuiltInFunctionEmulator::SetFunctionCalled(
294 BuiltInFunctionEmulator::TBuiltInFunction function) {
295 if (function == TFunctionUnknown || mFunctionMask[function] == false)
zmo@google.com32e97312011-08-24 01:03:11 +0000296 return false;
297 for (size_t i = 0; i < mFunctions.size(); ++i) {
298 if (mFunctions[i] == function)
299 return true;
300 }
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000301 mFunctions.push_back(function);
302 return true;
zmo@google.com32e97312011-08-24 01:03:11 +0000303}
304
305void BuiltInFunctionEmulator::OutputEmulatedFunctionDefinition(
306 TInfoSinkBase& out, bool withPrecision) const
307{
308 if (mFunctions.size() == 0)
309 return;
310 out << "// BEGIN: Generated code for built-in function emulation\n\n";
311 if (withPrecision) {
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000312 out << "#if defined(GL_FRAGMENT_PRECISION_HIGH)\n"
zmo@google.com93563fc2011-12-13 18:18:04 +0000313 << "#define webgl_emu_precision highp\n"
zmo@google.com32e97312011-08-24 01:03:11 +0000314 << "#else\n"
zmo@google.com93563fc2011-12-13 18:18:04 +0000315 << "#define webgl_emu_precision mediump\n"
zmo@google.com32e97312011-08-24 01:03:11 +0000316 << "#endif\n\n";
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000317 } else {
zmo@google.com93563fc2011-12-13 18:18:04 +0000318 out << "#define webgl_emu_precision\n\n";
zmo@google.com32e97312011-08-24 01:03:11 +0000319 }
320 for (size_t i = 0; i < mFunctions.size(); ++i) {
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000321 out << mFunctionSource[mFunctions[i]] << "\n\n";
zmo@google.com32e97312011-08-24 01:03:11 +0000322 }
323 out << "// END: Generated code for built-in function emulation\n\n";
324}
325
326BuiltInFunctionEmulator::TBuiltInFunction
zmo@google.comf420c422011-09-12 18:27:59 +0000327BuiltInFunctionEmulator::IdentifyFunction(
328 TOperator op, const TType& param)
zmo@google.com32e97312011-08-24 01:03:11 +0000329{
shannonwoods@chromium.org09e09882013-05-30 00:18:25 +0000330 if (param.getNominalSize() > 4 || param.getSecondarySize() > 4)
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000331 return TFunctionUnknown;
zmo@google.com32e97312011-08-24 01:03:11 +0000332 unsigned int function = TFunctionUnknown;
zmo@google.comf420c422011-09-12 18:27:59 +0000333 switch (op) {
zmo@google.comf420c422011-09-12 18:27:59 +0000334 case EOpCos:
335 function = TFunctionCos1;
336 break;
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000337 case EOpLength:
338 function = TFunctionLength1;
339 break;
340 case EOpNormalize:
341 function = TFunctionNormalize1;
zmo@google.comf420c422011-09-12 18:27:59 +0000342 break;
343 default:
344 break;
345 }
346 if (function == TFunctionUnknown)
347 return TFunctionUnknown;
348 if (param.isVector())
349 function += param.getNominalSize() - 1;
350 return static_cast<TBuiltInFunction>(function);
351}
zmo@google.com32e97312011-08-24 01:03:11 +0000352
zmo@google.comf420c422011-09-12 18:27:59 +0000353BuiltInFunctionEmulator::TBuiltInFunction
354BuiltInFunctionEmulator::IdentifyFunction(
355 TOperator op, const TType& param1, const TType& param2)
356{
357 // Right now for all the emulated functions with two parameters, the two
358 // parameters have the same type.
shannonwoods@chromium.org09e09882013-05-30 00:18:25 +0000359 if (param1.getNominalSize() != param2.getNominalSize() ||
360 param1.getSecondarySize() != param2.getSecondarySize() ||
361 param1.getNominalSize() > 4 || param1.getSecondarySize() > 4)
zmo@google.comf420c422011-09-12 18:27:59 +0000362 return TFunctionUnknown;
363
364 unsigned int function = TFunctionUnknown;
365 switch (op) {
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000366 case EOpDistance:
367 function = TFunctionDistance1_1;
368 break;
369 case EOpDot:
370 function = TFunctionDot1_1;
371 break;
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000372 case EOpReflect:
373 function = TFunctionReflect1_1;
zmo@google.comf420c422011-09-12 18:27:59 +0000374 break;
375 default:
376 break;
377 }
378 if (function == TFunctionUnknown)
379 return TFunctionUnknown;
380 if (param1.isVector())
381 function += param1.getNominalSize() - 1;
zmo@google.com32e97312011-08-24 01:03:11 +0000382 return static_cast<TBuiltInFunction>(function);
383}
384
385void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation(
386 TIntermNode* root)
387{
388 ASSERT(root);
389
390 BuiltInFunctionEmulationMarker marker(*this);
391 root->traverse(&marker);
392}
393
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000394void BuiltInFunctionEmulator::Cleanup()
395{
396 mFunctions.clear();
397}
398
zmo@google.com32e97312011-08-24 01:03:11 +0000399//static
400TString BuiltInFunctionEmulator::GetEmulatedFunctionName(
401 const TString& name)
402{
403 ASSERT(name[name.length() - 1] == '(');
404 return "webgl_" + name.substr(0, name.length() - 1) + "_emu(";
405}
406