|
1 // |
|
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 |
|
11 namespace { |
|
12 |
|
13 // 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. |
|
18 const char* kFunctionEmulationVertexSource[] = { |
|
19 "#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)", |
|
23 |
|
24 "#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)", |
|
28 |
|
29 "#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)", |
|
33 |
|
34 // |faceforward(N, I, Nref)| is |dot(NRef, I) < 0 ? N : -N| |
|
35 "#define webgl_faceforward_emu(N, I, Nref) (((Nref) * (I) < 0.0) ? (N) : -(N))", |
|
36 "#error no emulation for faceforward(vec2, vec2, vec2)", |
|
37 "#error no emulation for faceforward(vec3, vec3, vec3)", |
|
38 "#error no emulation for faceforward(vec4, vec4, vec4)", |
|
39 |
|
40 "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))", |
|
41 "#error no emulation for length(vec2)", |
|
42 "#error no emulation for length(vec3)", |
|
43 "#error no emulation for length(vec4)", |
|
44 |
|
45 "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))", |
|
46 "#error no emulation for normalize(vec2)", |
|
47 "#error no emulation for normalize(vec3)", |
|
48 "#error no emulation for normalize(vec4)", |
|
49 |
|
50 "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))", |
|
51 "#error no emulation for reflect(vec2, vec2)", |
|
52 "#error no emulation for reflect(vec3, vec3)", |
|
53 "#error no emulation for reflect(vec4, vec4)" |
|
54 }; |
|
55 |
|
56 const char* kFunctionEmulationFragmentSource[] = { |
|
57 "webgl_emu_precision float webgl_cos_emu(webgl_emu_precision float a) { return cos(a); }", |
|
58 "webgl_emu_precision vec2 webgl_cos_emu(webgl_emu_precision vec2 a) { return cos(a); }", |
|
59 "webgl_emu_precision vec3 webgl_cos_emu(webgl_emu_precision vec3 a) { return cos(a); }", |
|
60 "webgl_emu_precision vec4 webgl_cos_emu(webgl_emu_precision vec4 a) { return cos(a); }", |
|
61 |
|
62 "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))", |
|
63 "#error no emulation for distance(vec2, vec2)", |
|
64 "#error no emulation for distance(vec3, vec3)", |
|
65 "#error no emulation for distance(vec4, vec4)", |
|
66 |
|
67 "#define webgl_dot_emu(x, y) ((x) * (y))", |
|
68 "#error no emulation for dot(vec2, vec2)", |
|
69 "#error no emulation for dot(vec3, vec3)", |
|
70 "#error no emulation for dot(vec4, vec4)", |
|
71 |
|
72 // |faceforward(N, I, Nref)| is |dot(NRef, I) < 0 ? N : -N| |
|
73 "#define webgl_faceforward_emu(N, I, Nref) (((Nref) * (I) < 0.0) ? (N) : -(N))", |
|
74 "#error no emulation for faceforward(vec2, vec2, vec2)", |
|
75 "#error no emulation for faceforward(vec3, vec3, vec3)", |
|
76 "#error no emulation for faceforward(vec4, vec4, vec4)", |
|
77 |
|
78 "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))", |
|
79 "#error no emulation for length(vec2)", |
|
80 "#error no emulation for length(vec3)", |
|
81 "#error no emulation for length(vec4)", |
|
82 |
|
83 "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))", |
|
84 "#error no emulation for normalize(vec2)", |
|
85 "#error no emulation for normalize(vec3)", |
|
86 "#error no emulation for normalize(vec4)", |
|
87 |
|
88 "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))", |
|
89 "#error no emulation for reflect(vec2, vec2)", |
|
90 "#error no emulation for reflect(vec3, vec3)", |
|
91 "#error no emulation for reflect(vec4, vec4)" |
|
92 }; |
|
93 |
|
94 const bool kFunctionEmulationVertexMask[] = { |
|
95 #if defined(__APPLE__) |
|
96 // Work around ATI driver bugs in Mac. |
|
97 false, // TFunctionCos1 |
|
98 false, // TFunctionCos2 |
|
99 false, // TFunctionCos3 |
|
100 false, // TFunctionCos4 |
|
101 true, // TFunctionDistance1_1 |
|
102 false, // TFunctionDistance2_2 |
|
103 false, // TFunctionDistance3_3 |
|
104 false, // TFunctionDistance4_4 |
|
105 true, // TFunctionDot1_1 |
|
106 false, // TFunctionDot2_2 |
|
107 false, // TFunctionDot3_3 |
|
108 false, // TFunctionDot4_4 |
|
109 true, // TFunctionFaceForward1_1_1 |
|
110 false, // TFunctionFaceForward2_2_2 |
|
111 false, // TFunctionFaceForward3_3_3 |
|
112 false, // TFunctionFaceForward4_4_4 |
|
113 true, // TFunctionLength1 |
|
114 false, // TFunctionLength2 |
|
115 false, // TFunctionLength3 |
|
116 false, // TFunctionLength4 |
|
117 true, // TFunctionNormalize1 |
|
118 false, // TFunctionNormalize2 |
|
119 false, // TFunctionNormalize3 |
|
120 false, // TFunctionNormalize4 |
|
121 true, // TFunctionReflect1_1 |
|
122 false, // TFunctionReflect2_2 |
|
123 false, // TFunctionReflect3_3 |
|
124 false, // TFunctionReflect4_4 |
|
125 #else |
|
126 // Work around D3D driver bug in Win. |
|
127 false, // TFunctionCos1 |
|
128 false, // TFunctionCos2 |
|
129 false, // TFunctionCos3 |
|
130 false, // TFunctionCos4 |
|
131 false, // TFunctionDistance1_1 |
|
132 false, // TFunctionDistance2_2 |
|
133 false, // TFunctionDistance3_3 |
|
134 false, // TFunctionDistance4_4 |
|
135 false, // TFunctionDot1_1 |
|
136 false, // TFunctionDot2_2 |
|
137 false, // TFunctionDot3_3 |
|
138 false, // TFunctionDot4_4 |
|
139 false, // TFunctionFaceForward1_1_1 |
|
140 false, // TFunctionFaceForward2_2_2 |
|
141 false, // TFunctionFaceForward3_3_3 |
|
142 false, // TFunctionFaceForward4_4_4 |
|
143 false, // TFunctionLength1 |
|
144 false, // TFunctionLength2 |
|
145 false, // TFunctionLength3 |
|
146 false, // TFunctionLength4 |
|
147 false, // TFunctionNormalize1 |
|
148 false, // TFunctionNormalize2 |
|
149 false, // TFunctionNormalize3 |
|
150 false, // TFunctionNormalize4 |
|
151 false, // TFunctionReflect1_1 |
|
152 false, // TFunctionReflect2_2 |
|
153 false, // TFunctionReflect3_3 |
|
154 false, // TFunctionReflect4_4 |
|
155 #endif |
|
156 false // TFunctionUnknown |
|
157 }; |
|
158 |
|
159 const bool kFunctionEmulationFragmentMask[] = { |
|
160 #if defined(__APPLE__) |
|
161 // Work around ATI driver bugs in Mac. |
|
162 true, // TFunctionCos1 |
|
163 true, // TFunctionCos2 |
|
164 true, // TFunctionCos3 |
|
165 true, // TFunctionCos4 |
|
166 true, // TFunctionDistance1_1 |
|
167 false, // TFunctionDistance2_2 |
|
168 false, // TFunctionDistance3_3 |
|
169 false, // TFunctionDistance4_4 |
|
170 true, // TFunctionDot1_1 |
|
171 false, // TFunctionDot2_2 |
|
172 false, // TFunctionDot3_3 |
|
173 false, // TFunctionDot4_4 |
|
174 true, // TFunctionFaceForward1_1_1 |
|
175 false, // TFunctionFaceForward2_2_2 |
|
176 false, // TFunctionFaceForward3_3_3 |
|
177 false, // TFunctionFaceForward4_4_4 |
|
178 true, // TFunctionLength1 |
|
179 false, // TFunctionLength2 |
|
180 false, // TFunctionLength3 |
|
181 false, // TFunctionLength4 |
|
182 true, // TFunctionNormalize1 |
|
183 false, // TFunctionNormalize2 |
|
184 false, // TFunctionNormalize3 |
|
185 false, // TFunctionNormalize4 |
|
186 true, // TFunctionReflect1_1 |
|
187 false, // TFunctionReflect2_2 |
|
188 false, // TFunctionReflect3_3 |
|
189 false, // TFunctionReflect4_4 |
|
190 #else |
|
191 // Work around D3D driver bug in Win. |
|
192 false, // TFunctionCos1 |
|
193 false, // TFunctionCos2 |
|
194 false, // TFunctionCos3 |
|
195 false, // TFunctionCos4 |
|
196 false, // TFunctionDistance1_1 |
|
197 false, // TFunctionDistance2_2 |
|
198 false, // TFunctionDistance3_3 |
|
199 false, // TFunctionDistance4_4 |
|
200 false, // TFunctionDot1_1 |
|
201 false, // TFunctionDot2_2 |
|
202 false, // TFunctionDot3_3 |
|
203 false, // TFunctionDot4_4 |
|
204 false, // TFunctionFaceForward1_1_1 |
|
205 false, // TFunctionFaceForward2_2_2 |
|
206 false, // TFunctionFaceForward3_3_3 |
|
207 false, // TFunctionFaceForward4_4_4 |
|
208 false, // TFunctionLength1 |
|
209 false, // TFunctionLength2 |
|
210 false, // TFunctionLength3 |
|
211 false, // TFunctionLength4 |
|
212 false, // TFunctionNormalize1 |
|
213 false, // TFunctionNormalize2 |
|
214 false, // TFunctionNormalize3 |
|
215 false, // TFunctionNormalize4 |
|
216 false, // TFunctionReflect1_1 |
|
217 false, // TFunctionReflect2_2 |
|
218 false, // TFunctionReflect3_3 |
|
219 false, // TFunctionReflect4_4 |
|
220 #endif |
|
221 false // TFunctionUnknown |
|
222 }; |
|
223 |
|
224 class BuiltInFunctionEmulationMarker : public TIntermTraverser { |
|
225 public: |
|
226 BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator& emulator) |
|
227 : mEmulator(emulator) |
|
228 { |
|
229 } |
|
230 |
|
231 virtual bool visitUnary(Visit visit, TIntermUnary* node) |
|
232 { |
|
233 if (visit == PreVisit) { |
|
234 bool needToEmulate = mEmulator.SetFunctionCalled( |
|
235 node->getOp(), node->getOperand()->getType()); |
|
236 if (needToEmulate) |
|
237 node->setUseEmulatedFunction(); |
|
238 } |
|
239 return true; |
|
240 } |
|
241 |
|
242 virtual bool visitAggregate(Visit visit, TIntermAggregate* node) |
|
243 { |
|
244 if (visit == PreVisit) { |
|
245 // Here we handle all the built-in functions instead of the ones we |
|
246 // currently identified as problematic. |
|
247 switch (node->getOp()) { |
|
248 case EOpLessThan: |
|
249 case EOpGreaterThan: |
|
250 case EOpLessThanEqual: |
|
251 case EOpGreaterThanEqual: |
|
252 case EOpVectorEqual: |
|
253 case EOpVectorNotEqual: |
|
254 case EOpMod: |
|
255 case EOpPow: |
|
256 case EOpAtan: |
|
257 case EOpMin: |
|
258 case EOpMax: |
|
259 case EOpClamp: |
|
260 case EOpMix: |
|
261 case EOpStep: |
|
262 case EOpSmoothStep: |
|
263 case EOpDistance: |
|
264 case EOpDot: |
|
265 case EOpCross: |
|
266 case EOpFaceForward: |
|
267 case EOpReflect: |
|
268 case EOpRefract: |
|
269 case EOpMul: |
|
270 break; |
|
271 default: |
|
272 return true; |
|
273 }; |
|
274 const TIntermSequence& sequence = node->getSequence(); |
|
275 bool needToEmulate = false; |
|
276 |
|
277 if (sequence.size() == 2) { |
|
278 TIntermTyped* param1 = sequence[0]->getAsTyped(); |
|
279 TIntermTyped* param2 = sequence[1]->getAsTyped(); |
|
280 if (!param1 || !param2) |
|
281 return true; |
|
282 needToEmulate = mEmulator.SetFunctionCalled( |
|
283 node->getOp(), param1->getType(), param2->getType()); |
|
284 } else if (sequence.size() == 3) { |
|
285 TIntermTyped* param1 = sequence[0]->getAsTyped(); |
|
286 TIntermTyped* param2 = sequence[1]->getAsTyped(); |
|
287 TIntermTyped* param3 = sequence[2]->getAsTyped(); |
|
288 if (!param1 || !param2 || !param3) |
|
289 return true; |
|
290 needToEmulate = mEmulator.SetFunctionCalled( |
|
291 node->getOp(), param1->getType(), param2->getType(), param3->getType()); |
|
292 } else { |
|
293 return true; |
|
294 } |
|
295 |
|
296 if (needToEmulate) |
|
297 node->setUseEmulatedFunction(); |
|
298 } |
|
299 return true; |
|
300 } |
|
301 |
|
302 private: |
|
303 BuiltInFunctionEmulator& mEmulator; |
|
304 }; |
|
305 |
|
306 } // anonymous namepsace |
|
307 |
|
308 BuiltInFunctionEmulator::BuiltInFunctionEmulator(ShShaderType shaderType) |
|
309 { |
|
310 if (shaderType == SH_FRAGMENT_SHADER) { |
|
311 mFunctionMask = kFunctionEmulationFragmentMask; |
|
312 mFunctionSource = kFunctionEmulationFragmentSource; |
|
313 } else { |
|
314 mFunctionMask = kFunctionEmulationVertexMask; |
|
315 mFunctionSource = kFunctionEmulationVertexSource; |
|
316 } |
|
317 } |
|
318 |
|
319 bool BuiltInFunctionEmulator::SetFunctionCalled( |
|
320 TOperator op, const TType& param) |
|
321 { |
|
322 TBuiltInFunction function = IdentifyFunction(op, param); |
|
323 return SetFunctionCalled(function); |
|
324 } |
|
325 |
|
326 bool BuiltInFunctionEmulator::SetFunctionCalled( |
|
327 TOperator op, const TType& param1, const TType& param2) |
|
328 { |
|
329 TBuiltInFunction function = IdentifyFunction(op, param1, param2); |
|
330 return SetFunctionCalled(function); |
|
331 } |
|
332 |
|
333 bool BuiltInFunctionEmulator::SetFunctionCalled( |
|
334 TOperator op, const TType& param1, const TType& param2, const TType& param3) |
|
335 { |
|
336 TBuiltInFunction function = IdentifyFunction(op, param1, param2, param3); |
|
337 return SetFunctionCalled(function); |
|
338 } |
|
339 |
|
340 bool BuiltInFunctionEmulator::SetFunctionCalled( |
|
341 BuiltInFunctionEmulator::TBuiltInFunction function) { |
|
342 if (function == TFunctionUnknown || mFunctionMask[function] == false) |
|
343 return false; |
|
344 for (size_t i = 0; i < mFunctions.size(); ++i) { |
|
345 if (mFunctions[i] == function) |
|
346 return true; |
|
347 } |
|
348 mFunctions.push_back(function); |
|
349 return true; |
|
350 } |
|
351 |
|
352 void BuiltInFunctionEmulator::OutputEmulatedFunctionDefinition( |
|
353 TInfoSinkBase& out, bool withPrecision) const |
|
354 { |
|
355 if (mFunctions.size() == 0) |
|
356 return; |
|
357 out << "// BEGIN: Generated code for built-in function emulation\n\n"; |
|
358 if (withPrecision) { |
|
359 out << "#if defined(GL_FRAGMENT_PRECISION_HIGH)\n" |
|
360 << "#define webgl_emu_precision highp\n" |
|
361 << "#else\n" |
|
362 << "#define webgl_emu_precision mediump\n" |
|
363 << "#endif\n\n"; |
|
364 } else { |
|
365 out << "#define webgl_emu_precision\n\n"; |
|
366 } |
|
367 for (size_t i = 0; i < mFunctions.size(); ++i) { |
|
368 out << mFunctionSource[mFunctions[i]] << "\n\n"; |
|
369 } |
|
370 out << "// END: Generated code for built-in function emulation\n\n"; |
|
371 } |
|
372 |
|
373 BuiltInFunctionEmulator::TBuiltInFunction |
|
374 BuiltInFunctionEmulator::IdentifyFunction( |
|
375 TOperator op, const TType& param) |
|
376 { |
|
377 if (param.getNominalSize() > 4) |
|
378 return TFunctionUnknown; |
|
379 unsigned int function = TFunctionUnknown; |
|
380 switch (op) { |
|
381 case EOpCos: |
|
382 function = TFunctionCos1; |
|
383 break; |
|
384 case EOpLength: |
|
385 function = TFunctionLength1; |
|
386 break; |
|
387 case EOpNormalize: |
|
388 function = TFunctionNormalize1; |
|
389 break; |
|
390 default: |
|
391 break; |
|
392 } |
|
393 if (function == TFunctionUnknown) |
|
394 return TFunctionUnknown; |
|
395 if (param.isVector()) |
|
396 function += param.getNominalSize() - 1; |
|
397 return static_cast<TBuiltInFunction>(function); |
|
398 } |
|
399 |
|
400 BuiltInFunctionEmulator::TBuiltInFunction |
|
401 BuiltInFunctionEmulator::IdentifyFunction( |
|
402 TOperator op, const TType& param1, const TType& param2) |
|
403 { |
|
404 // Right now for all the emulated functions with two parameters, the two |
|
405 // parameters have the same type. |
|
406 if (param1.isVector() != param2.isVector() || |
|
407 param1.getNominalSize() != param2.getNominalSize() || |
|
408 param1.getNominalSize() > 4) |
|
409 return TFunctionUnknown; |
|
410 |
|
411 unsigned int function = TFunctionUnknown; |
|
412 switch (op) { |
|
413 case EOpDistance: |
|
414 function = TFunctionDistance1_1; |
|
415 break; |
|
416 case EOpDot: |
|
417 function = TFunctionDot1_1; |
|
418 break; |
|
419 case EOpReflect: |
|
420 function = TFunctionReflect1_1; |
|
421 break; |
|
422 default: |
|
423 break; |
|
424 } |
|
425 if (function == TFunctionUnknown) |
|
426 return TFunctionUnknown; |
|
427 if (param1.isVector()) |
|
428 function += param1.getNominalSize() - 1; |
|
429 return static_cast<TBuiltInFunction>(function); |
|
430 } |
|
431 |
|
432 BuiltInFunctionEmulator::TBuiltInFunction |
|
433 BuiltInFunctionEmulator::IdentifyFunction( |
|
434 TOperator op, const TType& param1, const TType& param2, const TType& param3) |
|
435 { |
|
436 // Check that all params have the same type, length, |
|
437 // and that they're not too large. |
|
438 if (param1.isVector() != param2.isVector() || |
|
439 param2.isVector() != param3.isVector() || |
|
440 param1.getNominalSize() != param2.getNominalSize() || |
|
441 param2.getNominalSize() != param3.getNominalSize() || |
|
442 param1.getNominalSize() > 4) |
|
443 return TFunctionUnknown; |
|
444 |
|
445 unsigned int function = TFunctionUnknown; |
|
446 switch (op) { |
|
447 case EOpFaceForward: |
|
448 function = TFunctionFaceForward1_1_1; |
|
449 break; |
|
450 default: |
|
451 break; |
|
452 } |
|
453 if (function == TFunctionUnknown) |
|
454 return TFunctionUnknown; |
|
455 if (param1.isVector()) |
|
456 function += param1.getNominalSize() - 1; |
|
457 return static_cast<TBuiltInFunction>(function); |
|
458 } |
|
459 |
|
460 void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation( |
|
461 TIntermNode* root) |
|
462 { |
|
463 ASSERT(root); |
|
464 |
|
465 BuiltInFunctionEmulationMarker marker(*this); |
|
466 root->traverse(&marker); |
|
467 } |
|
468 |
|
469 void BuiltInFunctionEmulator::Cleanup() |
|
470 { |
|
471 mFunctions.clear(); |
|
472 } |
|
473 |
|
474 //static |
|
475 TString BuiltInFunctionEmulator::GetEmulatedFunctionName( |
|
476 const TString& name) |
|
477 { |
|
478 ASSERT(name[name.length() - 1] == '('); |
|
479 return "webgl_" + name.substr(0, name.length() - 1) + "_emu("; |
|
480 } |
|
481 |