diff --git a/Test/baseResults/hlsl.aliasOpaque.frag.out b/Test/baseResults/hlsl.aliasOpaque.frag.out new file mode 100755 index 0000000000000000000000000000000000000000..370dcb1aa8007fd180300f7a65f4db80a18f65ca --- /dev/null +++ b/Test/baseResults/hlsl.aliasOpaque.frag.out @@ -0,0 +1,174 @@ +hlsl.aliasOpaque.frag +Shader version: 500 +gl_FragCoord origin is upper left +0:? Sequence +0:12 Function Definition: osCall(struct-OS-p1-f1-t211; ( temp 4-component vector of float) +0:12 Function Parameters: +0:? 'ss' ( in sampler) +0:? 'a' ( in float) +0:? 'tex' ( in texture2D) +0:? Sequence +0:13 Branch: Return with expression +0:13 vector-scale ( temp 4-component vector of float) +0:? 'a' ( in float) +0:13 texture ( temp 4-component vector of float) +0:13 Construct combined texture-sampler ( temp sampler2D) +0:? 'tex' ( in texture2D) +0:? 'ss' ( in sampler) +0:? Constant: +0:? 0.200000 +0:? 0.300000 +0:17 Function Definition: @main( ( temp 4-component vector of float) +0:17 Function Parameters: +0:? Sequence +0:19 'gss2' ( uniform sampler) +0:20 'gss' ( uniform sampler) +0:21 'gtex' ( uniform texture2D) +0:22 move second child to first child ( temp float) +0:? 'a' ( temp float) +0:22 Constant: +0:22 3.000000 +0:28 Branch: Return with expression +0:28 Function Call: osCall(struct-OS-p1-f1-t211; ( temp 4-component vector of float) +0:? 'gss' ( uniform sampler) +0:? 'a' ( temp float) +0:? 'gtex' ( uniform texture2D) +0:17 Function Definition: main( ( temp void) +0:17 Function Parameters: +0:? Sequence +0:17 move second child to first child ( temp 4-component vector of float) +0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float) +0:17 Function Call: @main( ( temp 4-component vector of float) +0:? Linker Objects +0:? 'gss' ( uniform sampler) +0:? 'gss2' ( uniform sampler) +0:? 'gtex' ( uniform texture2D) +0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float) + + +Linked fragment stage: + + +Shader version: 500 +gl_FragCoord origin is upper left +0:? Sequence +0:12 Function Definition: osCall(struct-OS-p1-f1-t211; ( temp 4-component vector of float) +0:12 Function Parameters: +0:? 'ss' ( in sampler) +0:? 'a' ( in float) +0:? 'tex' ( in texture2D) +0:? Sequence +0:13 Branch: Return with expression +0:13 vector-scale ( temp 4-component vector of float) +0:? 'a' ( in float) +0:13 texture ( temp 4-component vector of float) +0:13 Construct combined texture-sampler ( temp sampler2D) +0:? 'tex' ( in texture2D) +0:? 'ss' ( in sampler) +0:? Constant: +0:? 0.200000 +0:? 0.300000 +0:17 Function Definition: @main( ( temp 4-component vector of float) +0:17 Function Parameters: +0:? Sequence +0:19 'gss2' ( uniform sampler) +0:20 'gss' ( uniform sampler) +0:21 'gtex' ( uniform texture2D) +0:22 move second child to first child ( temp float) +0:? 'a' ( temp float) +0:22 Constant: +0:22 3.000000 +0:28 Branch: Return with expression +0:28 Function Call: osCall(struct-OS-p1-f1-t211; ( temp 4-component vector of float) +0:? 'gss' ( uniform sampler) +0:? 'a' ( temp float) +0:? 'gtex' ( uniform texture2D) +0:17 Function Definition: main( ( temp void) +0:17 Function Parameters: +0:? Sequence +0:17 move second child to first child ( temp 4-component vector of float) +0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float) +0:17 Function Call: @main( ( temp 4-component vector of float) +0:? Linker Objects +0:? 'gss' ( uniform sampler) +0:? 'gss2' ( uniform sampler) +0:? 'gtex' ( uniform texture2D) +0:? '@entryPointOutput' (layout( location=0) out 4-component vector of float) + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 48 + + Capability Shader + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Fragment 4 "main" 46 + ExecutionMode 4 OriginUpperLeft + Source HLSL 500 + Name 4 "main" + Name 17 "osCall(struct-OS-p1-f1-t211;" + Name 14 "ss" + Name 15 "a" + Name 16 "tex" + Name 20 "@main(" + Name 35 "gss2" + Name 36 "gss" + Name 37 "gtex" + Name 38 "a" + Name 40 "param" + Name 46 "@entryPointOutput" + Decorate 35(gss2) DescriptorSet 0 + Decorate 36(gss) DescriptorSet 0 + Decorate 37(gtex) DescriptorSet 0 + Decorate 46(@entryPointOutput) Location 0 + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeSampler + 7: TypePointer UniformConstant 6 + 8: TypeFloat 32 + 9: TypePointer Function 8(float) + 10: TypeImage 8(float) 2D sampled format:Unknown + 11: TypePointer UniformConstant 10 + 12: TypeVector 8(float) 4 + 13: TypeFunction 12(fvec4) 7(ptr) 9(ptr) 11(ptr) + 19: TypeFunction 12(fvec4) + 25: TypeSampledImage 10 + 27: TypeVector 8(float) 2 + 28: 8(float) Constant 1045220557 + 29: 8(float) Constant 1050253722 + 30: 27(fvec2) ConstantComposite 28 29 + 35(gss2): 7(ptr) Variable UniformConstant + 36(gss): 7(ptr) Variable UniformConstant + 37(gtex): 11(ptr) Variable UniformConstant + 39: 8(float) Constant 1077936128 + 45: TypePointer Output 12(fvec4) +46(@entryPointOutput): 45(ptr) Variable Output + 4(main): 2 Function None 3 + 5: Label + 47: 12(fvec4) FunctionCall 20(@main() + Store 46(@entryPointOutput) 47 + Return + FunctionEnd +17(osCall(struct-OS-p1-f1-t211;): 12(fvec4) Function None 13 + 14(ss): 7(ptr) FunctionParameter + 15(a): 9(ptr) FunctionParameter + 16(tex): 11(ptr) FunctionParameter + 18: Label + 22: 8(float) Load 15(a) + 23: 10 Load 16(tex) + 24: 6 Load 14(ss) + 26: 25 SampledImage 23 24 + 31: 12(fvec4) ImageSampleImplicitLod 26 30 + 32: 12(fvec4) VectorTimesScalar 31 22 + ReturnValue 32 + FunctionEnd + 20(@main(): 12(fvec4) Function None 19 + 21: Label + 38(a): 9(ptr) Variable Function + 40(param): 9(ptr) Variable Function + Store 38(a) 39 + 41: 8(float) Load 38(a) + Store 40(param) 41 + 42: 12(fvec4) FunctionCall 17(osCall(struct-OS-p1-f1-t211;) 36(gss) 40(param) 37(gtex) + ReturnValue 42 + FunctionEnd diff --git a/Test/hlsl.aliasOpaque.frag b/Test/hlsl.aliasOpaque.frag new file mode 100644 index 0000000000000000000000000000000000000000..8b1cceff91f11ae7db6bcdb7853da48269378afc --- /dev/null +++ b/Test/hlsl.aliasOpaque.frag @@ -0,0 +1,29 @@ +struct OS { + SamplerState ss; + float a; + Texture2D tex; +}; + +SamplerState gss; +SamplerState gss2; +Texture2D gtex; + +float4 osCall(OS s) +{ + return s.a * s.tex.Sample(s.ss, float2(0.2, 0.3)); +} + +float4 main() : SV_TARGET0 +{ + OS os; + os.ss = gss2; + os.ss = gss; + os.tex = gtex; + os.a = 3.0; + + // this should give an error + //SamplerState localss; + //localss = gss2; + + return osCall(os); +} diff --git a/gtests/Hlsl.FromFile.cpp b/gtests/Hlsl.FromFile.cpp index 91d7ae61a7ad61b05f98170cb91d09f301c0036c..9edc51af7324e989eae30db4745f6147aa6797a1 100644 --- a/gtests/Hlsl.FromFile.cpp +++ b/gtests/Hlsl.FromFile.cpp @@ -81,6 +81,7 @@ INSTANTIATE_TEST_CASE_P( ToSpirv, HlslCompileTest, ::testing::ValuesIn(std::vector<FileNameEntryPointPair>{ {"hlsl.amend.frag", "f1"}, + {"hlsl.aliasOpaque.frag", "main"}, {"hlsl.array.frag", "PixelShaderFunction"}, {"hlsl.array.implicit-size.frag", "PixelShaderFunction"}, {"hlsl.array.multidim.frag", "main"}, diff --git a/hlsl/hlslParseHelper.cpp b/hlsl/hlslParseHelper.cpp index 43949b51b9c1b01561a9f46445a2eb13ec48f92a..ff0c7771e1c904178d3f00f53df71daea61d44f8 100755 --- a/hlsl/hlslParseHelper.cpp +++ b/hlsl/hlslParseHelper.cpp @@ -147,7 +147,7 @@ bool HlslParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& // bool HlslParseContext::shouldConvertLValue(const TIntermNode* node) const { - if (node == nullptr) + if (node == nullptr || node->getAsTyped() == nullptr) return false; const TIntermAggregate* lhsAsAggregate = node->getAsAggregate(); @@ -157,10 +157,14 @@ bool HlslParseContext::shouldConvertLValue(const TIntermNode* node) const if (lhsAsBinary != nullptr && (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect)) lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate(); - if (lhsAsAggregate != nullptr && lhsAsAggregate->getOp() == EOpImageLoad) return true; + // If it's a syntactic write to a sampler, we will use that to establish + // a compile-time alias. + if (node->getAsTyped()->getBasicType() == EbtSampler) + return true; + return false; } @@ -233,7 +237,7 @@ bool HlslParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, T // // Most things are passed through unmodified, except for error checking. // -TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* op, TIntermTyped* node) +TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* op, TIntermTyped*& node) { if (node == nullptr) return nullptr; @@ -256,6 +260,10 @@ TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* // *** If we get here, we're going to apply some conversion to an l-value. + // Spin off sampler aliasing + if (node->getAsTyped()->getBasicType() == EbtSampler) + return handleSamplerLvalue(loc, op, node); + // Helper to create a load. const auto makeLoad = [&](TIntermSymbol* rhsTmp, TIntermTyped* object, TIntermTyped* coord, const TType& derefType) { TIntermAggregate* loadOp = new TIntermAggregate(EOpImageLoad); @@ -500,6 +508,42 @@ TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* return node; } +// Deal with sampler aliasing: turning assignments into aliases +TIntermTyped* HlslParseContext::handleSamplerLvalue(const TSourceLoc& loc, const char* op, TIntermTyped*& node) +{ + // Can only alias an assignment: "s1 = s2" + TIntermBinary* binary = node->getAsBinaryNode(); + if (binary == nullptr || node->getAsOperator()->getOp() != EOpAssign || + binary->getLeft() ->getAsSymbolNode() == nullptr || + binary->getRight()->getAsSymbolNode() == nullptr) { + error(loc, "can't modify sampler", op, ""); + return node; + } + + // Best is if we are aliasing a flattened struct member "S.s1 = s2", + // in which case we want to update the flattening information with the alias, + // making everything else work seamlessly. + TIntermSymbol* left = binary->getLeft()->getAsSymbolNode(); + TIntermSymbol* right = binary->getRight()->getAsSymbolNode(); + for (auto fit = flattenMap.begin(); fit != flattenMap.end(); ++fit) { + for (auto mit = fit->second.members.begin(); mit != fit->second.members.end(); ++mit) { + if ((*mit)->getUniqueId() == left->getId()) { + // found it: update with alias to the existing variable, and don't emit any code + (*mit) = new TVariable(&right->getName(), right->getType()); + (*mit)->setUniqueId(right->getId()); + // replace node (rest of compiler expects either an error or code to generate) + // with pointless access + node = binary->getRight(); + return node; + } + } + } + + warn(loc, "could not create alias for sampler", op, ""); + + return node; +} + void HlslParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens) { if (pragmaCallback) @@ -1284,7 +1328,7 @@ bool HlslParseContext::wasSplit(const TIntermTyped* node) const // Turn an access into an aggregate that was flattened to instead be // an access to the individual variable the member was flattened to. // Assumes shouldFlatten() or equivalent was called first. -// Also assumes that initFlattening() and finalizeFlattening() bracket usage. +// Also assumes that initFlattening() and finalizeFlattening() bracket the usage. TIntermTyped* HlslParseContext::flattenAccess(TIntermTyped* base, int member) { const TType dereferencedType(base->getType(), member); // dereferenced type diff --git a/hlsl/hlslParseHelper.h b/hlsl/hlslParseHelper.h index eeba37e5ee379a6c2450d42d0d782bb59f584c3f..9a98964239c421bbb751cdb495ce3b1304f6b7bc 100755 --- a/hlsl/hlslParseHelper.h +++ b/hlsl/hlslParseHelper.h @@ -187,7 +187,8 @@ public: virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr) override; // Apply L-value conversions. E.g, turning a write to a RWTexture into an ImageStore. - TIntermTyped* handleLvalue(const TSourceLoc&, const char* op, TIntermTyped* node); + TIntermTyped* handleLvalue(const TSourceLoc&, const char* op, TIntermTyped*& node); + TIntermTyped* handleSamplerLvalue(const TSourceLoc&, const char* op, TIntermTyped*& node); bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*) override; TLayoutFormat getLayoutFromTxType(const TSourceLoc&, const TType&);