diff --git a/Test/baseResults/hlsl.basic.geom.out b/Test/baseResults/hlsl.basic.geom.out new file mode 100644 index 000000000..2c20b439c --- /dev/null +++ b/Test/baseResults/hlsl.basic.geom.out @@ -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 index 000000000..79b061ee0 --- /dev/null +++ b/Test/hlsl.basic.geom @@ -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 OutputStream) +{ + PSInput Vert; + + Vert.myfloat = test[0] + test[1] + test[2]; + Vert.something = VertexID[0]; + + OutputStream.Append(Vert); + OutputStream.Append(Vert); + OutputStream.RestartStrip(); +} diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h index db61b7507..8f7ffcc0d 100644 --- a/glslang/Include/intermediate.h +++ b/glslang/Include/intermediate.h @@ -621,6 +621,10 @@ enum TOperator { EOpMethodGatherCmpGreen, // ... EOpMethodGatherCmpBlue, // ... EOpMethodGatherCmpAlpha, // ... + + // geometry methods + EOpMethodAppend, // Geometry shader methods + EOpMethodRestartStrip, // ... }; class TIntermTraverser; diff --git a/gtests/Hlsl.FromFile.cpp b/gtests/Hlsl.FromFile.cpp index 5487fb98e..2867140e3 100644 --- a/gtests/Hlsl.FromFile.cpp +++ b/gtests/Hlsl.FromFile.cpp @@ -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"}, diff --git a/hlsl/hlslAttributes.cpp b/hlsl/hlslAttributes.cpp index 957f282ee..966ff3521 100644 --- a/hlsl/hlslAttributes.cpp +++ b/hlsl/hlslAttributes.cpp @@ -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 diff --git a/hlsl/hlslAttributes.h b/hlsl/hlslAttributes.h index da5ee5ef7..312a45642 100644 --- a/hlsl/hlslAttributes.h +++ b/hlsl/hlslAttributes.h @@ -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, }; } diff --git a/hlsl/hlslGrammar.cpp b/hlsl/hlslGrammar.cpp index e6f4b6037..b2715eda8 100755 --- a/hlsl/hlslGrammar.cpp +++ b/hlsl/hlslGrammar.cpp @@ -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: // ... diff --git a/hlsl/hlslGrammar.h b/hlsl/hlslGrammar.h index c3e42f674..8804b217e 100755 --- a/hlsl/hlslGrammar.h +++ b/hlsl/hlslGrammar.h @@ -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&); diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp index ff2c3014b..495776776 100755 --- a/hlsl/hlslParseHelper.cpp +++ b/hlsl/hlslParseHelper.cpp @@ -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; @@ -2063,6 +2077,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. // @@ -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 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) @@ -5202,6 +5274,53 @@ void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier addQualifierToExisting(loc, qualifier, *identifiers[i]); } +// +// 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)); } diff --git a/hlsl/hlslParseHelper.h b/hlsl/hlslParseHelper.h index 9a7285f98..3862c10f0 100755 --- a/hlsl/hlslParseHelper.h +++ b/hlsl/hlslParseHelper.h @@ -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; diff --git a/hlsl/hlslParseables.cpp b/hlsl/hlslParseables.cpp index 7ab159dc0..f8fed0d8e 100755 --- a/hlsl/hlslParseables.cpp +++ b/hlsl/hlslParseables.cpp @@ -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); } // diff --git a/hlsl/hlslScanContext.cpp b/hlsl/hlslScanContext.cpp index e7cd1fcf1..2c45b3ff1 100755 --- a/hlsl/hlslScanContext.cpp +++ b/hlsl/hlslScanContext.cpp @@ -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: diff --git a/hlsl/hlslTokens.h b/hlsl/hlslTokens.h index d634c8eeb..6902070cb 100755 --- a/hlsl/hlslTokens.h +++ b/hlsl/hlslTokens.h @@ -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,