|
|
@@ -2301,6 +2301,63 @@ void HlslParseContext::handleFunctionArgument(TFunction* function,
|
|
|
arguments = newArg;
|
|
|
}
|
|
|
|
|
|
+// Position may require special handling: we can optionally invert Y.
|
|
|
+// See: https://github.com/KhronosGroup/glslang/issues/1173
|
|
|
+// https://github.com/KhronosGroup/glslang/issues/494
|
|
|
+TIntermTyped* HlslParseContext::assignPosition(const TSourceLoc& loc, TOperator op,
|
|
|
+ TIntermTyped* left, TIntermTyped* right)
|
|
|
+{
|
|
|
+ // If we are not asked for Y inversion, use a plain old assign.
|
|
|
+ if (!intermediate.getInvertY())
|
|
|
+ return intermediate.addAssign(op, left, right, loc);
|
|
|
+
|
|
|
+ // If we get here, we should invert Y.
|
|
|
+ TIntermAggregate* assignList = nullptr;
|
|
|
+
|
|
|
+ // If this is a complex rvalue, we don't want to dereference it many times. Create a temporary.
|
|
|
+ TVariable* rhsTempVar = nullptr;
|
|
|
+ rhsTempVar = makeInternalVariable("@position", right->getType());
|
|
|
+ rhsTempVar->getWritableType().getQualifier().makeTemporary();
|
|
|
+
|
|
|
+ {
|
|
|
+ TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
|
|
|
+ assignList = intermediate.growAggregate(assignList,
|
|
|
+ intermediate.addAssign(EOpAssign, rhsTempSym, right, loc), loc);
|
|
|
+ }
|
|
|
+
|
|
|
+ // pos.y = -pos.y
|
|
|
+ {
|
|
|
+ const int Y = 1;
|
|
|
+
|
|
|
+ TIntermTyped* tempSymL = intermediate.addSymbol(*rhsTempVar, loc);
|
|
|
+ TIntermTyped* tempSymR = intermediate.addSymbol(*rhsTempVar, loc);
|
|
|
+ TIntermTyped* index = intermediate.addConstantUnion(Y, loc);
|
|
|
+
|
|
|
+ TIntermTyped* lhsElement = intermediate.addIndex(EOpIndexDirect, tempSymL, index, loc);
|
|
|
+ TIntermTyped* rhsElement = intermediate.addIndex(EOpIndexDirect, tempSymR, index, loc);
|
|
|
+
|
|
|
+ const TType derefType(right->getType(), 0);
|
|
|
+
|
|
|
+ lhsElement->setType(derefType);
|
|
|
+ rhsElement->setType(derefType);
|
|
|
+
|
|
|
+ TIntermTyped* yNeg = intermediate.addUnaryMath(EOpNegative, rhsElement, loc);
|
|
|
+
|
|
|
+ assignList = intermediate.growAggregate(assignList, intermediate.addAssign(EOpAssign, lhsElement, yNeg, loc));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Assign the rhs temp (now with Y inversion) to the final output
|
|
|
+ {
|
|
|
+ TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc);
|
|
|
+ assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, rhsTempSym, loc));
|
|
|
+ }
|
|
|
+
|
|
|
+ assert(assignList != nullptr);
|
|
|
+ assignList->setOperator(EOpSequence);
|
|
|
+
|
|
|
+ return assignList;
|
|
|
+}
|
|
|
+
|
|
|
// Clip and cull distance require special handling due to a semantic mismatch. In HLSL,
|
|
|
// these can be float scalar, float vector, or arrays of float scalar or float vector.
|
|
|
// In SPIR-V, they are arrays of scalar floats in all cases. We must copy individual components
|
|
|
@@ -2566,6 +2623,12 @@ TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op
|
|
|
wasSplit(binaryNode->getLeft());
|
|
|
};
|
|
|
|
|
|
+ // Return true if this stage assigns clip position with potentially inverted Y
|
|
|
+ const auto assignsClipPos = [this](const TIntermTyped* node) -> bool {
|
|
|
+ return node->getType().getQualifier().builtIn == EbvPosition &&
|
|
|
+ (language == EShLangVertex || language == EShLangGeometry || language == EShLangTessEvaluation);
|
|
|
+ };
|
|
|
+
|
|
|
const bool isSplitLeft = wasSplit(left) || indexesSplit(left);
|
|
|
const bool isSplitRight = wasSplit(right) || indexesSplit(right);
|
|
|
|
|
|
@@ -2581,6 +2644,9 @@ TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op
|
|
|
|
|
|
const int semanticId = (isOutput ? left : right)->getType().getQualifier().layoutLocation;
|
|
|
return assignClipCullDistance(loc, op, semanticId, left, right);
|
|
|
+ } else if (assignsClipPos(left)) {
|
|
|
+ // Position can require special handling: see comment above assignPosition
|
|
|
+ return assignPosition(loc, op, left, right);
|
|
|
}
|
|
|
|
|
|
return intermediate.addAssign(op, left, right, loc);
|
|
|
@@ -2665,13 +2731,23 @@ TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op
|
|
|
// copy from interstage IO built-in if needed
|
|
|
subTree = intermediate.addSymbol(*builtInVar);
|
|
|
|
|
|
- // Arrayness of builtIn symbols isn't handled by the normal recursion:
|
|
|
- // it's been extracted and moved to the built-in.
|
|
|
- if (subTree->getType().isArray() && !arrayElement.empty()) {
|
|
|
- const TType splitDerefType(subTree->getType(), arrayElement.back());
|
|
|
- subTree = intermediate.addIndex(EOpIndexDirect, subTree,
|
|
|
- intermediate.addConstantUnion(arrayElement.back(), loc), loc);
|
|
|
- subTree->setType(splitDerefType);
|
|
|
+ if (subTree->getType().isArray()) {
|
|
|
+ // Arrayness of builtIn symbols isn't handled by the normal recursion:
|
|
|
+ // it's been extracted and moved to the built-in.
|
|
|
+ if (!arrayElement.empty()) {
|
|
|
+ const TType splitDerefType(subTree->getType(), arrayElement.back());
|
|
|
+ subTree = intermediate.addIndex(EOpIndexDirect, subTree,
|
|
|
+ intermediate.addConstantUnion(arrayElement.back(), loc), loc);
|
|
|
+ subTree->setType(splitDerefType);
|
|
|
+ } else if (splitNode->getAsOperator() != nullptr && (splitNode->getAsOperator()->getOp() == EOpIndexIndirect)) {
|
|
|
+ // This might also be a stage with arrayed outputs, in which case there's an index
|
|
|
+ // operation we should transfer to the output builtin.
|
|
|
+
|
|
|
+ const TType splitDerefType(subTree->getType(), 0);
|
|
|
+ subTree = intermediate.addIndex(splitNode->getAsOperator()->getOp(), subTree,
|
|
|
+ splitNode->getAsBinaryNode()->getRight(), loc);
|
|
|
+ subTree->setType(splitDerefType);
|
|
|
+ }
|
|
|
}
|
|
|
} else if (flattened && !shouldFlatten(derefType, isLeft ? leftStorage : rightStorage, false)) {
|
|
|
if (isLeft)
|
|
|
@@ -2792,7 +2868,10 @@ TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op
|
|
|
subSplitLeft, subSplitRight);
|
|
|
|
|
|
assignList = intermediate.growAggregate(assignList, clipCullAssign, loc);
|
|
|
-
|
|
|
+ } else if (assignsClipPos(subSplitLeft)) {
|
|
|
+ // Position can require special handling: see comment above assignPosition
|
|
|
+ TIntermTyped* positionAssign = assignPosition(loc, op, subSplitLeft, subSplitRight);
|
|
|
+ assignList = intermediate.growAggregate(assignList, positionAssign, loc);
|
|
|
} else if (!shouldFlattenSubsetLeft && !shouldFlattenSubsetRight &&
|
|
|
!typeL.containsBuiltIn() && !typeR.containsBuiltIn()) {
|
|
|
// If this is the final flattening (no nested types below to flatten)
|
|
|
@@ -5777,7 +5856,8 @@ void HlslParseContext::handleSemantic(TSourceLoc loc, TQualifier& qualifier, TBu
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- qualifier.builtIn = builtIn;
|
|
|
+ if (qualifier.builtIn == EbvNone)
|
|
|
+ qualifier.builtIn = builtIn;
|
|
|
qualifier.semanticName = intermediate.addSemanticName(upperCase);
|
|
|
}
|
|
|
|
|
|
@@ -9046,7 +9126,7 @@ bool HlslParseContext::isOutputBuiltIn(const TQualifier& qualifier) const
|
|
|
return language == EShLangFragment;
|
|
|
case EbvLayer:
|
|
|
case EbvViewportIndex:
|
|
|
- return language == EShLangGeometry;
|
|
|
+ return language == EShLangGeometry || language == EShLangVertex;
|
|
|
case EbvPrimitiveId:
|
|
|
return language == EShLangGeometry;
|
|
|
case EbvTessLevelInner:
|