blob: f44a0446188c033a16ebdc1949a9a192bab8606e [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
13const char* kFunctionEmulationSource[] = {
zmo@google.com32e97312011-08-24 01:03:11 +000014 "float webgl_abs_emu(float a) { float rt = abs(a); if (rt < 0.0) rt = 0.0; return rt; }",
15 "vec2 webgl_abs_emu(vec2 a) { vec2 rt = abs(a); if (rt[0] < 0.0) rt[0] = 0.0; return rt; }",
16 "vec3 webgl_abs_emu(vec3 a) { vec3 rt = abs(a); if (rt[0] < 0.0) rt[0] = 0.0; return rt; }",
17 "vec4 webgl_abs_emu(vec4 a) { vec4 rt = abs(a); if (rt[0] < 0.0) rt[0] = 0.0; return rt; }",
zmo@google.comf420c422011-09-12 18:27:59 +000018
19 "float webgl_atan_emu(float y, float x) { float rt = atan(y, x); if (rt > 2.0) rt = 0.0; return rt; }",
20 "vec2 webgl_atan_emu(vec2 y, vec2 x) { vec2 rt = atan(y, x); if (rt[0] > 2.0) rt[0] = 0.0; return rt; }",
21 "vec3 webgl_atan_emu(vec3 y, vec3 x) { vec3 rt = atan(y, x); if (rt[0] > 2.0) rt[0] = 0.0; return rt; }",
22 "vec4 webgl_atan_emu(vec4 y, vec4 x) { vec4 rt = atan(y, x); if (rt[0] > 2.0) rt[0] = 0.0; return rt; }",
23 "float webgl_atan_emu(float y_over_x) { float rt = atan(y_over_x); if (rt > 2.0) rt = 0.0; return rt; }",
24 "vec2 webgl_atan_emu(vec2 y_over_x) { vec2 rt = atan(y_over_x); if (rt[0] > 2.0) rt[0] = 0.0; return rt; }",
25 "vec3 webgl_atan_emu(vec3 y_over_x) { vec3 rt = atan(y_over_x); if (rt[0] > 2.0) rt[0] = 0.0; return rt; }",
26 "vec4 webgl_atan_emu(vec4 y_over_x) { vec4 rt = atan(y_over_x); if (rt[0] > 2.0) rt[0] = 0.0; return rt; }",
27
28 "float webgl_cos_emu(float a) { return cos(a); }",
29 "vec2 webgl_cos_emu(vec2 a) { return cos(a); }",
30 "vec3 webgl_cos_emu(vec3 a) { return cos(a); }",
31 "vec4 webgl_cos_emu(vec4 a) { return cos(a); }",
32
33 "float webgl_mod_emu(float x, float y) { float rt = mod(x, y); if (rt > x) rt = 0.0; return rt; }",
34 "vec2 webgl_mod_emu(vec2 x, vec2 y) { vec2 rt = mod(x, y); if (rt[0] > x[0]) rt[0] = 0.0; return rt; }",
35 "vec3 webgl_mod_emu(vec3 x, vec3 y) { vec3 rt = mod(x, y); if (rt[0] > x[0]) rt[0] = 0.0; return rt; }",
36 "vec4 webgl_mod_emu(vec4 x, vec4 y) { vec4 rt = mod(x, y); if (rt[0] > x[0]) rt[0] = 0.0; return rt; }",
37
zmo@google.com32e97312011-08-24 01:03:11 +000038 "float webgl_sign_emu(float a) { float rt = sign(a); if (rt > 1.0) rt = 1.0; return rt; }",
39 "vec2 webgl_sign_emu(vec2 a) { float rt = sign(a); if (rt[0] > 1.0) rt[0] = 1.0; return rt; }",
40 "vec3 webgl_sign_emu(vec3 a) { float rt = sign(a); if (rt[0] > 1.0) rt[0] = 1.0; return rt; }",
41 "vec4 webgl_sign_emu(vec4 a) { float rt = sign(a); if (rt[0] > 1.0) rt[0] = 1.0; return rt; }",
42};
43
zmo@google.comf420c422011-09-12 18:27:59 +000044const bool kFunctionEmulationVertexMask[] = {
45 true, // TFunctionAbs1
46 false, // TFunctionAbs2
47 false, // TFunctionAbs3
48 false, // TFunctionAbs4
49
50 true, // TFunctionAtan1
51 false, // TFunctionAtan2
52 false, // TFunctionAtan3
53 false, // TFunctionAtan4
54 false, // TFunctionAtan1_1
55 true, // TFunctionAtan2_2
56 true, // TFunctionAtan3_3
57 true, // TFunctionAtan4_4
58
59 false, // TFunctionCos1
60 false, // TFunctionCos2
61 false, // TFunctionCos3
62 false, // TFunctionCos4
63
64 false, // TFunctionMod1_1
65 true, // TFunctionMod2_2
66 true, // TFunctionMod3_3
67 true, // TFunctionMod4_4
68
69 true, // TFunctionSign1
70 false, // TFunctionSign2
71 false, // TFunctionSign3
72 false, // TFunctionSign4
73
74 false // TFunctionUnknown
75};
76
77const bool kFunctionEmulationFragmentMask[] = {
78 false, // TFunctionAbs1
79 false, // TFunctionAbs2
80 false, // TFunctionAbs3
81 false, // TFunctionAbs4
82
83 false, // TFunctionAtan1
84 false, // TFunctionAtan2
85 false, // TFunctionAtan3
86 false, // TFunctionAtan4
87 false, // TFunctionAtan1_1
88 false, // TFunctionAtan2_2
89 false, // TFunctionAtan3_3
90 false, // TFunctionAtan4_4
91
92#if defined(__APPLE__)
93 // Work around a ATI driver bug in Mac that causes crashes.
94 true, // TFunctionCos1
95 true, // TFunctionCos2
96 true, // TFunctionCos3
97 true, // TFunctionCos4
98#else
99 false, // TFunctionCos1
100 false, // TFunctionCos2
101 false, // TFunctionCos3
102 false, // TFunctionCos4
103#endif
104
105 false, // TFunctionMod1_1
106 false, // TFunctionMod2_2
107 false, // TFunctionMod3_3
108 false, // TFunctionMod4_4
109
110 false, // TFunctionSign1
111 false, // TFunctionSign2
112 false, // TFunctionSign3
113 false, // TFunctionSign4
114
115 false // TFunctionUnknown
116};
117
zmo@google.com32e97312011-08-24 01:03:11 +0000118class BuiltInFunctionEmulationMarker : public TIntermTraverser {
119public:
120 BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator& emulator)
121 : mEmulator(emulator)
122 {
123 }
124
125 virtual bool visitUnary(Visit visit, TIntermUnary* node)
126 {
127 if (visit == PreVisit) {
128 bool needToEmulate = mEmulator.SetFunctionCalled(
129 node->getOp(), node->getOperand()->getType());
130 if (needToEmulate)
131 node->setUseEmulatedFunction();
132 }
133 return true;
134 }
135
zmo@google.comf420c422011-09-12 18:27:59 +0000136 virtual bool visitAggregate(Visit visit, TIntermAggregate* node)
137 {
138 if (visit == PreVisit) {
139 // Here we handle all the built-in functions instead of the ones we
140 // currently identified as problematic.
141 switch (node->getOp()) {
142 case EOpLessThan:
143 case EOpGreaterThan:
144 case EOpLessThanEqual:
145 case EOpGreaterThanEqual:
146 case EOpVectorEqual:
147 case EOpVectorNotEqual:
148 case EOpMod:
149 case EOpPow:
150 case EOpAtan:
151 case EOpMin:
152 case EOpMax:
153 case EOpClamp:
154 case EOpMix:
155 case EOpStep:
156 case EOpSmoothStep:
157 case EOpDistance:
158 case EOpDot:
159 case EOpCross:
160 case EOpFaceForward:
161 case EOpReflect:
162 case EOpRefract:
163 case EOpMul:
164 break;
165 default:
166 return true;
167 };
168 const TIntermSequence& sequence = node->getSequence();
169 // Right now we only handle built-in functions with two parameters.
170 if (sequence.size() != 2)
171 return true;
172 TIntermTyped* param1 = sequence[0]->getAsTyped();
173 TIntermTyped* param2 = sequence[1]->getAsTyped();
174 if (!param1 || !param2)
175 return true;
176 bool needToEmulate = mEmulator.SetFunctionCalled(
177 node->getOp(), param1->getType(), param2->getType());
178 if (needToEmulate)
179 node->setUseEmulatedFunction();
180 }
181 return true;
182 }
183
zmo@google.com32e97312011-08-24 01:03:11 +0000184private:
185 BuiltInFunctionEmulator& mEmulator;
186};
187
188} // anonymous namepsace
189
zmo@google.comf420c422011-09-12 18:27:59 +0000190BuiltInFunctionEmulator::BuiltInFunctionEmulator(ShShaderType shaderType)
zmo@google.com32e97312011-08-24 01:03:11 +0000191 : mFunctionGroupMask(TFunctionGroupAll)
192{
zmo@google.comf420c422011-09-12 18:27:59 +0000193 if (shaderType == SH_FRAGMENT_SHADER)
194 mFunctionMask = kFunctionEmulationFragmentMask;
195 else
196 mFunctionMask = kFunctionEmulationVertexMask;
zmo@google.com32e97312011-08-24 01:03:11 +0000197}
198
199void BuiltInFunctionEmulator::SetFunctionGroupMask(
200 unsigned int functionGroupMask)
201{
202 mFunctionGroupMask = functionGroupMask;
203}
204
205bool BuiltInFunctionEmulator::SetFunctionCalled(
zmo@google.comf420c422011-09-12 18:27:59 +0000206 TOperator op, const TType& param)
zmo@google.com32e97312011-08-24 01:03:11 +0000207{
zmo@google.comf420c422011-09-12 18:27:59 +0000208 TBuiltInFunction function = IdentifyFunction(op, param);
209 return SetFunctionCalled(function);
210}
211
212bool BuiltInFunctionEmulator::SetFunctionCalled(
213 TOperator op, const TType& param1, const TType& param2)
214{
215 TBuiltInFunction function = IdentifyFunction(op, param1, param2);
216 return SetFunctionCalled(function);
217}
218
219bool BuiltInFunctionEmulator::SetFunctionCalled(
220 BuiltInFunctionEmulator::TBuiltInFunction function) {
221 if (function == TFunctionUnknown || mFunctionMask[function] == false)
zmo@google.com32e97312011-08-24 01:03:11 +0000222 return false;
223 for (size_t i = 0; i < mFunctions.size(); ++i) {
224 if (mFunctions[i] == function)
225 return true;
226 }
227 switch (function) {
zmo@google.com32e97312011-08-24 01:03:11 +0000228 case TFunctionAbs1:
229 case TFunctionAbs2:
230 case TFunctionAbs3:
231 case TFunctionAbs4:
232 if (mFunctionGroupMask & TFunctionGroupAbs) {
233 mFunctions.push_back(function);
234 return true;
235 }
236 break;
zmo@google.comf420c422011-09-12 18:27:59 +0000237 case TFunctionAtan1:
238 case TFunctionAtan2:
239 case TFunctionAtan3:
240 case TFunctionAtan4:
241 case TFunctionAtan1_1:
242 case TFunctionAtan2_2:
243 case TFunctionAtan3_3:
244 case TFunctionAtan4_4:
245 if (mFunctionGroupMask & TFunctionGroupAtan) {
246 mFunctions.push_back(function);
247 return true;
248 }
249 break;
250 case TFunctionCos1:
251 case TFunctionCos2:
252 case TFunctionCos3:
253 case TFunctionCos4:
254 if (mFunctionGroupMask & TFunctionGroupCos) {
255 mFunctions.push_back(function);
256 return true;
257 }
258 break;
259 case TFunctionMod1_1:
260 case TFunctionMod2_2:
261 case TFunctionMod3_3:
262 case TFunctionMod4_4:
263 if (mFunctionGroupMask & TFunctionGroupMod) {
264 mFunctions.push_back(function);
265 return true;
266 }
267 break;
zmo@google.com32e97312011-08-24 01:03:11 +0000268 case TFunctionSign1:
269 case TFunctionSign2:
270 case TFunctionSign3:
271 case TFunctionSign4:
272 if (mFunctionGroupMask & TFunctionGroupSign) {
273 mFunctions.push_back(function);
274 return true;
275 }
276 break;
277 default:
278 UNREACHABLE();
279 break;
280 }
281 return false;
282}
283
284void BuiltInFunctionEmulator::OutputEmulatedFunctionDefinition(
285 TInfoSinkBase& out, bool withPrecision) const
286{
287 if (mFunctions.size() == 0)
288 return;
289 out << "// BEGIN: Generated code for built-in function emulation\n\n";
290 if (withPrecision) {
291 out << "#if defined(GL_FRAGMENT_PRECISION_HIGH) && (GL_FRAGMENT_PRECISION_HIGH == 1)\n"
292 << "precision highp float;\n"
293 << "#else\n"
294 << "precision mediump float;\n"
295 << "#endif\n\n";
296 }
297 for (size_t i = 0; i < mFunctions.size(); ++i) {
298 out << kFunctionEmulationSource[mFunctions[i]] << "\n\n";
299 }
300 out << "// END: Generated code for built-in function emulation\n\n";
301}
302
303BuiltInFunctionEmulator::TBuiltInFunction
zmo@google.comf420c422011-09-12 18:27:59 +0000304BuiltInFunctionEmulator::IdentifyFunction(
305 TOperator op, const TType& param)
zmo@google.com32e97312011-08-24 01:03:11 +0000306{
307 unsigned int function = TFunctionUnknown;
zmo@google.comf420c422011-09-12 18:27:59 +0000308 switch (op) {
309 case EOpAbs:
310 function = TFunctionAbs1;
311 break;
312 case EOpAtan:
313 function = TFunctionAtan1;
314 break;
315 case EOpCos:
316 function = TFunctionCos1;
317 break;
318 case EOpSign:
319 function = TFunctionSign1;
320 break;
321 default:
322 break;
323 }
324 if (function == TFunctionUnknown)
325 return TFunctionUnknown;
326 if (param.isVector())
327 function += param.getNominalSize() - 1;
328 return static_cast<TBuiltInFunction>(function);
329}
zmo@google.com32e97312011-08-24 01:03:11 +0000330
zmo@google.comf420c422011-09-12 18:27:59 +0000331BuiltInFunctionEmulator::TBuiltInFunction
332BuiltInFunctionEmulator::IdentifyFunction(
333 TOperator op, const TType& param1, const TType& param2)
334{
335 // Right now for all the emulated functions with two parameters, the two
336 // parameters have the same type.
337 if (param1.isVector() != param2.isVector() ||
338 param1.getNominalSize() != param2.getNominalSize() ||
339 param1.getNominalSize() > 4)
340 return TFunctionUnknown;
341
342 unsigned int function = TFunctionUnknown;
343 switch (op) {
344 case EOpAtan:
345 function = TFunctionAtan1_1;
346 break;
347 case EOpMod:
348 function = TFunctionMod1_1;
349 break;
350 case EOpSign:
351 function = TFunctionSign1;
352 break;
353 default:
354 break;
355 }
356 if (function == TFunctionUnknown)
357 return TFunctionUnknown;
358 if (param1.isVector())
359 function += param1.getNominalSize() - 1;
zmo@google.com32e97312011-08-24 01:03:11 +0000360 return static_cast<TBuiltInFunction>(function);
361}
362
363void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation(
364 TIntermNode* root)
365{
366 ASSERT(root);
367
368 BuiltInFunctionEmulationMarker marker(*this);
369 root->traverse(&marker);
370}
371
372//static
373TString BuiltInFunctionEmulator::GetEmulatedFunctionName(
374 const TString& name)
375{
376 ASSERT(name[name.length() - 1] == '(');
377 return "webgl_" + name.substr(0, name.length() - 1) + "_emu(";
378}
379