blob: 8b7d9f8dd524a8c7bc563a5277e3ab61a83c83a5 [file] [log] [blame]
//
// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "angle_gl.h"
#include "compiler/translator/BuiltInFunctionEmulator.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/Cache.h"
namespace sh
{
class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTraverser
{
public:
BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator &emulator)
: TIntermTraverser(true, false, false), mEmulator(emulator)
{
}
bool visitUnary(Visit visit, TIntermUnary *node) override
{
if (visit == PreVisit)
{
bool needToEmulate =
mEmulator.setFunctionCalled(node->getOp(), node->getOperand()->getType());
if (needToEmulate)
node->setUseEmulatedFunction();
}
return true;
}
bool visitAggregate(Visit visit, TIntermAggregate *node) override
{
if (visit == PreVisit)
{
// Here we handle all the built-in functions mapped to ops, not just the ones that are
// currently identified as problematic.
if (node->isConstructor() || node->isFunctionCall())
{
return true;
}
const TIntermSequence &sequence = *(node->getSequence());
bool needToEmulate = false;
// Right now we only handle built-in functions with two or three parameters.
if (sequence.size() == 2)
{
TIntermTyped *param1 = sequence[0]->getAsTyped();
TIntermTyped *param2 = sequence[1]->getAsTyped();
if (!param1 || !param2)
return true;
needToEmulate = mEmulator.setFunctionCalled(node->getOp(), param1->getType(),
param2->getType());
}
else if (sequence.size() == 3)
{
TIntermTyped *param1 = sequence[0]->getAsTyped();
TIntermTyped *param2 = sequence[1]->getAsTyped();
TIntermTyped *param3 = sequence[2]->getAsTyped();
if (!param1 || !param2 || !param3)
return true;
needToEmulate = mEmulator.setFunctionCalled(node->getOp(), param1->getType(),
param2->getType(), param3->getType());
}
else if (sequence.size() == 4)
{
TIntermTyped *param1 = sequence[0]->getAsTyped();
TIntermTyped *param2 = sequence[1]->getAsTyped();
TIntermTyped *param3 = sequence[2]->getAsTyped();
TIntermTyped *param4 = sequence[3]->getAsTyped();
if (!param1 || !param2 || !param3 || !param4)
return true;
needToEmulate =
mEmulator.setFunctionCalled(node->getOp(), param1->getType(), param2->getType(),
param3->getType(), param4->getType());
}
else
{
return true;
}
if (needToEmulate)
node->setUseEmulatedFunction();
}
return true;
}
private:
BuiltInFunctionEmulator &mEmulator;
};
BuiltInFunctionEmulator::BuiltInFunctionEmulator()
{
}
BuiltInFunctionEmulator::FunctionId BuiltInFunctionEmulator::addEmulatedFunction(
TOperator op,
const TType *param,
const char *emulatedFunctionDefinition)
{
FunctionId id(op, param);
mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
return id;
}
BuiltInFunctionEmulator::FunctionId BuiltInFunctionEmulator::addEmulatedFunction(
TOperator op,
const TType *param1,
const TType *param2,
const char *emulatedFunctionDefinition)
{
FunctionId id(op, param1, param2);
mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
return id;
}
BuiltInFunctionEmulator::FunctionId BuiltInFunctionEmulator::addEmulatedFunctionWithDependency(
FunctionId dependency,
TOperator op,
const TType *param1,
const TType *param2,
const char *emulatedFunctionDefinition)
{
FunctionId id(op, param1, param2);
mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
mFunctionDependencies[id] = dependency;
return id;
}
BuiltInFunctionEmulator::FunctionId BuiltInFunctionEmulator::addEmulatedFunction(
TOperator op,
const TType *param1,
const TType *param2,
const TType *param3,
const char *emulatedFunctionDefinition)
{
FunctionId id(op, param1, param2, param3);
mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
return id;
}
BuiltInFunctionEmulator::FunctionId BuiltInFunctionEmulator::addEmulatedFunction(
TOperator op,
const TType *param1,
const TType *param2,
const TType *param3,
const TType *param4,
const char *emulatedFunctionDefinition)
{
FunctionId id(op, param1, param2, param3, param4);
mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
return id;
}
BuiltInFunctionEmulator::FunctionId BuiltInFunctionEmulator::addEmulatedFunctionWithDependency(
FunctionId dependency,
TOperator op,
const TType *param1,
const TType *param2,
const TType *param3,
const TType *param4,
const char *emulatedFunctionDefinition)
{
FunctionId id(op, param1, param2, param3, param4);
mEmulatedFunctions[id] = std::string(emulatedFunctionDefinition);
mFunctionDependencies[id] = dependency;
return id;
}
bool BuiltInFunctionEmulator::isOutputEmpty() const
{
return (mFunctions.size() == 0);
}
void BuiltInFunctionEmulator::outputEmulatedFunctions(TInfoSinkBase &out) const
{
for (size_t i = 0; i < mFunctions.size(); ++i)
{
out << mEmulatedFunctions.find(mFunctions[i])->second << "\n\n";
}
}
bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op, const TType &param)
{
return setFunctionCalled(FunctionId(op, &param));
}
bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op,
const TType &param1,
const TType &param2)
{
return setFunctionCalled(FunctionId(op, &param1, &param2));
}
bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op,
const TType &param1,
const TType &param2,
const TType &param3)
{
return setFunctionCalled(FunctionId(op, &param1, &param2, &param3));
}
bool BuiltInFunctionEmulator::setFunctionCalled(TOperator op,
const TType &param1,
const TType &param2,
const TType &param3,
const TType &param4)
{
return setFunctionCalled(FunctionId(op, &param1, &param2, &param3, &param4));
}
bool BuiltInFunctionEmulator::setFunctionCalled(const FunctionId &functionId)
{
if (mEmulatedFunctions.find(functionId) != mEmulatedFunctions.end())
{
for (size_t i = 0; i < mFunctions.size(); ++i)
{
if (mFunctions[i] == functionId)
return true;
}
// If the function depends on another, mark the dependency as called.
auto dependency = mFunctionDependencies.find(functionId);
if (dependency != mFunctionDependencies.end())
{
setFunctionCalled((*dependency).second);
}
// Copy the functionId if it needs to be stored, to make sure that the TType pointers inside
// remain valid and constant.
mFunctions.push_back(functionId.getCopy());
return true;
}
return false;
}
void BuiltInFunctionEmulator::markBuiltInFunctionsForEmulation(TIntermNode *root)
{
ASSERT(root);
if (mEmulatedFunctions.empty())
return;
BuiltInFunctionEmulationMarker marker(*this);
root->traverse(&marker);
}
void BuiltInFunctionEmulator::cleanup()
{
mFunctions.clear();
mFunctionDependencies.clear();
}
// static
void BuiltInFunctionEmulator::WriteEmulatedFunctionName(TInfoSinkBase &out, const char *name)
{
ASSERT(name[strlen(name) - 1] != '(');
out << "webgl_" << name << "_emu";
}
BuiltInFunctionEmulator::FunctionId::FunctionId()
: mOp(EOpNull),
mParam1(TCache::getType(EbtVoid)),
mParam2(TCache::getType(EbtVoid)),
mParam3(TCache::getType(EbtVoid)),
mParam4(TCache::getType(EbtVoid))
{
}
BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op, const TType *param)
: mOp(op),
mParam1(param),
mParam2(TCache::getType(EbtVoid)),
mParam3(TCache::getType(EbtVoid)),
mParam4(TCache::getType(EbtVoid))
{
}
BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op,
const TType *param1,
const TType *param2)
: mOp(op),
mParam1(param1),
mParam2(param2),
mParam3(TCache::getType(EbtVoid)),
mParam4(TCache::getType(EbtVoid))
{
}
BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op,
const TType *param1,
const TType *param2,
const TType *param3)
: mOp(op), mParam1(param1), mParam2(param2), mParam3(param3), mParam4(TCache::getType(EbtVoid))
{
}
BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op,
const TType *param1,
const TType *param2,
const TType *param3,
const TType *param4)
: mOp(op), mParam1(param1), mParam2(param2), mParam3(param3), mParam4(param4)
{
}
bool BuiltInFunctionEmulator::FunctionId::operator==(
const BuiltInFunctionEmulator::FunctionId &other) const
{
return (mOp == other.mOp && *mParam1 == *other.mParam1 && *mParam2 == *other.mParam2 &&
*mParam3 == *other.mParam3 && *mParam4 == *other.mParam4);
}
bool BuiltInFunctionEmulator::FunctionId::operator<(
const BuiltInFunctionEmulator::FunctionId &other) const
{
if (mOp != other.mOp)
return mOp < other.mOp;
if (*mParam1 != *other.mParam1)
return *mParam1 < *other.mParam1;
if (*mParam2 != *other.mParam2)
return *mParam2 < *other.mParam2;
if (*mParam3 != *other.mParam3)
return *mParam3 < *other.mParam3;
if (*mParam4 != *other.mParam4)
return *mParam4 < *other.mParam4;
return false; // all fields are equal
}
BuiltInFunctionEmulator::FunctionId BuiltInFunctionEmulator::FunctionId::getCopy() const
{
return FunctionId(mOp, new TType(*mParam1), new TType(*mParam2), new TType(*mParam3),
new TType(*mParam4));
}
} // namespace sh