From a6cef71cc0b03f929f1bc97152b302562f46bc53 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 12 Apr 2021 03:48:15 -0300
Subject: [PATCH] shader: Implement OUT

---
 src/shader_recompiler/CMakeLists.txt          |  1 +
 .../backend/spirv/emit_spirv.h                |  2 +
 .../backend/spirv/emit_spirv_special.cpp      |  8 ++++
 .../frontend/ir/ir_emitter.cpp                |  8 ++++
 .../frontend/ir/ir_emitter.h                  |  3 ++
 .../frontend/ir/microinstruction.cpp          |  2 +
 src/shader_recompiler/frontend/ir/opcodes.inc |  2 +
 .../translate/impl/load_store_attribute.cpp   |  7 +--
 .../translate/impl/not_implemented.cpp        | 12 -----
 .../translate/impl/output_geometry.cpp        | 45 +++++++++++++++++++
 10 files changed, 73 insertions(+), 17 deletions(-)
 create mode 100644 src/shader_recompiler/frontend/maxwell/translate/impl/output_geometry.cpp

diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 8c24c1377d..bbbfa98a3f 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -134,6 +134,7 @@ add_library(shader_recompiler STATIC
     frontend/maxwell/translate/impl/move_register_to_predicate.cpp
     frontend/maxwell/translate/impl/move_special_register.cpp
     frontend/maxwell/translate/impl/not_implemented.cpp
+    frontend/maxwell/translate/impl/output_geometry.cpp
     frontend/maxwell/translate/impl/predicate_set_predicate.cpp
     frontend/maxwell/translate/impl/predicate_set_register.cpp
     frontend/maxwell/translate/impl/select_source_with_predicate.cpp
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index 150477ff67..4400752126 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -34,6 +34,8 @@ void EmitMemoryBarrierDeviceLevel(EmitContext& ctx);
 void EmitMemoryBarrierSystemLevel(EmitContext& ctx);
 void EmitPrologue(EmitContext& ctx);
 void EmitEpilogue(EmitContext& ctx);
+void EmitEmitVertex(EmitContext& ctx, Id stream);
+void EmitEndPrimitive(EmitContext& ctx, Id stream);
 void EmitGetRegister(EmitContext& ctx);
 void EmitSetRegister(EmitContext& ctx);
 void EmitGetPred(EmitContext& ctx);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
index 5f80c189f5..d20f4def33 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
@@ -36,4 +36,12 @@ void EmitEpilogue(EmitContext& ctx) {
     }
 }
 
+void EmitEmitVertex(EmitContext& ctx, Id stream) {
+    ctx.OpEmitStreamVertex(stream);
+}
+
+void EmitEndPrimitive(EmitContext& ctx, Id stream) {
+    ctx.OpEndStreamPrimitive(stream);
+}
+
 } // 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 54a273a921..7d48fa1ba6 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -125,6 +125,14 @@ void IREmitter::Epilogue() {
     Inst(Opcode::Epilogue);
 }
 
+void IREmitter::EmitVertex(const U32& stream) {
+    Inst(Opcode::EmitVertex, stream);
+}
+
+void IREmitter::EndPrimitive(const U32& stream) {
+    Inst(Opcode::EndPrimitive, stream);
+}
+
 U32 IREmitter::GetReg(IR::Reg reg) {
     return Inst<U32>(Opcode::GetRegister, reg);
 }
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index d04224707a..033c4332e4 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -43,6 +43,9 @@ public:
     void Prologue();
     void Epilogue();
 
