From b5db38f50e9f81964bf0cc946e4ed5b00fe564d0 Mon Sep 17 00:00:00 2001
From: FernandoS27 <fsahmkow27@gmail.com>
Date: Fri, 26 Mar 2021 19:24:50 +0100
Subject: [PATCH] shader: Add IR opcode for ImageFetch

---
 .../backend/spirv/emit_spirv.h                |  4 +++
 .../backend/spirv/emit_spirv_composite.cpp    |  2 +-
 .../backend/spirv/emit_spirv_image.cpp        | 32 +++++++++++++++++--
 .../frontend/ir/ir_emitter.cpp                |  6 ++++
 .../frontend/ir/ir_emitter.h                  |  8 +++--
 src/shader_recompiler/frontend/ir/opcodes.inc |  3 ++
 src/shader_recompiler/ir_opt/texture_pass.cpp |  5 +++
 7 files changed, 55 insertions(+), 5 deletions(-)

diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index eaf94dad5..cc02f53f1 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -343,12 +343,14 @@ Id EmitBindlessImageSampleDrefImplicitLod(EmitContext&);
 Id EmitBindlessImageSampleDrefExplicitLod(EmitContext&);
 Id EmitBindlessImageGather(EmitContext&);
 Id EmitBindlessImageGatherDref(EmitContext&);
+Id EmitBindlessImageFetch(EmitContext&);
 Id EmitBoundImageSampleImplicitLod(EmitContext&);
 Id EmitBoundImageSampleExplicitLod(EmitContext&);
 Id EmitBoundImageSampleDrefImplicitLod(EmitContext&);
 Id EmitBoundImageSampleDrefExplicitLod(EmitContext&);
 Id EmitBoundImageGather(EmitContext&);
 Id EmitBoundImageGatherDref(EmitContext&);
+Id EmitBoundImageFetch(EmitContext&);
 Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
                               Id bias_lc, Id offset);
 Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
@@ -361,6 +363,8 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
                    Id offset2);
 Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
                        Id offset, Id offset2, Id dref);
+Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
+                  Id lod, Id ms);
 Id EmitVoteAll(EmitContext& ctx, Id pred);
 Id EmitVoteAny(EmitContext& ctx, Id pred);
 Id EmitVoteEqual(EmitContext& ctx, Id pred);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
index 0da682859..f01d69d91 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_composite.cpp
@@ -170,7 +170,7 @@ Id EmitCompositeConstructArrayU32x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id
         return ctx.ConstantComposite(ctx.array_U32x2, e1, e2, e3, e4);
     }
     if (ctx.profile.support_variadic_ptp) {
-        return OpCompositeConstruct(ctx.array_U32x2, e1, e2, e3, e4);
+        return ctx.OpCompositeConstruct(ctx.array_U32x2, e1, e2, e3, e4);
     }
     return {};
 }
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 776afd4ab..13bc8831f 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -39,6 +39,18 @@ public:
         }
     }
 
+    explicit ImageOperands([[maybe_unused]] EmitContext& ctx, Id offset, Id lod, Id ms) {
+        if (Sirit::ValidId(lod)) {
+            Add(spv::ImageOperandsMask::Lod, lod);
+        }
+        if (Sirit::ValidId(offset)) {
+            Add(spv::ImageOperandsMask::Offset, offset);
+        }
+        if (Sirit::ValidId(ms)) {
+            Add(spv::ImageOperandsMask::Sample, ms);
+        }
+    }
+
     void Add(spv::ImageOperandsMask new_mask, Id value) {
         mask = static_cast<spv::ImageOperandsMask>(static_cast<unsigned>(mask) |
                                                    static_cast<unsigned>(new_mask));
@@ -115,6 +127,10 @@ Id EmitBindlessImageGatherDref(EmitContext&) {
     throw LogicError("Unreachable instruction");
 }
 
+Id EmitBindlessImageFetch(EmitContext&) {
+    throw LogicError("Unreachable instruction");
+}
+
 Id EmitBoundImageSampleImplicitLod(EmitContext&) {
     throw LogicError("Unreachable instruction");
 }
@@ -139,6 +155,10 @@ Id EmitBoundImageGatherDref(EmitContext&) {
     throw LogicError("Unreachable instruction");
 }
 
+Id EmitBoundImageFetch(EmitContext&) {
+    throw LogicError("Unreachable instruction");
+}
+
 Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
                               Id bias_lc, Id offset) {
     const auto info{inst->Flags<IR::TextureInstInfo>()};
@@ -178,7 +198,7 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
 }
 
 Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
-                   [[maybe_unused]] Id offset2) {
+                   Id offset2) {
     const auto info{inst->Flags<IR::TextureInstInfo>()};
     const ImageOperands operands(ctx, offset, offset2);
     return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
@@ -188,11 +208,19 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
 }
 
 Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
-                       Id offset, [[maybe_unused]] Id offset2, Id dref) {
+                       Id offset, Id offset2, Id dref) {
     const auto info{inst->Flags<IR::TextureInstInfo>()};
     const ImageOperands operands(ctx, offset, offset2);
     return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst,
                 ctx.F32[4], Texture(ctx, index), coords, dref, operands.Mask(), operands.Span());
 }
 
+Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
+                  Id lod, Id ms) {
+    const auto info{inst->Flags<IR::TextureInstInfo>()};
+    const ImageOperands operands(ctx, offset, lod, ms);
+    return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
+                Texture(ctx, index), coords, operands.Mask(), operands.Span());
+}
+
 } // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index f49c30484..b8d36f362 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -1491,6 +1491,12 @@ Value IREmitter::ImageGatherDref(const Value& handle, const Value& coords, const
     return Inst(op, Flags{info}, handle, coords, offset, offset2, dref);
 }
 
