From 94eb0cd55fc6bf3e8e66534527381e19d982ded3 Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Sat, 2 May 2020 00:47:49 -0600 Subject: [PATCH] Implement swizzles containing 0 and 1 to represent numbers not indexes. --- .../MachineIndependent/ParseContextBase.cpp | 64 +++++++++++-- glslang/MachineIndependent/ParseHelper.cpp | 93 ++++++++++++++++++- glslang/MachineIndependent/ParseHelper.h | 5 +- hlsl/hlslParseHelper.cpp | 3 +- 4 files changed, 155 insertions(+), 10 deletions(-) diff --git a/glslang/MachineIndependent/ParseContextBase.cpp b/glslang/MachineIndependent/ParseContextBase.cpp index b46400914c..0852862f18 100644 --- a/glslang/MachineIndependent/ParseContextBase.cpp +++ b/glslang/MachineIndependent/ParseContextBase.cpp @@ -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& selector) + TSwizzleSelectors& 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& 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 diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 117c164498..fa83ef2903 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -886,7 +886,8 @@ TIntermTyped* TParseContext::handleDotSwizzle(const TSourceLoc& loc, TIntermType } TSwizzleSelectors 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& 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 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, diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index 9c5fdc7857..eb50dad378 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -217,7 +217,8 @@ class TParseContextBase : public TParseVersions { /* output */ bool& tie); virtual void parseSwizzleSelector(const TSourceLoc&, const TString&, int size, - TSwizzleSelectors&); + TSwizzleSelectors&, bool& numeric); + virtual void replicateRValue(TIntermTyped* node, int n, TVector& 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 @@ class TParseContext : public TParseContextBase { 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&); 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&); diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp index 2dc173fcad..32ace82d3c 100755 --- a/hlsl/hlslParseHelper.cpp +++ b/hlsl/hlslParseHelper.cpp @@ -952,7 +952,8 @@ TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TInt } } else if (base->isVector() || base->isScalar()) { TSwizzleSelectors selectors; - parseSwizzleSelector(loc, field, base->getVectorSize(), selectors); + bool numeric = false; + parseSwizzleSelector(loc, field, base->getVectorSize(), selectors, numeric); if (base->isScalar()) { if (selectors.size() == 1)