diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index e771411ef..4e36b6de8 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -750,6 +750,38 @@ private:
         }
     }
 
+    std::string WriteTexsInstruction(const Instruction& instr, const std::string& coord,
+                                     const std::string& texture) {
+        // Add an extra scope and declare the texture coords inside to prevent
+        // overwriting them in case they are used as outputs of the texs instruction.
+        shader.AddLine('{');
+        ++shader.scope;
+        shader.AddLine(coord);
+
+        // TEXS has two destination registers. RG goes into gpr0+0 and gpr0+1, and BA
+        // goes into gpr28+0 and gpr28+1
+        size_t texs_offset{};
+
+        for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
+            for (unsigned elem = 0; elem < 2; ++elem) {
+                if (!instr.texs.IsComponentEnabled(elem)) {
+                    // Skip disabled components
+                    continue;
+                }
+                regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false, elem);
+            }
+
+            if (!instr.texs.HasTwoDestinations()) {
+                // Skip the second destination
+                break;
+            }
+
+            texs_offset += 2;
+        }
+        --shader.scope;
+        shader.AddLine('}');
+    }
+
     /**
      * Compiles a single instruction from Tegra to GLSL.
      * @param offset the offset of the Tegra shader instruction.
@@ -1348,36 +1380,18 @@ private:
                 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
                 const std::string sampler = GetSampler(instr.sampler);
                 const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
-                // Add an extra scope and declare the texture coords inside to prevent
-                // overwriting them in case they are used as outputs of the texs instruction.
-                shader.AddLine("{");
-                ++shader.scope;
-                shader.AddLine(coord);
+
                 const std::string texture = "texture(" + sampler + ", coords)";
-
-                // TEXS has two destination registers. RG goes into gpr0+0 and gpr0+1, and BA
-                // goes into gpr28+0 and gpr28+1
-                size_t texs_offset{};
-
-                for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
-                    for (unsigned elem = 0; elem < 2; ++elem) {
-                        if (!instr.texs.IsComponentEnabled(elem)) {
-                            // Skip disabled components
-                            continue;
-                        }
-                        regs.SetRegisterToFloat(dest, elem + texs_offset, texture, 1, 4, false,
-                                                elem);
-                    }
-
-                    if (!instr.texs.HasTwoDestinations()) {
-                        // Skip the second destination
-                        break;
-                    }
-
-                    texs_offset += 2;
-                }
-                --shader.scope;
-                shader.AddLine("}");
+                WriteTexsInstruction(instr, coord, texture);
+                break;
+            }
+            case OpCode::Id::TLDS: {
+                const std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
+                const std::string op_b = regs.GetRegisterAsInteger(instr.gpr20);
+                const std::string sampler = GetSampler(instr.sampler);
+                const std::string coord = "ivec2 coords = ivec2(" + op_a + ", " + op_b + ");";
+                const std::string texture = "texelFetch(" + sampler + ", coords, 0)";
+                WriteTexsInstruction(instr, coord, texture);
                 break;
             }
             default: {