+    void EmitVertex(const U32& stream);
+    void EndPrimitive(const U32& stream);
+
     [[nodiscard]] U32 GetReg(IR::Reg reg);
     void SetReg(IR::Reg reg, const U32& value);
 
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp
index 0f66c56270..204c55fa85 100644
--- a/src/shader_recompiler/frontend/ir/microinstruction.cpp
+++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp
@@ -69,6 +69,8 @@ bool Inst::MayHaveSideEffects() const noexcept {
     case Opcode::MemoryBarrierSystemLevel:
     case Opcode::Prologue:
     case Opcode::Epilogue:
+    case Opcode::EmitVertex:
+    case Opcode::EndPrimitive:
     case Opcode::SetAttribute:
     case Opcode::SetAttributeIndexed:
     case Opcode::SetFragColor:
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index f700086822..0e487f1a78 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -25,6 +25,8 @@ OPCODE(MemoryBarrierSystemLevel,                            Void,
 // Special operations
 OPCODE(Prologue,                                            Void,                                                                                           )
 OPCODE(Epilogue,                                            Void,                                                                                           )
+OPCODE(EmitVertex,                                          Void,           U32,                                                                            )
+OPCODE(EndPrimitive,                                        Void,           U32,                                                                            )
 
 // Context getters/setters
 OPCODE(GetRegister,                                         U32,            Reg,                                                                            )
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp
index f629e71679..79293bd6b3 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp
@@ -64,7 +64,7 @@ void TranslatorVisitor::ALD(u64 insn) {
         BitField<8, 8, IR::Reg> index_reg;
         BitField<20, 10, u64> absolute_offset;
         BitField<20, 11, s64> relative_offset;
-        BitField<39, 8, IR::Reg> stream_reg;
+        BitField<39, 8, IR::Reg> array_reg;
         BitField<32, 1, u64> o;
         BitField<31, 1, u64> patch;
         BitField<47, 2, Size> size;
@@ -100,16 +100,13 @@ void TranslatorVisitor::AST(u64 insn) {
         BitField<20, 10, u64> absolute_offset;
         BitField<20, 11, s64> relative_offset;
         BitField<31, 1, u64> patch;
-        BitField<39, 8, IR::Reg> stream_reg;
+        BitField<39, 8, IR::Reg> array_reg;
         BitField<47, 2, Size> size;
     } const ast{insn};
 
     if (ast.patch != 0) {
         throw NotImplementedException("P");
     }
-    if (ast.stream_reg != IR::Reg::RZ) {
-        throw NotImplementedException("Stream store");
-    }
     if (ast.index_reg != IR::Reg::RZ) {
         throw NotImplementedException("Indexed store");
     }
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
index 694bdfccb1..a45d1e4be5 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
@@ -169,18 +169,6 @@ void TranslatorVisitor::NOP(u64) {
     // NOP is No-Op.
 }
 
-void TranslatorVisitor::OUT_reg(u64) {
-    ThrowNotImplemented(Opcode::OUT_reg);
-}
-
-void TranslatorVisitor::OUT_cbuf(u64) {
-    ThrowNotImplemented(Opcode::OUT_cbuf);
-}
-
-void TranslatorVisitor::OUT_imm(u64) {
-    ThrowNotImplemented(Opcode::OUT_imm);
-}
-
 void TranslatorVisitor::PBK() {
     // PBK is a no-op
 }
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/output_geometry.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/output_geometry.cpp
new file mode 100644
index 0000000000..01cfad88de
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/output_geometry.cpp
@@ -0,0 +1,45 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
+
+namespace Shader::Maxwell {
+namespace {
+void OUT(TranslatorVisitor& v, u64 insn, IR::U32 stream_index) {
+    union {
+        u64 raw;
+        BitField<0, 8, IR::Reg> dest_reg;
+        BitField<8, 8, IR::Reg> output_reg; // Not needed on host
+        BitField<39, 1, u64> emit;
+        BitField<40, 1, u64> cut;
+    } const out{insn};
+
+    stream_index = v.ir.BitwiseAnd(stream_index, v.ir.Imm32(0b11));
+
+    if (out.emit != 0) {
+        v.ir.EmitVertex(stream_index);
+    }
+    if (out.cut != 0) {
+        v.ir.EndPrimitive(stream_index);
+    }
+    // Host doesn't need the output register, but we can write to it to avoid undefined reads
+    v.X(out.dest_reg, v.ir.Imm32(0));
+}
+} // Anonymous namespace
+
+void TranslatorVisitor::OUT_reg(u64 insn) {
+    OUT(*this, insn, GetReg20(insn));
+}
+
+void TranslatorVisitor::OUT_cbuf(u64 insn) {
+    OUT(*this, insn, GetCbuf(insn));
+}
+
+void TranslatorVisitor::OUT_imm(u64 insn) {
+    OUT(*this, insn, GetImm20(insn));
+}
+
+} // namespace Shader::Maxwell