Implement swizzles containing 0 and 1 to represent numbers not indexes.

This commit is contained in:
John Kessenich 2020-05-02 00:47:49 -06:00
parent b5f003d7a3
commit 94eb0cd55f
4 changed files with 155 additions and 10 deletions

View File

@ -481,13 +481,32 @@ const TFunction* TParseContextBase::selectFunction(
// Look at a '.' field selector string and change it into numerical selectors
// for a vector or scalar.
//
// These are returned as indexes in selector.
// E.g. ".zy" will become selector = {2, 1}.
//
// Always return some form of swizzle, so the result is always usable.
//
// '0' and '1' in the field will mean to use the numeric values of 0 and 1
// rather than the result of an index into the vector.
// These are represented by:
// '0': MaxSwizzleSelectors
// '1': MaxSwizzleSelectors + 1
// E.g., ".z01" will become selector = {2, 4, 5} (if MasSwizzleSelectors == 4).
//
// A leading underscore (prefix) will get ignored.
//
void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TString& compString, int vecSize,
TSwizzleSelectors<TVectorSelector>& selector)
TSwizzleSelectors<TVectorSelector>& selector, bool& numeric)
{
// a swizzle does not contain numerics unless there are actually numbers
// in it, independent of whether there is a prefix
numeric = false;
// If the field uses prefix syntax, normalize it.
const int firstChar = compString[0] == '_';
// Too long?
if (compString.size() > MaxSwizzleSelectors)
if (compString.size() - firstChar > MaxSwizzleSelectors)
error(loc, "vector swizzle too long", compString.c_str(), "");
// Use this to test that all swizzle characters are from the same swizzle-namespace-set
@ -495,12 +514,13 @@ void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TStrin
exyzw,
ergba,
estpq,
} fieldSet[MaxSwizzleSelectors];
enumeric,
} fieldSet[MaxSwizzleSelectors + 1];
// Decode the swizzle string.
int size = std::min(MaxSwizzleSelectors, (int)compString.size());
const int size = std::min(MaxSwizzleSelectors, (int)compString.size() - firstChar);
for (int i = 0; i < size; ++i) {
switch (compString[i]) {
switch (compString[i + firstChar]) {
case 'x':
selector.push_back(0);
fieldSet[i] = exyzw;
@ -553,6 +573,17 @@ void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TStrin
fieldSet[i] = estpq;
break;
case '0':
selector.push_back(MaxSwizzleSelectors);
fieldSet[i] = enumeric;
numeric = true;
break;
case '1':
selector.push_back(MaxSwizzleSelectors + 1);
fieldSet[i] = enumeric;
numeric = true;
break;
default:
error(loc, "unknown swizzle selection", compString.c_str(), "");
break;
@ -561,13 +592,14 @@ void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TStrin
// Additional error checking.
for (int i = 0; i < selector.size(); ++i) {
if (selector[i] >= vecSize) {
if (selector[i] < MaxSwizzleSelectors && selector[i] >= vecSize) {
error(loc, "vector swizzle selection out of range", compString.c_str(), "");
selector.resize(i);
break;
}
if (i > 0 && fieldSet[i] != fieldSet[i-1]) {
if (i > 0 && fieldSet[i] != enumeric && fieldSet[i-1] != enumeric &&
fieldSet[i] != fieldSet[i-1]) {
error(loc, "vector swizzle selectors not from the same set", compString.c_str(), "");
selector.resize(i);
break;
@ -579,6 +611,24 @@ void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TStrin
selector.push_back(0);
}
void TParseContextBase::replicateRValue(TIntermTyped* node, int num, TVector<TIntermTyped*>& replicates)
{
if (num == 0)
return;
if (num == 1) {
replicates.push_back(node);
return;
}
if (node->getAsSymbolNode()) {
replicates.push_back(node);
for (int i = 1; i < num; ++i)
replicates.push_back(intermediate.addSymbol(*node->getAsSymbolNode()));
}
// WIP: a complex expression needs to be evaluated exactly once, and then
// copies of the result put into the replicates.
}
#ifdef ENABLE_HLSL
//
// Make the passed-in variable information become a member of the

View File

@ -886,7 +886,8 @@ TIntermTyped* TParseContext::handleDotSwizzle(const TSourceLoc& loc, TIntermType
}
TSwizzleSelectors<TVectorSelector> selectors;
parseSwizzleSelector(loc, field, base->getVectorSize(), selectors);
bool numeric = false;
parseSwizzleSelector(loc, field, base->getVectorSize(), selectors, numeric);
if (base->isVector() && selectors.size() != 1 && base->getType().contains16BitFloat())
requireFloat16Arithmetic(loc, ".", "can't swizzle types containing float16");
@ -895,6 +896,9 @@ TIntermTyped* TParseContext::handleDotSwizzle(const TSourceLoc& loc, TIntermType
if (base->isVector() && selectors.size() != 1 && base->getType().contains8BitInt())
requireInt8Arithmetic(loc, ".", "can't swizzle types containing (u)int8");
if (numeric)
return handleNumericDotSwizzle(loc, base, selectors);
if (base->isScalar()) {
if (selectors.size() == 1)
return result;
@ -927,6 +931,93 @@ TIntermTyped* TParseContext::handleDotSwizzle(const TSourceLoc& loc, TIntermType
return result;
}
// Handle a swizzle operation where at least one selector is numeric.
//
// Can return
// - a scalar constant (e.g. for ._1), but converted to the right type
// and constant folded
// - a vector constructor
// - a sequence containing
// 1. evaluation of 'base'
// 2. a scalar constant, converted, folded
// - a sequence containing
// 1. evaluation of 'base'
// 2. a vector constructor
//
// Note that none of the above include swizzle operations.
//
// Note: A vector constructor might require copies of the rvalue being swizzled,
// to avoid the tree accidentally becoming a DAG when there are multiple
// letter swizzles present needing multiple operations to get the
// components. This is quite unlike how swizzles are handled, or any
// other native GLSL operation.
//
TIntermTyped* TParseContext::handleNumericDotSwizzle(const TSourceLoc& loc, TIntermTyped* base,
const TSwizzleSelectors<TVectorSelector>& selectors)
{
const auto isLetter = [](int selector) { return selector < MaxSwizzleSelectors; };
const auto isNumber = [isLetter](int selector) { return !isLetter(selector); };
const auto getNumber = [](int selector) { return selector - MaxSwizzleSelectors; };
// The type of the result has the 'base' component type,
// but the component-count of 'selectors'.
TType type(base->getBasicType(), EvqTemporary, selectors.size());
// If only one selector, the result is a scalar.
// But, its type might be changing, so add a constructor.
// This will always result in an already folded scalar front-end constant.
if (selectors.size() == 1) {
assert(isNumber(selectors[0]));
return addConstructor(loc, intermediate.addConstantUnion(getNumber(selectors[0]), loc), type);
// WIP: this is incorrect if 'base' had side effects, it still needs to
// be evaluated as part of a sequence operation, unless the
// specification for this operation says those side effects are ignored.
}
// Otherwise, the result is like making a vector constructor,
// where we know we have more than one argument.
// Collect the arguments.
// This is complicated by the presence of more than one letter selector,
// because we need to reuse the r-value for each one, so it is a rare
// situation of needing to replicate the r-value.
// count the letter selectors (unless the base is a constant))
int letterCount = 0;
if (!base->getType().getQualifier().isFrontEndConstant()) {
for (int s = 0; s < selectors.size(); ++s)
letterCount += isLetter(selectors[s]) ? 1 : 0;
}
// get the replicates
TVector<TIntermTyped*> replicates;
replicateRValue(base, letterCount, replicates);
// process all the selectors to make the vector
TIntermAggregate* args = nullptr;
for (int s = 0; s < (int)selectors.size(); ++s) {
if (isNumber(selectors[s])) {
args = intermediate.growAggregate(args, intermediate.addConstantUnion(getNumber(selectors[s]), loc));
} else {
// traditional swizzle selector, which needs to consume the replicates
// (unless base is a constant)
TIntermTyped* arg;
if (base->getType().getQualifier().isFrontEndConstant()) {
arg = intermediate.foldDereference(base, selectors[s], loc);
} else {
TIntermTyped* rep = replicates.back();
replicates.pop_back();
TIntermTyped* index = intermediate.addConstantUnion(selectors[s], loc);
arg = intermediate.addIndex(EOpIndexDirect, rep, index, loc);
}
args = intermediate.growAggregate(args, arg);
}
}
// form the constructor
return addConstructor(loc, args, type)->getAsAggregate();
}
void TParseContext::blockMemberExtensionCheck(const TSourceLoc& loc, const TIntermTyped* base, int member, const TString& memberName)
{
// a block that needs extension checking is either 'base', or if arrayed,

View File

@ -217,7 +217,8 @@ protected:
/* output */ bool& tie);
virtual void parseSwizzleSelector(const TSourceLoc&, const TString&, int size,
TSwizzleSelectors<TVectorSelector>&);
TSwizzleSelectors<TVectorSelector>&, bool& numeric);
virtual void replicateRValue(TIntermTyped* node, int n, TVector<TIntermTyped*>& replicates);
// Manage the global uniform block (default uniforms in GLSL, $Global in HLSL)
TVariable* globalUniformBlock; // the actual block, inserted into the symbol table
@ -316,6 +317,8 @@ public:
TIntermTyped* handleUnaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* childNode);
TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field);
TIntermTyped* handleDotSwizzle(const TSourceLoc&, TIntermTyped* base, const TString& field);
TIntermTyped* handleNumericDotSwizzle(const TSourceLoc&, TIntermTyped* base,
const TSwizzleSelectors<TVectorSelector>&);
void blockMemberExtensionCheck(const TSourceLoc&, const TIntermTyped* base, int member, const TString& memberName);
TFunction* handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype);
TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&);

View File

@ -952,7 +952,8 @@ TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TInt
}
} else if (base->isVector() || base->isScalar()) {
TSwizzleSelectors<TVectorSelector> selectors;
parseSwizzleSelector(loc, field, base->getVectorSize(), selectors);
bool numeric = false;
parseSwizzleSelector(loc, field, base->getVectorSize(), selectors, numeric);
if (base->isScalar()) {
if (selectors.size() == 1)