HLSL: Convert run-time sampler assignments to compile-time aliases.

For "s.m = t", a sampler member assigned a sampler, make t an alias
for s.m, and when s.m is flattened, it will flatten to the alias t.
Normally, assignments to samplers are disallowed.
This commit is contained in:
John Kessenich
2017-06-02 16:28:39 -06:00
parent 750c2d07f7
commit f31507421b
5 changed files with 254 additions and 5 deletions

View File

@@ -147,7 +147,7 @@ bool HlslParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner&
//
bool HlslParseContext::shouldConvertLValue(const TIntermNode* node) const
{
if (node == nullptr)
if (node == nullptr || node->getAsTyped() == nullptr)
return false;
const TIntermAggregate* lhsAsAggregate = node->getAsAggregate();
@@ -157,10 +157,14 @@ bool HlslParseContext::shouldConvertLValue(const TIntermNode* node) const
if (lhsAsBinary != nullptr &&
(lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect))
lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate();
if (lhsAsAggregate != nullptr && lhsAsAggregate->getOp() == EOpImageLoad)
return true;
// If it's a syntactic write to a sampler, we will use that to establish
// a compile-time alias.
if (node->getAsTyped()->getBasicType() == EbtSampler)
return true;
return false;
}
@@ -233,7 +237,7 @@ bool HlslParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, T
//
// Most things are passed through unmodified, except for error checking.
//
TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* op, TIntermTyped* node)
TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* op, TIntermTyped*& node)
{
if (node == nullptr)
return nullptr;
@@ -256,6 +260,10 @@ TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char*
// *** If we get here, we're going to apply some conversion to an l-value.
// Spin off sampler aliasing
if (node->getAsTyped()->getBasicType() == EbtSampler)
return handleSamplerLvalue(loc, op, node);
// Helper to create a load.
const auto makeLoad = [&](TIntermSymbol* rhsTmp, TIntermTyped* object, TIntermTyped* coord, const TType& derefType) {
TIntermAggregate* loadOp = new TIntermAggregate(EOpImageLoad);
@@ -500,6 +508,42 @@ TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char*
return node;
}
// Deal with sampler aliasing: turning assignments into aliases
TIntermTyped* HlslParseContext::handleSamplerLvalue(const TSourceLoc& loc, const char* op, TIntermTyped*& node)
{
// Can only alias an assignment: "s1 = s2"
TIntermBinary* binary = node->getAsBinaryNode();
if (binary == nullptr || node->getAsOperator()->getOp() != EOpAssign ||
binary->getLeft() ->getAsSymbolNode() == nullptr ||
binary->getRight()->getAsSymbolNode() == nullptr) {
error(loc, "can't modify sampler", op, "");
return node;
}
// Best is if we are aliasing a flattened struct member "S.s1 = s2",
// in which case we want to update the flattening information with the alias,
// making everything else work seamlessly.
TIntermSymbol* left = binary->getLeft()->getAsSymbolNode();
TIntermSymbol* right = binary->getRight()->getAsSymbolNode();
for (auto fit = flattenMap.begin(); fit != flattenMap.end(); ++fit) {
for (auto mit = fit->second.members.begin(); mit != fit->second.members.end(); ++mit) {
if ((*mit)->getUniqueId() == left->getId()) {
// found it: update with alias to the existing variable, and don't emit any code
(*mit) = new TVariable(&right->getName(), right->getType());
(*mit)->setUniqueId(right->getId());
// replace node (rest of compiler expects either an error or code to generate)
// with pointless access
node = binary->getRight();
return node;
}
}
}
warn(loc, "could not create alias for sampler", op, "");
return node;
}
void HlslParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens)
{
if (pragmaCallback)
@@ -1284,7 +1328,7 @@ bool HlslParseContext::wasSplit(const TIntermTyped* node) const
// Turn an access into an aggregate that was flattened to instead be
// an access to the individual variable the member was flattened to.
// Assumes shouldFlatten() or equivalent was called first.
// Also assumes that initFlattening() and finalizeFlattening() bracket usage.
// Also assumes that initFlattening() and finalizeFlattening() bracket the usage.
TIntermTyped* HlslParseContext::flattenAccess(TIntermTyped* base, int member)
{
const TType dereferencedType(base->getType(), member); // dereferenced type