From 3d3ed53511dcd338806a97bfff2afa1644ddb424 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Tue, 18 May 2021 19:43:52 -0300
Subject: glasm: Implement textureGather instructions

---
 .../backend/glasm/emit_glasm_image.cpp             | 103 ++++++++++++++++++---
 1 file changed, 92 insertions(+), 11 deletions(-)

(limited to 'src/shader_recompiler/backend/glasm/emit_glasm_image.cpp')

diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
index 77da3400b0..9da4da3314 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
@@ -2,6 +2,8 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include <utility>
+
 #include "shader_recompiler/backend/glasm/emit_context.h"
 #include "shader_recompiler/backend/glasm/emit_glasm_instructions.h"
 #include "shader_recompiler/frontend/ir/modifiers.h"
@@ -95,6 +97,33 @@ std::string Offset(EmitContext& ctx, const IR::Value& offset) {
     return fmt::format(",offset({})", Register{ctx.reg_alloc.Consume(offset)});
 }
 
+std::pair<ScopedRegister, ScopedRegister> AllocOffsetsRegs(EmitContext& ctx,
+                                                           const IR::Value& offset2) {
+    if (offset2.IsEmpty()) {
+        return {};
+    } else {
+        return {ctx.reg_alloc, ctx.reg_alloc};
+    }
+}
+
+void SwizzleOffsets(EmitContext& ctx, Register off_x, Register off_y, const IR::Value& offset1,
+                    const IR::Value& offset2) {
+    const Register offsets_a{ctx.reg_alloc.Consume(offset1)};
+    const Register offsets_b{ctx.reg_alloc.Consume(offset2)};
+    // Input swizzle:  [XYXY] [XYXY]
+    // Output swizzle: [XXXX] [YYYY]
+    ctx.Add("MOV {}.x,{}.x;"
+            "MOV {}.y,{}.z;"
+            "MOV {}.z,{}.x;"
+            "MOV {}.w,{}.z;"
+            "MOV {}.x,{}.y;"
+            "MOV {}.y,{}.w;"
+            "MOV {}.z,{}.y;"
+            "MOV {}.w,{}.w;",
+            off_x, offsets_a, off_x, offsets_a, off_x, offsets_b, off_x, offsets_b, off_y,
+            offsets_a, off_y, offsets_a, off_y, offsets_b, off_y, offsets_b);
+}
+
 std::pair<std::string, ScopedRegister> Coord(EmitContext& ctx, const IR::Value& coord) {
     if (coord.IsImmediate()) {
         ScopedRegister scoped_reg(ctx.reg_alloc);
@@ -326,19 +355,71 @@ void EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst& inst, const IR::
     StoreSparse(ctx, sparse_inst);
 }
 
-void EmitImageGather([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
-                     [[maybe_unused]] const IR::Value& index, [[maybe_unused]] Register coord,
-                     [[maybe_unused]] const IR::Value& offset,
-                     [[maybe_unused]] const IR::Value& offset2) {
-    throw NotImplementedException("GLASM instruction");
+void EmitImageGather(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
+                     const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2) {
+    // Allocate offsets early so they don't overwrite any consumed register
+    const auto [off_x, off_y]{AllocOffsetsRegs(ctx, offset2)};
+    const auto info{inst.Flags<IR::TextureInstInfo>()};
+    const char comp{"xyzw"[info.gather_component]};
+    const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
+    const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
+    const std::string_view type{TextureType(info)};
+    const std::string texture{Texture(ctx, info, index)};
+    const Register coord_vec{ctx.reg_alloc.Consume(coord)};
+    const Register ret{ctx.reg_alloc.Define(inst)};
+    if (offset2.IsEmpty()) {
+        const std::string offset_vec{Offset(ctx, offset)};
+        ctx.Add("TXG.F{} {},{},{}.{},{}{};", sparse_mod, ret, coord_vec, texture, comp, type,
+                offset_vec);
+    } else {
+        SwizzleOffsets(ctx, off_x.reg, off_y.reg, offset, offset2);
+        ctx.Add("TXGO.F{} {},{},{},{},{}.{},{};", sparse_mod, ret, coord_vec, off_x.reg, off_y.reg,
+                texture, comp, type);
+    }
+    StoreSparse(ctx, sparse_inst);
 }
 
-void EmitImageGatherDref([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
-                         [[maybe_unused]] const IR::Value& index, [[maybe_unused]] Register coord,
-                         [[maybe_unused]] const IR::Value& offset,
-                         [[maybe_unused]] const IR::Value& offset2,
-                         [[maybe_unused]] Register dref) {
-    throw NotImplementedException("GLASM instruction");
+void EmitImageGatherDref(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
+                         const IR::Value& coord, const IR::Value& offset, const IR::Value& offset2,
+                         const IR::Value& dref) {
+    // FIXME: This instruction is not working as expected
+
+    // Allocate offsets early so they don't overwrite any consumed register
+    const auto [off_x, off_y]{AllocOffsetsRegs(ctx, offset2)};
+    const auto info{inst.Flags<IR::TextureInstInfo>()};
+    const auto sparse_inst{inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
+    const std::string_view sparse_mod{sparse_inst ? ".SPARSE" : ""};
+    const std::string_view type{ShadowTextureType(info)};
+    const std::string texture{Texture(ctx, info, index)};
+    const Register coord_vec{ctx.reg_alloc.Consume(coord)};
+    const ScalarF32 dref_value{ctx.reg_alloc.Consume(dref)};
+    const Register ret{ctx.reg_alloc.Define(inst)};
+    std::string args;
+    switch (info.type) {
+    case TextureType::Color2D:
+        ctx.Add("MOV.F {}.z,{};", coord_vec, dref_value);
+        args = fmt::to_string(coord_vec);
+        break;
+    case TextureType::ColorArray2D:
+    case TextureType::ColorCube:
+        ctx.Add("MOV.F {}.w,{};", coord_vec, dref_value);
+        args = fmt::to_string(coord_vec);
+        break;
+    case TextureType::ColorArrayCube:
+        args = fmt::format("{},{}", coord_vec, dref_value);
+        break;
+    default:
+        throw NotImplementedException("Invalid type {}", info.type.Value());
+    }
+    if (offset2.IsEmpty()) {
+        const std::string offset_vec{Offset(ctx, offset)};
+        ctx.Add("TXG.F{} {},{},{},{}{};", sparse_mod, ret, args, texture, type, offset_vec);
+    } else {
+        SwizzleOffsets(ctx, off_x.reg, off_y.reg, offset, offset2);
+        ctx.Add("TXGO.F{} {},{},{},{},{},{};", sparse_mod, ret, args, off_x.reg, off_y.reg, texture,
+                type);
+    }
+    StoreSparse(ctx, sparse_inst);
 }
 
 void EmitImageFetch([[maybe_unused]] EmitContext& ctx, [[maybe_unused]] IR::Inst& inst,
-- 
cgit v1.2.3-70-g09d2