WIP: HLSL: add structuredbuffer pass by reference in fn params
This PR adds the ability to pass structuredbuffer types by reference as function parameters. It also changes the representation of structuredbuffers from anonymous blocks with named members, to named blocks with pseudonymous members. That should not be an externally visible change.
This commit is contained in:
@@ -691,6 +691,22 @@ TIntermTyped* HlslParseContext::handleBracketOperator(const TSourceLoc& loc, TIn
|
||||
}
|
||||
}
|
||||
|
||||
// Handle operator[] on structured buffers: this indexes into the array element of the buffer.
|
||||
// indexStructBufferContent returns nullptr if it isn't a structuredbuffer (SSBO).
|
||||
TIntermTyped* sbArray = indexStructBufferContent(loc, base);
|
||||
if (sbArray != nullptr) {
|
||||
if (sbArray == nullptr)
|
||||
return nullptr;
|
||||
|
||||
// Now we'll apply the [] index to that array
|
||||
const TOperator idxOp = (index->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect;
|
||||
|
||||
TIntermTyped* element = intermediate.addIndex(idxOp, sbArray, index, loc);
|
||||
const TType derefType(sbArray->getType(), 0);
|
||||
element->setType(derefType);
|
||||
return element;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -866,9 +882,7 @@ TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TInt
|
||||
const int vecSize = sampler.isShadow() ? 1 : 4; // TODO: handle arbitrary sample return sizes
|
||||
return intermediate.addMethod(base, TType(sampler.type, EvqTemporary, vecSize), &field, loc);
|
||||
}
|
||||
} else if (isStructBufferMethod(field) &&
|
||||
base->getType().isRuntimeSizedArray() &&
|
||||
(base->getQualifier().storage == EvqUniform || base->getQualifier().storage == EvqBuffer)) {
|
||||
} else if (isStructBufferType(base->getType())) {
|
||||
TType retType(base->getType(), 0);
|
||||
return intermediate.addMethod(base, retType, &field, loc);
|
||||
} else if (field == "Append" ||
|
||||
@@ -1919,9 +1933,11 @@ void HlslParseContext::remapNonEntryPointIO(TFunction& function)
|
||||
if (function.getType().getBasicType() != EbtVoid)
|
||||
clearUniformInputOutput(function.getWritableType().getQualifier());
|
||||
|
||||
// parameters
|
||||
// parameters.
|
||||
// References to structuredbuffer types are left unmodified
|
||||
for (int i = 0; i < function.getParamCount(); i++)
|
||||
clearUniformInputOutput(function[i].type->getQualifier());
|
||||
if (!isReference(*function[i].type))
|
||||
clearUniformInputOutput(function[i].type->getQualifier());
|
||||
}
|
||||
|
||||
// Handle function returns, including type conversions to the function return type
|
||||
@@ -2284,13 +2300,17 @@ void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TInte
|
||||
|
||||
const TOperator op = node->getAsOperator()->getOp();
|
||||
TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
|
||||
|
||||
TIntermTyped* argArray = argAggregate ? argAggregate->getSequence()[0]->getAsTyped() : nullptr; // array
|
||||
|
||||
// Bail out if not a block method
|
||||
if (argArray == nullptr || !argArray->getType().isRuntimeSizedArray())
|
||||
if (argAggregate == nullptr)
|
||||
return;
|
||||
|
||||
// Buffer is the object upon which method is called, so always arg 0
|
||||
TIntermTyped* bufferObj = argAggregate->getSequence()[0]->getAsTyped();
|
||||
|
||||
// Index to obtain the runtime sized array out of the buffer.
|
||||
TIntermTyped* argArray = indexStructBufferContent(loc, bufferObj);
|
||||
if (argArray == nullptr)
|
||||
return; // It might not be a struct buffer method.
|
||||
|
||||
switch (op) {
|
||||
case EOpMethodLoad:
|
||||
{
|
||||
@@ -3643,7 +3663,7 @@ TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunct
|
||||
// the symbol table for an arbitrary type. This is a temporary hack until that ability exists.
|
||||
// It will have false positives, since it doesn't check arg counts or types.
|
||||
if (arguments && arguments->getAsAggregate()) {
|
||||
if (arguments->getAsAggregate()->getSequence()[0]->getAsTyped()->getType().isRuntimeSizedArray()) {
|
||||
if (isStructBufferType(arguments->getAsAggregate()->getSequence()[0]->getAsTyped()->getType())) {
|
||||
if (isStructBufferMethod(function->getName())) {
|
||||
const TString mangle = function->getName() + "(";
|
||||
TSymbol* symbol = symbolTable.find(mangle, &builtIn);
|
||||
@@ -5033,6 +5053,101 @@ void HlslParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& n
|
||||
trackLinkage(*block);
|
||||
}
|
||||
|
||||
//
|
||||
// Generate index to the array element in a structure buffer (SSBO)
|
||||
//
|
||||
TIntermTyped* HlslParseContext::indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const
|
||||
{
|
||||
// Bail out if not a struct buffer
|
||||
if (buffer == nullptr || ! isStructBufferType(buffer->getType()))
|
||||
return nullptr;
|
||||
|
||||
// Runtime sized array is always the last element.
|
||||
const TTypeList* bufferStruct = buffer->getType().getStruct();
|
||||
TIntermTyped* arrayPosition = intermediate.addConstantUnion(unsigned(bufferStruct->size()-1), loc);
|
||||
|
||||
TIntermTyped* argArray = intermediate.addIndex(EOpIndexDirectStruct, buffer, arrayPosition, loc);
|
||||
argArray->setType(*(*bufferStruct)[bufferStruct->size()-1].type);
|
||||
|
||||
return argArray;
|
||||
}
|
||||
|
||||
//
|
||||
// IFF type is a structuredbuffer/byteaddressbuffer type, return the content
|
||||
// (template) type. E.g, StructuredBuffer<MyType> -> MyType. Else return nullptr.
|
||||
//
|
||||
TType* HlslParseContext::getStructBufferContentType(const TType& type) const
|
||||
{
|
||||
if (type.getBasicType() != EbtBlock)
|
||||
return nullptr;
|
||||
|
||||
const int memberCount = type.getStruct()->size();
|
||||
assert(memberCount > 0);
|
||||
|
||||
TType* contentType = (*type.getStruct())[memberCount-1].type;
|
||||
|
||||
return contentType->isRuntimeSizedArray() ? contentType : nullptr;
|
||||
}
|
||||
|
||||
//
|
||||
// If an existing struct buffer has a sharable type, then share it.
|
||||
//
|
||||
void HlslParseContext::shareStructBufferType(TType& type)
|
||||
{
|
||||
// PackOffset must be equivalent to share types on a per-member basis.
|
||||
// Note: cannot use auto type due to recursion. Thus, this is a std::function.
|
||||
const std::function<bool(TType& lhs, TType& rhs)>
|
||||
compareQualifiers = [&](TType& lhs, TType& rhs) -> bool {
|
||||
if (lhs.getQualifier().layoutOffset != rhs.getQualifier().layoutOffset)
|
||||
return false;
|
||||
|
||||
if (lhs.isStruct() != rhs.isStruct())
|
||||
return false;
|
||||
|
||||
if (lhs.isStruct() && rhs.isStruct()) {
|
||||
if (lhs.getStruct()->size() != rhs.getStruct()->size())
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < int(lhs.getStruct()->size()); ++i)
|
||||
if (!compareQualifiers(*(*lhs.getStruct())[i].type, *(*rhs.getStruct())[i].type))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// We need to compare certain qualifiers in addition to the type.
|
||||
const auto typeEqual = [compareQualifiers](TType& lhs, TType& rhs) -> bool {
|
||||
if (lhs.getQualifier().readonly != rhs.getQualifier().readonly)
|
||||
return false;
|
||||
|
||||
// If both are structures, recursively look for packOffset equality
|
||||
// as well as type equality.
|
||||
return compareQualifiers(lhs, rhs) && lhs == rhs;
|
||||
};
|
||||
|
||||
// TString typeName;
|
||||
// type.appendMangledName(typeName);
|
||||
// type.setTypeName(typeName);
|
||||
|
||||
// This is an exhaustive O(N) search, but real world shaders have
|
||||
// only a small number of these.
|
||||
for (int idx = 0; idx < int(structBufferTypes.size()); ++idx) {
|
||||
// If the deep structure matches, modulo qualifiers, use it
|
||||
if (typeEqual(*structBufferTypes[idx], type)) {
|
||||
type.shallowCopy(*structBufferTypes[idx]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, remember it:
|
||||
TType* typeCopy = new TType;
|
||||
typeCopy->shallowCopy(type);
|
||||
structBufferTypes.push_back(typeCopy);
|
||||
|
||||
// structBuffTypes.push_back(type.getWritableStruct());
|
||||
}
|
||||
|
||||
void HlslParseContext::paramFix(TType& type)
|
||||
{
|
||||
switch (type.getQualifier().storage) {
|
||||
@@ -5043,6 +5158,18 @@ void HlslParseContext::paramFix(TType& type)
|
||||
case EvqTemporary:
|
||||
type.getQualifier().storage = EvqIn;
|
||||
break;
|
||||
case EvqBuffer:
|
||||
{
|
||||
// SSBO parameter. These do not go through the declareBlock path since they are fn parameters.
|
||||
correctUniform(type.getQualifier());
|
||||
TQualifier bufferQualifier = globalBufferDefaults;
|
||||
mergeObjectLayoutQualifiers(bufferQualifier, type.getQualifier(), true);
|
||||
bufferQualifier.storage = type.getQualifier().storage;
|
||||
bufferQualifier.readonly = type.getQualifier().readonly;
|
||||
bufferQualifier.coherent = type.getQualifier().coherent;
|
||||
type.getQualifier() = bufferQualifier;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -5914,6 +6041,7 @@ TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, TString& i
|
||||
if (it != ioTypeMap.end())
|
||||
type.setStruct(it->second.uniform);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -6551,8 +6679,6 @@ void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TS
|
||||
error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", "");
|
||||
}
|
||||
|
||||
if (memberQualifier.hasPacking())
|
||||
error(memberLoc, "member of block cannot have a packing layout qualifier", typeList[member].type->getFieldName().c_str(), "");
|
||||
if (memberQualifier.hasLocation()) {
|
||||
switch (type.getQualifier().storage) {
|
||||
case EvqVaryingIn:
|
||||
@@ -6564,10 +6690,6 @@ void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TS
|
||||
}
|
||||
} else
|
||||
memberWithoutLocation = true;
|
||||
if (memberQualifier.hasAlign()) {
|
||||
if (defaultQualification.layoutPacking != ElpStd140 && defaultQualification.layoutPacking != ElpStd430)
|
||||
error(memberLoc, "can only be used with std140 or std430 layout packing", "align", "");
|
||||
}
|
||||
|
||||
TQualifier newMemberQualification = defaultQualification;
|
||||
mergeQualifiers(newMemberQualification, memberQualifier);
|
||||
|
||||
Reference in New Issue
Block a user