From 44d7fcff399888d311f61772a53146d924ee5b62 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Fri, 29 May 2020 05:51:10 -0300
Subject: [PATCH] Implement FIFO semaphore (#1286)

* Implement FIFO semaphore

* New enum for FIFO semaphore operation
---
 Ryujinx.Graphics.Gpu/Engine/MethodFifo.cs     | 28 ++++++++++++++++++-
 Ryujinx.Graphics.Gpu/Engine/MethodReport.cs   | 13 ++++-----
 Ryujinx.Graphics.Gpu/Engine/Methods.cs        |  3 +-
 .../State/FifoSemaphoreOperation.cs           |  9 ++++++
 Ryujinx.Graphics.Gpu/State/MethodOffset.cs    |  3 +-
 .../{ReportMode.cs => SemaphoreOperation.cs}  |  4 +--
 .../{ReportState.cs => SemaphoreState.cs}     |  4 +--
 7 files changed, 50 insertions(+), 14 deletions(-)
 create mode 100644 Ryujinx.Graphics.Gpu/State/FifoSemaphoreOperation.cs
 rename Ryujinx.Graphics.Gpu/State/{ReportMode.cs => SemaphoreOperation.cs} (71%)
 rename Ryujinx.Graphics.Gpu/State/{ReportState.cs => SemaphoreState.cs} (80%)

diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodFifo.cs b/Ryujinx.Graphics.Gpu/Engine/MethodFifo.cs
index 1c0e72e58d..c1f45941c5 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MethodFifo.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MethodFifo.cs
@@ -1,11 +1,37 @@
 using Ryujinx.Graphics.Gpu.State;
-using System;
 using System.Threading;
 
 namespace Ryujinx.Graphics.Gpu.Engine
 {
     partial class Methods
     {
+        /// <summary>
+        /// Writes a GPU counter to guest memory.
+        /// </summary>
+        /// <param name="state">Current GPU state</param>
+        /// <param name="argument">Method call argument</param>
+        public void Semaphore(GpuState state, int argument)
+        {
+            FifoSemaphoreOperation op = (FifoSemaphoreOperation)(argument & 3);
+
+            var semaphore = state.Get<SemaphoreState>(MethodOffset.Semaphore);
+
+            int value = semaphore.Payload;
+
+            if (op == FifoSemaphoreOperation.Counter)
+            {
+                // TODO: There's much more that should be done here.
+                // NVN only supports the "Accumulate" mode, so we
+                // can't currently guess which bits specify the
+                // reduction operation.
+                value += _context.MemoryAccessor.Read<int>(semaphore.Address.Pack());
+            }
+
+            _context.MemoryAccessor.Write(semaphore.Address.Pack(), value);
+
+            _context.AdvanceSequence();
+        }
+
         /// <summary>
         /// Waits for the GPU to be idle.
         /// </summary>
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs b/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs
index e8efddeaa0..224a4da181 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs
@@ -21,14 +21,13 @@ namespace Ryujinx.Graphics.Gpu.Engine
         /// <param name="argument">Method call argument</param>
         private void Report(GpuState state, int argument)
         {
-            ReportMode mode = (ReportMode)(argument & 3);
-
+            SemaphoreOperation op = (SemaphoreOperation)(argument & 3);
             ReportCounterType type = (ReportCounterType)((argument >> 23) & 0x1f);
 
-            switch (mode)
+            switch (op)
             {
-                case ReportMode.Release: ReleaseSemaphore(state);    break;
-                case ReportMode.Counter: ReportCounter(state, type); break;
+                case SemaphoreOperation.Release: ReleaseSemaphore(state);    break;
+                case SemaphoreOperation.Counter: ReportCounter(state, type); break;
             }
         }
 
@@ -38,7 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
         /// <param name="state">Current GPU state</param>
         private void ReleaseSemaphore(GpuState state)
         {
-            var rs = state.Get<ReportState>(MethodOffset.ReportState);
+            var rs = state.Get<SemaphoreState>(MethodOffset.ReportState);
 
             _context.MemoryAccessor.Write(rs.Address.Pack(), rs.Payload);
 
@@ -64,7 +63,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
         {
             CounterData counterData = new CounterData();
 
-            var rs = state.Get<ReportState>(MethodOffset.ReportState);
+            var rs = state.Get<SemaphoreState>(MethodOffset.ReportState);
 
             ulong gpuVa = rs.Address.Pack();
 
diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
index d67ce2d111..5957bb624f 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
@@ -104,6 +104,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
         /// <param name="state">GPU state where the triggers will be registered</param>
         public void RegisterCallbacksForFifo(GpuState state)
         {
+            state.RegisterCallback(MethodOffset.Semaphore,              Semaphore);
             state.RegisterCallback(MethodOffset.FenceAction,            FenceAction);
             state.RegisterCallback(MethodOffset.WaitForIdle,            WaitForIdle);
             state.RegisterCallback(MethodOffset.SendMacroCodeData,      SendMacroCodeData);
@@ -430,7 +431,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
             _context.Renderer.Pipeline.SetOrigin(origin);
 
             // The triangle rast flip flag only affects rasterization, the viewport is not flipped.
-            // Setting the origin mode to upper left on the host, however, not onlyy affects rasterization,
+            // Setting the origin mode to upper left on the host, however, not only affects rasterization,
             // but also flips the viewport.
             // We negate the effects of flipping the viewport by flipping it again using the viewport swizzle.
             if (origin == Origin.UpperLeft)
diff --git a/Ryujinx.Graphics.Gpu/State/FifoSemaphoreOperation.cs b/Ryujinx.Graphics.Gpu/State/FifoSemaphoreOperation.cs
new file mode 100644
index 0000000000..a6ccdcfe4d
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/State/FifoSemaphoreOperation.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Graphics.Gpu.State
+{
+    enum FifoSemaphoreOperation
+    {
+        Counter = 0,
+        Acquire = 1,
+        Release = 2
+    }
+}
diff --git a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs
index b39ff9b5e0..da54a46980 100644
--- a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs
+++ b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs
@@ -8,7 +8,8 @@ namespace Ryujinx.Graphics.Gpu.State
     /// </remarks>
     enum MethodOffset
     {
-        BindChannel                     = 0x00,
+        BindChannel                     = 0x0,
+        Semaphore                       = 0x4,
         FenceValue                      = 0x1c,
         FenceAction                     = 0x1d,
         WaitForIdle                     = 0x44,
diff --git a/Ryujinx.Graphics.Gpu/State/ReportMode.cs b/Ryujinx.Graphics.Gpu/State/SemaphoreOperation.cs
similarity index 71%
rename from Ryujinx.Graphics.Gpu/State/ReportMode.cs
rename to Ryujinx.Graphics.Gpu/State/SemaphoreOperation.cs
index 0625f3f610..67f3c127f6 100644
--- a/Ryujinx.Graphics.Gpu/State/ReportMode.cs
+++ b/Ryujinx.Graphics.Gpu/State/SemaphoreOperation.cs
@@ -1,9 +1,9 @@
 namespace Ryujinx.Graphics.Gpu.State
 {
     /// <summary>
-    /// GPU counter report mode.
+    /// GPU semaphore operation.
     /// </summary>
-    enum ReportMode
+    enum SemaphoreOperation
     {
         Release = 0,
         Acquire = 1,
diff --git a/Ryujinx.Graphics.Gpu/State/ReportState.cs b/Ryujinx.Graphics.Gpu/State/SemaphoreState.cs
similarity index 80%
rename from Ryujinx.Graphics.Gpu/State/ReportState.cs
rename to Ryujinx.Graphics.Gpu/State/SemaphoreState.cs
index 0c14bfe953..bfc5720a40 100644
--- a/Ryujinx.Graphics.Gpu/State/ReportState.cs
+++ b/Ryujinx.Graphics.Gpu/State/SemaphoreState.cs
@@ -1,9 +1,9 @@
 namespace Ryujinx.Graphics.Gpu.State
 {
     /// <summary>
-    /// GPU counter report state.
+    /// GPU semaphore state.
     /// </summary>
-    struct ReportState
+    struct SemaphoreState
     {
 #pragma warning disable CS0649
         public GpuVa Address;