From 77ea30bdc9925c545f0af346b09069c482b30d70 Mon Sep 17 00:00:00 2001 From: John Kessenich <cepheus@frii.com> Date: Sat, 30 Sep 2017 14:34:50 -0600 Subject: [PATCH] HLSL: Additional attribute support: [[]], namespace, parameters: - support C++11 style brackets [[...]] - support namespaces [[vk::...]] - support these on parameter declarations in functions - support location, binding/set, input attachments --- Test/baseResults/hlsl.attributeC11.frag.out | 176 ++++++++++++++++++++ Test/hlsl.attributeC11.frag | 18 ++ gtests/Hlsl.FromFile.cpp | 1 + hlsl/hlslAttributes.cpp | 22 ++- hlsl/hlslAttributes.h | 7 +- hlsl/hlslGrammar.cpp | 62 +++++-- hlsl/hlslParseHelper.cpp | 39 ++++- hlsl/hlslParseHelper.h | 1 + 8 files changed, 310 insertions(+), 16 deletions(-) create mode 100755 Test/baseResults/hlsl.attributeC11.frag.out create mode 100644 Test/hlsl.attributeC11.frag diff --git a/Test/baseResults/hlsl.attributeC11.frag.out b/Test/baseResults/hlsl.attributeC11.frag.out new file mode 100755 index 000000000..ac58bf1ea --- /dev/null +++ b/Test/baseResults/hlsl.attributeC11.frag.out @@ -0,0 +1,176 @@ +hlsl.attributeC11.frag +Shader version: 500 +gl_FragCoord origin is upper left +0:? Sequence +0:16 Function Definition: @main(vf4; ( temp 4-component vector of float) +0:16 Function Parameters: +0:16 'input' ( in 4-component vector of float) +0:? Sequence +0:17 Branch: Return with expression +0:17 add ( temp 4-component vector of float) +0:17 'input' ( in 4-component vector of float) +0:17 textureFetch ( temp 4-component vector of float) +0:17 'attach' ( uniform texture2D) +0:17 vector swizzle ( temp int) +0:17 Constant: +0:17 0 (const int) +0:17 0 (const int) +0:17 Sequence +0:17 Constant: +0:17 0 (const int) +0:17 direct index ( temp int) +0:17 Constant: +0:17 0 (const int) +0:17 0 (const int) +0:17 Constant: +0:17 1 (const int) +0:16 Function Definition: main( ( temp void) +0:16 Function Parameters: +0:? Sequence +0:16 move second child to first child ( temp 4-component vector of float) +0:? 'input' ( temp 4-component vector of float) +0:? 'input' (layout( location=8) in 4-component vector of float) +0:16 move second child to first child ( temp 4-component vector of float) +0:? '@entryPointOutput' (layout( location=7) out 4-component vector of float) +0:16 Function Call: @main(vf4; ( temp 4-component vector of float) +0:? 'input' ( temp 4-component vector of float) +0:? Linker Objects +0:? 'buffer1' (layout( set=0 binding=1 row_major std430) readonly buffer block{layout( row_major std430) buffer implicitly-sized array of structure{ temp 2-component vector of float f} @data}) +0:? 'buffer3' (layout( set=2 binding=3 row_major std430) readonly buffer block{layout( row_major std430) buffer implicitly-sized array of structure{ temp 2-component vector of float f} @data}) +0:? 'attach' ( uniform texture2D) +0:? '@entryPointOutput' (layout( location=7) out 4-component vector of float) +0:? 'input' (layout( location=8) in 4-component vector of float) + + +Linked fragment stage: + + +Shader version: 500 +gl_FragCoord origin is upper left +0:? Sequence +0:16 Function Definition: @main(vf4; ( temp 4-component vector of float) +0:16 Function Parameters: +0:16 'input' ( in 4-component vector of float) +0:? Sequence +0:17 Branch: Return with expression +0:17 add ( temp 4-component vector of float) +0:17 'input' ( in 4-component vector of float) +0:17 textureFetch ( temp 4-component vector of float) +0:17 'attach' ( uniform texture2D) +0:17 vector swizzle ( temp int) +0:17 Constant: +0:17 0 (const int) +0:17 0 (const int) +0:17 Sequence +0:17 Constant: +0:17 0 (const int) +0:17 direct index ( temp int) +0:17 Constant: +0:17 0 (const int) +0:17 0 (const int) +0:17 Constant: +0:17 1 (const int) +0:16 Function Definition: main( ( temp void) +0:16 Function Parameters: +0:? Sequence +0:16 move second child to first child ( temp 4-component vector of float) +0:? 'input' ( temp 4-component vector of float) +0:? 'input' (layout( location=8) in 4-component vector of float) +0:16 move second child to first child ( temp 4-component vector of float) +0:? '@entryPointOutput' (layout( location=7) out 4-component vector of float) +0:16 Function Call: @main(vf4; ( temp 4-component vector of float) +0:? 'input' ( temp 4-component vector of float) +0:? Linker Objects +0:? 'buffer1' (layout( set=0 binding=1 row_major std430) readonly buffer block{layout( row_major std430) buffer implicitly-sized array of structure{ temp 2-component vector of float f} @data}) +0:? 'buffer3' (layout( set=2 binding=3 row_major std430) readonly buffer block{layout( row_major std430) buffer implicitly-sized array of structure{ temp 2-component vector of float f} @data}) +0:? 'attach' ( uniform texture2D) +0:? '@entryPointOutput' (layout( location=7) out 4-component vector of float) +0:? 'input' (layout( location=8) in 4-component vector of float) + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 47 + + Capability Shader + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Fragment 4 "main" 33 36 + ExecutionMode 4 OriginUpperLeft + Source HLSL 500 + Name 4 "main" + Name 11 "@main(vf4;" + Name 10 "input" + Name 16 "attach" + Name 31 "input" + Name 33 "input" + Name 36 "@entryPointOutput" + Name 37 "param" + Name 41 "S" + MemberName 41(S) 0 "f" + Name 43 "buffer1" + MemberName 43(buffer1) 0 "@data" + Name 45 "buffer1" + Name 46 "buffer3" + Decorate 16(attach) DescriptorSet 0 + Decorate 16(attach) InputAttachmentIndex 4 + Decorate 33(input) Location 8 + Decorate 36(@entryPointOutput) Location 7 + MemberDecorate 41(S) 0 Offset 0 + Decorate 42 ArrayStride 8 + MemberDecorate 43(buffer1) 0 NonWritable + MemberDecorate 43(buffer1) 0 Offset 0 + Decorate 43(buffer1) BufferBlock + Decorate 45(buffer1) DescriptorSet 0 + Decorate 45(buffer1) Binding 1 + Decorate 46(buffer3) DescriptorSet 2 + Decorate 46(buffer3) Binding 3 + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeFloat 32 + 7: TypeVector 6(float) 4 + 8: TypePointer Function 7(fvec4) + 9: TypeFunction 7(fvec4) 8(ptr) + 14: TypeImage 6(float) 2D sampled format:Unknown + 15: TypePointer UniformConstant 14 + 16(attach): 15(ptr) Variable UniformConstant + 18: TypeInt 32 1 + 19: TypeVector 18(int) 2 + 20: 18(int) Constant 0 + 21: 19(ivec2) ConstantComposite 20 20 + 22: TypeInt 32 0 + 23: 22(int) Constant 0 + 25: 22(int) Constant 1 + 32: TypePointer Input 7(fvec4) + 33(input): 32(ptr) Variable Input + 35: TypePointer Output 7(fvec4) +36(@entryPointOutput): 35(ptr) Variable Output + 40: TypeVector 6(float) 2 + 41(S): TypeStruct 40(fvec2) + 42: TypeRuntimeArray 41(S) + 43(buffer1): TypeStruct 42 + 44: TypePointer Uniform 43(buffer1) + 45(buffer1): 44(ptr) Variable Uniform + 46(buffer3): 44(ptr) Variable Uniform + 4(main): 2 Function None 3 + 5: Label + 31(input): 8(ptr) Variable Function + 37(param): 8(ptr) Variable Function + 34: 7(fvec4) Load 33(input) + Store 31(input) 34 + 38: 7(fvec4) Load 31(input) + Store 37(param) 38 + 39: 7(fvec4) FunctionCall 11(@main(vf4;) 37(param) + Store 36(@entryPointOutput) 39 + Return + FunctionEnd + 11(@main(vf4;): 7(fvec4) Function None 9 + 10(input): 8(ptr) FunctionParameter + 12: Label + 13: 7(fvec4) Load 10(input) + 17: 14 Load 16(attach) + 24: 18(int) CompositeExtract 21 0 + 26: 18(int) CompositeExtract 21 1 + 27: 7(fvec4) ImageFetch 17 24 Lod 26 + 28: 7(fvec4) FAdd 13 27 + ReturnValue 28 + FunctionEnd diff --git a/Test/hlsl.attributeC11.frag b/Test/hlsl.attributeC11.frag new file mode 100644 index 000000000..4fe663a2a --- /dev/null +++ b/Test/hlsl.attributeC11.frag @@ -0,0 +1,18 @@ +struct S { + float2 f; +}; + +[[vk::binding(1)]] +StructuredBuffer<S> buffer1; + +[[vk::binding(3, 2)]] +StructuredBuffer<S> buffer3; + +[[vk::input_attachment_index(4)]] +Texture2D<float4> attach; + +[[vk::location(7)]] float4 +main([[vk::location(8)]] float4 input: A) : B +{ + return input + attach.Load(float2(0.5)); +} diff --git a/gtests/Hlsl.FromFile.cpp b/gtests/Hlsl.FromFile.cpp index 4e810ded8..dfdadf5ee 100644 --- a/gtests/Hlsl.FromFile.cpp +++ b/gtests/Hlsl.FromFile.cpp @@ -99,6 +99,7 @@ INSTANTIATE_TEST_CASE_P( {"hlsl.assoc.frag", "PixelShaderFunction"}, {"hlsl.attribute.frag", "PixelShaderFunction"}, {"hlsl.attribute.expression.comp", "main"}, + {"hlsl.attributeC11.frag", "main"}, {"hlsl.basic.comp", "main"}, {"hlsl.basic.geom", "main"}, {"hlsl.boolConv.vert", "main"}, diff --git a/hlsl/hlslAttributes.cpp b/hlsl/hlslAttributes.cpp index 14f7a7bd3..61ef8055d 100644 --- a/hlsl/hlslAttributes.cpp +++ b/hlsl/hlslAttributes.cpp @@ -40,11 +40,27 @@ namespace glslang { // Map the given string to an attribute enum from TAttributeType, // or EatNone if invalid. - TAttributeType TAttributeMap::attributeFromName(const TString& name) + TAttributeType TAttributeMap::attributeFromName(const TString& nameSpace, const TString& name) { // These are case insensitive. TString lowername(name); std::transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower); + TString lowernameSpace(nameSpace); + std::transform(lowernameSpace.begin(), lowernameSpace.end(), lowernameSpace.begin(), ::tolower); + + // handle names within a namespace + + if (lowernameSpace == "vk") { + if (lowername == "input_attachment_index") + return EatInputAttachment; + else if (lowername == "location") + return EatLocation; + else if (lowername == "binding") + return EatBinding; + } else if (lowernameSpace.size() > 0) + return EatNone; + + // handle names with no namespace if (lowername == "allow_uav_condition") return EatAllow_uav_condition; @@ -88,12 +104,12 @@ namespace glslang { // Look up entry, inserting if it's not there, and if name is a valid attribute name // as known by attributeFromName. - TAttributeType TAttributeMap::setAttribute(const TString* name, TIntermAggregate* value) + TAttributeType TAttributeMap::setAttribute(const TString& nameSpace, const TString* name, TIntermAggregate* value) { if (name == nullptr) return EatNone; - const TAttributeType attr = attributeFromName(*name); + const TAttributeType attr = attributeFromName(nameSpace, *name); if (attr != EatNone) attributes[attr] = value; diff --git a/hlsl/hlslAttributes.h b/hlsl/hlslAttributes.h index b32a53cbe..16ec31dad 100644 --- a/hlsl/hlslAttributes.h +++ b/hlsl/hlslAttributes.h @@ -63,6 +63,9 @@ namespace glslang { EatPatchSize, EatUnroll, EatLoop, + EatBinding, + EatLocation, + EatInputAttachment }; } @@ -82,7 +85,7 @@ namespace glslang { public: // Search for and potentially add the attribute into the map. Return the // attribute type enum for it, if found, else EatNone. - TAttributeType setAttribute(const TString* name, TIntermAggregate* value); + TAttributeType setAttribute(const TString& nameSpace, const TString* name, TIntermAggregate* value); // Const lookup: search for (but do not modify) the attribute in the map. const TIntermAggregate* operator[](TAttributeType) const; @@ -92,7 +95,7 @@ namespace glslang { protected: // Find an attribute enum given its name. - static TAttributeType attributeFromName(const TString&); + static TAttributeType attributeFromName(const TString& nameSpace, const TString& name); std::unordered_map<TAttributeType, TIntermAggregate*> attributes; }; diff --git a/hlsl/hlslGrammar.cpp b/hlsl/hlslGrammar.cpp index fcdeff403..4914f20c1 100755 --- a/hlsl/hlslGrammar.cpp +++ b/hlsl/hlslGrammar.cpp @@ -295,13 +295,16 @@ bool HlslGrammar::acceptSamplerDeclarationDX9(TType& /*type*/) } // declaration +// : attributes attributed_declaration +// | NAMESPACE IDENTIFIER LEFT_BRACE declaration_list RIGHT_BRACE +// +// attributed_declaration // : sampler_declaration_dx9 post_decls SEMICOLON // | fully_specified_type // for cbuffer/tbuffer // | fully_specified_type declarator_list SEMICOLON // for non cbuffer/tbuffer // | fully_specified_type identifier function_parameters post_decls compound_statement // function definition // | fully_specified_type identifier sampler_state post_decls compound_statement // sampler definition // | typedef declaration -// | NAMESPACE IDENTIFIER LEFT_BRACE declaration_list RIGHT_BRACE // // declarator_list // : declarator COMMA declarator COMMA declarator... // zero or more declarators @@ -376,6 +379,8 @@ bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) if (! acceptFullySpecifiedType(declaredType, nodeList)) return false; + parseContext.transferTypeAttributes(declarator.attributes, declaredType); + // cbuffer and tbuffer end with the closing '}'. // No semicolon is included. if (forbidDeclarators) @@ -2371,16 +2376,25 @@ bool HlslGrammar::acceptDefaultParameterDeclaration(const TType& type, TIntermTy } // parameter_declaration +// : attributes attributed_declaration +// +// attributed_declaration // : fully_specified_type post_decls [ = default_parameter_declaration ] // | fully_specified_type identifier array_specifier post_decls [ = default_parameter_declaration ] // bool HlslGrammar::acceptParameterDeclaration(TFunction& function) { + // attributes + TAttributeMap attributes; + acceptAttributes(attributes); + // fully_specified_type TType* type = new TType; if (! acceptFullySpecifiedType(*type)) return false; + parseContext.transferTypeAttributes(attributes, *type); + // identifier HlslToken idToken; acceptIdentifier(idToken); @@ -3256,7 +3270,15 @@ bool HlslGrammar::acceptStatement(TIntermNode*& statement) } // attributes -// : list of zero or more of: LEFT_BRACKET attribute RIGHT_BRACKET +// : [zero or more:] bracketed-attribute +// +// bracketed-attribute: +// : LEFT_BRACKET scoped-attribute RIGHT_BRACKET +// : LEFT_BRACKET LEFT_BRACKET scoped-attribute RIGHT_BRACKET RIGHT_BRACKET +// +// scoped-attribute: +// : attribute +// | namespace COLON COLON attribute // // attribute: // : UNROLL @@ -3283,18 +3305,33 @@ void HlslGrammar::acceptAttributes(TAttributeMap& attributes) // numthreads, which is used to set the CS local size. // TODO: subset to correct set? Pass on? do { - HlslToken idToken; + HlslToken attributeToken; // LEFT_BRACKET? if (! acceptTokenClass(EHTokLeftBracket)) return; + // another LEFT_BRACKET? + bool doubleBrackets = false; + if (acceptTokenClass(EHTokLeftBracket)) + doubleBrackets = true; + + // attribute? (could be namespace; will adjust later) + if (!acceptIdentifier(attributeToken)) { + if (!peekTokenClass(EHTokRightBracket)) { + expected("namespace or attribute identifier"); + advanceToken(); + } + } - // attribute - if (acceptIdentifier(idToken)) { - // 'idToken.string' is the attribute - } else if (! peekTokenClass(EHTokRightBracket)) { - expected("identifier"); - advanceToken(); + TString nameSpace; + if (acceptTokenClass(EHTokColonColon)) { + // namespace COLON COLON + nameSpace = *attributeToken.string; + // attribute + if (!acceptIdentifier(attributeToken)) { + expected("attribute identifier"); + return; + } } TIntermAggregate* expressions = nullptr; @@ -3327,10 +3364,15 @@ void HlslGrammar::acceptAttributes(TAttributeMap& attributes) expected("]"); return; } + // another RIGHT_BRACKET? + if (doubleBrackets && !acceptTokenClass(EHTokRightBracket)) { + expected("]]"); + return; + } // Add any values we found into the attribute map. This accepts // (and ignores) values not mapping to a known TAttributeType; - attributes.setAttribute(idToken.string, expressions); + attributes.setAttribute(nameSpace, attributeToken.string, expressions); } while (true); } diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp index 6f023de58..74afb031c 100755 --- a/hlsl/hlslParseHelper.cpp +++ b/hlsl/hlslParseHelper.cpp @@ -1666,7 +1666,6 @@ TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& l return paramNodes; } - // Handle all [attrib] attribute for the shader entry point void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributeMap& attributes) { @@ -1815,6 +1814,44 @@ void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const T } } +// Update the given type with any type-like attribute information in the +// attributes. +void HlslParseContext::transferTypeAttributes(const TAttributeMap& attributes, TType& type) +{ + // extract integers out of attribute arguments stored in attribute aggregate + const auto getInt = [&](TAttributeType attr, int argNum, int& value) -> bool { + const TIntermAggregate* attrAgg = attributes[attr]; + if (attrAgg == nullptr) + return false; + if (argNum >= attrAgg->getSequence().size()) + return false; + const TConstUnion& intConst = attrAgg->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0]; + if (intConst == nullptr) + return false; + value = intConst.getIConst(); + return true; + }; + + // location + int value; + if (getInt(EatLocation, 0, value)) + type.getQualifier().layoutLocation = value; + + // binding + if (getInt(EatBinding, 0, value)) { + type.getQualifier().layoutBinding = value; + type.getQualifier().layoutSet = 0; + } + + // set + if (getInt(EatBinding, 1, value)) + type.getQualifier().layoutSet = value; + + // input attachment + if (getInt(EatInputAttachment, 0, value)) + type.getQualifier().layoutAttachment = value; +} + // // Do all special handling for the entry point, including wrapping // the shader's entry point with the official entry point that will call it. diff --git a/hlsl/hlslParseHelper.h b/hlsl/hlslParseHelper.h index 077beb918..c1468ed07 100755 --- a/hlsl/hlslParseHelper.h +++ b/hlsl/hlslParseHelper.h @@ -83,6 +83,7 @@ public: TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&, const TAttributeMap&, TIntermNode*& entryPointTree); TIntermNode* transformEntryPoint(const TSourceLoc&, TFunction&, const TAttributeMap&); void handleEntryPointAttributes(const TSourceLoc&, const TAttributeMap&); + void transferTypeAttributes(const TAttributeMap&, TType&); void handleFunctionBody(const TSourceLoc&, TFunction&, TIntermNode* functionBody, TIntermNode*& node); void remapEntryPointIO(TFunction& function, TVariable*& returnValue, TVector<TVariable*>& inputs, TVector<TVariable*>& outputs); void remapNonEntryPointIO(TFunction& function); -- GitLab