From 5fd92780b2d463fd4668472c41ef32ae4c15e9e6 Mon Sep 17 00:00:00 2001
From: ameerj <52414509+ameerj@users.noreply.github.com>
Date: Sat, 29 May 2021 16:58:33 -0400
Subject: [PATCH] glsl: TLD4 implementation

---
 .../backend/glsl/emit_glsl_image.cpp          | 91 ++++++++++++++++++-
 1 file changed, 89 insertions(+), 2 deletions(-)

diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index 78e2d5bac..e12d7b850 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -67,6 +67,23 @@ std::string ShadowSamplerVecCast(TextureType type) {
     }
 }
 
+std::string PtpOffsets(const IR::Value& offset, const IR::Value& offset2) {
+    const std::array values{offset.InstRecursive(), offset2.InstRecursive()};
+    if (!values[0]->AreAllArgsImmediates() || !values[1]->AreAllArgsImmediates()) {
+        // LOG_WARNING("Not all arguments in PTP are immediate, STUBBING");
+        return "";
+    }
+    const IR::Opcode opcode{values[0]->GetOpcode()};
+    if (opcode != values[1]->GetOpcode() || opcode != IR::Opcode::CompositeConstructU32x4) {
+        throw LogicError("Invalid PTP arguments");
+    }
+    auto read{[&](unsigned int a, unsigned int b) { return values[a]->Arg(b).U32(); }};
+
+    return fmt::format("ivec2[](ivec2({},{}),ivec2({},{}),ivec2({},{}),ivec2({},{}))", read(0, 0),
+                       read(0, 1), read(0, 2), read(0, 3), read(1, 0), read(1, 1), read(1, 2),
+                       read(1, 3));
+}
+
 IR::Inst* PrepareSparse(IR::Inst& inst) {
     const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
     if (sparse_inst) {
@@ -213,7 +230,45 @@ void EmitImageGather([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Ins
                      [[maybe_unused]] std::string_view coords,
                      [[maybe_unused]] const IR::Value& offset,
                      [[maybe_unused]] const IR::Value& offset2) {
-    throw NotImplementedException("GLSL Instruction");
+    const auto info{inst.Flags<IR::TextureInstInfo>()};
+    const auto texture{Texture(ctx, info, index)};
+    const auto texel{ctx.reg_alloc.Define(inst, Type::F32x4)};
+    const auto sparse_inst{PrepareSparse(inst)};
+    if (!offset2.IsEmpty()) {
+        ctx.Add("/*OFFSET 2 IS {}*/", ctx.reg_alloc.Consume(offset2));
+    }
+    if (!sparse_inst) {
+        if (offset.IsEmpty()) {
+            ctx.Add("{}=textureGather({},{},int({}));", texel, texture, coords,
+                    info.gather_component);
+            return;
+        }
+        if (offset2.IsEmpty()) {
+            ctx.Add("{}=textureGatherOffset({},{},{},int({}));", texel, texture, coords,
+                    CastToIntVec(ctx.reg_alloc.Consume(offset), info), info.gather_component);
+            return;
+        }
+        // PTP
+        const auto offsets{PtpOffsets(offset, offset2)};
+        ctx.Add("{}=textureGatherOffsets({},{},{},int({}));", texel, texture, coords, offsets,
+                info.gather_component);
+        return;
+    }
+    // TODO: Query sparseTexels extension support
+    if (offset.IsEmpty()) {
+        ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherARB({},{},{},int({})));",
+                  *sparse_inst, texture, coords, texel, info.gather_component);
+    }
+    if (offset2.IsEmpty()) {
+        ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},{},int({})));",
+                  *sparse_inst, texture, CastToIntVec(coords, info),
+                  CastToIntVec(ctx.reg_alloc.Consume(offset), info), texel, info.gather_component);
+    }
+    // PTP
+    const auto offsets{PtpOffsets(offset, offset2)};
+    ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},{},int({})));",
+              *sparse_inst, texture, CastToIntVec(coords, info), offsets, texel,
+              info.gather_component);
 }
 
 void EmitImageGatherDref([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
@@ -222,7 +277,39 @@ void EmitImageGatherDref([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR:
                          [[maybe_unused]] const IR::Value& offset,
                          [[maybe_unused]] const IR::Value& offset2,
                          [[maybe_unused]] std::string_view dref) {
-    throw NotImplementedException("GLSL Instruction");
+    const auto info{inst.Flags<IR::TextureInstInfo>()};
+    const auto texture{Texture(ctx, info, index)};
+    const auto texel{ctx.reg_alloc.Define(inst, Type::F32x4)};
+    const auto sparse_inst{PrepareSparse(inst)};
+    if (!sparse_inst) {
+        if (offset.IsEmpty()) {
+            ctx.Add("{}=textureGather({},{},{});", texel, texture, coords, dref);
+            return;
+        }
+        if (offset2.IsEmpty()) {
+            ctx.Add("{}=textureGatherOffset({},{},{},{});", texel, texture, coords, dref,
+                    CastToIntVec(ctx.reg_alloc.Consume(offset), info));
+            return;
+        }
+        // PTP
+        const auto offsets{PtpOffsets(offset, offset2)};
+        ctx.Add("{}=textureGatherOffsets({},{},{},{});", texel, texture, coords, dref, offsets);
+        return;
+    }
+    // TODO: Query sparseTexels extension support
+    if (offset.IsEmpty()) {
+        ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherARB({},{},{},{}));", *sparse_inst,
+                  texture, coords, dref, texel);
+    }
+    if (offset2.IsEmpty()) {
+        ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},,{},{}));",
+                  *sparse_inst, texture, CastToIntVec(coords, info), dref,
+                  CastToIntVec(ctx.reg_alloc.Consume(offset), info), texel);
+    }
+    // PTP
+    const auto offsets{PtpOffsets(offset, offset2)};
+    ctx.AddU1("{}=sparseTexelsResidentARB(sparseTextureGatherOffsetARB({},{},{},,{},{}));",
+              *sparse_inst, texture, CastToIntVec(coords, info), dref, offsets, texel);
 }
 
 void EmitImageFetch([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,