HLSL default function parameters

This PR adds support for default function parameters in the following cases:

1. Simple constants, such as void fn(int x, float myparam = 3)
2. Expressions that can be const folded, such a ... myparam = sin(some_const)
3. Initializer lists that can be const folded, such as ... float2 myparam = {1,2}

New tests are added: hlsl.params.default.frag and hlsl.params.default.err.frag
(for testing error situations, such as ambiguity or non-const-foldable).

In order to avoid sampler method ambiguity, the hlsl better() lambda now
considers sampler matches.  Previously, all sampler types looked identical
since only the basic type of EbtSampler was considered.
This commit is contained in:
steve-lunarg 2016-12-23 18:56:57 -07:00
parent 807a0d9e2f
commit 26d3145334
15 changed files with 1263 additions and 24 deletions

View file

@ -629,6 +629,9 @@ TIntermTyped* TIntermConstantUnion::fold(TOperator op, const TType& returnType)
//
TIntermTyped* TIntermediate::fold(TIntermAggregate* aggrNode)
{
if (aggrNode == nullptr)
return aggrNode;
if (! areAllChildConst(aggrNode))
return aggrNode;

View file

@ -348,13 +348,18 @@ const TFunction* TParseContextBase::selectFunction(
for (auto it = candidateList.begin(); it != candidateList.end(); ++it) {
const TFunction& candidate = *(*it);
// to even be a potential match, number of arguments has to match
if (call.getParamCount() != candidate.getParamCount())
// to even be a potential match, number of arguments must be >= the number of
// fixed (non-default) parameters, and <= the total (including parameter with defaults).
if (call.getParamCount() < candidate.getFixedParamCount() ||
call.getParamCount() > candidate.getParamCount())
continue;
// see if arguments are convertible
bool viable = true;
for (int param = 0; param < candidate.getParamCount(); ++param) {
// The call can have fewer parameters than the candidate, if some have defaults.
const int paramCount = std::min(call.getParamCount(), candidate.getParamCount());
for (int param = 0; param < paramCount; ++param) {
if (candidate[param].type->getQualifier().isParamInput()) {
if (! convertible(*call[param].type, *candidate[param].type, candidate.getBuiltInOp(), param)) {
viable = false;
@ -382,7 +387,7 @@ const TFunction* TParseContextBase::selectFunction(
return viableCandidates.front();
// 4. find best...
auto betterParam = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool {
const auto betterParam = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool {
// is call -> can2 better than call -> can1 for any parameter
bool hasBetterParam = false;
for (int param = 0; param < call.getParamCount(); ++param) {
@ -394,6 +399,16 @@ const TFunction* TParseContextBase::selectFunction(
return hasBetterParam;
};
const auto equivalentParams = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool {
// is call -> can2 equivalent to call -> can1 for all the call parameters?
for (int param = 0; param < call.getParamCount(); ++param) {
if (better(*call[param].type, *can1[param].type, *can2[param].type) ||
better(*call[param].type, *can2[param].type, *can1[param].type))
return false;
}
return true;
};
const TFunction* incumbent = viableCandidates.front();
for (auto it = viableCandidates.begin() + 1; it != viableCandidates.end(); ++it) {
const TFunction& candidate = *(*it);
@ -406,7 +421,10 @@ const TFunction* TParseContextBase::selectFunction(
if (incumbent == *it)
continue;
const TFunction& candidate = *(*it);
if (betterParam(*incumbent, candidate))
// In the case of default parameters, it may have an identical initial set, which is
// also ambiguous
if (betterParam(*incumbent, candidate) || equivalentParams(*incumbent, candidate))
tie = true;
}

View file

@ -295,6 +295,7 @@ TFunction::TFunction(const TFunction& copyOf) : TSymbol(copyOf)
op = copyOf.op;
defined = copyOf.defined;
prototyped = copyOf.prototyped;
defaultParamCount = copyOf.defaultParamCount;
}
TFunction* TFunction::clone() const

View file

@ -191,6 +191,7 @@ protected:
struct TParameter {
TString *name;
TType* type;
TIntermTyped* defaultValue;
void copyParam(const TParameter& param)
{
if (param.name)
@ -198,6 +199,7 @@ struct TParameter {
else
name = 0;
type = param.type->clone();
defaultValue = param.defaultValue;
}
};
@ -209,12 +211,12 @@ public:
explicit TFunction(TOperator o) :
TSymbol(0),
op(o),
defined(false), prototyped(false) { }
defined(false), prototyped(false), defaultParamCount(0) { }
TFunction(const TString *name, const TType& retType, TOperator tOp = EOpNull) :
TSymbol(name),
mangledName(*name + '('),
op(tOp),
defined(false), prototyped(false) { returnType.shallowCopy(retType); }
defined(false), prototyped(false), defaultParamCount(0) { returnType.shallowCopy(retType); }
virtual TFunction* clone() const;
virtual ~TFunction();
@ -226,6 +228,9 @@ public:
assert(writable);
parameters.push_back(p);
p.type->appendMangledName(mangledName);
if (p.defaultValue != nullptr)
defaultParamCount++;
}
virtual const TString& getMangledName() const { return mangledName; }
@ -238,7 +243,13 @@ public:
virtual void setPrototyped() { assert(writable); prototyped = true; }
virtual bool isPrototyped() const { return prototyped; }
// Return total number of parameters
virtual int getParamCount() const { return static_cast<int>(parameters.size()); }
// Return number of parameters with default values.
virtual int getDefaultParamCount() const { return defaultParamCount; }
// Return number of fixed parameters (without default values)
virtual int getFixedParamCount() const { return getParamCount() - getDefaultParamCount(); }
virtual TParameter& operator[](int i) { assert(writable); return parameters[i]; }
virtual const TParameter& operator[](int i) const { return parameters[i]; }
@ -255,6 +266,7 @@ protected:
TOperator op;
bool defined;
bool prototyped;
int defaultParamCount;
};
//