gfx/angle/src/compiler/BuiltInFunctionEmulator.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial