WIP: HLSL: Add GS support
authorsteve-lunarg <steve_gh@khasekhemwy.net>
Thu, 17 Nov 2016 22:04:20 +0000 (15:04 -0700)
committersteve-lunarg <steve_gh@khasekhemwy.net>
Tue, 22 Nov 2016 01:25:08 +0000 (18:25 -0700)
This PR adds:

[maxvertexcount(n)] attributes

point/line/triangle/lineadj/triangleadj qualifiers

PointStream/LineStream/TriangleStream templatized types

Append method on above template types

RestartStrip method on above template types.

13 files changed:
Test/baseResults/hlsl.basic.geom.out [new file with mode: 0644]
Test/hlsl.basic.geom [new file with mode: 0644]
glslang/Include/intermediate.h
gtests/Hlsl.FromFile.cpp
hlsl/hlslAttributes.cpp
hlsl/hlslAttributes.h
hlsl/hlslGrammar.cpp
hlsl/hlslGrammar.h
hlsl/hlslParseHelper.cpp
hlsl/hlslParseHelper.h
hlsl/hlslParseables.cpp
hlsl/hlslScanContext.cpp
hlsl/hlslTokens.h

diff --git a/Test/baseResults/hlsl.basic.geom.out b/Test/baseResults/hlsl.basic.geom.out
new file mode 100644 (file)
index 0000000..2c20b43
--- /dev/null
@@ -0,0 +1,201 @@
+hlsl.basic.geom
+Shader version: 450
+invocations = -1
+max_vertices = 4
+input primitive = triangles
+output primitive = line_strip
+0:? Sequence
+0:16  Function Definition: main(u1[3];u1[3];struct-PSInput-f1-i11; (temp void)
+0:16    Function Parameters: 
+0:16      'VertexID' (layout(location=0 ) in 3-element array of uint)
+0:16      'test' (layout(location=3 ) in 3-element array of uint)
+0:16      'OutputStream' (out structure{temp float myfloat, temp int something})
+0:?     Sequence
+0:19      move second child to first child (temp float)
+0:19        myfloat: direct index for structure (temp float)
+0:19          'Vert' (temp structure{temp float myfloat, temp int something})
+0:19          Constant:
+0:19            0 (const int)
+0:19        Convert uint to float (temp float)
+0:19          add (temp uint)
+0:19            add (temp uint)
+0:19              direct index (layout(location=3 ) temp uint)
+0:19                'test' (layout(location=3 ) in 3-element array of uint)
+0:19                Constant:
+0:19                  0 (const int)
+0:19              direct index (layout(location=3 ) temp uint)
+0:19                'test' (layout(location=3 ) in 3-element array of uint)
+0:19                Constant:
+0:19                  1 (const int)
+0:19            direct index (layout(location=3 ) temp uint)
+0:19              'test' (layout(location=3 ) in 3-element array of uint)
+0:19              Constant:
+0:19                2 (const int)
+0:20      move second child to first child (temp int)
+0:20        something: direct index for structure (temp int)
+0:20          'Vert' (temp structure{temp float myfloat, temp int something})
+0:20          Constant:
+0:20            1 (const int)
+0:20        Convert uint to int (temp int)
+0:20          direct index (layout(location=0 ) temp uint)
+0:20            'VertexID' (layout(location=0 ) in 3-element array of uint)
+0:20            Constant:
+0:20              0 (const int)
+0:22      Sequence
+0:22        move second child to first child (temp structure{temp float myfloat, temp int something})
+0:22          'OutputStream' (out structure{temp float myfloat, temp int something})
+0:22          'Vert' (temp structure{temp float myfloat, temp int something})
+0:22        EmitVertex (temp void)
+0:23      Sequence
+0:23        move second child to first child (temp structure{temp float myfloat, temp int something})
+0:23          'OutputStream' (out structure{temp float myfloat, temp int something})
+0:23          'Vert' (temp structure{temp float myfloat, temp int something})
+0:23        EmitVertex (temp void)
+0:24      EndPrimitive (temp void)
+0:?   Linker Objects
+0:?     'VertexID' (layout(location=0 ) in 3-element array of uint)
+0:?     'test' (layout(location=3 ) in 3-element array of uint)
+0:?     'myfloat' (layout(location=0 ) out float)
+0:?     'something' (layout(location=1 ) out int)
+
+
+Linked geometry stage:
+
+
+Shader version: 450
+invocations = 1
+max_vertices = 4
+input primitive = triangles
+output primitive = line_strip
+0:? Sequence
+0:16  Function Definition: main(u1[3];u1[3];struct-PSInput-f1-i11; (temp void)
+0:16    Function Parameters: 
+0:16      'VertexID' (layout(location=0 ) in 3-element array of uint)
+0:16      'test' (layout(location=3 ) in 3-element array of uint)
+0:16      'OutputStream' (out structure{temp float myfloat, temp int something})
+0:?     Sequence
+0:19      move second child to first child (temp float)
+0:19        myfloat: direct index for structure (temp float)
+0:19          'Vert' (temp structure{temp float myfloat, temp int something})
+0:19          Constant:
+0:19            0 (const int)
+0:19        Convert uint to float (temp float)
+0:19          add (temp uint)
+0:19            add (temp uint)
+0:19              direct index (layout(location=3 ) temp uint)
+0:19                'test' (layout(location=3 ) in 3-element array of uint)
+0:19                Constant:
+0:19                  0 (const int)
+0:19              direct index (layout(location=3 ) temp uint)
+0:19                'test' (layout(location=3 ) in 3-element array of uint)
+0:19                Constant:
+0:19                  1 (const int)
+0:19            direct index (layout(location=3 ) temp uint)
+0:19              'test' (layout(location=3 ) in 3-element array of uint)
+0:19              Constant:
+0:19                2 (const int)
+0:20      move second child to first child (temp int)
+0:20        something: direct index for structure (temp int)
+0:20          'Vert' (temp structure{temp float myfloat, temp int something})
+0:20          Constant:
+0:20            1 (const int)
+0:20        Convert uint to int (temp int)
+0:20          direct index (layout(location=0 ) temp uint)
+0:20            'VertexID' (layout(location=0 ) in 3-element array of uint)
+0:20            Constant:
+0:20              0 (const int)
+0:22      Sequence
+0:22        move second child to first child (temp structure{temp float myfloat, temp int something})
+0:22          'OutputStream' (out structure{temp float myfloat, temp int something})
+0:22          'Vert' (temp structure{temp float myfloat, temp int something})
+0:22        EmitVertex (temp void)
+0:23      Sequence
+0:23        move second child to first child (temp structure{temp float myfloat, temp int something})
+0:23          'OutputStream' (out structure{temp float myfloat, temp int something})
+0:23          'Vert' (temp structure{temp float myfloat, temp int something})
+0:23        EmitVertex (temp void)
+0:24      EndPrimitive (temp void)
+0:?   Linker Objects
+0:?     'VertexID' (layout(location=0 ) in 3-element array of uint)
+0:?     'test' (layout(location=3 ) in 3-element array of uint)
+0:?     'myfloat' (layout(location=0 ) out float)
+0:?     'something' (layout(location=1 ) out int)
+
+// Module Version 10000
+// Generated by (magic number): 80001
+// Id's are bound by 45
+
+                              Capability Geometry
+               1:             ExtInstImport  "GLSL.std.450"
+                              MemoryModel Logical GLSL450
+                              EntryPoint Geometry 4  "main" 16 31 38 42 44
+                              ExecutionMode 4 Triangles
+                              ExecutionMode 4 Invocations 1
+                              ExecutionMode 4 OutputLineStrip
+                              ExecutionMode 4 OutputVertices 4
+                              Name 4  "main"
+                              Name 8  "PSInput"
+                              MemberName 8(PSInput) 0  "myfloat"
+                              MemberName 8(PSInput) 1  "something"
+                              Name 10  "Vert"
+                              Name 16  "test"
+                              Name 31  "VertexID"
+                              Name 38  "OutputStream"
+                              Name 42  "myfloat"
+                              Name 44  "something"
+                              Decorate 16(test) Location 3
+                              Decorate 31(VertexID) Location 0
+                              Decorate 42(myfloat) Location 0
+                              Decorate 44(something) Location 1
+               2:             TypeVoid
+               3:             TypeFunction 2
+               6:             TypeFloat 32
+               7:             TypeInt 32 1
+      8(PSInput):             TypeStruct 6(float) 7(int)
+               9:             TypePointer Function 8(PSInput)
+              11:      7(int) Constant 0
+              12:             TypeInt 32 0
+              13:     12(int) Constant 3
+              14:             TypeArray 12(int) 13
+              15:             TypePointer Input 14
+        16(test):     15(ptr) Variable Input
+              17:             TypePointer Input 12(int)
+              20:      7(int) Constant 1
+              24:      7(int) Constant 2
+              29:             TypePointer Function 6(float)
+    31(VertexID):     15(ptr) Variable Input
+              35:             TypePointer Function 7(int)
+              37:             TypePointer Output 8(PSInput)
+38(OutputStream):     37(ptr) Variable Output
+              41:             TypePointer Output 6(float)
+     42(myfloat):     41(ptr) Variable Output
+              43:             TypePointer Output 7(int)
+   44(something):     43(ptr) Variable Output
+         4(main):           2 Function None 3
+               5:             Label
+        10(Vert):      9(ptr) Variable Function
+              18:     17(ptr) AccessChain 16(test) 11
+              19:     12(int) Load 18
+              21:     17(ptr) AccessChain 16(test) 20
+              22:     12(int) Load 21
+              23:     12(int) IAdd 19 22
+              25:     17(ptr) AccessChain 16(test) 24
+              26:     12(int) Load 25
+              27:     12(int) IAdd 23 26
+              28:    6(float) ConvertUToF 27
+              30:     29(ptr) AccessChain 10(Vert) 11
+                              Store 30 28
+              32:     17(ptr) AccessChain 31(VertexID) 11
+              33:     12(int) Load 32
+              34:      7(int) Bitcast 33
+              36:     35(ptr) AccessChain 10(Vert) 20
+                              Store 36 34
+              39:  8(PSInput) Load 10(Vert)
+                              Store 38(OutputStream) 39
+                              EmitVertex
+              40:  8(PSInput) Load 10(Vert)
+                              Store 38(OutputStream) 40
+                              EmitVertex
+                              EndPrimitive
+                              Return
+                              FunctionEnd
diff --git a/Test/hlsl.basic.geom b/Test/hlsl.basic.geom
new file mode 100644 (file)
index 0000000..79b061e
--- /dev/null
@@ -0,0 +1,25 @@
+struct PSInput
+{
+    float  myfloat    : SOME_SEMANTIC;
+    int    something  : ANOTHER_SEMANTIC;
+};
+
+struct nametest {
+    int Append;        // these are valid names even though they are also method names.
+    int RestartStrip;  // ...
+};
+
+[maxvertexcount(4)]
+void main(triangle in uint VertexID[3] : VertexID,
+          triangle uint test[3] : FOO, 
+          inout LineStream<PSInput> OutputStream)
+{
+    PSInput Vert;
+
+    Vert.myfloat    = test[0] + test[1] + test[2];
+    Vert.something  = VertexID[0];
+
+    OutputStream.Append(Vert);
+    OutputStream.Append(Vert);
+    OutputStream.RestartStrip();
+}
index db61b75..8f7ffcc 100644 (file)
@@ -621,6 +621,10 @@ enum TOperator {
     EOpMethodGatherCmpGreen,             // ...
     EOpMethodGatherCmpBlue,              // ...
     EOpMethodGatherCmpAlpha,             // ...
+
+    // geometry methods
+    EOpMethodAppend,                     // Geometry shader methods
+    EOpMethodRestartStrip,               // ...
 };
 
 class TIntermTraverser;
index 5487fb9..2867140 100644 (file)
@@ -88,6 +88,7 @@ INSTANTIATE_TEST_CASE_P(
         {"hlsl.attribute.frag", "PixelShaderFunction"},
         {"hlsl.attribute.expression.comp", "main"},
         {"hlsl.basic.comp", "main"},
+        {"hlsl.basic.geom", "main"},
         {"hlsl.buffer.frag", "PixelShaderFunction"},
         {"hlsl.calculatelod.dx10.frag", "main"},
         {"hlsl.calculatelodunclamped.dx10.frag", "main"},
index 957f282..966ff35 100644 (file)
@@ -55,27 +55,29 @@ namespace glslang {
         else if (lowername == "domain")
             return EatDomain;
         else if (lowername == "earlydepthstencil")
-            return EatEarlydepthstencil;
+            return EatEarlyDepthStencil;
         else if (lowername == "fastopt")
-            return EatFastopt;
+            return EatFastOpt;
         else if (lowername == "flatten")
             return EatFlatten;
         else if (lowername == "forcecase")
-            return EatForcecase;
+            return EatForceCase;
         else if (lowername == "instance")
             return EatInstance;
         else if (lowername == "maxtessfactor")
-            return EatMaxtessfactor;
+            return EatMaxTessFactor;
+        else if (lowername == "maxvertexcount")
+            return EatMaxVertexCount;
         else if (lowername == "numthreads")
-            return EatNumthreads;
+            return EatNumThreads;
         else if (lowername == "outputcontrolpoints")
-            return EatOutputcontrolpoints;
+            return EatOutputControlPoints;
         else if (lowername == "outputtopology")
-            return EatOutputtopology;
+            return EatOutputTopology;
         else if (lowername == "partitioning")
             return EatPartitioning;
         else if (lowername == "patchconstantfunc")
-            return EatPatchconstantfunc;
+            return EatPatchConstantFunc;
         else if (lowername == "unroll")
             return EatUnroll;
         else
index da5ee5e..312a456 100644 (file)
@@ -48,17 +48,18 @@ namespace glslang {
         EatBranch,
         EatCall,
         EatDomain,
-        EatEarlydepthstencil,
-        EatFastopt,
+        EatEarlyDepthStencil,
+        EatFastOpt,
         EatFlatten,
-        EatForcecase,
+        EatForceCase,
         EatInstance,
-        EatMaxtessfactor,
-        EatNumthreads,
-        EatOutputcontrolpoints,
-        EatOutputtopology,
+        EatMaxTessFactor,
+        EatNumThreads,
+        EatMaxVertexCount,
+        EatOutputControlPoints,
+        EatOutputTopology,
         EatPartitioning,
-        EatPatchconstantfunc,
+        EatPatchConstantFunc,
         EatUnroll,
     };
 }
index e6f4b60..b2715ed 100755 (executable)
@@ -469,9 +469,14 @@ bool HlslGrammar::acceptFullySpecifiedType(TType& type)
         // Some qualifiers are set when parsing the type.  Merge those with
         // whatever comes from acceptQualifier.
         assert(qualifier.layoutFormat == ElfNone);
+
         qualifier.layoutFormat = type.getQualifier().layoutFormat;
         qualifier.precision    = type.getQualifier().precision;
-        type.getQualifier() = qualifier;
+
+        if (type.getQualifier().storage == EvqVaryingOut)
+            qualifier.storage      = type.getQualifier().storage;
+
+        type.getQualifier()    = qualifier;
     }
 
     return true;
@@ -544,6 +549,35 @@ bool HlslGrammar::acceptQualifier(TQualifier& qualifier)
             if (! acceptLayoutQualifierList(qualifier))
                 return false;
             continue;
+
+        // GS geometries: these are specified on stage input variables, and are an error (not verified here)
+        // for output variables.
+        case EHTokPoint:
+            qualifier.storage = EvqIn;
+            if (!parseContext.handleInputGeometry(token.loc, ElgPoints))
+                return false;
+            break;
+        case EHTokLine:
+            qualifier.storage = EvqIn;
+            if (!parseContext.handleInputGeometry(token.loc, ElgLines))
+                return false;
+            break;
+        case EHTokTriangle:
+            qualifier.storage = EvqIn;
+            if (!parseContext.handleInputGeometry(token.loc, ElgTriangles))
+                return false;
+            break;
+        case EHTokLineAdj:
+            qualifier.storage = EvqIn;
+            if (!parseContext.handleInputGeometry(token.loc, ElgLinesAdjacency))
+                return false;
+            break; 
+        case EHTokTriangleAdj:
+            qualifier.storage = EvqIn;
+            if (!parseContext.handleInputGeometry(token.loc, ElgTrianglesAdjacency))
+                return false;
+            break; 
+            
         default:
             return true;
         }
@@ -608,7 +642,7 @@ bool HlslGrammar::acceptLayoutQualifierList(TQualifier& qualifier)
 //      | UINT
 //      | BOOL
 //
-bool HlslGrammar::acceptTemplateType(TBasicType& basicType)
+bool HlslGrammar::acceptTemplateVecMatBasicType(TBasicType& basicType)
 {
     switch (peek()) {
     case EHTokFloat:
@@ -652,7 +686,7 @@ bool HlslGrammar::acceptVectorTemplateType(TType& type)
     }
 
     TBasicType basicType;
-    if (! acceptTemplateType(basicType)) {
+    if (! acceptTemplateVecMatBasicType(basicType)) {
         expected("scalar type");
         return false;
     }
@@ -704,7 +738,7 @@ bool HlslGrammar::acceptMatrixTemplateType(TType& type)
     }
 
     TBasicType basicType;
-    if (! acceptTemplateType(basicType)) {
+    if (! acceptTemplateVecMatBasicType(basicType)) {
         expected("scalar type");
         return false;
     }
@@ -753,6 +787,56 @@ bool HlslGrammar::acceptMatrixTemplateType(TType& type)
     return true;
 }
 
+// layout_geometry
+//      : LINESTREAM
+//      | POINTSTREAM
+//      | TRIANGLESTREAM
+//
+bool HlslGrammar::acceptOutputPrimitiveGeometry(TLayoutGeometry& geometry)
+{
+    // read geometry type
+    const EHlslTokenClass geometryType = peek();
+
+    switch (geometryType) {
+    case EHTokPointStream:    geometry = ElgPoints;        break;
+    case EHTokLineStream:     geometry = ElgLineStrip;     break;
+    case EHTokTriangleStream: geometry = ElgTriangleStrip; break;
+    default:
+        return false;  // not a layout geometry
+    }
+
+    advanceToken();  // consume the layout keyword
+    return true;
+}
+
+// stream_out_template_type
+//      : output_primitive_geometry_type LEFT_ANGLE type RIGHT_ANGLE
+//
+bool HlslGrammar::acceptStreamOutTemplateType(TType& type, TLayoutGeometry& geometry)
+{
+    geometry = ElgNone;
+
+    if (! acceptOutputPrimitiveGeometry(geometry))
+        return false;
+
+    if (! acceptTokenClass(EHTokLeftAngle))
+        return false;
+    
+    if (! acceptType(type)) {
+        expected("stream output type");
+        return false;
+    }
+
+    type.getQualifier().storage = EvqVaryingOut;
+
+    if (! acceptTokenClass(EHTokRightAngle)) {
+        expected("right angle bracket");
+        return false;
+    }
+
+    return true;
+}
+    
 // annotations
 //      : LEFT_ANGLE declaration SEMI_COLON ... declaration SEMICOLON RIGHT_ANGLE
 //
@@ -989,6 +1073,20 @@ bool HlslGrammar::acceptType(TType& type)
         return acceptMatrixTemplateType(type);
         break;
 
+    case EHTokPointStream:            // fall through
+    case EHTokLineStream:             // ...
+    case EHTokTriangleStream:         // ...
+        {
+            TLayoutGeometry geometry;
+            if (! acceptStreamOutTemplateType(type, geometry))
+                return false;
+
+            if (! parseContext.handleOutputGeometry(token.loc, geometry))
+                return false;
+            
+            return true;
+        }
+
     case EHTokSampler:                // fall through
     case EHTokSampler1d:              // ...
     case EHTokSampler2d:              // ...
index c3e42f6..8804b21 100755 (executable)
@@ -72,9 +72,11 @@ namespace glslang {
         bool acceptQualifier(TQualifier&);
         bool acceptLayoutQualifierList(TQualifier&);
         bool acceptType(TType&);
-        bool acceptTemplateType(TBasicType&);
+        bool acceptTemplateVecMatBasicType(TBasicType&);
         bool acceptVectorTemplateType(TType&);
         bool acceptMatrixTemplateType(TType&);
+        bool acceptStreamOutTemplateType(TType&, TLayoutGeometry&);
+        bool acceptOutputPrimitiveGeometry(TLayoutGeometry&);
         bool acceptAnnotations(TQualifier&);
         bool acceptSamplerType(TType&);
         bool acceptTextureType(TType&);
index ff2c301..4957767 100755 (executable)
@@ -758,6 +758,13 @@ TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TInt
                 return intermediate.addMethod(base, TType(sampler.type, EvqTemporary, vecSize), &field, loc);
             }
         }
+    } else if (field == "Append" ||
+               field == "RestartStrip") {
+        // These methods only valid on stage in variables
+        // TODO: ... which are stream out types, if there's any way to test that here.
+        if (base->getType().getQualifier().storage == EvqVaryingOut) {
+            return intermediate.addMethod(base, TType(EbtVoid), &field, loc);
+        }
     }
 
     // It's not .length() if we get to here.
@@ -1137,12 +1144,19 @@ TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& l
     postMainReturn = false;
 
     // Handle function attributes
-    const TIntermAggregate* numThreadliterals = attributes[EatNumthreads];
-    if (numThreadliterals != nullptr && inEntryPoint) {
-        const TIntermSequence& sequence = numThreadliterals->getSequence();
+    if (inEntryPoint) {
+        const TIntermAggregate* numThreads = attributes[EatNumThreads];
+        if (numThreads != nullptr) {
+            const TIntermSequence& sequence = numThreads->getSequence();
  
-        for (int lid = 0; lid < int(sequence.size()); ++lid)
-            intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
+            for (int lid = 0; lid < int(sequence.size()); ++lid)
+                intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
+        }
+
+        const TIntermAggregate* maxVertexCount = attributes[EatMaxVertexCount];
+        if (maxVertexCount != nullptr) {
+            intermediate.setVertices(maxVertexCount->getSequence()[0]->getAsConstantUnion()->getConstArray()[0].getIConst());
+        }
     }
 
     return paramNodes;
@@ -2064,6 +2078,55 @@ void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermType
 }
 
 //
+// Decompose geometry shader methods
+//
+void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
+{
+    if (!node || !node->getAsOperator())
+        return;
+
+    const TOperator op  = node->getAsOperator()->getOp();
+    const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr;
+
+    switch (op) {
+    case EOpMethodAppend:
+        if (argAggregate) {
+            TIntermAggregate* sequence = nullptr;
+            TIntermAggregate* emit = new TIntermAggregate(EOpEmitVertex);
+
+            emit->setLoc(loc);
+            emit->setType(TType(EbtVoid));
+
+            sequence = intermediate.growAggregate(sequence,
+                                                  intermediate.addAssign(EOpAssign, 
+                                                                         argAggregate->getSequence()[0]->getAsTyped(),
+                                                                         argAggregate->getSequence()[1]->getAsTyped(), loc),
+                                                  loc);
+
+            sequence = intermediate.growAggregate(sequence, emit);
+
+            sequence->setOperator(EOpSequence);
+            sequence->setLoc(loc);
+            sequence->setType(TType(EbtVoid));
+            node = sequence;
+        }
+        break;
+
+    case EOpMethodRestartStrip:
+        {
+            TIntermAggregate* cut = new TIntermAggregate(EOpEndPrimitive);
+            cut->setLoc(loc);
+            cut->setType(TType(EbtVoid));
+            node = cut;
+        }
+        break;
+
+    default:
+        break; // most pass through unchanged
+    }
+}
+
+//
 // Optionally decompose intrinsics to AST opcodes.
 //
 void HlslParseContext::decomposeIntrinsic(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments)
@@ -2546,8 +2609,9 @@ TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunct
                 result = addOutputArgumentConversions(*fnCandidate, *result->getAsAggregate());
             }
 
-            decomposeIntrinsic(loc, result, arguments);      // HLSL->AST intrinsic decompositions
-            decomposeSampleMethods(loc, result, arguments);  // HLSL->AST sample method decompositions
+            decomposeIntrinsic(loc, result, arguments);       // HLSL->AST intrinsic decompositions
+            decomposeSampleMethods(loc, result, arguments);   // HLSL->AST sample method decompositions
+            decomposeGeometryMethods(loc, result, arguments); // HLSL->AST geometry method decompositions
         }
     }
 
@@ -4295,6 +4359,14 @@ const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, const TFu
     TVector<const TFunction*> candidateList;
     symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn);
     
+    // These builtin ops can accept any type, so we bypass the argument selection
+    if (candidateList.size() == 1 && builtIn &&
+        (candidateList[0]->getBuiltInOp() == EOpMethodAppend ||
+         candidateList[0]->getBuiltInOp() == EOpMethodRestartStrip)) {
+
+        return candidateList[0];
+    }
+
     // can 'from' convert to 'to'?
     const auto convertible = [this](const TType& from, const TType& to) -> bool {
         if (from == to)
@@ -5203,6 +5275,53 @@ void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier
 }
 
 //
+// Update the intermediate for the given input geometry
+//
+bool HlslParseContext::handleInputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
+{
+    switch (geometry) {
+    case ElgPoints:             // fall through
+    case ElgLines:              // ...
+    case ElgTriangles:          // ...
+    case ElgLinesAdjacency:     // ...
+    case ElgTrianglesAdjacency: // ...
+        if (! intermediate.setInputPrimitive(geometry)) {
+            error(loc, "input primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
+            return false;
+        }
+        break;
+
+    default:
+        error(loc, "cannot apply to 'in'", TQualifier::getGeometryString(geometry), "");
+        return false;
+    }
+
+    return true;
+}
+
+//
+// Update the intermediate for the given output geometry
+//
+bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry)
+{
+    switch (geometry) {
+    case ElgPoints:
+    case ElgLineStrip:
+    case ElgTriangleStrip:
+        if (! intermediate.setOutputPrimitive(geometry)) {
+            error(loc, "output primitive geometry redefinition", TQualifier::getGeometryString(geometry), "");
+            return false;
+        }
+        break;
+    default:
+        error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(geometry), "");
+        return false;
+    }
+
+    return true;
+}
+
+//
 // Updating default qualifier for the case of a declaration with just a qualifier,
 // no type, block, or identifier.
 //
@@ -5231,16 +5350,7 @@ void HlslParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc,
                 error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), "");
             }
         } else if (publicType.qualifier.storage == EvqVaryingOut) {
-            switch (publicType.shaderQualifiers.geometry) {
-            case ElgPoints:
-            case ElgLineStrip:
-            case ElgTriangleStrip:
-                if (! intermediate.setOutputPrimitive(publicType.shaderQualifiers.geometry))
-                    error(loc, "cannot change previously set output primitive", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), "");
-                break;
-            default:
-                error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), "");
-            }
+            handleOutputGeometry(loc, publicType.shaderQualifiers.geometry);
         } else
             error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), GetStorageQualifierString(publicType.qualifier.storage));
     }
index 9a7285f..3862c10 100755 (executable)
@@ -81,6 +81,7 @@ public:
     TIntermTyped* handleFunctionCall(const TSourceLoc&, TFunction*, TIntermNode*);
     void decomposeIntrinsic(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments);
     void decomposeSampleMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments);
+    void decomposeGeometryMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments);
     TIntermTyped* handleLengthMethod(const TSourceLoc&, TFunction*, TIntermNode*);
     void addInputArgumentConversions(const TFunction&, TIntermNode*&) const;
     TIntermTyped* addOutputArgumentConversions(const TFunction&, TIntermAggregate&);
@@ -160,6 +161,9 @@ public:
 
     TLayoutFormat getLayoutFromTxType(const TSourceLoc&, const TType&);
 
+    bool handleOutputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry);
+    bool handleInputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry);
+
 protected:
     void inheritGlobalDefaults(TQualifier& dst) const;
     TVariable* makeInternalVariable(const char* name, const TType&) const;
index 7ab159d..f8fed0d 100755 (executable)
@@ -502,6 +502,7 @@ void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, c
     static const EShLanguageMask EShLangCS     = EShLangAll;
     static const EShLanguageMask EShLangPS     = EShLangAll;
     static const EShLanguageMask EShLangHS     = EShLangAll;
+    static const EShLanguageMask EShLangGS     = EShLangAll;
 
     // This structure encodes the prototype information for each HLSL intrinsic.
     // Because explicit enumeration would be cumbersome, it's procedurally generated.
@@ -831,6 +832,10 @@ void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, c
         { "GatherCmpAlpha",  /* O-4 */        "V4",    nullptr,   "%@,S,V,S,V,,,",  "FIU,s,F,,I,,,",  EShLangAll },
         { "GatherCmpAlpha",  /* O-4, status */"V4",    nullptr,   "%@,S,V,S,V,,,,S","FIU,s,F,,I,,,,U",EShLangAll },
 
+        // geometry methods
+        { "Append",                           "-",     "-",       "-",              "-",              EShLangGS  },
+        { "RestartStrip",                     "-",     "-",       "-",              "-",              EShLangGS  },
+
         // Mark end of list, since we want to avoid a range-based for, as some compilers don't handle it yet.
         { nullptr,                            nullptr, nullptr,   nullptr,      nullptr,  0 },
     };
@@ -1140,6 +1145,10 @@ void TBuiltInParseablesHlsl::identifyBuiltIns(int /*version*/, EProfile /*profil
     symbolTable.relateToOperator("GatherCmpGreen",              EOpMethodGatherCmpGreen);
     symbolTable.relateToOperator("GatherCmpBlue",               EOpMethodGatherCmpBlue);
     symbolTable.relateToOperator("GatherCmpAlpha",              EOpMethodGatherCmpAlpha);
+
+    // GS methods
+    symbolTable.relateToOperator("Append",                      EOpMethodAppend);
+    symbolTable.relateToOperator("RestartStrip",                EOpMethodRestartStrip);
 }
 
 //
index e7cd1fc..2c45b3f 100755 (executable)
@@ -119,6 +119,16 @@ void HlslScanContext::fillInKeywordMap()
     (*KeywordMap)["inout"] =                   EHTokInOut;
     (*KeywordMap)["layout"] =                  EHTokLayout;
 
+    (*KeywordMap)["point"] =                   EHTokPoint;
+    (*KeywordMap)["line"] =                    EHTokLine;
+    (*KeywordMap)["triangle"] =                EHTokTriangle;
+    (*KeywordMap)["lineadj"] =                 EHTokLineAdj;
+    (*KeywordMap)["triangleadj"] =             EHTokTriangleAdj;
+
+    (*KeywordMap)["PointStream"] =             EHTokPointStream;
+    (*KeywordMap)["LineStream"] =              EHTokLineStream;
+    (*KeywordMap)["TriangleStream"] =          EHTokTriangleStream;
+
     (*KeywordMap)["Buffer"] =                  EHTokBuffer;
     (*KeywordMap)["vector"] =                  EHTokVector;
     (*KeywordMap)["matrix"] =                  EHTokMatrix;
@@ -496,7 +506,20 @@ EHlslTokenClass HlslScanContext::tokenizeIdentifier()
     case EHTokLayout:
         return keyword;
 
-    // template types
+    // primitive types
+    case EHTokPoint:
+    case EHTokLine:
+    case EHTokTriangle:
+    case EHTokLineAdj:
+    case EHTokTriangleAdj:
+        return keyword;
+
+    // stream out types
+    case EHTokPointStream:
+    case EHTokLineStream:
+    case EHTokTriangleStream:
+        return keyword;
+
     case EHTokBuffer:
     case EHTokVector:
     case EHTokMatrix:
index d634c8e..6902070 100755 (executable)
@@ -66,6 +66,18 @@ enum EHlslTokenClass {
     EHTokInOut,
     EHTokLayout,
 
+    // primitive types
+    EHTokPoint,
+    EHTokLine,
+    EHTokTriangle,
+    EHTokLineAdj,
+    EHTokTriangleAdj,
+
+    // stream out types
+    EHTokPointStream,
+    EHTokLineStream,
+    EHTokTriangleStream,
+
     // template types
     EHTokBuffer,
     EHTokVector,