diff --git a/Test/baseResults/spv.texture.sampler.transform.frag.out b/Test/baseResults/spv.texture.sampler.transform.frag.out new file mode 100644 index 0000000000000000000000000000000000000000..198c48196d74c1b062f81ee73058f2d32d8d90d9 --- /dev/null +++ b/Test/baseResults/spv.texture.sampler.transform.frag.out @@ -0,0 +1,38 @@ +spv.texture.sampler.transform.frag +Warning, version 440 is not yet complete; most version-specific features are present, but some are missing. + +// Module Version 10000 +// Generated by (magic number): 80001 +// Id's are bound by 19 + + Capability Shader + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Fragment 4 "main" 9 16 + ExecutionMode 4 OriginUpperLeft + Source GLSL 440 + Name 4 "main" + Name 9 "color" + Name 12 "tex" + Name 16 "coord" + Decorate 12(tex) DescriptorSet 0 + 2: TypeVoid + 3: TypeFunction 2 + 6: TypeFloat 32 + 7: TypeVector 6(float) 4 + 8: TypePointer Output 7(fvec4) + 9(color): 8(ptr) Variable Output + 10: TypeImage 6(float) 2D sampled format:Unknown + 11: TypePointer UniformConstant 10 + 12(tex): 11(ptr) Variable UniformConstant + 14: TypeVector 6(float) 2 + 15: TypePointer Input 14(fvec2) + 16(coord): 15(ptr) Variable Input + 4(main): 2 Function None 3 + 5: Label + 13: 10 Load 12(tex) + 17: 14(fvec2) Load 16(coord) + 18: 7(fvec4) ImageSampleImplicitLod 13 17 + Store 9(color) 18 + Return + FunctionEnd diff --git a/Test/spv.texture.sampler.transform.frag b/Test/spv.texture.sampler.transform.frag new file mode 100644 index 0000000000000000000000000000000000000000..872d9b04d26cc12c8bccc5a0fa7da41208520b90 --- /dev/null +++ b/Test/spv.texture.sampler.transform.frag @@ -0,0 +1,13 @@ +#version 440 + +uniform sampler smp; +uniform texture2D tex; + +in vec2 coord; + +out vec4 color; + +void main() +{ + color = texture(sampler2D(tex, smp), coord); +} diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h index 6bcbe4088b227343f5a184853e5957ff5a08badb..5eac73e0da79d13732212ece4dd234d2cf1bac30 100644 --- a/glslang/Include/Types.h +++ b/glslang/Include/Types.h @@ -1310,6 +1310,7 @@ public: virtual TBasicType getBasicType() const { return basicType; } virtual const TSampler& getSampler() const { return sampler; } + virtual TSampler& getSampler() { return sampler; } virtual TQualifier& getQualifier() { return qualifier; } virtual const TQualifier& getQualifier() const { return qualifier; } diff --git a/glslang/MachineIndependent/Intermediate.cpp b/glslang/MachineIndependent/Intermediate.cpp index 663e30c66afe942366e7d7aac3b8f6a916ad7b03..ad9720c09477ae5621249cc826aab1db0f403528 100644 --- a/glslang/MachineIndependent/Intermediate.cpp +++ b/glslang/MachineIndependent/Intermediate.cpp @@ -1776,6 +1776,14 @@ bool TIntermediate::postProcess(TIntermNode* root, EShLanguage /*language*/) // Propagate 'noContraction' label in backward from 'precise' variables. glslang::PropagateNoContraction(*this); + switch (textureSamplerTransformMode) { + case EShTexSampTransKeep: + break; + case EShTexSampTransUpgradeTextureRemoveSampler: + performTextureUpgradeAndSamplerRemovalTransformation(root); + break; + } + return true; } @@ -2943,4 +2951,41 @@ bool TIntermediate::specConstantPropagates(const TIntermTyped& node1, const TInt (node2.getType().getQualifier().isSpecConstant() && node1.getType().getQualifier().isConstant()); } +struct TextureUpgradeAndSamplerRemovalTransform : public TIntermTraverser { + bool visitAggregate(TVisit, TIntermAggregate* ag) override { + using namespace std; + TIntermSequence& seq = ag->getSequence(); + // remove pure sampler variables + TIntermSequence::iterator newEnd = remove_if(seq.begin(), seq.end(), [](TIntermNode* node) { + TIntermSymbol* symbol = node->getAsSymbolNode(); + if (!symbol) + return false; + + return (symbol->getBasicType() == EbtSampler && symbol->getType().getSampler().isPureSampler()); + }); + seq.erase(newEnd, seq.end()); + // replace constructors with sampler/textures + // update textures into sampled textures + for_each(seq.begin(), seq.end(), [](TIntermNode*& node) { + TIntermSymbol* symbol = node->getAsSymbolNode(); + if (!symbol) { + TIntermAggregate *constructor = node->getAsAggregate(); + if (constructor && constructor->getOp() == EOpConstructTextureSampler) { + if (!constructor->getSequence().empty()) + node = constructor->getSequence()[0]; + } + } else if (symbol->getBasicType() == EbtSampler && symbol->getType().getSampler().isTexture()) { + symbol->getWritableType().getSampler().combined = true; + } + }); + return true; + } +}; + +void TIntermediate::performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root) +{ + TextureUpgradeAndSamplerRemovalTransform transform; + root->traverse(&transform); +} + } // end namespace glslang diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index 1f7c005f3e0ed160d750226da5076f606692b88b..d563bc2cdb0b5ddb5167ed1a2ab39c7a155373fe 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -1578,6 +1578,7 @@ void TShader::setHlslIoMapping(bool hlslIoMap) { intermediate->setHlslI void TShader::setFlattenUniformArrays(bool flatten) { intermediate->setFlattenUniformArrays(flatten); } void TShader::setNoStorageFormat(bool useUnknownFormat) { intermediate->setNoStorageFormat(useUnknownFormat); } void TShader::setResourceSetBinding(const std::vector<std::string>& base) { intermediate->setResourceSetBinding(base); } +void TShader::setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { intermediate->setTextureSamplerTransformMode(mode); } // // Turn the shader strings into a parse tree in the TIntermediate. diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h index 444d341d51de7c6c8546f02e7fb7214cf65eb0b9..8f6fdffe4486ecbc7591ca693e538fd427acee75 100644 --- a/glslang/MachineIndependent/localintermediate.h +++ b/glslang/MachineIndependent/localintermediate.h @@ -182,7 +182,8 @@ public: useUnknownFormat(false), hlslOffsets(false), useStorageBuffer(false), - hlslIoMapping(false) + hlslIoMapping(false), + textureSamplerTransformMode(EShTexSampTransKeep) { localSize[0] = 1; localSize[1] = 1; @@ -233,6 +234,7 @@ public: bool usingStorageBuffer() const { return useStorageBuffer; } void setHlslIoMapping(bool b) { hlslIoMapping = b; } bool usingHlslIoMapping() { return hlslIoMapping; } + void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { textureSamplerTransformMode = mode; } void setVersion(int v) { version = v; } int getVersion() const { return version; } @@ -472,6 +474,7 @@ protected: void pushSelector(TIntermSequence&, const TVectorSelector&, const TSourceLoc&); void pushSelector(TIntermSequence&, const TMatrixSelector&, const TSourceLoc&); bool specConstantPropagates(const TIntermTyped&, const TIntermTyped&); + void performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root); const EShLanguage language; // stage, known at construction time EShSource source; // source language, known a bit later @@ -536,6 +539,8 @@ protected: std::unordered_set<int> usedConstantId; // specialization constant ids used std::set<TString> semanticNameSet; + EShTextureSamplerTransformMode textureSamplerTransformMode; + private: void operator=(TIntermediate&); // prevent assignments }; diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index 3be66d43eaab91c55649b61f7ecc3bd7f4648a45..9cd999ba29ef440db8a8de4b3249903bc31f8c66 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -134,6 +134,14 @@ typedef enum { EShOptFull, // Optimizations that will take more time } EShOptimizationLevel; +// +// Texture and Sampler transformation mode. +// +typedef enum { + EShTexSampTransKeep, // keep textures and samplers as is (default) + EShTexSampTransUpgradeTextureRemoveSampler, // change texture w/o embeded sampler into sampled texture and throw away all samplers +} EShTextureSamplerTransformMode; + // // Message choices for what errors and warnings are given. // @@ -313,6 +321,7 @@ public: void setHlslIoMapping(bool hlslIoMap); void setFlattenUniformArrays(bool flatten); void setNoStorageFormat(bool useUnknownFormat); + void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode); // Interface to #include handlers. // diff --git a/gtests/Spv.FromFile.cpp b/gtests/Spv.FromFile.cpp index fd9c585c72db4be7f0d2dbf680942d6d79888c58..b551d646187a4bcad6df08074dd6ba37e9e99440 100644 --- a/gtests/Spv.FromFile.cpp +++ b/gtests/Spv.FromFile.cpp @@ -75,6 +75,7 @@ using CompileVulkanToSpirvTestAMD = GlslangTest<::testing::TestWithParam<std::st #ifdef NV_EXTENSIONS using CompileVulkanToSpirvTestNV = GlslangTest<::testing::TestWithParam<std::string>>; #endif +using CompileUpgradeTextureToSampledTextureAndDropSamplersTest = GlslangTest<::testing::TestWithParam<std::string>>; // Compiling GLSL to SPIR-V under Vulkan semantics. Expected to successfully // generate SPIR-V. @@ -172,6 +173,15 @@ TEST_P(CompileVulkanToSpirvTestNV, FromFile) } #endif +TEST_P(CompileUpgradeTextureToSampledTextureAndDropSamplersTest, FromFile) +{ + loadCompileUpgradeTextureToSampledTextureAndDropSamplersAndCheck(GlobalTestSettings.testRoot, + GetParam(), + Source::GLSL, + Semantics::Vulkan, + Target::Spv); +} + // clang-format off INSTANTIATE_TEST_CASE_P( Glsl, CompileVulkanToSpirvTest, @@ -407,6 +417,14 @@ INSTANTIATE_TEST_CASE_P( FileNameAsCustomTestSuffix ); #endif + +INSTANTIATE_TEST_CASE_P( + Glsl, CompileUpgradeTextureToSampledTextureAndDropSamplersTest, + ::testing::ValuesIn(std::vector<std::string>({ + "spv.texture.sampler.transform.frag", + })), + FileNameAsCustomTestSuffix +); // clang-format on } // anonymous namespace diff --git a/gtests/TestFixture.h b/gtests/TestFixture.h index c00645bed2002e9776ceab495652ec427225071a..a503b5fb221802b01dcfd3ffd8723216babc7f88 100644 --- a/gtests/TestFixture.h +++ b/gtests/TestFixture.h @@ -197,12 +197,14 @@ public: GlslangResult compileAndLink( const std::string shaderName, const std::string& code, const std::string& entryPointName, EShMessages controls, - bool flattenUniformArrays = false) + bool flattenUniformArrays = false, + EShTextureSamplerTransformMode texSampTransMode = EShTexSampTransKeep) { const EShLanguage kind = GetShaderStage(GetSuffix(shaderName)); glslang::TShader shader(kind); shader.setAutoMapLocations(true); + shader.setTextureSamplerTransformMode(texSampTransMode); shader.setFlattenUniformArrays(flattenUniformArrays); bool success = compile(&shader, code, entryPointName, controls); @@ -570,6 +572,31 @@ public: expectedErrorFname); } + void loadCompileUpgradeTextureToSampledTextureAndDropSamplersAndCheck(const std::string& testDir, + const std::string& testName, + Source source, + Semantics semantics, + Target target, + const std::string& entryPointName = "") + { + const std::string inputFname = testDir + "/" + testName; + const std::string expectedOutputFname = testDir + "/baseResults/" + testName + ".out"; + std::string input, expectedOutput; + + tryLoadFile(inputFname, "input", &input); + tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + + const EShMessages controls = DeriveOptions(source, semantics, target); + GlslangResult result = compileAndLink(testName, input, entryPointName, controls, false, EShTexSampTransUpgradeTextureRemoveSampler); + + // Generate the hybrid output in the way of glslangValidator. + std::ostringstream stream; + outputResultToStream(&stream, result, controls); + + checkEqAndUpdateIfRequested(expectedOutput, stream.str(), + expectedOutputFname); + } + private: const int defaultVersion; const EProfile defaultProfile;