diff --git a/Test/baseResults/hlsl.tristream-append.geom.out b/Test/baseResults/hlsl.tristream-append.geom.out new file mode 100644 index 000000000..2444cfb47 --- /dev/null +++ b/Test/baseResults/hlsl.tristream-append.geom.out @@ -0,0 +1,212 @@ +hlsl.tristream-append.geom +Shader version: 500 +invocations = -1 +max_vertices = 3 +input primitive = triangles +output primitive = triangle_strip +0:? Sequence +0:8 Function Definition: EmitVertex(struct-GSPS_INPUT1;struct-GSPS_INPUT1; ( temp void) +0:8 Function Parameters: +0:8 'output' ( in structure{}) +0:8 'TriStream' ( out structure{}) +0:? Sequence +0:9 Sequence +0:9 Sequence +0:9 move second child to first child ( temp structure{}) +0:9 'TriStream' ( out structure{}) +0:9 'output' ( in structure{}) +0:9 EmitVertex ( temp void) +0:14 Function Definition: @main(struct-GSPS_INPUT1[3];struct-GSPS_INPUT1; ( temp void) +0:14 Function Parameters: +0:14 'input' ( in 3-element array of structure{}) +0:14 'TriStream' ( out structure{}) +0:? Sequence +0:15 Function Call: EmitVertex(struct-GSPS_INPUT1;struct-GSPS_INPUT1; ( temp void) +0:15 direct index ( temp structure{}) +0:15 'input' ( in 3-element array of structure{}) +0:15 Constant: +0:15 0 (const int) +0:15 'TriStream' ( out structure{}) +0:16 Function Call: EmitVertex(struct-GSPS_INPUT1;struct-GSPS_INPUT1; ( temp void) +0:16 direct index ( temp structure{}) +0:16 'input' ( in 3-element array of structure{}) +0:16 Constant: +0:16 1 (const int) +0:16 'TriStream' ( out structure{}) +0:17 Function Call: EmitVertex(struct-GSPS_INPUT1;struct-GSPS_INPUT1; ( temp void) +0:17 direct index ( temp structure{}) +0:17 'input' ( in 3-element array of structure{}) +0:17 Constant: +0:17 2 (const int) +0:17 'TriStream' ( out structure{}) +0:14 Function Definition: main( ( temp void) +0:14 Function Parameters: +0:? Sequence +0:14 move second child to first child ( temp 3-element array of structure{}) +0:? 'input' ( temp 3-element array of structure{}) +0:? 'input' ( in 3-element array of structure{}) +0:14 Function Call: @main(struct-GSPS_INPUT1[3];struct-GSPS_INPUT1; ( temp void) +0:? 'input' ( temp 3-element array of structure{}) +0:? 'TriStream' ( temp structure{}) +0:? Linker Objects + + +Linked geometry stage: + + +Shader version: 500 +invocations = 1 +max_vertices = 3 +input primitive = triangles +output primitive = triangle_strip +0:? Sequence +0:8 Function Definition: EmitVertex(struct-GSPS_INPUT1;struct-GSPS_INPUT1; ( temp void) +0:8 Function Parameters: +0:8 'output' ( in structure{}) +0:8 'TriStream' ( out structure{}) +0:? Sequence +0:9 Sequence +0:9 Sequence +0:9 move second child to first child ( temp structure{}) +0:9 'TriStream' ( out structure{}) +0:9 'output' ( in structure{}) +0:9 EmitVertex ( temp void) +0:14 Function Definition: @main(struct-GSPS_INPUT1[3];struct-GSPS_INPUT1; ( temp void) +0:14 Function Parameters: +0:14 'input' ( in 3-element array of structure{}) +0:14 'TriStream' ( out structure{}) +0:? Sequence +0:15 Function Call: EmitVertex(struct-GSPS_INPUT1;struct-GSPS_INPUT1; ( temp void) +0:15 direct index ( temp structure{}) +0:15 'input' ( in 3-element array of structure{}) +0:15 Constant: +0:15 0 (const int) +0:15 'TriStream' ( out structure{}) +0:16 Function Call: EmitVertex(struct-GSPS_INPUT1;struct-GSPS_INPUT1; ( temp void) +0:16 direct index ( temp structure{}) +0:16 'input' ( in 3-element array of structure{}) +0:16 Constant: +0:16 1 (const int) +0:16 'TriStream' ( out structure{}) +0:17 Function Call: EmitVertex(struct-GSPS_INPUT1;struct-GSPS_INPUT1; ( temp void) +0:17 direct index ( temp structure{}) +0:17 'input' ( in 3-element array of structure{}) +0:17 Constant: +0:17 2 (const int) +0:17 'TriStream' ( out structure{}) +0:14 Function Definition: main( ( temp void) +0:14 Function Parameters: +0:? Sequence +0:14 move second child to first child ( temp 3-element array of structure{}) +0:? 'input' ( temp 3-element array of structure{}) +0:? 'input' ( in 3-element array of structure{}) +0:14 Function Call: @main(struct-GSPS_INPUT1[3];struct-GSPS_INPUT1; ( temp void) +0:? 'input' ( temp 3-element array of structure{}) +0:? 'TriStream' ( temp structure{}) +0:? Linker Objects + +// Module Version 10000 +// Generated by (magic number): 80006 +// Id's are bound by 57 + + Capability Geometry + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Geometry 4 "main" + ExecutionMode 4 Triangles + ExecutionMode 4 Invocations 1 + ExecutionMode 4 OutputTriangleStrip + ExecutionMode 4 OutputVertices 3 + Source HLSL 500 + Name 4 "main" + Name 6 "GSPS_INPUT" + Name 11 "EmitVertex(struct-GSPS_INPUT1;struct-GSPS_INPUT1;" + Name 9 "output" + Name 10 "TriStream" + Name 20 "@main(struct-GSPS_INPUT1[3];struct-GSPS_INPUT1;" + Name 18 "input" + Name 19 "TriStream" + Name 23 "TriStream" + Name 27 "param" + Name 30 "param" + Name 34 "param" + Name 37 "param" + Name 41 "param" + Name 44 "param" + Name 47 "input" + Name 49 "input" + Name 51 "TriStream" + Name 52 "param" + Name 54 "param" + 2: TypeVoid + 3: TypeFunction 2 + 6(GSPS_INPUT): TypeStruct + 7: TypePointer Function 6(GSPS_INPUT) + 8: TypeFunction 2 7(ptr) 7(ptr) + 13: TypeInt 32 0 + 14: 13(int) Constant 3 + 15: TypeArray 6(GSPS_INPUT) 14 + 16: TypePointer Function 15 + 17: TypeFunction 2 16(ptr) 7(ptr) + 22: TypePointer Output 6(GSPS_INPUT) + 23(TriStream): 22(ptr) Variable Output + 25: TypeInt 32 1 + 26: 25(int) Constant 0 + 33: 25(int) Constant 1 + 40: 25(int) Constant 2 + 48: TypePointer Input 15 + 49(input): 48(ptr) Variable Input + 4(main): 2 Function None 3 + 5: Label + 47(input): 16(ptr) Variable Function + 51(TriStream): 7(ptr) Variable Function + 52(param): 16(ptr) Variable Function + 54(param): 7(ptr) Variable Function + 50: 15 Load 49(input) + Store 47(input) 50 + 53: 15 Load 47(input) + Store 52(param) 53 + 55: 2 FunctionCall 20(@main(struct-GSPS_INPUT1[3];struct-GSPS_INPUT1;) 52(param) 54(param) + 56:6(GSPS_INPUT) Load 54(param) + Store 51(TriStream) 56 + Return + FunctionEnd +11(EmitVertex(struct-GSPS_INPUT1;struct-GSPS_INPUT1;): 2 Function None 8 + 9(output): 7(ptr) FunctionParameter + 10(TriStream): 7(ptr) FunctionParameter + 12: Label + 24:6(GSPS_INPUT) Load 9(output) + Store 23(TriStream) 24 + EmitVertex + Return + FunctionEnd +20(@main(struct-GSPS_INPUT1[3];struct-GSPS_INPUT1;): 2 Function None 17 + 18(input): 16(ptr) FunctionParameter + 19(TriStream): 7(ptr) FunctionParameter + 21: Label + 27(param): 7(ptr) Variable Function + 30(param): 7(ptr) Variable Function + 34(param): 7(ptr) Variable Function + 37(param): 7(ptr) Variable Function + 41(param): 7(ptr) Variable Function + 44(param): 7(ptr) Variable Function + 28: 7(ptr) AccessChain 18(input) 26 + 29:6(GSPS_INPUT) Load 28 + Store 27(param) 29 + 31: 2 FunctionCall 11(EmitVertex(struct-GSPS_INPUT1;struct-GSPS_INPUT1;) 27(param) 30(param) + 32:6(GSPS_INPUT) Load 30(param) + Store 19(TriStream) 32 + 35: 7(ptr) AccessChain 18(input) 33 + 36:6(GSPS_INPUT) Load 35 + Store 34(param) 36 + 38: 2 FunctionCall 11(EmitVertex(struct-GSPS_INPUT1;struct-GSPS_INPUT1;) 34(param) 37(param) + 39:6(GSPS_INPUT) Load 37(param) + Store 19(TriStream) 39 + 42: 7(ptr) AccessChain 18(input) 40 + 43:6(GSPS_INPUT) Load 42 + Store 41(param) 43 + 45: 2 FunctionCall 11(EmitVertex(struct-GSPS_INPUT1;struct-GSPS_INPUT1;) 41(param) 44(param) + 46:6(GSPS_INPUT) Load 44(param) + Store 19(TriStream) 46 + Return + FunctionEnd diff --git a/Test/hlsl.tristream-append.geom b/Test/hlsl.tristream-append.geom new file mode 100644 index 000000000..208607d37 --- /dev/null +++ b/Test/hlsl.tristream-append.geom @@ -0,0 +1,18 @@ +struct GSPS_INPUT +{ +}; + +// Test Append() method appearing before declaration of entry point's stream output. + +void EmitVertex(in GSPS_INPUT output, inout TriangleStream TriStream) +{ + TriStream.Append( output ); +} + +[maxvertexcount(3)] +void main( triangle GSPS_INPUT input[3], inout TriangleStream TriStream ) +{ + EmitVertex(input[0], TriStream); + EmitVertex(input[1], TriStream); + EmitVertex(input[2], TriStream); +} diff --git a/gtests/Hlsl.FromFile.cpp b/gtests/Hlsl.FromFile.cpp index 79e9592ce..feeee9965 100644 --- a/gtests/Hlsl.FromFile.cpp +++ b/gtests/Hlsl.FromFile.cpp @@ -370,6 +370,7 @@ INSTANTIATE_TEST_CASE_P( {"hlsl.targetStruct1.frag", "main"}, {"hlsl.targetStruct2.frag", "main"}, {"hlsl.templatetypes.frag", "PixelShaderFunction"}, + {"hlsl.tristream-append.geom", "main"}, {"hlsl.tx.bracket.frag", "main"}, {"hlsl.tx.overload.frag", "main"}, {"hlsl.type.half.frag", "main"}, diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp index ef45da10a..9f842b784 100755 --- a/hlsl/hlslParseHelper.cpp +++ b/hlsl/hlslParseHelper.cpp @@ -4487,23 +4487,18 @@ void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTy emit->setLoc(loc); emit->setType(TType(EbtVoid)); - // find the matching output - if (gsStreamOutput == nullptr) { - error(loc, "unable to find output symbol for Append()", "", ""); - return; - } - - sequence = intermediate.growAggregate(sequence, - handleAssign(loc, EOpAssign, - intermediate.addSymbol(*gsStreamOutput, loc), - argAggregate->getSequence()[1]->getAsTyped()), - loc); + TIntermTyped* data = argAggregate->getSequence()[1]->getAsTyped(); + // This will be patched in finalization during finalizeAppendMethods() + sequence = intermediate.growAggregate(sequence, data, loc); sequence = intermediate.growAggregate(sequence, emit); sequence->setOperator(EOpSequence); sequence->setLoc(loc); sequence->setType(TType(EbtVoid)); + + gsAppends.push_back({sequence, loc}); + node = sequence; } break; @@ -9919,6 +9914,31 @@ void HlslParseContext::fixTextureShadowModes() } } +// Finalization step: patch append methods to use proper stream output, which isn't known until +// main is parsed, which could happen after the append method is parsed. +void HlslParseContext::finalizeAppendMethods() +{ + TSourceLoc loc; + loc.init(); + + // Nothing to do: bypass test for valid stream output. + if (gsAppends.empty()) + return; + + if (gsStreamOutput == nullptr) { + error(loc, "unable to find output symbol for Append()", "", ""); + return; + } + + // Patch append sequences, now that we know the stream output symbol. + for (auto append = gsAppends.begin(); append != gsAppends.end(); ++append) { + append->node->getSequence()[0] = + handleAssign(append->loc, EOpAssign, + intermediate.addSymbol(*gsStreamOutput, append->loc), + append->node->getSequence()[0]->getAsTyped()); + } +} + // post-processing void HlslParseContext::finish() { @@ -9931,6 +9951,7 @@ void HlslParseContext::finish() removeUnusedStructBufferCounters(); addPatchConstantInvocation(); fixTextureShadowModes(); + finalizeAppendMethods(); // Communicate out (esp. for command line) that we formed AST that will make // illegal AST SPIR-V and it needs transforms to legalize it. diff --git a/hlsl/hlslParseHelper.h b/hlsl/hlslParseHelper.h index b16a8c2c3..32a192394 100755 --- a/hlsl/hlslParseHelper.h +++ b/hlsl/hlslParseHelper.h @@ -266,6 +266,7 @@ protected: TVariable* getSplitNonIoVar(int id) const; void addPatchConstantInvocation(); void fixTextureShadowModes(); + void finalizeAppendMethods(); TIntermTyped* makeIntegerIndex(TIntermTyped*); void fixBuiltInIoType(TType&); @@ -460,6 +461,17 @@ protected: TVector mipsOperatorMipArg; + // The geometry output stream is not copied out from the entry point as a typical output variable + // is. It's written via EmitVertex (hlsl=Append), which may happen in arbitrary control flow. + // For this we need the real output symbol. Since it may not be known at the time and Append() + // method is parsed, the sequence will be patched during finalization. + struct tGsAppendData { + TIntermAggregate* node; + TSourceLoc loc; + }; + + TVector gsAppends; + // A texture object may be used with shadow and non-shadow samplers, but both may not be // alive post-DCE in the same shader. We do not know at compilation time which are alive: that's // only known post-DCE. If a texture is used both ways, we create two textures, and