Ver Fonte

add cross-stage check for missing outputs

If an 'in' is present in a shader stage, make sure a matching 'out'
is present in the previous stage. Only enabled when doing Vulkan.

This commit also fixes a bug where previous stage's linkerObjects
got polluted with 'in' variables from the next stage when merging
linker objects.
Malcolm Bechard há 1 ano atrás
pai
commit
69249e46b6

+ 313 - 0
Test/baseResults/iomap.crossStage.vk.2.vert.out

@@ -0,0 +1,313 @@
+iomap.crossStage.vk.2.vert
+Shader version: 460
+0:? Sequence
+0:8  Function Definition: main( ( global void)
+0:8    Function Parameters: 
+0:10    Sequence
+0:10      move second child to first child ( temp highp 4-component vector of float)
+0:10        val: direct index for structure ( out highp 4-component vector of float)
+0:10          'anon@0' ( out block{ out highp 4-component vector of float val})
+0:10          Constant:
+0:10            0 (const uint)
+0:10        Constant:
+0:10          0.500000
+0:10          0.500000
+0:10          0.500000
+0:10          0.500000
+0:11      move second child to first child ( temp highp 4-component vector of float)
+0:11        'color' ( smooth out highp 4-component vector of float)
+0:11        Constant:
+0:11          1.000000
+0:11          1.000000
+0:11          1.000000
+0:11          1.000000
+0:12      move second child to first child ( temp 4-component vector of float)
+0:12        gl_Position: direct index for structure ( gl_Position 4-component vector of float Position)
+0:12          'anon@1' ( out block{ gl_Position 4-component vector of float Position gl_Position,  gl_PointSize float PointSize gl_PointSize,  out unsized 1-element array of float ClipDistance gl_ClipDistance,  out unsized 1-element array of float CullDistance gl_CullDistance})
+0:12          Constant:
+0:12            0 (const uint)
+0:12        Constant:
+0:12          1.000000
+0:12          1.000000
+0:12          1.000000
+0:12          1.000000
+0:?   Linker Objects
+0:?     'anon@0' ( out block{ out highp 4-component vector of float val})
+0:?     'color' ( smooth out highp 4-component vector of float)
+0:?     'anon@1' ( out block{ gl_Position 4-component vector of float Position gl_Position,  gl_PointSize float PointSize gl_PointSize,  out unsized 1-element array of float ClipDistance gl_ClipDistance,  out unsized 1-element array of float CullDistance gl_CullDistance})
+
+iomap.crossStage.vk.2.geom
+Shader version: 460
+invocations = -1
+max_vertices = 3
+input primitive = points
+output primitive = triangle_strip
+0:? Sequence
+0:23  Function Definition: main( ( global void)
+0:23    Function Parameters: 
+0:25    Sequence
+0:25      Sequence
+0:25        Sequence
+0:25          move second child to first child ( temp highp int)
+0:25            'i' ( temp highp int)
+0:25            Constant:
+0:25              0 (const int)
+0:25        Loop with condition tested first
+0:25          Loop Condition
+0:25          Compare Less Than ( temp bool)
+0:25            'i' ( temp highp int)
+0:25            Constant:
+0:25              3 (const int)
+0:25          Loop Body
+0:26          Sequence
+0:26            move second child to first child ( temp highp 4-component vector of float)
+0:26              'colorOut' (layout( stream=0) out highp 4-component vector of float)
+0:26              component-wise multiply ( temp highp 4-component vector of float)
+0:26                indirect index ( temp highp 4-component vector of float)
+0:26                  'color' ( in 1-element array of highp 4-component vector of float)
+0:26                  'i' ( temp highp int)
+0:26                val: direct index for structure ( in highp 4-component vector of float)
+0:26                  indirect index ( temp block{ in highp 4-component vector of float val})
+0:26                    'vv' ( in 1-element array of block{ in highp 4-component vector of float val})
+0:26                    'i' ( temp highp int)
+0:26                  Constant:
+0:26                    0 (const int)
+0:27            move second child to first child ( temp highp 4-component vector of float)
+0:27              vv2Val: direct index for structure (layout( stream=0) out highp 4-component vector of float)
+0:27                'anon@0' (layout( stream=0) out block{layout( stream=0) out highp 4-component vector of float vv2Val})
+0:27                Constant:
+0:27                  0 (const uint)
+0:27              Constant:
+0:27                1.000000
+0:27                1.000000
+0:27                1.000000
+0:27                1.000000
+0:28            EmitVertex ( global void)
+0:25          Loop Terminal Expression
+0:25          Post-Increment ( temp highp int)
+0:25            'i' ( temp highp int)
+0:30      EndPrimitive ( global void)
+0:?   Linker Objects
+0:?     'vgo1' ( in 1-element array of highp 4-component vector of float)
+0:?     'color' ( in 1-element array of highp 4-component vector of float)
+0:?     'colorOut' (layout( stream=0) out highp 4-component vector of float)
+0:?     'vv' ( in 1-element array of block{ in highp 4-component vector of float val})
+0:?     'anon@0' (layout( stream=0) out block{layout( stream=0) out highp 4-component vector of float vv2Val})
+
+iomap.crossStage.vk.2.frag
+Shader version: 460
+gl_FragCoord origin is upper left
+0:? Sequence
+0:19  Function Definition: main( ( global void)
+0:19    Function Parameters: 
+0:21    Sequence
+0:21      move second child to first child ( temp highp 4-component vector of float)
+0:21        'fragColor' ( out highp 4-component vector of float)
+0:21        add ( temp highp 4-component vector of float)
+0:21          'colorOut' ( smooth in highp 4-component vector of float)
+0:21          component-wise multiply ( temp highp 4-component vector of float)
+0:21            component-wise multiply ( temp highp 4-component vector of float)
+0:21              component-wise multiply ( temp highp 4-component vector of float)
+0:21                'unsetColor' ( smooth in highp 4-component vector of float)
+0:21                Construct vec4 ( temp highp 4-component vector of float)
+0:21                  vector swizzle ( temp highp 4-component vector of float)
+0:21                    val: direct index for structure ( in highp 2-component vector of float)
+0:21                      'iVert' ( in block{ in highp 2-component vector of float val})
+0:21                      Constant:
+0:21                        0 (const int)
+0:21                    Sequence
+0:21                      Constant:
+0:21                        0 (const int)
+0:21                      Constant:
+0:21                        0 (const int)
+0:21                      Constant:
+0:21                        1 (const int)
+0:21                      Constant:
+0:21                        1 (const int)
+0:21              Construct vec4 ( temp highp 4-component vector of float)
+0:21                vector swizzle ( temp highp 4-component vector of float)
+0:21                  val2: direct index for structure ( in highp 2-component vector of float)
+0:21                    'anon@0' ( in block{ in highp 2-component vector of float val2})
+0:21                    Constant:
+0:21                      0 (const uint)
+0:21                  Sequence
+0:21                    Constant:
+0:21                      0 (const int)
+0:21                    Constant:
+0:21                      0 (const int)
+0:21                    Constant:
+0:21                      1 (const int)
+0:21                    Constant:
+0:21                      1 (const int)
+0:22            'vv2Val' ( smooth in highp 4-component vector of float)
+0:?   Linker Objects
+0:?     'unsetColor' ( smooth in highp 4-component vector of float)
+0:?     'colorOut' ( smooth in highp 4-component vector of float)
+0:?     'fragColor' ( out highp 4-component vector of float)
+0:?     'iVert' ( in block{ in highp 2-component vector of float val})
+0:?     'anon@0' ( in block{ in highp 2-component vector of float val2})
+0:?     'vv2Val' ( smooth in highp 4-component vector of float)
+
+
+Linked vertex stage:
+
+
+Linked geometry stage:
+
+
+Linked fragment stage:
+
+ERROR: Linking vertex and geometry stages: Input 'vgo1' in geometry shader has no corresponding output in vertex shader.
+ERROR: Linking geometry and fragment stages: Input 'unsetColor' in fragment shader has no corresponding output in geometry shader.
+ERROR: Linking geometry and fragment stages: Input 'Vertex' in fragment shader has no corresponding output in geometry shader.
+ERROR: Linking geometry and fragment stages: Input 'Vertex2' in fragment shader has no corresponding output in geometry shader.
+ERROR: Linking geometry and fragment stages: Input 'vv2Val' in fragment shader has no corresponding output in geometry shader.
+
+Shader version: 460
+0:? Sequence
+0:8  Function Definition: main( ( global void)
+0:8    Function Parameters: 
+0:10    Sequence
+0:10      move second child to first child ( temp highp 4-component vector of float)
+0:10        val: direct index for structure ( out highp 4-component vector of float)
+0:10          'anon@0' ( out block{ out highp 4-component vector of float val})
+0:10          Constant:
+0:10            0 (const uint)
+0:10        Constant:
+0:10          0.500000
+0:10          0.500000
+0:10          0.500000
+0:10          0.500000
+0:11      move second child to first child ( temp highp 4-component vector of float)
+0:11        'color' ( smooth out highp 4-component vector of float)
+0:11        Constant:
+0:11          1.000000
+0:11          1.000000
+0:11          1.000000
+0:11          1.000000
+0:12      move second child to first child ( temp 4-component vector of float)
+0:12        gl_Position: direct index for structure ( gl_Position 4-component vector of float Position)
+0:12          'anon@1' ( out block{ gl_Position 4-component vector of float Position gl_Position,  gl_PointSize float PointSize gl_PointSize,  out 1-element array of float ClipDistance gl_ClipDistance,  out 1-element array of float CullDistance gl_CullDistance})
+0:12          Constant:
+0:12            0 (const uint)
+0:12        Constant:
+0:12          1.000000
+0:12          1.000000
+0:12          1.000000
+0:12          1.000000
+0:?   Linker Objects
+0:?     'anon@0' ( out block{ out highp 4-component vector of float val})
+0:?     'color' ( smooth out highp 4-component vector of float)
+0:?     'anon@1' ( out block{ gl_Position 4-component vector of float Position gl_Position,  gl_PointSize float PointSize gl_PointSize,  out 1-element array of float ClipDistance gl_ClipDistance,  out 1-element array of float CullDistance gl_CullDistance})
+Shader version: 460
+invocations = 1
+max_vertices = 3
+input primitive = points
+output primitive = triangle_strip
+0:? Sequence
+0:23  Function Definition: main( ( global void)
+0:23    Function Parameters: 
+0:25    Sequence
+0:25      Sequence
+0:25        Sequence
+0:25          move second child to first child ( temp highp int)
+0:25            'i' ( temp highp int)
+0:25            Constant:
+0:25              0 (const int)
+0:25        Loop with condition tested first
+0:25          Loop Condition
+0:25          Compare Less Than ( temp bool)
+0:25            'i' ( temp highp int)
+0:25            Constant:
+0:25              3 (const int)
+0:25          Loop Body
+0:26          Sequence
+0:26            move second child to first child ( temp highp 4-component vector of float)
+0:26              'colorOut' (layout( stream=0) out highp 4-component vector of float)
+0:26              component-wise multiply ( temp highp 4-component vector of float)
+0:26                indirect index ( temp highp 4-component vector of float)
+0:26                  'color' ( in 1-element array of highp 4-component vector of float)
+0:26                  'i' ( temp highp int)
+0:26                val: direct index for structure ( in highp 4-component vector of float)
+0:26                  indirect index ( temp block{ in highp 4-component vector of float val})
+0:26                    'vv' ( in 1-element array of block{ in highp 4-component vector of float val})
+0:26                    'i' ( temp highp int)
+0:26                  Constant:
+0:26                    0 (const int)
+0:27            move second child to first child ( temp highp 4-component vector of float)
+0:27              vv2Val: direct index for structure (layout( stream=0) out highp 4-component vector of float)
+0:27                'anon@0' (layout( stream=0) out block{layout( stream=0) out highp 4-component vector of float vv2Val})
+0:27                Constant:
+0:27                  0 (const uint)
+0:27              Constant:
+0:27                1.000000
+0:27                1.000000
+0:27                1.000000
+0:27                1.000000
+0:28            EmitVertex ( global void)
+0:25          Loop Terminal Expression
+0:25          Post-Increment ( temp highp int)
+0:25            'i' ( temp highp int)
+0:30      EndPrimitive ( global void)
+0:?   Linker Objects
+0:?     'vgo1' ( in 1-element array of highp 4-component vector of float)
+0:?     'color' ( in 1-element array of highp 4-component vector of float)
+0:?     'colorOut' (layout( stream=0) out highp 4-component vector of float)
+0:?     'vv' ( in 1-element array of block{ in highp 4-component vector of float val})
+0:?     'anon@0' (layout( stream=0) out block{layout( stream=0) out highp 4-component vector of float vv2Val})
+Shader version: 460
+gl_FragCoord origin is upper left
+0:? Sequence
+0:19  Function Definition: main( ( global void)
+0:19    Function Parameters: 
+0:21    Sequence
+0:21      move second child to first child ( temp highp 4-component vector of float)
+0:21        'fragColor' ( out highp 4-component vector of float)
+0:21        add ( temp highp 4-component vector of float)
+0:21          'colorOut' ( smooth in highp 4-component vector of float)
+0:21          component-wise multiply ( temp highp 4-component vector of float)
+0:21            component-wise multiply ( temp highp 4-component vector of float)
+0:21              component-wise multiply ( temp highp 4-component vector of float)
+0:21                'unsetColor' ( smooth in highp 4-component vector of float)
+0:21                Construct vec4 ( temp highp 4-component vector of float)
+0:21                  vector swizzle ( temp highp 4-component vector of float)
+0:21                    val: direct index for structure ( in highp 2-component vector of float)
+0:21                      'iVert' ( in block{ in highp 2-component vector of float val})
+0:21                      Constant:
+0:21                        0 (const int)
+0:21                    Sequence
+0:21                      Constant:
+0:21                        0 (const int)
+0:21                      Constant:
+0:21                        0 (const int)
+0:21                      Constant:
+0:21                        1 (const int)
+0:21                      Constant:
+0:21                        1 (const int)
+0:21              Construct vec4 ( temp highp 4-component vector of float)
+0:21                vector swizzle ( temp highp 4-component vector of float)
+0:21                  val2: direct index for structure ( in highp 2-component vector of float)
+0:21                    'anon@0' ( in block{ in highp 2-component vector of float val2})
+0:21                    Constant:
+0:21                      0 (const uint)
+0:21                  Sequence
+0:21                    Constant:
+0:21                      0 (const int)
+0:21                    Constant:
+0:21                      0 (const int)
+0:21                    Constant:
+0:21                      1 (const int)
+0:21                    Constant:
+0:21                      1 (const int)
+0:22            'vv2Val' ( smooth in highp 4-component vector of float)
+0:?   Linker Objects
+0:?     'unsetColor' ( smooth in highp 4-component vector of float)
+0:?     'colorOut' ( smooth in highp 4-component vector of float)
+0:?     'fragColor' ( out highp 4-component vector of float)
+0:?     'iVert' ( in block{ in highp 2-component vector of float val})
+0:?     'anon@0' ( in block{ in highp 2-component vector of float val2})
+0:?     'vv2Val' ( smooth in highp 4-component vector of float)
+Mismatched cross-stage IO
+
+Validation failed
+SPIR-V is not generated for failed compile or link

+ 24 - 0
Test/iomap.crossStage.vk.2.frag

@@ -0,0 +1,24 @@
+#version 460
+
+in vec4 unsetColor;
+in vec4 colorOut;
+out vec4 fragColor;
+
+in Vertex
+{
+	vec2 val;
+} iVert;
+
+in Vertex2
+{
+	vec2 val2;
+};
+
+in vec4 vv2Val;
+
+void main()
+{
+    fragColor = colorOut + unsetColor * vec4(iVert.val.xxyy) * vec4(val2.xxyy) * 
+				vv2Val;
+}
+

+ 32 - 0
Test/iomap.crossStage.vk.2.geom

@@ -0,0 +1,32 @@
+#version 460
+
+layout(points) in;
+layout(triangle_strip, max_vertices=3) out;
+
+// Not written by vertex shader
+in vec4 vgo1[];
+
+in vec4 color[];
+
+out vec4 colorOut;
+
+in VV
+{
+	vec4 val;
+} vv[];
+
+out VV2
+{
+	vec4 vv2Val;
+};
+
+void main()
+{
+    for (int i = 0; i < 3; i++) {
+        colorOut = color[i] * vv[i].val;
+		vv2Val = vec4(1.0);
+        EmitVertex();
+    }
+    EndPrimitive();
+}
+

+ 14 - 0
Test/iomap.crossStage.vk.2.vert

@@ -0,0 +1,14 @@
+#version 460
+
+out VV
+{
+	vec4 val;
+};
+out vec4 color;
+void main()
+{
+	val = vec4(0.5);
+    color = vec4(1.0);
+    gl_Position = vec4(1.0);
+}
+

+ 55 - 25
glslang/MachineIndependent/linkValidate.cpp

@@ -113,6 +113,28 @@ void TIntermediate::mergeUniformObjects(TInfoSink& infoSink, TIntermediate& unit
     mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects, unit.getStage());
 }
 
+static inline bool isSameInterface(TIntermSymbol* symbol, EShLanguage stage, TIntermSymbol* unitSymbol, EShLanguage unitStage) {
+    return // 1) same stage and same shader interface
+        (stage == unitStage && symbol->getType().getShaderInterface() == unitSymbol->getType().getShaderInterface()) ||
+        // 2) accross stages and both are uniform or buffer
+        (symbol->getQualifier().storage == EvqUniform  && unitSymbol->getQualifier().storage == EvqUniform) ||
+        (symbol->getQualifier().storage == EvqBuffer   && unitSymbol->getQualifier().storage == EvqBuffer) ||
+        // 3) in/out matched across stage boundary
+        (stage < unitStage && symbol->getQualifier().storage == EvqVaryingOut  && unitSymbol->getQualifier().storage == EvqVaryingIn) ||
+        (unitStage < stage && symbol->getQualifier().storage == EvqVaryingIn && unitSymbol->getQualifier().storage == EvqVaryingOut);
+}
+
+static bool isSameSymbol(TIntermSymbol* symbol1, EShLanguage stage1, TIntermSymbol* symbol2, EShLanguage stage2) {
+    // If they are both blocks in the same shader interface,
+    // match by the block-name, not the identifier name.
+    if (symbol1->getType().getBasicType() == EbtBlock && symbol2->getType().getBasicType() == EbtBlock) {
+        if (isSameInterface(symbol1, stage1, symbol2, stage2)) {
+            return symbol1->getType().getTypeName() == symbol2->getType().getTypeName();
+        }
+    } else if (symbol1->getName() == symbol2->getName())
+        return true;
+    return false;
+}
 //
 // do error checking on the shader boundary in / out vars
 //
@@ -137,7 +159,32 @@ void TIntermediate::checkStageIO(TInfoSink& infoSink, TIntermediate& unit) {
     // do matching and error checking
     mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects, unit.getStage());
 
-    // TODO: final check; make sure that any statically used `in` have matching `out` written to
+    // Check that all of our inputs have matching outputs from the previous stage.
+    // Only do this for Vulkan, since GL_ARB_separate_shader_objects allows for
+    // the in/out to not match
+    if (spvVersion.vulkan > 0) {
+        for (auto& nextStageInterm : unitLinkerObjects) {
+            auto* nextStageSymbol = nextStageInterm->getAsSymbolNode();
+            bool found = false;
+            for (auto& curStageInterm : linkerObjects) {
+                if (isSameSymbol(curStageInterm->getAsSymbolNode(), getStage(), nextStageSymbol, unit.getStage())) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                TString errmsg;
+                errmsg.append("Input '");
+                if (nextStageSymbol->getType().getBasicType() == EbtBlock)
+                    errmsg.append(nextStageSymbol->getType().getTypeName());
+                else
+                    errmsg.append(nextStageSymbol->getName());
+                errmsg.append("' in ").append(StageName(unit.getStage()));
+                errmsg.append(" shader has no corresponding output in ").append(StageName(getStage())).append(" shader.");
+                error(infoSink, errmsg.c_str(), unit.getStage());
+            }
+        }
+    }
 }
 
 void TIntermediate::mergeCallGraphs(TInfoSink& infoSink, TIntermediate& unit)
@@ -511,17 +558,6 @@ void TIntermediate::mergeBodies(TInfoSink& infoSink, TIntermSequence& globals, c
     globals.insert(globals.end() - 1, unitGlobals.begin(), unitGlobals.end() - 1);
 }
 
-static inline bool isSameInterface(TIntermSymbol* symbol, EShLanguage stage, TIntermSymbol* unitSymbol, EShLanguage unitStage) {
-    return // 1) same stage and same shader interface
-        (stage == unitStage && symbol->getType().getShaderInterface() == unitSymbol->getType().getShaderInterface()) ||
-        // 2) accross stages and both are uniform or buffer
-        (symbol->getQualifier().storage == EvqUniform  && unitSymbol->getQualifier().storage == EvqUniform) ||
-        (symbol->getQualifier().storage == EvqBuffer   && unitSymbol->getQualifier().storage == EvqBuffer) ||
-        // 3) in/out matched across stage boundary
-        (stage < unitStage && symbol->getQualifier().storage == EvqVaryingOut  && unitSymbol->getQualifier().storage == EvqVaryingIn) ||
-        (unitStage < stage && symbol->getQualifier().storage == EvqVaryingIn && unitSymbol->getQualifier().storage == EvqVaryingOut);
-}
-
 //
 // Global Unfiform block stores any default uniforms (i.e. uniforms without a block)
 // If two linked stages declare the same member, they are meant to be the same uniform
@@ -707,24 +743,18 @@ void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& lin
     // Error check and merge the linker objects (duplicates should not be created)
     std::size_t initialNumLinkerObjects = linkerObjects.size();
     for (unsigned int unitLinkObj = 0; unitLinkObj < unitLinkerObjects.size(); ++unitLinkObj) {
+        TIntermSymbol* unitSymbol = unitLinkerObjects[unitLinkObj]->getAsSymbolNode();
         bool merge = true;
+
+        // Don't merge inputs backwards into previous stages
+        if (getStage() != unitStage && unitSymbol->getQualifier().storage == EvqVaryingIn)
+            merge = false;
+
         for (std::size_t linkObj = 0; linkObj < initialNumLinkerObjects; ++linkObj) {
             TIntermSymbol* symbol = linkerObjects[linkObj]->getAsSymbolNode();
-            TIntermSymbol* unitSymbol = unitLinkerObjects[unitLinkObj]->getAsSymbolNode();
             assert(symbol && unitSymbol);
 
-            bool isSameSymbol = false;
-            // If they are both blocks in the same shader interface,
-            // match by the block-name, not the identifier name.
-            if (symbol->getType().getBasicType() == EbtBlock && unitSymbol->getType().getBasicType() == EbtBlock) {
-                if (isSameInterface(symbol, getStage(), unitSymbol, unitStage)) {
-                    isSameSymbol = symbol->getType().getTypeName() == unitSymbol->getType().getTypeName();
-                }
-            }
-            else if (symbol->getName() == unitSymbol->getName())
-                isSameSymbol = true;
-
-            if (isSameSymbol) {
+            if (isSameSymbol(symbol, getStage(), unitSymbol, unitStage)) {
                 // filter out copy
                 merge = false;
 

+ 1 - 0
gtests/GlslMapIO.FromFile.cpp

@@ -345,6 +345,7 @@ INSTANTIATE_TEST_SUITE_P(
         {{"iomap.variableOutBlockIn.2.vert", "iomap.variableOutBlockIn.geom"}, Semantics::OpenGL},
         // vulkan semantics
         {{"iomap.crossStage.vk.vert", "iomap.crossStage.vk.geom", "iomap.crossStage.vk.frag" }, Semantics::Vulkan},
+        {{"iomap.crossStage.vk.2.vert", "iomap.crossStage.vk.2.geom", "iomap.crossStage.vk.2.frag" }, Semantics::Vulkan},
     }))
 );
 // clang-format on