From 1905069857786f6baab96324e7904145f370d441 Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 11 Feb 2019 11:50:24 +0000 Subject: [PATCH] Add option to unwrap I/O block aggregates in reflection * We follow similar rules to uniform block exploding. --- StandAlone/StandAlone.cpp | 4 + Test/baseResults/reflection.linked.options.out | 4 +- Test/baseResults/reflection.options.frag.out | 2 +- Test/baseResults/reflection.options.geom.out | 25 +++++ Test/baseResults/reflection.options.vert.out | 7 +- Test/reflection.options.geom | 31 ++++++ Test/reflection.options.vert | 11 ++- Test/runtests | 8 +- glslang/MachineIndependent/reflection.cpp | 127 ++++++++++++++++++------- glslang/Public/ShaderLang.h | 1 + 10 files changed, 176 insertions(+), 44 deletions(-) create mode 100644 Test/baseResults/reflection.options.geom.out create mode 100644 Test/reflection.options.geom diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index 740ec7b..bb37a84 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -534,6 +534,8 @@ void ProcessArguments(std::vector>& workItem ReflectOptions |= EShReflectionSeparateBuffers; } else if (lowerword == "reflect-all-block-variables") { ReflectOptions |= EShReflectionAllBlockVariables; + } else if (lowerword == "reflect-unwrap-io-blocks") { + ReflectOptions |= EShReflectionUnwrapIOBlocks; } else if (lowerword == "resource-set-bindings" || // synonyms lowerword == "resource-set-binding" || lowerword == "rsb") { @@ -1538,6 +1540,8 @@ void usage() " separately to uniforms\n" " --reflect-all-block-variables reflect all variables in blocks, whether\n" " inactive or active\n" + " --reflect-unwrap-io-blocks unwrap input/output blocks the same as\n" + " uniform blocks\n" " --resource-set-binding [stage] name set binding\n" " set descriptor set and binding for\n" " individual resources\n" diff --git a/Test/baseResults/reflection.linked.options.out b/Test/baseResults/reflection.linked.options.out index b121626..af07bbd 100644 --- a/Test/baseResults/reflection.linked.options.out +++ b/Test/baseResults/reflection.linked.options.out @@ -14,8 +14,8 @@ Buffer variable reflection: Buffer block reflection: Pipeline input reflection: -vertin: offset 0, type 1406, size 0, index 0, binding -1, stages 1 +vertin: offset 0, type 1406, size 1, index 0, binding -1, stages 1 Pipeline output reflection: -fragout: offset 0, type 1406, size 0, index 0, binding -1, stages 16 +fragout: offset 0, type 1406, size 1, index 0, binding -1, stages 16 diff --git a/Test/baseResults/reflection.options.frag.out b/Test/baseResults/reflection.options.frag.out index f3ea4ca..ec12392 100644 --- a/Test/baseResults/reflection.options.frag.out +++ b/Test/baseResults/reflection.options.frag.out @@ -8,7 +8,7 @@ Buffer variable reflection: Buffer block reflection: Pipeline input reflection: -inval: offset 0, type 1406, size 0, index 0, binding -1, stages 16 +inval: offset 0, type 1406, size 1, index 0, binding -1, stages 16 Pipeline output reflection: diff --git a/Test/baseResults/reflection.options.geom.out b/Test/baseResults/reflection.options.geom.out new file mode 100644 index 0000000..7ffe82d --- /dev/null +++ b/Test/baseResults/reflection.options.geom.out @@ -0,0 +1,25 @@ +reflection.options.geom +Uniform reflection: + +Uniform block reflection: + +Buffer variable reflection: + +Buffer block reflection: + +Pipeline input reflection: +gl_PerVertex.gl_Position: offset 0, type 8b52, size 1, index 0, binding -1, stages 8 +gl_PerVertex.gl_PointSize: offset 0, type 1406, size 1, index 0, binding -1, stages 8 +gl_PerVertex.gl_ClipDistance[0]: offset 0, type 1406, size 1, index 0, binding -1, stages 8 +block.Color: offset 0, type 8b50, size 1, index 0, binding -1, stages 8 +block.Texcoord: offset 0, type 8b50, size 1, index 0, binding -1, stages 8 +block.in_a: offset 0, type 8b54, size 1, index 0, binding -1, stages 8 + +Pipeline output reflection: +gl_Position: offset 0, type 8b52, size 1, index 0, binding -1, stages 8 +gl_PointSize: offset 0, type 1406, size 1, index 0, binding -1, stages 8 +gl_ClipDistance[0]: offset 0, type 1406, size 1, index 0, binding -1, stages 8 +block.Color: offset 0, type 8b52, size 1, index 0, binding -1, stages 8 +block.a: offset 0, type 8b52, size 1, index 0, binding -1, stages 8 +block.b[0]: offset 0, type 8b50, size 3, index 0, binding -1, stages 8 + diff --git a/Test/baseResults/reflection.options.vert.out b/Test/baseResults/reflection.options.vert.out index 0a3b118..45a7a87 100644 --- a/Test/baseResults/reflection.options.vert.out +++ b/Test/baseResults/reflection.options.vert.out @@ -33,8 +33,11 @@ VertexCollection: offset -1, type ffffffff, size 400, index -1, binding -1, stag MultipleArrays: offset -1, type ffffffff, size 500, index -1, binding -1, stages 1, numMembers 9 Pipeline input reflection: -gl_InstanceID: offset 0, type 1404, size 0, index 0, binding -1, stages 1 +gl_InstanceID: offset 0, type 1404, size 1, index 0, binding -1, stages 1 Pipeline output reflection: -outval: offset 0, type 1406, size 0, index 0, binding -1, stages 1 +outval.val: offset 0, type 1406, size 1, index 0, binding -1, stages 1 +outval.a: offset 0, type 8b51, size 1, index 0, binding -1, stages 1 +outval.b[0]: offset 0, type 8b50, size 4, index 0, binding -1, stages 1 +outval.c: offset 0, type 8b5a, size 1, index 0, binding -1, stages 1 diff --git a/Test/reflection.options.geom b/Test/reflection.options.geom new file mode 100644 index 0000000..76ba3c0 --- /dev/null +++ b/Test/reflection.options.geom @@ -0,0 +1,31 @@ +#version 330 core + +precision highp float; + +layout(triangles) in; +layout(triangle_strip, max_vertices = 4) out; + +in block +{ + vec2 Color; + vec2 Texcoord; + flat ivec3 in_a; +} In[]; + +out block +{ + vec4 Color; + vec4 a; + vec2 b[3]; +} Out; + +void main() +{ + for(int i = 0; i < gl_in.length(); ++i) + { + gl_Position = gl_in[i].gl_Position; + Out.Color = vec4(In[i].Color, 0, 1); + EmitVertex(); + } + EndPrimitive(); +} diff --git a/Test/reflection.options.vert b/Test/reflection.options.vert index b8ecc89..38c82ba 100644 --- a/Test/reflection.options.vert +++ b/Test/reflection.options.vert @@ -26,7 +26,14 @@ uniform UBO { uvec4 unused; } ubo; -out float outval; +struct OutputStruct { + float val; + vec3 a; + vec2 b[4]; + mat2x2 c; +}; + +out OutputStruct outval; void main() { @@ -40,5 +47,5 @@ void main() f += ubo.verts[gl_InstanceID].position[0]; f += ubo.flt[gl_InstanceID]; TriangleInfo tlocal[5] = t; - outval = f; + outval.val = f; } diff --git a/Test/runtests b/Test/runtests index f659da4..cc34306 100755 --- a/Test/runtests +++ b/Test/runtests @@ -32,15 +32,17 @@ diff -b $BASEDIR/badMacroArgs.frag.out $TARGETDIR/badMacroArgs.frag.out || HASER echo Running reflection... $EXE -l -q -C reflection.vert > $TARGETDIR/reflection.vert.out diff -b $BASEDIR/reflection.vert.out $TARGETDIR/reflection.vert.out || HASERROR=1 -$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io --reflect-separate-buffers --reflect-all-block-variables reflection.options.vert > $TARGETDIR/reflection.options.vert.out +$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io --reflect-separate-buffers --reflect-all-block-variables --reflect-unwrap-io-blocks reflection.options.vert > $TARGETDIR/reflection.options.vert.out diff -b $BASEDIR/reflection.options.vert.out $TARGETDIR/reflection.options.vert.out || HASERROR=1 $EXE -l -q -C reflection.frag > $TARGETDIR/reflection.frag.out diff -b $BASEDIR/reflection.frag.out $TARGETDIR/reflection.frag.out || HASERROR=1 -$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io --reflect-separate-buffers --reflect-all-block-variables reflection.frag > $TARGETDIR/reflection.options.frag.out +$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io --reflect-separate-buffers --reflect-all-block-variables --reflect-unwrap-io-blocks reflection.frag > $TARGETDIR/reflection.options.frag.out diff -b $BASEDIR/reflection.options.frag.out $TARGETDIR/reflection.options.frag.out || HASERROR=1 +$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io --reflect-separate-buffers --reflect-all-block-variables --reflect-unwrap-io-blocks reflection.options.geom > $TARGETDIR/reflection.options.geom.out +diff -b $BASEDIR/reflection.options.geom.out $TARGETDIR/reflection.options.geom.out || HASERROR=1 $EXE -l -q -C reflection.linked.vert reflection.linked.frag > $TARGETDIR/reflection.linked.out diff -b $BASEDIR/reflection.linked.out $TARGETDIR/reflection.linked.out || HASERROR=1 -$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io --reflect-separate-buffers --reflect-all-block-variables reflection.linked.vert reflection.linked.frag > $TARGETDIR/reflection.linked.options.out +$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io --reflect-separate-buffers --reflect-all-block-variables --reflect-unwrap-io-blocks reflection.linked.vert reflection.linked.frag > $TARGETDIR/reflection.linked.options.out diff -b $BASEDIR/reflection.linked.options.out $TARGETDIR/reflection.linked.options.out || HASERROR=1 $EXE -D -Od -e flizv -l -q -C -V -Od hlsl.reflection.vert > $TARGETDIR/hlsl.reflection.vert.out diff -b $BASEDIR/hlsl.reflection.vert.out $TARGETDIR/hlsl.reflection.vert.out || HASERROR=1 diff --git a/glslang/MachineIndependent/reflection.cpp b/glslang/MachineIndependent/reflection.cpp index df81879..92fa968 100644 --- a/glslang/MachineIndependent/reflection.cpp +++ b/glslang/MachineIndependent/reflection.cpp @@ -98,46 +98,46 @@ public: } } - void addPipeInput(const TIntermSymbol& base) + void addPipeIOVariable(const TIntermSymbol& base) { if (processedDerefs.find(&base) == processedDerefs.end()) { processedDerefs.insert(&base); const TString &name = base.getName(); const TType &type = base.getType(); + const bool input = base.getQualifier().isPipeInput(); - TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str()); - if (it == reflection.nameToIndex.end()) { - reflection.nameToIndex[name.c_str()] = (int)reflection.indexToPipeInput.size(); - reflection.indexToPipeInput.push_back(TObjectReflection(name.c_str(), type, 0, mapToGlType(type), 0, 0)); + TReflection::TMapIndexToReflection &ioItems = + input ? reflection.indexToPipeInput : reflection.indexToPipeOutput; - EShLanguageMask& stages = reflection.indexToPipeInput.back().stages; - stages = static_cast(stages | 1 << intermediate.getStage()); - } else { - EShLanguageMask& stages = reflection.indexToPipeInput[it->second].stages; - stages = static_cast(stages | 1 << intermediate.getStage()); - } - } - } + if (reflection.options & EShReflectionUnwrapIOBlocks) { + bool anonymous = IsAnonymous(name); - void addPipeOutput(const TIntermSymbol& base) - { - if (processedDerefs.find(&base) == processedDerefs.end()) { - processedDerefs.insert(&base); - - const TString &name = base.getName(); - const TType &type = base.getType(); - - TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str()); - if (it == reflection.nameToIndex.end()) { - reflection.nameToIndex[name.c_str()] = (int)reflection.indexToPipeOutput.size(); - reflection.indexToPipeOutput.push_back(TObjectReflection(name.c_str(), type, 0, mapToGlType(type), 0, 0)); + TString baseName; + if (type.getBasicType() == EbtBlock) { + baseName = anonymous ? TString() : type.getTypeName(); + } else { + baseName = anonymous ? TString() : name; + } - EShLanguageMask& stages = reflection.indexToPipeOutput.back().stages; - stages = static_cast(stages | 1 << intermediate.getStage()); + // by convention if this is an arrayed block we ignore the array in the reflection + if (type.isArray()) { + blowUpIOAggregate(input, baseName, TType(type, 0)); + } else { + blowUpIOAggregate(input, baseName, type); + } } else { - EShLanguageMask& stages = reflection.indexToPipeOutput[it->second].stages; - stages = static_cast(stages | 1 << intermediate.getStage()); + TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str()); + if (it == reflection.nameToIndex.end()) { + reflection.nameToIndex[name.c_str()] = (int)ioItems.size(); + ioItems.push_back(TObjectReflection(name.c_str(), type, 0, mapToGlType(type), 0, 0)); + + EShLanguageMask& stages = ioItems.back().stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } else { + EShLanguageMask& stages = ioItems[it->second].stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } } } } @@ -473,6 +473,67 @@ public: } } } + + // similar to blowUpActiveAggregate, but with simpler rules and no dereferences to follow. + void blowUpIOAggregate(bool input, const TString &baseName, const TType &type) + { + TString name = baseName; + + // if the type is still too coarse a granularity, this is still an aggregate to expand, expand it... + if (! isReflectionGranularity(type)) { + if (type.isArray()) { + // Visit all the indices of this array, and for each one, + // fully explode the remaining aggregate to dereference + for (int i = 0; i < std::max(type.getOuterArraySize(), 1); ++i) { + TString newBaseName = name; + newBaseName.append(TString("[") + String(i) + "]"); + TType derefType(type, 0); + + blowUpIOAggregate(input, newBaseName, derefType); + } + } else { + // Visit all members of this aggregate, and for each one, + // fully explode the remaining aggregate to dereference + const TTypeList& typeList = *type.getStruct(); + + for (int i = 0; i < (int)typeList.size(); ++i) { + TString newBaseName = name; + if (newBaseName.size() > 0) + newBaseName.append("."); + newBaseName.append(typeList[i].type->getFieldName()); + TType derefType(type, i); + + blowUpIOAggregate(input, newBaseName, derefType); + } + } + + // it was all completed in the recursive calls above + return; + } + + if ((reflection.options & EShReflectionBasicArraySuffix) && type.isArray()) { + name.append(TString("[0]")); + } + + TReflection::TMapIndexToReflection &ioItems = + input ? reflection.indexToPipeInput : reflection.indexToPipeOutput; + + std::string namespacedName = input ? "in " : "out "; + namespacedName += name.c_str(); + + TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(namespacedName); + if (it == reflection.nameToIndex.end()) { + reflection.nameToIndex[namespacedName] = (int)ioItems.size(); + ioItems.push_back( + TObjectReflection(name.c_str(), type, 0, mapToGlType(type), mapToGlArraySize(type), 0)); + + EShLanguageMask& stages = ioItems.back().stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } else { + EShLanguageMask& stages = ioItems[it->second].stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } // Add a uniform dereference where blocks/struct/arrays are involved in the access. // Handles the situation where the left node is at the correct or too coarse a @@ -1027,11 +1088,9 @@ void TReflectionTraverser::visitSymbol(TIntermSymbol* base) if (base->getQualifier().storage == EvqUniform) addUniform(*base); - if (intermediate.getStage() == reflection.firstStage && base->getQualifier().isPipeInput()) - addPipeInput(*base); - - if (intermediate.getStage() == reflection.lastStage && base->getQualifier().isPipeOutput()) - addPipeOutput(*base); + if ((intermediate.getStage() == reflection.firstStage && base->getQualifier().isPipeInput()) || + (intermediate.getStage() == reflection.lastStage && base->getQualifier().isPipeOutput())) + addPipeIOVariable(*base); } // diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index b40acef..0c25569 100755 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -246,6 +246,7 @@ typedef enum { EShReflectionIntermediateIO = (1 << 2), // reflect inputs and outputs to program, even with no vertex shader 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 } EShReflectionOptions; // -- 2.7.4