Add Shared/Std140 SSBO process & top-level array elements related (#2231)

* Add Shared/Std140 SSBO process & top-level array elements related
process

1.Add process options for shared/std140 ssbo, following ubo process
2.Add IO Variables reflection option, would keep all input/output
variables in reflection
3.Add Top-level related process, fix top-level array size issues,
following spec
4.Split ssbo/ubo reflection options, merge blowup expanding all into
function blowupActiveAggregate to allow other functions keep same entry
format.

Add options in StandAlone and test symbols.

1. Add options in StandAlone for std140/shared ubo/ssbo and all io variables reflection.
2. Add test for ssbo. When EShReflectionSharedStd140SSBO turns on, generated symbol and output would be different, to remind the difference. Defaultly disabled and nothing would change, nor blocking normal test.

* Add options in runtest script, refresh test results.

Add options in StandAlone:
--reflect-all-io-variables --reflect-shared-std140-ubo --reflect-shared-std140-ssbo

refresh test results.
Now the index, size of unsized array are expected.
This commit is contained in:
Chow 2020-06-04 15:47:18 +08:00 committed by GitHub
parent ff6dcca575
commit 8111268575
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 222 additions and 161 deletions

View file

@ -164,7 +164,9 @@ typedef enum {
GLSLANG_REFLECTION_SEPARATE_BUFFERS_BIT = (1 << 3),
GLSLANG_REFLECTION_ALL_BLOCK_VARIABLES_BIT = (1 << 4),
GLSLANG_REFLECTION_UNWRAP_IO_BLOCKS_BIT = (1 << 5),
GLSLANG_REFLECTION_SHARED_STD140_BLOCKS_BIT = (1 << 6),
GLSLANG_REFLECTION_ALL_IO_VARIABLES_BIT = (1 << 6),
GLSLANG_REFLECTION_SHARED_STD140_SSBO_BIT = (1 << 7),
GLSLANG_REFLECTION_SHARED_STD140_UBO_BIT = (1 << 8),
LAST_ELEMENT_MARKER(GLSLANG_REFLECTION_COUNT),
} glslang_reflection_options_t;

View file

@ -309,26 +309,43 @@ struct TSymbolValidater
TIntermSymbol* base = ent1.symbol;
const TType& type = ent1.symbol->getType();
const TString& name = entKey.first;
TString mangleName1, mangleName2;
type.appendMangledName(mangleName1);
EShLanguage stage = ent1.stage;
TString mangleName1, mangleName2;
if (currentStage != stage) {
preStage = currentStage;
currentStage = stage;
nextStage = EShLangCount;
for (int i = currentStage + 1; i < EShLangCount; i++) {
if (inVarMaps[i] != nullptr)
if (inVarMaps[i] != nullptr) {
nextStage = static_cast<EShLanguage>(i);
break;
}
}
}
if (type.getQualifier().isArrayedIo(stage)) {
TType subType(type, 0);
subType.appendMangledName(mangleName1);
} else {
type.appendMangledName(mangleName1);
}
if (base->getQualifier().storage == EvqVaryingIn) {
// validate stage in;
if (preStage == EShLangCount)
return;
if (name == "gl_PerVertex")
return;
if (outVarMaps[preStage] != nullptr) {
auto ent2 = outVarMaps[preStage]->find(name);
if (ent2 != outVarMaps[preStage]->end()) {
ent2->second.symbol->getType().appendMangledName(mangleName2);
if (ent2->second.symbol->getType().getQualifier().isArrayedIo(preStage)) {
TType subType(ent2->second.symbol->getType(), 0);
subType.appendMangledName(mangleName2);
}
else {
ent2->second.symbol->getType().appendMangledName(mangleName2);
}
if (mangleName1 == mangleName2)
return;
else {
@ -343,10 +360,18 @@ struct TSymbolValidater
// validate stage out;
if (nextStage == EShLangCount)
return;
if (name == "gl_PerVertex")
return;
if (outVarMaps[nextStage] != nullptr) {
auto ent2 = inVarMaps[nextStage]->find(name);
if (ent2 != inVarMaps[nextStage]->end()) {
ent2->second.symbol->getType().appendMangledName(mangleName2);
if (ent2->second.symbol->getType().getQualifier().isArrayedIo(nextStage)) {
TType subType(ent2->second.symbol->getType(), 0);
subType.appendMangledName(mangleName2);
}
else {
ent2->second.symbol->getType().appendMangledName(mangleName2);
}
if (mangleName1 == mangleName2)
return;
else {

View file

@ -1550,7 +1550,9 @@ int TIntermediate::getBaseAlignment(const TType& type, int& size, int& stride, T
RoundToPow2(size, alignment);
stride = size; // uses full matrix size for stride of an array of matrices (not quite what rule 6/8, but what's expected)
// uses the assumption for rule 10 in the comment above
size = stride * type.getOuterArraySize();
// use one element to represent the last member of SSBO which is unsized array
int arraySize = (type.isUnsizedArray() && (type.getOuterArraySize() == 0)) ? 1 : type.getOuterArraySize();
size = stride * arraySize;
return alignment;
}

View file

@ -107,22 +107,13 @@ public:
else
baseName = "";
if (base.getType().isArray()) {
TType derefType(base.getType(), 0);
assert(!anonymous);
for (int e = 0; e < base.getType().getCumulativeArraySize(); ++e)
blockIndex = addBlockName(blockName + "[" + String(e) + "]", derefType,
intermediate.getBlockSize(base.getType()));
}
else
blockIndex = addBlockName(blockName, base.getType(), intermediate.getBlockSize(base.getType()));
blockIndex = addBlockName(blockName, base.getType(), intermediate.getBlockSize(base.getType()));
}
// Use a degenerate (empty) set of dereferences to immediately put as at the end of
// the dereference change expected by blowUpActiveAggregate.
blowUpActiveAggregate(base.getType(), baseName, derefs, derefs.end(), offset, blockIndex, 0, 0,
base.getQualifier().storage, updateStageMasks);
blowUpActiveAggregate(base.getType(), baseName, derefs, derefs.end(), offset, blockIndex, 0, -1, 0,
base.getQualifier().storage, updateStageMasks);
}
}
@ -259,7 +250,7 @@ public:
// A value of 0 for arraySize will mean to use the full array's size.
void blowUpActiveAggregate(const TType& baseType, const TString& baseName, const TList<TIntermBinary*>& derefs,
TList<TIntermBinary*>::const_iterator deref, int offset, int blockIndex, int arraySize,
int topLevelArrayStride, TStorageQualifier baseStorage, bool active)
int topLevelArraySize, int topLevelArrayStride, TStorageQualifier baseStorage, bool active)
{
// when strictArraySuffix is enabled, we closely follow the rules from ARB_program_interface_query.
// Broadly:
@ -288,14 +279,15 @@ public:
// Visit all the indices of this array, and for each one add on the remaining dereferencing
for (int i = 0; i < std::max(visitNode->getLeft()->getType().getOuterArraySize(), 1); ++i) {
TString newBaseName = name;
if (strictArraySuffix && blockParent)
if (terminalType->getBasicType() == EbtBlock) {}
else if (strictArraySuffix && blockParent)
newBaseName.append(TString("[0]"));
else if (strictArraySuffix || baseType.getBasicType() != EbtBlock)
newBaseName.append(TString("[") + String(i) + "]");
TList<TIntermBinary*>::const_iterator nextDeref = deref;
++nextDeref;
blowUpActiveAggregate(*terminalType, newBaseName, derefs, nextDeref, offset, blockIndex, arraySize,
topLevelArrayStride, baseStorage, active);
topLevelArraySize, topLevelArrayStride, baseStorage, active);
if (offset >= 0)
offset += stride;
@ -308,9 +300,10 @@ public:
int stride = getArrayStride(baseType, visitNode->getLeft()->getType());
index = visitNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
if (strictArraySuffix && blockParent) {
if (terminalType->getBasicType() == EbtBlock) {}
else if (strictArraySuffix && blockParent)
name.append(TString("[0]"));
} else if (strictArraySuffix || baseType.getBasicType() != EbtBlock) {
else if (strictArraySuffix || baseType.getBasicType() != EbtBlock) {
name.append(TString("[") + String(index) + "]");
if (offset >= 0)
@ -320,7 +313,10 @@ public:
if (topLevelArrayStride == 0)
topLevelArrayStride = stride;
blockParent = false;
// expand top-level arrays in blocks with [0] suffix
if (topLevelArrayStride != 0 && visitNode->getLeft()->getType().isArray()) {
blockParent = false;
}
break;
}
case EOpIndexDirectStruct:
@ -330,6 +326,12 @@ public:
if (name.size() > 0)
name.append(".");
name.append((*visitNode->getLeft()->getType().getStruct())[index].type->getFieldName());
// expand non top-level arrays with [x] suffix
if (visitNode->getLeft()->getType().getBasicType() != EbtBlock && terminalType->isArray())
{
blockParent = false;
}
break;
default:
break;
@ -349,14 +351,16 @@ public:
if (offset >= 0)
stride = getArrayStride(baseType, *terminalType);
if (topLevelArrayStride == 0)
topLevelArrayStride = stride;
int arrayIterateSize = std::max(terminalType->getOuterArraySize(), 1);
// for top-level arrays in blocks, only expand [0] to avoid explosion of items
if (strictArraySuffix && blockParent)
if ((strictArraySuffix && blockParent) ||
((topLevelArraySize == arrayIterateSize) && (topLevelArrayStride == 0))) {
arrayIterateSize = 1;
}
if (topLevelArrayStride == 0)
topLevelArrayStride = stride;
for (int i = 0; i < arrayIterateSize; ++i) {
TString newBaseName = name;
@ -367,7 +371,7 @@ public:
offset = baseOffset + stride * i;
blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), offset, blockIndex, 0,
topLevelArrayStride, baseStorage, active);
topLevelArraySize, topLevelArrayStride, baseStorage, active);
}
} else {
// Visit all members of this aggregate, and for each one,
@ -396,8 +400,31 @@ public:
arrayStride = getArrayStride(baseType, derefType);
}
blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), offset, blockIndex, 0,
arrayStride, baseStorage, active);
if (topLevelArraySize == -1 && arrayStride == 0 && blockParent)
topLevelArraySize = 1;
if (strictArraySuffix && blockParent) {
// if this member is an array, store the top-level array stride but start the explosion from
// the inner struct type.
if (derefType.isArray() && derefType.isStruct()) {
newBaseName.append("[0]");
auto dimSize = derefType.isUnsizedArray() ? 0 : derefType.getArraySizes()->getDimSize(0);
blowUpActiveAggregate(TType(derefType, 0), newBaseName, derefs, derefs.end(), memberOffsets[i],
blockIndex, 0, dimSize, arrayStride, terminalType->getQualifier().storage, false);
}
else if (derefType.isArray()) {
auto dimSize = derefType.isUnsizedArray() ? 0 : derefType.getArraySizes()->getDimSize(0);
blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), memberOffsets[i], blockIndex,
0, dimSize, 0, terminalType->getQualifier().storage, false);
}
else {
blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), memberOffsets[i], blockIndex,
0, 1, 0, terminalType->getQualifier().storage, false);
}
} else {
blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), offset, blockIndex, 0,
topLevelArraySize, arrayStride, baseStorage, active);
}
}
}
@ -433,6 +460,7 @@ public:
if ((reflection.options & EShReflectionSeparateBuffers) && terminalType->isAtomic())
reflection.atomicCounterUniformIndices.push_back(uniformIndex);
variables.back().topLevelArraySize = topLevelArraySize;
variables.back().topLevelArrayStride = topLevelArrayStride;
if ((reflection.options & EShReflectionAllBlockVariables) && active) {
@ -564,65 +592,17 @@ public:
if (! anonymous)
baseName = blockName;
if (base->getType().isArray()) {
TType derefType(base->getType(), 0);
assert(! anonymous);
for (int e = 0; e < base->getType().getCumulativeArraySize(); ++e)
blockIndex = addBlockName(blockName + "[" + String(e) + "]", derefType,
intermediate.getBlockSize(base->getType()));
baseName.append(TString("[0]"));
} else
blockIndex = addBlockName(blockName, base->getType(), intermediate.getBlockSize(base->getType()));
blockIndex = addBlockName(blockName, base->getType(), intermediate.getBlockSize(base->getType()));
if (reflection.options & EShReflectionAllBlockVariables) {
// Use a degenerate (empty) set of dereferences to immediately put as at the end of
// the dereference change expected by blowUpActiveAggregate.
TList<TIntermBinary*> derefs;
// because we don't have any derefs, the first thing blowUpActiveAggregate will do is iterate over each
// member in the struct definition. This will lose any information about whether the parent was a buffer
// block. So if we're using strict array rules which don't expand the first child of a buffer block we
// instead iterate over the children here.
const bool strictArraySuffix = (reflection.options & EShReflectionStrictArraySuffix);
bool blockParent = (base->getType().getBasicType() == EbtBlock && base->getQualifier().storage == EvqBuffer);
if (strictArraySuffix && blockParent) {
TType structDerefType(base->getType(), 0);
const TType &structType = base->getType().isArray() ? structDerefType : base->getType();
const TTypeList& typeList = *structType.getStruct();
TVector<int> memberOffsets;
memberOffsets.resize(typeList.size());
getOffsets(structType, memberOffsets);
for (int i = 0; i < (int)typeList.size(); ++i) {
TType derefType(structType, i);
TString name = baseName;
if (name.size() > 0)
name.append(".");
name.append(typeList[i].type->getFieldName());
// if this member is an array, store the top-level array stride but start the explosion from
// the inner struct type.
if (derefType.isArray() && derefType.isStruct()) {
name.append("[0]");
blowUpActiveAggregate(TType(derefType, 0), name, derefs, derefs.end(), memberOffsets[i],
blockIndex, 0, getArrayStride(structType, derefType),
base->getQualifier().storage, false);
} else {
blowUpActiveAggregate(derefType, name, derefs, derefs.end(), memberOffsets[i], blockIndex,
0, 0, base->getQualifier().storage, false);
}
}
} else {
// otherwise - if we're not using strict array suffix rules, or this isn't a block so we are
// expanding root arrays anyway, just start the iteration from the base block type.
blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.end(), 0, blockIndex, 0, 0,
// otherwise - if we're not using strict array suffix rules, or this isn't a block so we are
// expanding root arrays anyway, just start the iteration from the base block type.
blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.end(), 0, blockIndex, 0, -1, 0,
base->getQualifier().storage, false);
}
}
}
@ -653,31 +633,37 @@ public:
else
baseName = base->getName();
}
blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.begin(), offset, blockIndex, arraySize, 0,
blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.begin(), offset, blockIndex, arraySize, -1, 0,
base->getQualifier().storage, true);
}
int addBlockName(const TString& name, const TType& type, int size)
{
TReflection::TMapIndexToReflection& blocks = reflection.GetBlockMapForStorage(type.getQualifier().storage);
int blockIndex;
TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str());
if (reflection.nameToIndex.find(name.c_str()) == reflection.nameToIndex.end()) {
blockIndex = (int)blocks.size();
reflection.nameToIndex[name.c_str()] = blockIndex;
blocks.push_back(TObjectReflection(name.c_str(), type, -1, -1, size, -1));
if (type.isArray()) {
TType derefType(type, 0);
for (int e = 0; e < type.getOuterArraySize(); ++e) {
uint32_t memberBlockIndex = addBlockName(name + "[" + String(e) + "]", derefType, size);
if (e == 0)
blockIndex = memberBlockIndex;
}
} else {
TReflection::TMapIndexToReflection& blocks = reflection.GetBlockMapForStorage(type.getQualifier().storage);
blocks.back().numMembers = countAggregateMembers(type);
TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str());
if (reflection.nameToIndex.find(name.c_str()) == reflection.nameToIndex.end()) {
blockIndex = (int)blocks.size();
reflection.nameToIndex[name.c_str()] = blockIndex;
blocks.push_back(TObjectReflection(name.c_str(), type, -1, -1, size, blockIndex));
blocks.back().numMembers = countAggregateMembers(type);
if (updateStageMasks) {
EShLanguageMask& stages = blocks.back().stages;
stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
}
} else {
blockIndex = it->second;
else {
blockIndex = it->second;
if (updateStageMasks) {
EShLanguageMask& stages = blocks[blockIndex].stages;
stages = static_cast<EShLanguageMask>(stages | 1 << intermediate.getStage());
}
@ -1064,7 +1050,7 @@ void TReflectionTraverser::visitSymbol(TIntermSymbol* base)
{
if (base->getQualifier().storage == EvqUniform) {
if (base->getBasicType() == EbtBlock) {
if (reflection.options & EShReflectionSharedStd140Blocks) {
if (reflection.options & EShReflectionSharedStd140UBO) {
addUniform(*base);
}
} else {
@ -1072,6 +1058,13 @@ void TReflectionTraverser::visitSymbol(TIntermSymbol* base)
}
}
// #TODO add std140/layout active rules for ssbo, same with ubo.
// Storage buffer blocks will be collected and expanding in this part.
if((reflection.options & EShReflectionSharedStd140SSBO) &&
(base->getQualifier().storage == EvqBuffer && base->getBasicType() == EbtBlock &&
(base->getQualifier().layoutPacking == ElpStd140 || base->getQualifier().layoutPacking == ElpShared)))
addUniform(*base);
if ((intermediate.getStage() == reflection.firstStage && base->getQualifier().isPipeInput()) ||
(intermediate.getStage() == reflection.lastStage && base->getQualifier().isPipeOutput()))
addPipeIOVariable(*base);
@ -1182,15 +1175,23 @@ bool TReflection::addStage(EShLanguage stage, const TIntermediate& intermediate)
TIntermAggregate* linkerObjects = sequnence->getAsAggregate();
for (auto& sequnence : linkerObjects->getSequence()) {
auto pNode = sequnence->getAsSymbolNode();
if (pNode != nullptr && pNode->getQualifier().storage == EvqUniform &&
(options & EShReflectionSharedStd140Blocks)) {
if (pNode->getBasicType() == EbtBlock) {
if (pNode != nullptr) {
if ((pNode->getQualifier().storage == EvqUniform &&
(options & EShReflectionSharedStd140UBO)) ||
(pNode->getQualifier().storage == EvqBuffer &&
(options & EShReflectionSharedStd140SSBO))) {
// collect std140 and shared uniform block form AST
if (pNode->getQualifier().layoutPacking == ElpStd140 ||
pNode->getQualifier().layoutPacking == ElpShared) {
pNode->traverse(&it);
if ((pNode->getBasicType() == EbtBlock) &&
((pNode->getQualifier().layoutPacking == ElpStd140) ||
(pNode->getQualifier().layoutPacking == ElpShared))) {
pNode->traverse(&it);
}
}
else if ((options & EShReflectionAllIOVariables) &&
(pNode->getQualifier().isPipeInput() || pNode->getQualifier().isPipeOutput()))
{
pNode->traverse(&it);
}
}
}
} else {

View file

@ -271,7 +271,9 @@ typedef enum {
EShReflectionSeparateBuffers = (1 << 3), // buffer variables and buffer blocks are reflected separately
EShReflectionAllBlockVariables = (1 << 4), // reflect all variables in blocks, even if they are inactive
EShReflectionUnwrapIOBlocks = (1 << 5), // unwrap input/output blocks the same as with uniform blocks
EShReflectionSharedStd140Blocks = (1 << 6), // Apply std140/shared rules for ubo to ssbo
EShReflectionAllIOVariables = (1 << 6), // reflect all input/output variables, even if they are inactive
EShReflectionSharedStd140SSBO = (1 << 7), // Apply std140/shared rules for ubo to ssbo
EShReflectionSharedStd140UBO = (1 << 8), // Apply std140/shared rules for ubo to ssbo
LAST_ELEMENT_MARKER(EShReflectionCount),
} EShReflectionOptions;
@ -696,6 +698,7 @@ public:
int counterIndex;
int numMembers;
int arrayStride; // stride of an array variable
int topLevelArraySize; // size of the top-level variable in a storage buffer member
int topLevelArrayStride; // stride of the top-level variable in a storage buffer member
EShLanguageMask stages;