blob: a7950b9900ca75bf50b79f13259eb908be4b9d91 [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
7#include "compiler/BuiltInFunctionEmulator.h"
8
9#include "compiler/SymbolTable.h"
10
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[] = {
19 "#error no emulation for atan(float, float)",
20 "vec2 webgl_atan_emu(vec2 y, vec2 x) { return vec2(atan(y[0], x[0]), atan(y[1], x[1])); }",
21 "vec3 webgl_atan_emu(vec3 y, vec3 x) { return vec3(atan(y[0], x[0]), atan(y[1], x[1]), atan(y[2], x[2])); }",
22 "vec4 webgl_atan_emu(vec4 y, vec4 x) { return vec4(atan(y[0], x[0]), atan(y[1], x[1]), atan(y[2], x[2]), atan(y[3], x[3])); }",
zmo@google.comf420c422011-09-12 18:27:59 +000023
zmo@google.coma3b4ab42011-09-16 00:53:26 +000024 "#error no emulation for cos(float)",
25 "#error no emulation for cos(vec2)",
26 "#error no emulation for cos(vec3)",
27 "#error no emulation for cos(vec4)",
zmo@google.comf420c422011-09-12 18:27:59 +000028
zmo@google.coma3b4ab42011-09-16 00:53:26 +000029 "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))",
30 "#error no emulation for distance(vec2, vec2)",
31 "#error no emulation for distance(vec3, vec3)",
32 "#error no emulation for distance(vec4, vec4)",
zmo@google.comf420c422011-09-12 18:27:59 +000033
zmo@google.coma3b4ab42011-09-16 00:53:26 +000034 "#define webgl_dot_emu(x, y) ((x) * (y))",
35 "#error no emulation for dot(vec2, vec2)",
36 "#error no emulation for dot(vec3, vec3)",
37 "#error no emulation for dot(vec4, vec4)",
zmo@google.comf420c422011-09-12 18:27:59 +000038
zmo@google.coma3b4ab42011-09-16 00:53:26 +000039 "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))",
40 "#error no emulation for length(vec2)",
41 "#error no emulation for length(vec3)",
42 "#error no emulation for length(vec4)",
43
44 "#error no emulation for mod(float, float)",
45 "vec2 webgl_mod_emu(vec2 x, vec2 y) { return vec2(mod(x[0], y[0]), mod(x[1], y[1])); }",
46 "vec3 webgl_mod_emu(vec3 x, vec3 y) { return vec3(mod(x[0], y[0]), mod(x[1], y[1]), mod(x[2], y[2])); }",
47 "vec4 webgl_mod_emu(vec4 x, vec4 y) { return vec4(mod(x[0], y[0]), mod(x[1], y[1]), mod(x[2], y[2]), mod(x[3], y[3])); }",
48
49 "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))",
50 "#error no emulation for normalize(vec2)",
51 "#error no emulation for normalize(vec3)",
52 "#error no emulation for normalize(vec4)",
53
54 "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))",
55 "#error no emulation for reflect(vec2, vec2)",
56 "#error no emulation for reflect(vec3, vec3)",
57 "#error no emulation for reflect(vec4, vec4)"
58};
59
60const char* kFunctionEmulationFragmentSource[] = {
61 "#error no emulation for atan(float, float)",
62 "#error no emulation for atan(vec2, vec2)",
63 "#error no emulation for atan(vec3, vec3)",
64 "#error no emulation for atan(vec4, vec4)",
65
66 "webgl_emu_precision float webgl_cos_emu(webgl_emu_precision float a) { return cos(a); }",
67 "webgl_emu_precision vec2 webgl_cos_emu(webgl_emu_precision vec2 a) { return cos(a); }",
68 "webgl_emu_precision vec3 webgl_cos_emu(webgl_emu_precision vec3 a) { return cos(a); }",
69 "webgl_emu_precision vec4 webgl_cos_emu(webgl_emu_precision vec4 a) { return cos(a); }",
70
71 "#error no emulation for distance(float, float)",
72 "#error no emulation for distance(vec2, vec2)",
73 "#error no emulation for distance(vec3, vec3)",
74 "#error no emulation for distance(vec4, vec4)",
75
76 "#error no emulation for dot(float, float)",
77 "#error no emulation for dot(vec2, vec2)",
78 "#error no emulation for dot(vec3, vec3)",
79 "#error no emulation for dot(vec4, vec4)",
80
81 "#error no emulation for length(float)",
82 "#error no emulation for length(vec2)",
83 "#error no emulation for length(vec3)",
84 "#error no emulation for length(vec4)",
85
86 "#error no emulation for mod(float, float)",
87 "#error no emulation for mod(vec2, vec2)",
88 "#error no emulation for mod(vec3, vec3)",
89 "#error no emulation for mod(vec4, vec4)",
90
91 "#error no emulation for normalize(float)",
92 "#error no emulation for normalize(vec2)",
93 "#error no emulation for normalize(vec3)",
94 "#error no emulation for normalize(vec4)",
95
96 "#error no emulation for reflect(float, float)",
97 "#error no emulation for reflect(vec2, vec2)",
98 "#error no emulation for reflect(vec3, vec3)",
99 "#error no emulation for reflect(vec4, vec4)"
zmo@google.com32e97312011-08-24 01:03:11 +0000100};
101
zmo@google.comf420c422011-09-12 18:27:59 +0000102const bool kFunctionEmulationVertexMask[] = {
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000103#if defined(__APPLE__)
104 // Work around ATI driver bugs in Mac.
zmo@google.comf420c422011-09-12 18:27:59 +0000105 false, // TFunctionAtan1_1
106 false, // TFunctionAtan2_2
107 false, // TFunctionAtan3_3
108 false, // TFunctionAtan4_4
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000109 false, // TFunctionCos1
110 false, // TFunctionCos2
111 false, // TFunctionCos3
112 false, // TFunctionCos4
113 true, // TFunctionDistance1_1
114 false, // TFunctionDistance2_2
115 false, // TFunctionDistance3_3
116 false, // TFunctionDistance4_4
117 true, // TFunctionDot1_1
118 false, // TFunctionDot2_2
119 false, // TFunctionDot3_3
120 false, // TFunctionDot4_4
121 true, // TFunctionLength1
122 false, // TFunctionLength2
123 false, // TFunctionLength3
124 false, // TFunctionLength4
125 false, // TFunctionMod1_1
126 false, // TFunctionMod2_2
127 false, // TFunctionMod3_3
128 false, // TFunctionMod4_4
129 true, // TFunctionNormalize1
130 false, // TFunctionNormalize2
131 false, // TFunctionNormalize3
132 false, // TFunctionNormalize4
133 true, // TFunctionReflect1_1
134 false, // TFunctionReflect2_2
135 false, // TFunctionReflect3_3
136 false, // TFunctionReflect4_4
137#else
138 // Work around D3D driver bug in Win.
139 false, // TFunctionAtan1_1
140 true, // TFunctionAtan2_2
141 true, // TFunctionAtan3_3
142 true, // TFunctionAtan4_4
143 false, // TFunctionCos1
144 false, // TFunctionCos2
145 false, // TFunctionCos3
146 false, // TFunctionCos4
147 false, // TFunctionDistance1_1
148 false, // TFunctionDistance2_2
149 false, // TFunctionDistance3_3
150 false, // TFunctionDistance4_4
151 false, // TFunctionDot1_1
152 false, // TFunctionDot2_2
153 false, // TFunctionDot3_3
154 false, // TFunctionDot4_4
155 false, // TFunctionLength1
156 false, // TFunctionLength2
157 false, // TFunctionLength3
158 false, // TFunctionLength4
159 false, // TFunctionMod1_1
160 true, // TFunctionMod2_2
161 true, // TFunctionMod3_3
162 true, // TFunctionMod4_4
163 false, // TFunctionNormalize1
164 false, // TFunctionNormalize2
165 false, // TFunctionNormalize3
166 false, // TFunctionNormalize4
167 false, // TFunctionReflect1_1
168 false, // TFunctionReflect2_2
169 false, // TFunctionReflect3_3
170 false, // TFunctionReflect4_4
171#endif
172 false // TFunctionUnknown
173};
zmo@google.comf420c422011-09-12 18:27:59 +0000174
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000175const bool kFunctionEmulationFragmentMask[] = {
176 false, // TFunctionAtan1_1
177 false, // TFunctionAtan2_2
178 false, // TFunctionAtan3_3
179 false, // TFunctionAtan4_4
zmo@google.comf420c422011-09-12 18:27:59 +0000180#if defined(__APPLE__)
181 // Work around a ATI driver bug in Mac that causes crashes.
182 true, // TFunctionCos1
183 true, // TFunctionCos2
184 true, // TFunctionCos3
185 true, // TFunctionCos4
186#else
187 false, // TFunctionCos1
188 false, // TFunctionCos2
189 false, // TFunctionCos3
190 false, // TFunctionCos4
191#endif
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000192 false, // TFunctionDistance1_1
193 false, // TFunctionDistance2_2
194 false, // TFunctionDistance3_3
195 false, // TFunctionDistance4_4
196 false, // TFunctionDot1_1
197 false, // TFunctionDot2_2
198 false, // TFunctionDot3_3
199 false, // TFunctionDot4_4
200 false, // TFunctionLength1
201 false, // TFunctionLength2
202 false, // TFunctionLength3
203 false, // TFunctionLength4
zmo@google.comf420c422011-09-12 18:27:59 +0000204 false, // TFunctionMod1_1
205 false, // TFunctionMod2_2
206 false, // TFunctionMod3_3
207 false, // TFunctionMod4_4
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000208 false, // TFunctionNormalize1
209 false, // TFunctionNormalize2
210 false, // TFunctionNormalize3
211 false, // TFunctionNormalize4
212 false, // TFunctionReflect1_1
213 false, // TFunctionReflect2_2
214 false, // TFunctionReflect3_3
215 false, // TFunctionReflect4_4
zmo@google.comf420c422011-09-12 18:27:59 +0000216 false // TFunctionUnknown
217};
218
zmo@google.com32e97312011-08-24 01:03:11 +0000219class BuiltInFunctionEmulationMarker : public TIntermTraverser {
220public:
221 BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator& emulator)
222 : mEmulator(emulator)
223 {
224 }
225
226 virtual bool visitUnary(Visit visit, TIntermUnary* node)
227 {
228 if (visit == PreVisit) {
229 bool needToEmulate = mEmulator.SetFunctionCalled(
230 node->getOp(), node->getOperand()->getType());
231 if (needToEmulate)
232 node->setUseEmulatedFunction();
233 }
234 return true;
235 }
236
zmo@google.comf420c422011-09-12 18:27:59 +0000237 virtual bool visitAggregate(Visit visit, TIntermAggregate* node)
238 {
239 if (visit == PreVisit) {
240 // Here we handle all the built-in functions instead of the ones we
241 // currently identified as problematic.
242 switch (node->getOp()) {
243 case EOpLessThan:
244 case EOpGreaterThan:
245 case EOpLessThanEqual:
246 case EOpGreaterThanEqual:
247 case EOpVectorEqual:
248 case EOpVectorNotEqual:
249 case EOpMod:
250 case EOpPow:
251 case EOpAtan:
252 case EOpMin:
253 case EOpMax:
254 case EOpClamp:
255 case EOpMix:
256 case EOpStep:
257 case EOpSmoothStep:
258 case EOpDistance:
259 case EOpDot:
260 case EOpCross:
261 case EOpFaceForward:
262 case EOpReflect:
263 case EOpRefract:
264 case EOpMul:
265 break;
266 default:
267 return true;
268 };
269 const TIntermSequence& sequence = node->getSequence();
270 // Right now we only handle built-in functions with two parameters.
271 if (sequence.size() != 2)
272 return true;
273 TIntermTyped* param1 = sequence[0]->getAsTyped();
274 TIntermTyped* param2 = sequence[1]->getAsTyped();
275 if (!param1 || !param2)
276 return true;
277 bool needToEmulate = mEmulator.SetFunctionCalled(
278 node->getOp(), param1->getType(), param2->getType());
279 if (needToEmulate)
280 node->setUseEmulatedFunction();
281 }
282 return true;
283 }
284
zmo@google.com32e97312011-08-24 01:03:11 +0000285private:
286 BuiltInFunctionEmulator& mEmulator;
287};
288
289} // anonymous namepsace
290
zmo@google.comf420c422011-09-12 18:27:59 +0000291BuiltInFunctionEmulator::BuiltInFunctionEmulator(ShShaderType shaderType)
zmo@google.com32e97312011-08-24 01:03:11 +0000292{
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000293 if (shaderType == SH_FRAGMENT_SHADER) {
zmo@google.comf420c422011-09-12 18:27:59 +0000294 mFunctionMask = kFunctionEmulationFragmentMask;
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000295 mFunctionSource = kFunctionEmulationFragmentSource;
296 } else {
zmo@google.comf420c422011-09-12 18:27:59 +0000297 mFunctionMask = kFunctionEmulationVertexMask;
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000298 mFunctionSource = kFunctionEmulationVertexSource;
299 }
zmo@google.com32e97312011-08-24 01:03:11 +0000300}
301
302bool BuiltInFunctionEmulator::SetFunctionCalled(
zmo@google.comf420c422011-09-12 18:27:59 +0000303 TOperator op, const TType& param)
zmo@google.com32e97312011-08-24 01:03:11 +0000304{
zmo@google.comf420c422011-09-12 18:27:59 +0000305 TBuiltInFunction function = IdentifyFunction(op, param);
306 return SetFunctionCalled(function);
307}
308
309bool BuiltInFunctionEmulator::SetFunctionCalled(
310 TOperator op, const TType& param1, const TType& param2)
311{
312 TBuiltInFunction function = IdentifyFunction(op, param1, param2);
313 return SetFunctionCalled(function);
314}
315
316bool BuiltInFunctionEmulator::SetFunctionCalled(
317 BuiltInFunctionEmulator::TBuiltInFunction function) {
318 if (function == TFunctionUnknown || mFunctionMask[function] == false)
zmo@google.com32e97312011-08-24 01:03:11 +0000319 return false;
320 for (size_t i = 0; i < mFunctions.size(); ++i) {
321 if (mFunctions[i] == function)
322 return true;
323 }
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000324 mFunctions.push_back(function);
325 return true;
zmo@google.com32e97312011-08-24 01:03:11 +0000326}
327
328void BuiltInFunctionEmulator::OutputEmulatedFunctionDefinition(
329 TInfoSinkBase& out, bool withPrecision) const
330{
331 if (mFunctions.size() == 0)
332 return;
333 out << "// BEGIN: Generated code for built-in function emulation\n\n";
334 if (withPrecision) {
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000335 out << "#if defined(GL_FRAGMENT_PRECISION_HIGH)\n"
zmo@google.com93563fc2011-12-13 18:18:04 +0000336 << "#define webgl_emu_precision highp\n"
zmo@google.com32e97312011-08-24 01:03:11 +0000337 << "#else\n"
zmo@google.com93563fc2011-12-13 18:18:04 +0000338 << "#define webgl_emu_precision mediump\n"
zmo@google.com32e97312011-08-24 01:03:11 +0000339 << "#endif\n\n";
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000340 } else {
zmo@google.com93563fc2011-12-13 18:18:04 +0000341 out << "#define webgl_emu_precision\n\n";
zmo@google.com32e97312011-08-24 01:03:11 +0000342 }
343 for (size_t i = 0; i < mFunctions.size(); ++i) {
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000344 out << mFunctionSource[mFunctions[i]] << "\n\n";
zmo@google.com32e97312011-08-24 01:03:11 +0000345 }
346 out << "// END: Generated code for built-in function emulation\n\n";
347}
348
349BuiltInFunctionEmulator::TBuiltInFunction
zmo@google.comf420c422011-09-12 18:27:59 +0000350BuiltInFunctionEmulator::IdentifyFunction(
351 TOperator op, const TType& param)
zmo@google.com32e97312011-08-24 01:03:11 +0000352{
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000353 if (param.getNominalSize() > 4)
354 return TFunctionUnknown;
zmo@google.com32e97312011-08-24 01:03:11 +0000355 unsigned int function = TFunctionUnknown;
zmo@google.comf420c422011-09-12 18:27:59 +0000356 switch (op) {
zmo@google.comf420c422011-09-12 18:27:59 +0000357 case EOpCos:
358 function = TFunctionCos1;
359 break;
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000360 case EOpLength:
361 function = TFunctionLength1;
362 break;
363 case EOpNormalize:
364 function = TFunctionNormalize1;
zmo@google.comf420c422011-09-12 18:27:59 +0000365 break;
366 default:
367 break;
368 }
369 if (function == TFunctionUnknown)
370 return TFunctionUnknown;
371 if (param.isVector())
372 function += param.getNominalSize() - 1;
373 return static_cast<TBuiltInFunction>(function);
374}
zmo@google.com32e97312011-08-24 01:03:11 +0000375
zmo@google.comf420c422011-09-12 18:27:59 +0000376BuiltInFunctionEmulator::TBuiltInFunction
377BuiltInFunctionEmulator::IdentifyFunction(
378 TOperator op, const TType& param1, const TType& param2)
379{
380 // Right now for all the emulated functions with two parameters, the two
381 // parameters have the same type.
382 if (param1.isVector() != param2.isVector() ||
383 param1.getNominalSize() != param2.getNominalSize() ||
384 param1.getNominalSize() > 4)
385 return TFunctionUnknown;
386
387 unsigned int function = TFunctionUnknown;
388 switch (op) {
389 case EOpAtan:
390 function = TFunctionAtan1_1;
391 break;
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000392 case EOpDistance:
393 function = TFunctionDistance1_1;
394 break;
395 case EOpDot:
396 function = TFunctionDot1_1;
397 break;
zmo@google.comf420c422011-09-12 18:27:59 +0000398 case EOpMod:
399 function = TFunctionMod1_1;
400 break;
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000401 case EOpReflect:
402 function = TFunctionReflect1_1;
zmo@google.comf420c422011-09-12 18:27:59 +0000403 break;
404 default:
405 break;
406 }
407 if (function == TFunctionUnknown)
408 return TFunctionUnknown;
409 if (param1.isVector())
410 function += param1.getNominalSize() - 1;
zmo@google.com32e97312011-08-24 01:03:11 +0000411 return static_cast<TBuiltInFunction>(function);
412}
413
414void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation(
415 TIntermNode* root)
416{
417 ASSERT(root);
418
419 BuiltInFunctionEmulationMarker marker(*this);
420 root->traverse(&marker);
421}
422
zmo@google.coma3b4ab42011-09-16 00:53:26 +0000423void BuiltInFunctionEmulator::Cleanup()
424{
425 mFunctions.clear();
426}
427
zmo@google.com32e97312011-08-24 01:03:11 +0000428//static
429TString BuiltInFunctionEmulator::GetEmulatedFunctionName(
430 const TString& name)
431{
432 ASSERT(name[name.length() - 1] == '(');
433 return "webgl_" + name.substr(0, name.length() - 1) + "_emu(";
434}
435