|
@@ -693,7 +693,7 @@ TIntermTyped* HlslParseContext::handleVariable(const TSourceLoc& loc, const TStr
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Recovery, if it wasn't found or was not a variable.
|
|
// Recovery, if it wasn't found or was not a variable.
|
|
|
- if (! variable) {
|
|
|
|
|
|
|
+ if (variable == nullptr) {
|
|
|
error(loc, "unknown variable", string->c_str(), "");
|
|
error(loc, "unknown variable", string->c_str(), "");
|
|
|
variable = new TVariable(string, TType(EbtVoid));
|
|
variable = new TVariable(string, TType(EbtVoid));
|
|
|
}
|
|
}
|
|
@@ -791,7 +791,7 @@ TIntermTyped* HlslParseContext::handleBracketDereference(const TSourceLoc& loc,
|
|
|
index = makeIntegerIndex(index);
|
|
index = makeIntegerIndex(index);
|
|
|
|
|
|
|
|
if (index == nullptr) {
|
|
if (index == nullptr) {
|
|
|
- error(loc, " unknown undex type ", "", "");
|
|
|
|
|
|
|
+ error(loc, " unknown index type ", "", "");
|
|
|
return nullptr;
|
|
return nullptr;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -867,7 +867,7 @@ void HlslParseContext::checkIndex(const TSourceLoc& /*loc*/, const TType& /*type
|
|
|
TIntermTyped* HlslParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right)
|
|
TIntermTyped* HlslParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right)
|
|
|
{
|
|
{
|
|
|
TIntermTyped* result = intermediate.addBinaryMath(op, left, right, loc);
|
|
TIntermTyped* result = intermediate.addBinaryMath(op, left, right, loc);
|
|
|
- if (! result)
|
|
|
|
|
|
|
+ if (result == nullptr)
|
|
|
binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString());
|
|
binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString());
|
|
|
|
|
|
|
|
return result;
|
|
return result;
|
|
@@ -1492,9 +1492,25 @@ void HlslParseContext::fixBuiltInIoType(TType& type)
|
|
|
switch (type.getQualifier().builtIn) {
|
|
switch (type.getQualifier().builtIn) {
|
|
|
case EbvTessLevelOuter: requiredArraySize = 4; break;
|
|
case EbvTessLevelOuter: requiredArraySize = 4; break;
|
|
|
case EbvTessLevelInner: requiredArraySize = 2; break;
|
|
case EbvTessLevelInner: requiredArraySize = 2; break;
|
|
|
- case EbvClipDistance: // TODO: ...
|
|
|
|
|
- case EbvCullDistance: // TODO: ...
|
|
|
|
|
- return;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ case EbvClipDistance:
|
|
|
|
|
+ case EbvCullDistance:
|
|
|
|
|
+ {
|
|
|
|
|
+ // ClipDistance and CullDistance are handled specially in the entry point output
|
|
|
|
|
+ // copy algorithm, because they may need to be unpacked from components of vectors
|
|
|
|
|
+ // (or a scalar) into a float array. Here, we make the array the right size and type,
|
|
|
|
|
+ // which is the number of components per vector times the array size (or 1 if not
|
|
|
|
|
+ // an array). The copy itself is handled in assignClipCullDistance().
|
|
|
|
|
+ requiredArraySize = type.getVectorSize() * (type.isArray() ? type.getOuterArraySize() : 1);
|
|
|
|
|
+
|
|
|
|
|
+ TType clipCullType(EbtFloat, type.getQualifier().storage, 1);
|
|
|
|
|
+
|
|
|
|
|
+ clipCullType.getQualifier() = type.getQualifier();
|
|
|
|
|
+ type.shallowCopy(clipCullType);
|
|
|
|
|
+
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
case EbvTessCoord:
|
|
case EbvTessCoord:
|
|
|
{
|
|
{
|
|
|
// tesscoord is always a vec3 for the IO variable, no matter the shader's
|
|
// tesscoord is always a vec3 for the IO variable, no matter the shader's
|
|
@@ -1525,15 +1541,15 @@ void HlslParseContext::fixBuiltInIoType(TType& type)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Variables that correspond to the user-interface in and out of a stage
|
|
// Variables that correspond to the user-interface in and out of a stage
|
|
|
-// (not the built-in interface) are assigned locations and
|
|
|
|
|
-// registered as a linkage node (part of the stage's external interface).
|
|
|
|
|
-//
|
|
|
|
|
|
|
+// (not the built-in interface) are
|
|
|
|
|
+// - assigned locations
|
|
|
|
|
+// - registered as a linkage node (part of the stage's external interface).
|
|
|
// Assumes it is called in the order in which locations should be assigned.
|
|
// Assumes it is called in the order in which locations should be assigned.
|
|
|
-void HlslParseContext::assignLocations(TVariable& variable)
|
|
|
|
|
|
|
+void HlslParseContext::assignToInterface(TVariable& variable)
|
|
|
{
|
|
{
|
|
|
const auto assignLocation = [&](TVariable& variable) {
|
|
const auto assignLocation = [&](TVariable& variable) {
|
|
|
- const TType& type = variable.getType();
|
|
|
|
|
- const TQualifier& qualifier = type.getQualifier();
|
|
|
|
|
|
|
+ TType& type = variable.getWritableType();
|
|
|
|
|
+ TQualifier& qualifier = type.getQualifier();
|
|
|
if (qualifier.storage == EvqVaryingIn || qualifier.storage == EvqVaryingOut) {
|
|
if (qualifier.storage == EvqVaryingIn || qualifier.storage == EvqVaryingOut) {
|
|
|
if (qualifier.builtIn == EbvNone) {
|
|
if (qualifier.builtIn == EbvNone) {
|
|
|
// Strip off the outer array dimension for those having an extra one.
|
|
// Strip off the outer array dimension for those having an extra one.
|
|
@@ -1552,7 +1568,6 @@ void HlslParseContext::assignLocations(TVariable& variable)
|
|
|
nextOutLocation += size;
|
|
nextOutLocation += size;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
trackLinkage(variable);
|
|
trackLinkage(variable);
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
@@ -1664,7 +1679,7 @@ TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& l
|
|
|
TSymbol* symbol = symbolTable.find(function.getMangledName());
|
|
TSymbol* symbol = symbolTable.find(function.getMangledName());
|
|
|
TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr;
|
|
TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr;
|
|
|
|
|
|
|
|
- if (! prevDec)
|
|
|
|
|
|
|
+ if (prevDec == nullptr)
|
|
|
error(loc, "can't find function", function.getName().c_str(), "");
|
|
error(loc, "can't find function", function.getName().c_str(), "");
|
|
|
// Note: 'prevDec' could be 'function' if this is the first time we've seen function
|
|
// Note: 'prevDec' could be 'function' if this is the first time we've seen function
|
|
|
// as it would have just been put in the symbol table. Otherwise, we're looking up
|
|
// as it would have just been put in the symbol table. Otherwise, we're looking up
|
|
@@ -1914,9 +1929,11 @@ void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const T
|
|
|
// ret = @shaderEntryPoint(args...);
|
|
// ret = @shaderEntryPoint(args...);
|
|
|
// oargs = args<that are output>...;
|
|
// oargs = args<that are output>...;
|
|
|
// }
|
|
// }
|
|
|
|
|
+// retType @shaderEntryPoint(args...)
|
|
|
|
|
+// { body }
|
|
|
//
|
|
//
|
|
|
// The symbol table will still map the original entry point name to the
|
|
// The symbol table will still map the original entry point name to the
|
|
|
-// the modified function and it's new name:
|
|
|
|
|
|
|
+// the modified function and its new name:
|
|
|
//
|
|
//
|
|
|
// symbol table: shaderEntryPoint -> @shaderEntryPoint
|
|
// symbol table: shaderEntryPoint -> @shaderEntryPoint
|
|
|
//
|
|
//
|
|
@@ -1966,7 +1983,7 @@ TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunct
|
|
|
split(variable);
|
|
split(variable);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- assignLocations(variable);
|
|
|
|
|
|
|
+ assignToInterface(variable);
|
|
|
};
|
|
};
|
|
|
if (entryPointOutput)
|
|
if (entryPointOutput)
|
|
|
makeVariableInOut(*entryPointOutput);
|
|
makeVariableInOut(*entryPointOutput);
|
|
@@ -2012,11 +2029,8 @@ TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunct
|
|
|
for (int i = 0; i < userFunction.getParamCount(); i++) {
|
|
for (int i = 0; i < userFunction.getParamCount(); i++) {
|
|
|
TParameter& param = userFunction[i];
|
|
TParameter& param = userFunction[i];
|
|
|
argVars.push_back(makeInternalVariable(*param.name, *param.type));
|
|
argVars.push_back(makeInternalVariable(*param.name, *param.type));
|
|
|
-
|
|
|
|
|
argVars.back()->getWritableType().getQualifier().makeTemporary();
|
|
argVars.back()->getWritableType().getQualifier().makeTemporary();
|
|
|
-
|
|
|
|
|
TIntermSymbol* arg = intermediate.addSymbol(*argVars.back());
|
|
TIntermSymbol* arg = intermediate.addSymbol(*argVars.back());
|
|
|
-
|
|
|
|
|
handleFunctionArgument(&callee, callingArgs, arg);
|
|
handleFunctionArgument(&callee, callingArgs, arg);
|
|
|
if (param.type->getQualifier().isParamInput()) {
|
|
if (param.type->getQualifier().isParamInput()) {
|
|
|
intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg,
|
|
intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg,
|
|
@@ -2062,7 +2076,6 @@ TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunct
|
|
|
} else {
|
|
} else {
|
|
|
returnAssign = handleAssign(loc, EOpAssign, intermediate.addSymbol(*entryPointOutput), callReturn);
|
|
returnAssign = handleAssign(loc, EOpAssign, intermediate.addSymbol(*entryPointOutput), callReturn);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
intermediate.growAggregate(synthBody, returnAssign);
|
|
intermediate.growAggregate(synthBody, returnAssign);
|
|
|
} else
|
|
} else
|
|
|
intermediate.growAggregate(synthBody, callReturn);
|
|
intermediate.growAggregate(synthBody, callReturn);
|
|
@@ -2120,12 +2133,63 @@ void HlslParseContext::handleFunctionBody(const TSourceLoc& loc, TFunction& func
|
|
|
void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& returnValue,
|
|
void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& returnValue,
|
|
|
TVector<TVariable*>& inputs, TVector<TVariable*>& outputs)
|
|
TVector<TVariable*>& inputs, TVector<TVariable*>& outputs)
|
|
|
{
|
|
{
|
|
|
|
|
+ // We might have in input structure type with no decorations that caused it
|
|
|
|
|
+ // to look like an input type, yet it has (e.g.) interpolation types that
|
|
|
|
|
+ // must be modified that turn it into an input type.
|
|
|
|
|
+ // Hence, a missing ioTypeMap for 'input' might need to be synthesized.
|
|
|
|
|
+ const auto synthesizeEditedInput = [this](TType& type) {
|
|
|
|
|
+ // True if a type needs to be 'flat'
|
|
|
|
|
+ const auto needsFlat = [](const TType& type) {
|
|
|
|
|
+ return type.containsBasicType(EbtInt) ||
|
|
|
|
|
+ type.containsBasicType(EbtUint) ||
|
|
|
|
|
+ type.containsBasicType(EbtInt64) ||
|
|
|
|
|
+ type.containsBasicType(EbtUint64) ||
|
|
|
|
|
+ type.containsBasicType(EbtBool) ||
|
|
|
|
|
+ type.containsBasicType(EbtDouble);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if (language == EShLangFragment && needsFlat(type)) {
|
|
|
|
|
+ if (type.isStruct()) {
|
|
|
|
|
+ TTypeList* finalList = nullptr;
|
|
|
|
|
+ auto it = ioTypeMap.find(type.getStruct());
|
|
|
|
|
+ if (it == ioTypeMap.end() || it->second.input == nullptr) {
|
|
|
|
|
+ // Getting here means we have no input struct, but we need one.
|
|
|
|
|
+ auto list = new TTypeList;
|
|
|
|
|
+ for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) {
|
|
|
|
|
+ TType* newType = new TType;
|
|
|
|
|
+ newType->shallowCopy(*member->type);
|
|
|
|
|
+ TTypeLoc typeLoc = { newType, member->loc };
|
|
|
|
|
+ list->push_back(typeLoc);
|
|
|
|
|
+ }
|
|
|
|
|
+ // install the new input type
|
|
|
|
|
+ if (it == ioTypeMap.end()) {
|
|
|
|
|
+ tIoKinds newLists = { list, nullptr, nullptr };
|
|
|
|
|
+ ioTypeMap[type.getStruct()] = newLists;
|
|
|
|
|
+ } else
|
|
|
|
|
+ it->second.input = list;
|
|
|
|
|
+ finalList = list;
|
|
|
|
|
+ } else
|
|
|
|
|
+ finalList = it->second.input;
|
|
|
|
|
+ // edit for 'flat'
|
|
|
|
|
+ for (auto member = finalList->begin(); member != finalList->end(); ++member) {
|
|
|
|
|
+ if (needsFlat(*member->type)) {
|
|
|
|
|
+ member->type->getQualifier().clearInterpolation();
|
|
|
|
|
+ member->type->getQualifier().flat = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ type.getQualifier().clearInterpolation();
|
|
|
|
|
+ type.getQualifier().flat = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
// Do the actual work to make a type be a shader input or output variable,
|
|
// Do the actual work to make a type be a shader input or output variable,
|
|
|
// and clear the original to be non-IO (for use as a normal function parameter/return).
|
|
// and clear the original to be non-IO (for use as a normal function parameter/return).
|
|
|
const auto makeIoVariable = [this](const char* name, TType& type, TStorageQualifier storage) -> TVariable* {
|
|
const auto makeIoVariable = [this](const char* name, TType& type, TStorageQualifier storage) -> TVariable* {
|
|
|
TVariable* ioVariable = makeInternalVariable(name, type);
|
|
TVariable* ioVariable = makeInternalVariable(name, type);
|
|
|
clearUniformInputOutput(type.getQualifier());
|
|
clearUniformInputOutput(type.getQualifier());
|
|
|
- if (type.getStruct() != nullptr) {
|
|
|
|
|
|
|
+ if (type.isStruct()) {
|
|
|
auto newLists = ioTypeMap.find(ioVariable->getType().getStruct());
|
|
auto newLists = ioTypeMap.find(ioVariable->getType().getStruct());
|
|
|
if (newLists != ioTypeMap.end()) {
|
|
if (newLists != ioTypeMap.end()) {
|
|
|
if (storage == EvqVaryingIn && newLists->second.input)
|
|
if (storage == EvqVaryingIn && newLists->second.input)
|
|
@@ -2178,6 +2242,7 @@ void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& return
|
|
|
for (int i = 0; i < function.getParamCount(); i++) {
|
|
for (int i = 0; i < function.getParamCount(); i++) {
|
|
|
TType& paramType = *function[i].type;
|
|
TType& paramType = *function[i].type;
|
|
|
if (paramType.getQualifier().isParamInput()) {
|
|
if (paramType.getQualifier().isParamInput()) {
|
|
|
|
|
+ synthesizeEditedInput(paramType);
|
|
|
TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingIn);
|
|
TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingIn);
|
|
|
inputs.push_back(argAsGlobal);
|
|
inputs.push_back(argAsGlobal);
|
|
|
|
|
|
|
@@ -2241,6 +2306,88 @@ void HlslParseContext::handleFunctionArgument(TFunction* function,
|
|
|
arguments = newArg;
|
|
arguments = newArg;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 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
|
|
|
|
|
+// (e.g, both x and y components of a float2) out into the destination float array.
|
|
|
|
|
+//
|
|
|
|
|
+// The values are assigned to sequential members of the output array. The inner dimension
|
|
|
|
|
+// is vector components. The outer dimension is array elements.
|
|
|
|
|
+TIntermAggregate* HlslParseContext::assignClipCullDistance(const TSourceLoc& loc, TOperator op,
|
|
|
|
|
+ TIntermTyped* left, TIntermTyped* right)
|
|
|
|
|
+{
|
|
|
|
|
+ // ***
|
|
|
|
|
+ // TODO: this does not yet handle the index coming from the semantic's ID, as in SV_ClipDistance[012345...]
|
|
|
|
|
+ // ***
|
|
|
|
|
+
|
|
|
|
|
+ // left has got to be an array of scalar floats, per SPIR-V semantics.
|
|
|
|
|
+ // fixBuiltInIoType() should have handled that upstream.
|
|
|
|
|
+ assert(left->getType().isArray());
|
|
|
|
|
+ assert(left->getType().getVectorSize() == 1);
|
|
|
|
|
+ assert(left->getType().getBasicType() == EbtFloat);
|
|
|
|
|
+
|
|
|
|
|
+ // array sizes, or 1 if it's not an array:
|
|
|
|
|
+ const int lhsArraySize = (left->getType().isArray() ? left->getType().getOuterArraySize() : 1);
|
|
|
|
|
+ const int rhsArraySize = (right->getType().isArray() ? right->getType().getOuterArraySize() : 1);
|
|
|
|
|
+ // vector sizes:
|
|
|
|
|
+ const int lhsVectorSize = left->getType().getVectorSize();
|
|
|
|
|
+ const int rhsVectorSize = right->getType().getVectorSize();
|
|
|
|
|
+
|
|
|
|
|
+ // We may be creating multiple sub-assignments. This is an aggregate to hold them.
|
|
|
|
|
+ // TODO: it would be possible to be clever sometimes and avoid the sequence node if not needed.
|
|
|
|
|
+ TIntermAggregate* assignList = nullptr;
|
|
|
|
|
+
|
|
|
|
|
+ // If the types are homomorphic, use a simple assign. No need to mess about with
|
|
|
|
|
+ // individual components.
|
|
|
|
|
+ if (left->getType().isArray() == right->getType().isArray() &&
|
|
|
|
|
+ lhsArraySize == rhsArraySize &&
|
|
|
|
|
+ lhsVectorSize == rhsVectorSize) {
|
|
|
|
|
+ assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc));
|
|
|
|
|
+ assignList->setOperator(EOpSequence);
|
|
|
|
|
+ return assignList;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // We are going to copy each component of the right (per array element if indicated) to sequential
|
|
|
|
|
+ // array elements of the left. This tracks the lhs element we're writing to as we go along.
|
|
|
|
|
+ int lhsArrayPos = 0;
|
|
|
|
|
+
|
|
|
|
|
+ // Loop through every component of every element of the RHS, and copy to LHS elements in turn.
|
|
|
|
|
+ for (int rhsArrayPos = 0; rhsArrayPos < rhsArraySize; ++rhsArrayPos) {
|
|
|
|
|
+ for (int rhsComponent = 0; rhsComponent < rhsVectorSize; ++rhsComponent) {
|
|
|
|
|
+ // LHS array member to write to:
|
|
|
|
|
+ TIntermTyped* lhsMember = intermediate.addIndex(EOpIndexDirect, left,
|
|
|
|
|
+ intermediate.addConstantUnion(lhsArrayPos++, loc), loc);
|
|
|
|
|
+
|
|
|
|
|
+ TIntermTyped* rhsMember = right;
|
|
|
|
|
+
|
|
|
|
|
+ // If right is an array, extract the element of interest
|
|
|
|
|
+ if (right->getType().isArray()) {
|
|
|
|
|
+ const TType derefType(rhsMember->getType(), 0);
|
|
|
|
|
+ rhsMember = intermediate.addIndex(EOpIndexDirect, rhsMember,
|
|
|
|
|
+ intermediate.addConstantUnion(rhsArrayPos, loc), loc);
|
|
|
|
|
+ rhsMember->setType(derefType);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If right is a vector, extract the component of interest.
|
|
|
|
|
+ if (right->getType().isVector()) {
|
|
|
|
|
+ const TType derefType(rhsMember->getType(), 0);
|
|
|
|
|
+ rhsMember = intermediate.addIndex(EOpIndexDirect, rhsMember,
|
|
|
|
|
+ intermediate.addConstantUnion(rhsComponent, loc), loc);
|
|
|
|
|
+ rhsMember->setType(derefType);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Assign: to the proper lhs member.
|
|
|
|
|
+ assignList = intermediate.growAggregate(assignList,
|
|
|
|
|
+ intermediate.addAssign(op, lhsMember, rhsMember, loc));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ assert(assignList != nullptr);
|
|
|
|
|
+ assignList->setOperator(EOpSequence);
|
|
|
|
|
+
|
|
|
|
|
+ return assignList;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// Some simple source assignments need to be flattened to a sequence
|
|
// Some simple source assignments need to be flattened to a sequence
|
|
|
// of AST assignments. Catch these and flatten, otherwise, pass through
|
|
// of AST assignments. Catch these and flatten, otherwise, pass through
|
|
|
// to intermediate.addAssign().
|
|
// to intermediate.addAssign().
|
|
@@ -2263,8 +2410,14 @@ TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op
|
|
|
|
|
|
|
|
// OK to do a single assign if both are split, or both are unsplit. But if one is and the other
|
|
// OK to do a single assign if both are split, or both are unsplit. But if one is and the other
|
|
|
// isn't, we fall back to a member-wise copy.
|
|
// isn't, we fall back to a member-wise copy.
|
|
|
- if (! isFlattenLeft && ! isFlattenRight && !isSplitLeft && !isSplitRight)
|
|
|
|
|
|
|
+ if (! isFlattenLeft && ! isFlattenRight && !isSplitLeft && !isSplitRight) {
|
|
|
|
|
+ // Clip and cull distance requires more processing. See comment above assignClipCullDistance.
|
|
|
|
|
+ const TBuiltInVariable leftBuiltIn = left->getType().getQualifier().builtIn;
|
|
|
|
|
+ if (leftBuiltIn == EbvClipDistance || leftBuiltIn == EbvCullDistance)
|
|
|
|
|
+ return assignClipCullDistance(loc, op, left, right);
|
|
|
|
|
+
|
|
|
return intermediate.addAssign(op, left, right, loc);
|
|
return intermediate.addAssign(op, left, right, loc);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
TIntermAggregate* assignList = nullptr;
|
|
TIntermAggregate* assignList = nullptr;
|
|
|
const TVector<TVariable*>* leftVariables = nullptr;
|
|
const TVector<TVariable*>* leftVariables = nullptr;
|
|
@@ -2425,13 +2578,21 @@ TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op
|
|
|
TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left, member, splitLeft, memberL) : subLeft;
|
|
TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left, member, splitLeft, memberL) : subLeft;
|
|
|
TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right, member, splitRight, memberR) : subRight;
|
|
TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right, member, splitRight, memberR) : subRight;
|
|
|
|
|
|
|
|
- // If this is the final flattening (no nested types below to flatten) we'll copy the member, else
|
|
|
|
|
- // recurse into the type hierarchy. However, if splitting the struct, that means we can copy a whole
|
|
|
|
|
- // subtree here IFF it does not itself contain any interstage built-in IO variables, so we only have to
|
|
|
|
|
- // recurse into it if there's something for splitting to do. That can save a lot of AST verbosity for
|
|
|
|
|
- // a bunch of memberwise copies.
|
|
|
|
|
- if ((!isFlattenLeft && !isFlattenRight &&
|
|
|
|
|
- !typeL.containsBuiltInInterstageIO(language) && !typeR.containsBuiltInInterstageIO(language))) {
|
|
|
|
|
|
|
+ const TBuiltInVariable leftBuiltIn = subSplitLeft->getType().getQualifier().builtIn;
|
|
|
|
|
+
|
|
|
|
|
+ if (leftBuiltIn == EbvClipDistance || leftBuiltIn == EbvCullDistance) {
|
|
|
|
|
+
|
|
|
|
|
+ // Clip and cull distance builtin assignment is complex in its own right, and is handled in
|
|
|
|
|
+ // a separate function dedicated to that task. See comment above assignClipCullDistance;
|
|
|
|
|
+ assignList = intermediate.growAggregate(assignList, assignClipCullDistance(loc, op, subSplitLeft, subSplitRight), loc);
|
|
|
|
|
+ } else if ((!isFlattenLeft && !isFlattenRight &&
|
|
|
|
|
+ !typeL.containsBuiltInInterstageIO(language) && !typeR.containsBuiltInInterstageIO(language))) {
|
|
|
|
|
+ // If this is the final flattening (no nested types below to flatten) we'll copy the member, else
|
|
|
|
|
+ // recurse into the type hierarchy. However, if splitting the struct, that means we can copy a whole
|
|
|
|
|
+ // subtree here IFF it does not itself contain any interstage built-in IO variables, so we only have to
|
|
|
|
|
+ // recurse into it if there's something for splitting to do. That can save a lot of AST verbosity for
|
|
|
|
|
+ // a bunch of memberwise copies.
|
|
|
|
|
+
|
|
|
assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, subSplitLeft, subSplitRight, loc), loc);
|
|
assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, subSplitLeft, subSplitRight, loc), loc);
|
|
|
} else {
|
|
} else {
|
|
|
traverse(subLeft, subRight, subSplitLeft, subSplitRight);
|
|
traverse(subLeft, subRight, subSplitLeft, subSplitRight);
|
|
@@ -3031,7 +3192,7 @@ TIntermConstantUnion* HlslParseContext::getSamplePosArray(int count)
|
|
|
//
|
|
//
|
|
|
void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
|
|
void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
|
|
|
{
|
|
{
|
|
|
- if (!node || !node->getAsOperator())
|
|
|
|
|
|
|
+ if (node == nullptr || !node->getAsOperator())
|
|
|
return;
|
|
return;
|
|
|
|
|
|
|
|
const auto clampReturn = [&loc, &node, this](TIntermTyped* result, const TSampler& sampler) -> TIntermTyped* {
|
|
const auto clampReturn = [&loc, &node, this](TIntermTyped* result, const TSampler& sampler) -> TIntermTyped* {
|
|
@@ -3773,7 +3934,7 @@ void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermType
|
|
|
//
|
|
//
|
|
|
void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
|
|
void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
|
|
|
{
|
|
{
|
|
|
- if (!node || !node->getAsOperator())
|
|
|
|
|
|
|
+ if (node == nullptr || !node->getAsOperator())
|
|
|
return;
|
|
return;
|
|
|
|
|
|
|
|
const TOperator op = node->getAsOperator()->getOp();
|
|
const TOperator op = node->getAsOperator()->getOp();
|
|
@@ -4967,7 +5128,7 @@ void HlslParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fn
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (arg > 0) {
|
|
if (arg > 0) {
|
|
|
- if (! aggArgs[arg]->getAsConstantUnion())
|
|
|
|
|
|
|
+ if (aggArgs[arg]->getAsConstantUnion() == nullptr)
|
|
|
error(loc, "argument must be compile-time constant", "texel offset", "");
|
|
error(loc, "argument must be compile-time constant", "texel offset", "");
|
|
|
else {
|
|
else {
|
|
|
const TType& type = aggArgs[arg]->getAsTyped()->getType();
|
|
const TType& type = aggArgs[arg]->getAsTyped()->getType();
|
|
@@ -5706,7 +5867,7 @@ void HlslParseContext::arrayDimMerge(TType& type, const TArraySizes* sizes)
|
|
|
//
|
|
//
|
|
|
void HlslParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type, TSymbol*& symbol, bool track)
|
|
void HlslParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type, TSymbol*& symbol, bool track)
|
|
|
{
|
|
{
|
|
|
- if (! symbol) {
|
|
|
|
|
|
|
+ if (symbol == nullptr) {
|
|
|
bool currentScope;
|
|
bool currentScope;
|
|
|
symbol = symbolTable.find(identifier, nullptr, ¤tScope);
|
|
symbol = symbolTable.find(identifier, nullptr, ¤tScope);
|
|
|
|
|
|
|
@@ -5737,7 +5898,7 @@ void HlslParseContext::declareArray(const TSourceLoc& loc, const TString& identi
|
|
|
// Process a redeclaration.
|
|
// Process a redeclaration.
|
|
|
//
|
|
//
|
|
|
|
|
|
|
|
- if (! symbol) {
|
|
|
|
|
|
|
+ if (symbol == nullptr) {
|
|
|
error(loc, "array variable name expected", identifier.c_str(), "");
|
|
error(loc, "array variable name expected", identifier.c_str(), "");
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -5856,7 +6017,7 @@ void HlslParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& n
|
|
|
// If the block was not found, this must be a version/profile/stage
|
|
// If the block was not found, this must be a version/profile/stage
|
|
|
// that doesn't have it, or the instance name is wrong.
|
|
// that doesn't have it, or the instance name is wrong.
|
|
|
const char* errorName = instanceName ? instanceName->c_str() : newTypeList.front().type->getFieldName().c_str();
|
|
const char* errorName = instanceName ? instanceName->c_str() : newTypeList.front().type->getFieldName().c_str();
|
|
|
- if (! block) {
|
|
|
|
|
|
|
+ if (block == nullptr) {
|
|
|
error(loc, "no declaration found for redeclaration", errorName, "");
|
|
error(loc, "no declaration found for redeclaration", errorName, "");
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -6967,7 +7128,7 @@ TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TStr
|
|
|
declareArray(loc, identifier, type, symbol, !flattenVar);
|
|
declareArray(loc, identifier, type, symbol, !flattenVar);
|
|
|
} else {
|
|
} else {
|
|
|
// non-array case
|
|
// non-array case
|
|
|
- if (! symbol)
|
|
|
|
|
|
|
+ if (symbol == nullptr)
|
|
|
symbol = declareNonArray(loc, identifier, type, !flattenVar);
|
|
symbol = declareNonArray(loc, identifier, type, !flattenVar);
|
|
|
else if (type != symbol->getType())
|
|
else if (type != symbol->getType())
|
|
|
error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str());
|
|
error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str());
|
|
@@ -6976,7 +7137,7 @@ TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TStr
|
|
|
if (flattenVar)
|
|
if (flattenVar)
|
|
|
flatten(loc, *symbol->getAsVariable());
|
|
flatten(loc, *symbol->getAsVariable());
|
|
|
|
|
|
|
|
- if (! symbol)
|
|
|
|
|
|
|
+ if (symbol == nullptr)
|
|
|
return nullptr;
|
|
return nullptr;
|
|
|
|
|
|
|
|
// Deal with initializer
|
|
// Deal with initializer
|
|
@@ -6993,7 +7154,7 @@ TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TStr
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
TVariable* variable = symbol->getAsVariable();
|
|
TVariable* variable = symbol->getAsVariable();
|
|
|
- if (! variable) {
|
|
|
|
|
|
|
+ if (variable == nullptr) {
|
|
|
error(loc, "initializer requires a variable, not a member", identifier.c_str(), "");
|
|
error(loc, "initializer requires a variable, not a member", identifier.c_str(), "");
|
|
|
return nullptr;
|
|
return nullptr;
|
|
|
}
|
|
}
|
|
@@ -7089,7 +7250,7 @@ TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TInterm
|
|
|
skeletalType.getQualifier().makeTemporary();
|
|
skeletalType.getQualifier().makeTemporary();
|
|
|
if (initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull)
|
|
if (initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull)
|
|
|
initializer = convertInitializerList(loc, skeletalType, initializer, nullptr);
|
|
initializer = convertInitializerList(loc, skeletalType, initializer, nullptr);
|
|
|
- if (! initializer) {
|
|
|
|
|
|
|
+ if (initializer == nullptr) {
|
|
|
// error recovery; don't leave const without constant values
|
|
// error recovery; don't leave const without constant values
|
|
|
if (qualifier == EvqConst)
|
|
if (qualifier == EvqConst)
|
|
|
variable->getWritableType().getQualifier().storage = EvqTemporary;
|
|
variable->getWritableType().getQualifier().storage = EvqTemporary;
|
|
@@ -7152,7 +7313,7 @@ TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TInterm
|
|
|
specializationCheck(loc, initializer->getType(), "initializer");
|
|
specializationCheck(loc, initializer->getType(), "initializer");
|
|
|
TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
|
|
TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc);
|
|
|
TIntermNode* initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer);
|
|
TIntermNode* initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer);
|
|
|
- if (! initNode)
|
|
|
|
|
|
|
+ if (initNode == nullptr)
|
|
|
assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
|
|
assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
|
|
|
|
|
|
|
|
return initNode;
|
|
return initNode;
|
|
@@ -7182,7 +7343,7 @@ TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, co
|
|
|
|
|
|
|
|
// see if we have bottomed out in the tree within the initializer-list part
|
|
// see if we have bottomed out in the tree within the initializer-list part
|
|
|
TIntermAggregate* initList = initializer->getAsAggregate();
|
|
TIntermAggregate* initList = initializer->getAsAggregate();
|
|
|
- if (! initList || initList->getOp() != EOpNull) {
|
|
|
|
|
|
|
+ if (initList == nullptr || initList->getOp() != EOpNull) {
|
|
|
// We don't have a list, but if it's a scalar and the 'type' is a
|
|
// We don't have a list, but if it's a scalar and the 'type' is a
|
|
|
// composite, we need to lengthen below to make it useful.
|
|
// composite, we need to lengthen below to make it useful.
|
|
|
// Otherwise, this is an already formed object to initialize with.
|
|
// Otherwise, this is an already formed object to initialize with.
|
|
@@ -7650,7 +7811,7 @@ TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TTyp
|
|
|
{
|
|
{
|
|
|
// Handle cases that map more 1:1 between constructor arguments and constructed.
|
|
// Handle cases that map more 1:1 between constructor arguments and constructed.
|
|
|
TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped());
|
|
TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped());
|
|
|
- if (! converted || converted->getType() != type) {
|
|
|
|
|
|
|
+ if (converted == nullptr || converted->getType() != type) {
|
|
|
error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount,
|
|
error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount,
|
|
|
node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str());
|
|
node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str());
|
|
|
|
|
|
|
@@ -7803,7 +7964,7 @@ void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TS
|
|
|
|
|
|
|
|
// Add the variable, as anonymous or named instanceName.
|
|
// Add the variable, as anonymous or named instanceName.
|
|
|
// Make an anonymous variable if no name was provided.
|
|
// Make an anonymous variable if no name was provided.
|
|
|
- if (! instanceName)
|
|
|
|
|
|
|
+ if (instanceName == nullptr)
|
|
|
instanceName = NewPoolTString("");
|
|
instanceName = NewPoolTString("");
|
|
|
|
|
|
|
|
TVariable& variable = *new TVariable(instanceName, blockType);
|
|
TVariable& variable = *new TVariable(instanceName, blockType);
|
|
@@ -7953,7 +8114,7 @@ void HlslParseContext::fixBlockUniformOffsets(const TQualifier& qualifier, TType
|
|
|
void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier)
|
|
void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier)
|
|
|
{
|
|
{
|
|
|
TSymbol* symbol = symbolTable.find(identifier);
|
|
TSymbol* symbol = symbolTable.find(identifier);
|
|
|
- if (! symbol) {
|
|
|
|
|
|
|
+ if (symbol == nullptr) {
|
|
|
error(loc, "identifier not previously declared", identifier.c_str(), "");
|
|
error(loc, "identifier not previously declared", identifier.c_str(), "");
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
@@ -8842,7 +9003,7 @@ void HlslParseContext::addPatchConstantInvocation()
|
|
|
if (pcfOutput->getType().containsBuiltInInterstageIO(language))
|
|
if (pcfOutput->getType().containsBuiltInInterstageIO(language))
|
|
|
split(*pcfOutput);
|
|
split(*pcfOutput);
|
|
|
|
|
|
|
|
- assignLocations(*pcfOutput);
|
|
|
|
|
|
|
+ assignToInterface(*pcfOutput);
|
|
|
|
|
|
|
|
TIntermSymbol* pcfOutputSym = intermediate.addSymbol(*pcfOutput, loc);
|
|
TIntermSymbol* pcfOutputSym = intermediate.addSymbol(*pcfOutput, loc);
|
|
|
|
|
|