From 9245b426e3d011e768cf3609e44df120e9667daa Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Fri, 1 Sep 2023 22:41:01 -0400
Subject: [PATCH] shader_recompiler: fix emulation of 3D textureGrad

---
 .../backend/glsl/emit_glsl_image.cpp          |  2 +-
 .../backend/spirv/emit_spirv_image.cpp        | 35 +++++++++++++++++--
 2 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index 3ad668a47..d9872ecc2 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -558,7 +558,7 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
     if (multi_component) {
         if (info.num_derivates >= 3) {
             const auto offset_vec{ctx.var_alloc.Consume(offset)};
-            ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yz, {}.y));", texel, texture,
+            ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yw, {}.y));", texel, texture,
                     coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec);
             return;
         }
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 7d901c04b..34240b36f 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -91,6 +91,34 @@ public:
         }
     }
 
+    explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates_1, Id derivates_2,
+                           Id offset, Id lod_clamp) {
+        if (!Sirit::ValidId(derivates_1) || !Sirit::ValidId(derivates_2)) {
+            throw LogicError("Derivates must be present");
+        }
+        boost::container::static_vector<Id, 3> deriv_1_accum{
+            ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 0),
+            ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 2),
+            ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 0),
+        };
+        boost::container::static_vector<Id, 3> deriv_2_accum{
+            ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 1),
+            ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 3),
+            ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 1),
+        };
+        const Id derivates_id1{ctx.OpCompositeConstruct(
+            ctx.F32[3], std::span{deriv_1_accum.data(), deriv_1_accum.size()})};
+        const Id derivates_id2{ctx.OpCompositeConstruct(
+            ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})};
+        Add(spv::ImageOperandsMask::Grad, derivates_id1, derivates_id2);
+        if (Sirit::ValidId(offset)) {
+            Add(spv::ImageOperandsMask::Offset, offset);
+        }
+        if (has_lod_clamp) {
+            Add(spv::ImageOperandsMask::MinLod, lod_clamp);
+        }
+    }
+
     std::span<const Id> Span() const noexcept {
         return std::span{operands.data(), operands.size()};
     }
@@ -524,8 +552,11 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
 Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
                      Id derivates, Id offset, Id lod_clamp) {
     const auto info{inst->Flags<IR::TextureInstInfo>()};
-    const ImageOperands operands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates,
-                                 offset, lod_clamp);
+    const auto operands =
+        info.num_derivates == 3
+            ? ImageOperands(ctx, info.has_lod_clamp != 0, derivates, offset, {}, lod_clamp)
+            : ImageOperands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, offset,
+                            lod_clamp);
     return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
                 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
                 Texture(ctx, info, index), coords, operands.Mask(), operands.Span());