From c00c638ecc2c7266a19e49fe4427ce840a333f55 Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Mon, 2 Sep 2019 01:55:38 +0200
Subject: [PATCH] NvHostChannelIoctl: Implement setter for SetSubmitTimeout,
 SetPriority and SetTimeslice (#747)

- Implement accurate setter for SetPriority.
- Implement accurate setter for SetTimeslice (close #666).
- Implement basic setter for SetSubmitTimeout (close #678).

(plus some comments and a missing `PrintStub` call)
---
 .../Services/Nv/NvHostChannel/NvChannel.cs    |  2 +
 .../Nv/NvHostChannel/NvChannelPriority.cs     |  9 +++
 .../Nv/NvHostChannel/NvHostChannelIoctl.cs    | 56 ++++++++++++++++++-
 3 files changed, 65 insertions(+), 2 deletions(-)
 create mode 100644 Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvChannelPriority.cs

diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvChannel.cs b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvChannel.cs
index 54e709f9d3..74d27a7594 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvChannel.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvChannel.cs
@@ -3,5 +3,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
     class NvChannel
     {
         public int Timeout;
+        public int SubmitTimeout;
+        public int Timeslice;
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvChannelPriority.cs b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvChannelPriority.cs
new file mode 100644
index 0000000000..d41e7609de
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvChannelPriority.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
+{
+    enum NvChannelPriority
+    {
+        Low    = 50,
+        Medium = 100,
+        High   = 150
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
index e7879f4a29..fdfb3fa5cf 100644
--- a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
+++ b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs
@@ -25,6 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
                 case 0x0001: return Submit           (context);
                 case 0x0002: return GetSyncpoint     (context);
                 case 0x0003: return GetWaitBase      (context);
+                case 0x0007: return SetSubmitTimeout (context);
                 case 0x0009: return MapBuffer        (context);
                 case 0x000a: return UnmapBuffer      (context);
                 case 0x4714: return SetUserData      (context);
@@ -37,6 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
                 case 0x480d: return SetPriority      (context);
                 case 0x481a: return AllocGpfifoEx2   (context);
                 case 0x481b: return KickoffPbWithAttr(context);
+                case 0x481d: return SetTimeslice     (context);
             }
 
             throw new NotImplementedException(cmd.ToString("x8"));
@@ -103,6 +105,19 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
             return NvResult.Success;
         }
 
+        private static int SetSubmitTimeout(ServiceCtx context)
+        {
+            long inputPosition = context.Request.GetBufferType0x21().Position;
+
+            GetChannel(context).SubmitTimeout = context.Memory.ReadInt32(inputPosition);
+
+            // TODO: Handle the timeout in the submit method.
+
+            Logger.PrintStub(LogClass.ServiceNv);
+
+            return NvResult.Success;
+        }
+
         private static int MapBuffer(ServiceCtx context)
         {
             long inputPosition  = context.Request.GetBufferType0x21().Position;
@@ -200,6 +215,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
 
             GetChannel(context).Timeout = context.Memory.ReadInt32(inputPosition);
 
+            Logger.PrintStub(LogClass.ServiceNv);
+
             return NvResult.Success;
         }
 
@@ -259,11 +276,27 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
 
         private static int SetPriority(ServiceCtx context)
         {
-            long inputPosition  = context.Request.GetBufferType0x21().Position;
-            long outputPosition = context.Request.GetBufferType0x22().Position;
+            long inputPosition = context.Request.GetBufferType0x21().Position;
+
+            switch ((NvChannelPriority)context.Memory.ReadInt32(inputPosition))
+            {
+                case NvChannelPriority.Low:
+                    GetChannel(context).Timeslice = 1300; // Timeslice low priority in micro-seconds
+                    break;
+                case NvChannelPriority.Medium:
+                    GetChannel(context).Timeslice = 2600; // Timeslice medium priority in micro-seconds
+                    break;
+                case NvChannelPriority.High:
+                    GetChannel(context).Timeslice = 5200; // Timeslice high priority in micro-seconds
+                    break;
+                default:
+                    return NvResult.InvalidInput;
+            }
 
             Logger.PrintStub(LogClass.ServiceNv);
 
+            // TODO: disable and preempt channel when GPU scheduler will be implemented.
+
             return NvResult.Success;
         }
 
@@ -301,6 +334,25 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
             return NvResult.Success;
         }
 
+        private static int SetTimeslice(ServiceCtx context)
+        {
+            long inputPosition = context.Request.GetBufferType0x21().Position;
+            int  timeslice     = context.Memory.ReadInt32(inputPosition);
+
+            if (timeslice < 1000 || timeslice > 50000)
+            {
+                return NvResult.InvalidInput;
+            }
+
+            GetChannel(context).Timeslice = timeslice; // in micro-seconds
+
+            Logger.PrintStub(LogClass.ServiceNv);
+
+            // TODO: disable and preempt channel when GPU scheduler will be implemented.
+
+            return NvResult.Success;
+        }
+
         private static void PushGpfifo(ServiceCtx context, NvGpuVmm vmm, long gpfifo)
         {
             context.Device.Gpu.Pusher.Push(vmm, gpfifo);