From bd4ec251cdb389542c6ff76aeb03892215cef5e7 Mon Sep 17 00:00:00 2001
From: Steveice10 <1269164+Steveice10@users.noreply.github.com>
Date: Mon, 25 Dec 2023 08:29:17 -0800
Subject: [PATCH] gsp_gpu: Implement TryAcquireRight and stub
 SetInternalPriorities. (#7285)

* gsp_gpu: Implement TryAcquireRight.

* gsp_gpu: Stub SetInternalPriorities.

* gsp_gpu: Move serialization logic into implementation.

* gsp_gpu: Replace UINT32_MAX with std::numeric_limits<u32>::max().
---
 src/core/hle/service/gsp/gsp_gpu.cpp | 100 +++++++++++++++++++++------
 src/core/hle/service/gsp/gsp_gpu.h   |  65 ++++++++++-------
 2 files changed, 119 insertions(+), 46 deletions(-)

diff --git a/src/core/hle/service/gsp/gsp_gpu.cpp b/src/core/hle/service/gsp/gsp_gpu.cpp
index 100e03f52..d62f0d294 100644
--- a/src/core/hle/service/gsp/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp/gsp_gpu.cpp
@@ -4,6 +4,9 @@
 
 #include <span>
 #include <vector>
+#include <boost/serialization/base_object.hpp>
+#include <boost/serialization/optional.hpp>
+#include <boost/serialization/shared_ptr.hpp>
 #include "common/archives.h"
 #include "common/bit_field.h"
 #include "common/microprofile.h"
@@ -478,7 +481,7 @@ void GSP_GPU::SignalInterrupt(InterruptId interrupt_id) {
     }
 
     // For normal interrupts, don't do anything if no process has acquired the GPU right.
-    if (active_thread_id == UINT32_MAX) {
+    if (active_thread_id == std::numeric_limits<u32>::max()) {
         return;
     }
 
@@ -664,7 +667,7 @@ void GSP_GPU::TriggerCmdReqQueue(Kernel::HLERequestContext& ctx) {
 void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp(ctx);
 
-    if (active_thread_id == UINT32_MAX) {
+    if (active_thread_id == std::numeric_limits<u32>::max()) {
         LOG_WARNING(Service_GSP, "Called without an active thread.");
 
         // TODO: Find the right error code.
@@ -792,37 +795,57 @@ void GSP_GPU::RestoreVramSysArea(Kernel::HLERequestContext& ctx) {
     rb.Push(RESULT_SUCCESS);
 }
 
-void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) {
-    IPC::RequestParser rp(ctx);
+ResultCode GSP_GPU::AcquireGpuRight(const Kernel::HLERequestContext& ctx,
+                                    const std::shared_ptr<Kernel::Process>& process, u32 flag,
+                                    bool blocking) {
+    const auto session_data = GetSessionData(ctx.Session());
 
-    u32 flag = rp.Pop<u32>();
-    auto process = rp.PopObject<Kernel::Process>();
-
-    SessionData* session_data = GetSessionData(ctx.Session());
-
-    LOG_WARNING(Service_GSP, "called flag={:08X} process={} thread_id={}", flag,
-                process->process_id, session_data->thread_id);
-
-    IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+    LOG_DEBUG(Service_GSP, "called flag={:08X} process={} thread_id={}", flag, process->process_id,
+              session_data->thread_id);
 
     if (active_thread_id == session_data->thread_id) {
-        rb.Push(ResultCode(ErrorDescription::AlreadyDone, ErrorModule::GX, ErrorSummary::Success,
-                           ErrorLevel::Success));
-        return;
+        return {ErrorDescription::AlreadyDone, ErrorModule::GX, ErrorSummary::Success,
+                ErrorLevel::Success};
     }
 
-    // TODO(Subv): This case should put the caller thread to sleep until the right is released.
-    ASSERT_MSG(active_thread_id == UINT32_MAX, "GPU right has already been acquired");
+    if (blocking) {
+        // TODO: The thread should be put to sleep until acquired.
+        ASSERT_MSG(active_thread_id == std::numeric_limits<u32>::max(),
+                   "Sleeping for GPU right is not yet supported.");
+    } else if (active_thread_id != std::numeric_limits<u32>::max()) {
+        return {ErrorDescription::Busy, ErrorModule::GX, ErrorSummary::WouldBlock,
+                ErrorLevel::Status};
+    }
 
     active_thread_id = session_data->thread_id;
+    return RESULT_SUCCESS;
+}
 
-    rb.Push(RESULT_SUCCESS);
+void GSP_GPU::TryAcquireRight(Kernel::HLERequestContext& ctx) {
+    IPC::RequestParser rp(ctx);
+    const auto process = rp.PopObject<Kernel::Process>();
+
+    const auto result = AcquireGpuRight(ctx, process, 0, false);
+
+    IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+    rb.Push(result);
+}
+
+void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) {
+    IPC::RequestParser rp(ctx);
+    const auto flag = rp.Pop<u32>();
+    const auto process = rp.PopObject<Kernel::Process>();
+
+    const auto result = AcquireGpuRight(ctx, process, flag, true);
+
+    IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+    rb.Push(result);
 }
 
 void GSP_GPU::ReleaseRight(const SessionData* session_data) {
     ASSERT_MSG(active_thread_id == session_data->thread_id,
                "Wrong thread tried to release GPU right");
-    active_thread_id = UINT32_MAX;
+    active_thread_id = std::numeric_limits<u32>::max();
 }
 
 void GSP_GPU::ReleaseRight(Kernel::HLERequestContext& ctx) {
@@ -863,6 +886,18 @@ void GSP_GPU::SetLedForceOff(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_GSP, "(STUBBED) called");
 }
 
+void GSP_GPU::SetInternalPriorities(Kernel::HLERequestContext& ctx) {
+    IPC::RequestParser rp(ctx);
+    const auto priority = rp.Pop<u32>();
+    const auto priority_with_rights = rp.Pop<u32>();
+
+    IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+    rb.Push(RESULT_SUCCESS);
+
+    LOG_DEBUG(Service_GSP, "(STUBBED) called priority={:#02X}, priority_with_rights={:#02X}",
+              priority, priority_with_rights);
+}
+
 SessionData* GSP_GPU::FindRegisteredThreadData(u32 thread_id) {
     for (auto& session_info : connected_sessions) {
         SessionData* data = static_cast<SessionData*>(session_info.data.get());
@@ -874,6 +909,17 @@ SessionData* GSP_GPU::FindRegisteredThreadData(u32 thread_id) {
     return nullptr;
 }
 
+template <class Archive>
+void GSP_GPU::serialize(Archive& ar, const unsigned int) {
+    ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
+    ar& shared_memory;
+    ar& active_thread_id;
+    ar& first_initialization;
+    ar& used_thread_ids;
+    ar& saved_vram;
+}
+SERIALIZE_IMPL(GSP_GPU)
+
 GSP_GPU::GSP_GPU(Core::System& system) : ServiceFramework("gsp::Gpu", 4), system(system) {
     static const FunctionInfo functions[] = {
         // clang-format off
@@ -897,7 +943,7 @@ GSP_GPU::GSP_GPU(Core::System& system) : ServiceFramework("gsp::Gpu", 4), system
         {0x0012, nullptr, "GetPerfLog"},
         {0x0013, &GSP_GPU::RegisterInterruptRelayQueue, "RegisterInterruptRelayQueue"},
         {0x0014, &GSP_GPU::UnregisterInterruptRelayQueue, "UnregisterInterruptRelayQueue"},
-        {0x0015, nullptr, "TryAcquireRight"},
+        {0x0015, &GSP_GPU::TryAcquireRight, "TryAcquireRight"},
         {0x0016, &GSP_GPU::AcquireRight, "AcquireRight"},
         {0x0017, &GSP_GPU::ReleaseRight, "ReleaseRight"},
         {0x0018, &GSP_GPU::ImportDisplayCaptureInfo, "ImportDisplayCaptureInfo"},
@@ -906,7 +952,7 @@ GSP_GPU::GSP_GPU(Core::System& system) : ServiceFramework("gsp::Gpu", 4), system
         {0x001B, nullptr, "ResetGpuCore"},
         {0x001C, &GSP_GPU::SetLedForceOff, "SetLedForceOff"},
         {0x001D, nullptr, "SetTestCommand"},
-        {0x001E, nullptr, "SetInternalPriorities"},
+        {0x001E, &GSP_GPU::SetInternalPriorities, "SetInternalPriorities"},
         {0x001F, &GSP_GPU::StoreDataCache, "StoreDataCache"},
         // clang-format on
     };
@@ -926,6 +972,16 @@ std::unique_ptr<Kernel::SessionRequestHandler::SessionDataBase> GSP_GPU::MakeSes
     return std::make_unique<SessionData>(this);
 }
 
+template <class Archive>
+void SessionData::serialize(Archive& ar, const unsigned int) {
+    ar& boost::serialization::base_object<Kernel::SessionRequestHandler::SessionDataBase>(*this);
+    ar& gsp;
+    ar& interrupt_event;
+    ar& thread_id;
+    ar& registered;
+}
+SERIALIZE_IMPL(SessionData)
+
 SessionData::SessionData(GSP_GPU* gsp) : gsp(gsp) {
     // Assign a new thread id to this session when it connects. Note: In the real GSP service this
     // is done through a real thread (svcCreateThread) but we have to simulate it since our HLE
diff --git a/src/core/hle/service/gsp/gsp_gpu.h b/src/core/hle/service/gsp/gsp_gpu.h
index 048733898..bc3b5017d 100644
--- a/src/core/hle/service/gsp/gsp_gpu.h
+++ b/src/core/hle/service/gsp/gsp_gpu.h
@@ -7,9 +7,8 @@
 #include <cstddef>
 #include <memory>
 #include <string>
-#include <boost/serialization/base_object.hpp>
-#include <boost/serialization/optional.hpp>
-#include <boost/serialization/shared_ptr.hpp>
+#include <boost/optional/optional.hpp>
+#include <boost/serialization/export.hpp>
 #include "common/bit_field.h"
 #include "common/common_types.h"
 #include "core/hle/kernel/event.h"
@@ -22,6 +21,8 @@ class System;
 }
 
 namespace Kernel {
+class HLERequestContext;
+class Process;
 class SharedMemory;
 } // namespace Kernel
 
@@ -214,14 +215,7 @@ public:
 
 private:
     template <class Archive>
-    void serialize(Archive& ar, const unsigned int) {
-        ar& boost::serialization::base_object<Kernel::SessionRequestHandler::SessionDataBase>(
-            *this);
-        ar& gsp;
-        ar& interrupt_event;
-        ar& thread_id;
-        ar& registered;
-    }
+    void serialize(Archive& ar, const unsigned int);
     friend class boost::serialization::access;
 };
 
@@ -379,9 +373,25 @@ private:
     void UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx);
 
     /**
-     * GSP_GPU::AcquireRight service function
+     * GSP_GPU::TryAcquireRight service function
+     *  Inputs:
+     *      0 : Header code [0x00150002]
+     *      1 : Handle translate header (0x0)
+     *      2 : Process handle
      *  Outputs:
-     *      1: Result code
+     *      1 : Result of function, 0 on success, otherwise error code
+     */
+    void TryAcquireRight(Kernel::HLERequestContext& ctx);
+
+    /**
+     * GSP_GPU::AcquireRight service function
+     *  Inputs:
+     *      0 : Header code [0x00160042]
+     *      1 : Flags
+     *      2 : Handle translate header (0x0)
+     *      3 : Process handle
+     *  Outputs:
+     *      1 : Result of function, 0 on success, otherwise error code
      */
     void AcquireRight(Kernel::HLERequestContext& ctx);
 
@@ -464,6 +474,17 @@ private:
     /// Force the 3D LED State (0 = On, Non-Zero = Off)
     void SetLedForceOff(Kernel::HLERequestContext& ctx);
 
+    /**
+     * GSP_GPU::SetInternalPriorities service function
+     *  Inputs:
+     *      0 : Header code [0x001E0080]
+     *      1 : Session thread priority
+     *      2 : Session thread priority with rights
+     *  Outputs:
+     *      1 : Result of function, 0 on success, otherwise error code
+     */
+    void SetInternalPriorities(Kernel::HLERequestContext& ctx);
+
     /// Returns the session data for the specified registered thread id, or nullptr if not found.
     SessionData* FindRegisteredThreadData(u32 thread_id);
 
@@ -471,13 +492,17 @@ private:
 
     std::unique_ptr<Kernel::SessionRequestHandler::SessionDataBase> MakeSessionData() override;
 
+    ResultCode AcquireGpuRight(const Kernel::HLERequestContext& ctx,
+                               const std::shared_ptr<Kernel::Process>& process, u32 flag,
+                               bool blocking);
+
     Core::System& system;
 
     /// GSP shared memory
     std::shared_ptr<Kernel::SharedMemory> shared_memory;
 
-    /// Thread id that currently has GPU rights or UINT32_MAX if none.
-    u32 active_thread_id = UINT32_MAX;
+    /// Thread id that currently has GPU rights or std::numeric_limits<u32>::max() if none.
+    u32 active_thread_id = std::numeric_limits<u32>::max();
 
     bool first_initialization = true;
 
@@ -493,15 +518,7 @@ private:
     friend class SessionData;
 
     template <class Archive>
-    void serialize(Archive& ar, const unsigned int) {
-        ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
-        ar& shared_memory;
-        ar& active_thread_id;
-        ar& first_initialization;
-        ar& used_thread_ids;
-        ar& saved_vram;
-    }
-
+    void serialize(Archive& ar, const unsigned int);
     friend class boost::serialization::access;
 };