+Value IREmitter::ImageFetch(const Value& handle, const Value& coords, const Value& offset,
+                            const U32& lod, const U32& multisampling, TextureInstInfo info) {
+    const Opcode op{handle.IsImmediate() ? Opcode::BoundImageFetch : Opcode::BindlessImageFetch};
+    return Inst(op, Flags{info}, handle, coords, offset, lod, multisampling);
+}
+
 U1 IREmitter::VoteAll(const U1& value) {
     return Inst<U1>(Opcode::VoteAll, value);
 }
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index 04b43197f..446fd7785 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -243,8 +243,12 @@ public:
     [[nodiscard]] Value ImageGather(const Value& handle, const Value& coords, const Value& offset,
                                     const Value& offset2, TextureInstInfo info);
 
-    [[nodiscard]] Value ImageGatherDref(const Value& handle, const Value& coords, const Value& offset,
-                                      const Value& offset2, const F32& dref, TextureInstInfo info);
+    [[nodiscard]] Value ImageGatherDref(const Value& handle, const Value& coords,
+                                        const Value& offset, const Value& offset2, const F32& dref,
+                                        TextureInstInfo info);
+
+    [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset,
+                                   const U32& lod, const U32& multisampling, TextureInstInfo info);
 
     [[nodiscard]] U1 VoteAll(const U1& value);
     [[nodiscard]] U1 VoteAny(const U1& value);
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index 0dc0aabdf..3dacd7b6b 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -356,6 +356,7 @@ OPCODE(BindlessImageSampleDrefImplicitLod,                  F32,            U32,
 OPCODE(BindlessImageSampleDrefExplicitLod,                  F32,            U32,            Opaque,         F32,            Opaque,         Opaque,         )
 OPCODE(BindlessImageGather,                                 F32x4,          U32,            Opaque,         Opaque,         Opaque,                         )
 OPCODE(BindlessImageGatherDref,                             F32x4,          U32,            Opaque,         Opaque,         Opaque,         F32,            )
+OPCODE(BindlessImageFetch,                                  F32x4,          U32,            Opaque,         U32,            U32,                            )
 
 OPCODE(BoundImageSampleImplicitLod,                         F32x4,          U32,            Opaque,         Opaque,         Opaque,                         )
 OPCODE(BoundImageSampleExplicitLod,                         F32x4,          U32,            Opaque,         Opaque,         Opaque,                         )
@@ -363,6 +364,7 @@ OPCODE(BoundImageSampleDrefImplicitLod,                     F32,            U32,
 OPCODE(BoundImageSampleDrefExplicitLod,                     F32,            U32,            Opaque,         F32,            Opaque,         Opaque,         )
 OPCODE(BoundImageGather,                                    F32x4,          U32,            Opaque,         Opaque,         Opaque,                         )
 OPCODE(BoundImageGatherDref,                                F32x4,          U32,            Opaque,         Opaque,         Opaque,         F32,            )
+OPCODE(BoundImageFetch,                                     F32x4,          U32,            Opaque,         U32,            U32,                            )
 
 OPCODE(ImageSampleImplicitLod,                              F32x4,          U32,            Opaque,         Opaque,         Opaque,                         )
 OPCODE(ImageSampleExplicitLod,                              F32x4,          U32,            Opaque,         Opaque,         Opaque,                         )
@@ -370,6 +372,7 @@ OPCODE(ImageSampleDrefImplicitLod,                          F32,            U32,
 OPCODE(ImageSampleDrefExplicitLod,                          F32,            U32,            Opaque,         F32,            Opaque,         Opaque,         )
 OPCODE(ImageGather,                                         F32x4,          U32,            Opaque,         Opaque,         Opaque,                         )
 OPCODE(ImageGatherDref,                                     F32x4,          U32,            Opaque,         Opaque,         Opaque,         F32,            )
+OPCODE(ImageFetch,                                          F32x4,          U32,            Opaque,         U32,            U32,                            )
 
 // Warp operations
 OPCODE(VoteAll,                                             U1,             U1,                                                                             )
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index 454ac3e71..0167dd06e 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -51,6 +51,9 @@ IR::Opcode IndexedInstruction(const IR::Inst& inst) {
     case IR::Opcode::BindlessImageGatherDref:
     case IR::Opcode::BoundImageGatherDref:
         return IR::Opcode::ImageGatherDref;
+    case IR::Opcode::BindlessImageFetch:
+    case IR::Opcode::BoundImageFetch:
+        return IR::Opcode::ImageFetch;
     default:
         return IR::Opcode::Void;
     }
@@ -64,6 +67,7 @@ bool IsBindless(const IR::Inst& inst) {
     case IR::Opcode::BindlessImageSampleDrefExplicitLod:
     case IR::Opcode::BindlessImageGather:
     case IR::Opcode::BindlessImageGatherDref:
+    case IR::Opcode::BindlessImageFetch:
         return true;
     case IR::Opcode::BoundImageSampleImplicitLod:
     case IR::Opcode::BoundImageSampleExplicitLod:
@@ -71,6 +75,7 @@ bool IsBindless(const IR::Inst& inst) {
     case IR::Opcode::BoundImageSampleDrefExplicitLod:
     case IR::Opcode::BoundImageGather:
     case IR::Opcode::BoundImageGatherDref:
+    case IR::Opcode::BoundImageFetch:
         return false;
     default:
         throw InvalidArgument("Invalid opcode {}", inst.Opcode());