From 14a57bd12c255e42957ff08761521b54b1c8e42a Mon Sep 17 00:00:00 2001
From: gameblabla <gameblabla@protonmail.com>
Date: Tue, 5 Mar 2024 10:54:50 +0100
Subject: [PATCH] spirv: fix multisampled image fetch

Fixes Vulkan shader compiler crash in Fire Emblem Engage.
Original fix by @liamwhite
---
 backend/spirv/emit_spirv_image.cpp   | 4 ++++
 backend/spirv/spirv_emit_context.cpp | 5 +++--
 ir_opt/texture_pass.cpp              | 8 ++++++++
 shader_info.h                        | 1 +
 4 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/backend/spirv/emit_spirv_image.cpp b/backend/spirv/emit_spirv_image.cpp
index 68ea763..3f679dc 100644
--- a/backend/spirv/emit_spirv_image.cpp
+++ b/backend/spirv/emit_spirv_image.cpp
@@ -437,6 +437,10 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
     if (info.type == TextureType::Buffer) {
         lod = Id{};
     }
+    if (Sirit::ValidId(ms)) {
+        // This image is multisampled, lod must be implicit
+        lod = Id{};
+    }
     const ImageOperands operands(offset, lod, ms);
     return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
                 TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
diff --git a/backend/spirv/spirv_emit_context.cpp b/backend/spirv/spirv_emit_context.cpp
index ac04b6c..3e53b11 100644
--- a/backend/spirv/spirv_emit_context.cpp
+++ b/backend/spirv/spirv_emit_context.cpp
@@ -29,6 +29,7 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
     const spv::ImageFormat format{spv::ImageFormat::Unknown};
     const Id type{ctx.F32[1]};
     const bool depth{desc.is_depth};
+    const bool ms{desc.is_multisample};
     switch (desc.type) {
     case TextureType::Color1D:
         return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format);
@@ -36,9 +37,9 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
         return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);
     case TextureType::Color2D:
     case TextureType::Color2DRect:
-        return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format);
+		return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, ms, 1, format);
     case TextureType::ColorArray2D:
-        return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format);
+        return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, ms, 1, format);
     case TextureType::Color3D:
         return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format);
     case TextureType::ColorCube:
diff --git a/ir_opt/texture_pass.cpp b/ir_opt/texture_pass.cpp
index 9d081aa..6b88560 100644
--- a/ir_opt/texture_pass.cpp
+++ b/ir_opt/texture_pass.cpp
@@ -525,6 +525,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
 
         const auto& cbuf{texture_inst.cbuf};
         auto flags{inst->Flags<IR::TextureInstInfo>()};
+        bool is_multisample{false};
         switch (inst->GetOpcode()) {
         case IR::Opcode::ImageQueryDimensions:
             flags.type.Assign(ReadTextureType(env, cbuf));
@@ -539,6 +540,12 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
             }
             break;
         case IR::Opcode::ImageFetch:
+           if (flags.type == TextureType::Color2D || flags.type == TextureType::Color2DRect ||
+                flags.type == TextureType::ColorArray2D) {
+                is_multisample = !inst->Arg(4).IsEmpty();
+            } else {
+                inst->SetArg(4, IR::U32{});
+            }
             if (flags.type != TextureType::Color1D) {
                 break;
             }
@@ -614,6 +621,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
                 index = descriptors.Add(TextureDescriptor{
                     .type = flags.type,
                     .is_depth = flags.is_depth != 0,
+                    .is_multisample = is_multisample,
                     .has_secondary = cbuf.has_secondary,
                     .cbuf_index = cbuf.index,
                     .cbuf_offset = cbuf.offset,
diff --git a/shader_info.h b/shader_info.h
index 7744514..dfadcc2 100644
--- a/shader_info.h
+++ b/shader_info.h
@@ -109,6 +109,7 @@ using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescrip
 struct TextureDescriptor {
     TextureType type;
     bool is_depth;
+    bool is_multisample;
     bool has_secondary;
     u32 cbuf_index;
     u32 cbuf_offset;