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;