From 445db4d013f78247faefed402b4d439c1df3fb0a Mon Sep 17 00:00:00 2001 From: MarcZX100 Date: Wed, 14 Oct 2026 14:02:27 +0000 Subject: [PATCH] feat(windows): name presenter and queue worker threads --- .../src/Driver/Queues/WorkerAffinity.cpp | 5 +++ core/libs/prx/libc/CMakeLists.txt | 3 ++ .../prx/libc/include/general/ThreadName.hpp | 41 +++++++++++++++++++ core/libs/tests/ThreadName.cpp | 17 ++++++++ 4 files changed, 66 insertions(+) create mode 100644 core/libs/prx/libc/include/general/ThreadName.hpp create mode 100644 core/libs/tests/ThreadName.cpp diff --git a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Queues/WorkerAffinity.cpp b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Queues/WorkerAffinity.cpp index 0bac6c5eb9..792b920c92 100644 --- a/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Queues/WorkerAffinity.cpp +++ b/core/libs/prx/libSceAgcDriver/Execution/src/Driver/Queues/WorkerAffinity.cpp @@ -2,6 +2,7 @@ #include "prx/libSceAgcDriver/Execution/include/Driver/Queues/WorkerAffinity.hpp" #include "prx/libSceAgcDriver/Execution/include/TracyZone.hpp" #include "prx/libc/include/CpuTopology.hpp" +#include "prx/libc/include/general/ThreadName.hpp" #include namespace AgcDriver::DriverDetail { @@ -22,6 +23,10 @@ std::uint64_t WorkerAffinityMask() { void PinWorkerThread(const char* role) { TracyZone::NameThread(role); +#ifdef _WIN32 + const HRESULT nameResult = ThreadName::SetCurrent(role); + if (FAILED(nameResult)) std::fprintf(stderr, "[thread-name] could not name %s (0x%08lx)\n", role, static_cast(nameResult)); +#endif const auto mask = WorkerAffinityMask(); if (mask == 0) return; static std::once_flag summary; diff --git a/core/libs/prx/libc/CMakeLists.txt b/core/libs/prx/libc/CMakeLists.txt index 62f3b7a43b..7939d78662 100644 --- a/core/libs/prx/libc/CMakeLists.txt +++ b/core/libs/prx/libc/CMakeLists.txt @@ -619,6 +619,9 @@ if(BUILD_TESTING) if(WIN32) set_tests_properties(log_macros PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") + add_test_executable(thread_name_tests ${LIBS_INCLUDE_DIR}/tests/ThreadName.cpp) + target_include_directories(thread_name_tests PRIVATE ${LIBS_INCLUDE_DIR}) + add_test(NAME thread_name COMMAND thread_name_tests) endif() add_test_executable(guest_heap_report_for_game_tests ${LIBS_INCLUDE_DIR}/tests/GuestHeapReportForGame.cpp) diff --git a/core/libs/prx/libc/include/general/ThreadName.hpp b/core/libs/prx/libc/include/general/ThreadName.hpp new file mode 100644 index 0000000000..df1ff39530 --- /dev/null +++ b/core/libs/prx/libc/include/general/ThreadName.hpp @@ -0,0 +1,41 @@ +#ifndef CORE_LIBS_PRX_LIBC_INCLUDE_GENERAL_THREADNAME_HPP +#define CORE_LIBS_PRX_LIBC_INCLUDE_GENERAL_THREADNAME_HPP + +#ifdef _WIN32 +#include +#include +#include + +namespace ThreadName { + +template +inline TFunction Resolve(const char* name) { + TFunction function = nullptr; + const auto kernel = GetModuleHandleW(L"Kernel32.dll"); + if (kernel == nullptr) return function; + const FARPROC address = GetProcAddress(kernel, name); + static_assert(sizeof(function) == sizeof(address)); + std::memcpy(&function, &address, sizeof(function)); + return function; +} + +inline HRESULT SetCurrent(const char* name) { + if (name == nullptr) return E_INVALIDARG; + wchar_t wideName[64]; + std::size_t length = 0; + while (name[length] != '\0' && length < (sizeof(wideName) / sizeof(wideName[0])) - 1) { + wideName[length] = static_cast(name[length]); + ++length; + } + if (name[length] != '\0') return E_INVALIDARG; + wideName[length] = L'\0'; + using SetThreadDescriptionFunction = HRESULT(WINAPI*)(HANDLE, PCWSTR); + const auto setThreadDescription = Resolve("SetThreadDescription"); + if (setThreadDescription == nullptr) return S_FALSE; + return setThreadDescription(GetCurrentThread(), wideName); +} + +} +#endif + +#endif diff --git a/core/libs/tests/ThreadName.cpp b/core/libs/tests/ThreadName.cpp new file mode 100644 index 0000000000..c5035a890a --- /dev/null +++ b/core/libs/tests/ThreadName.cpp @@ -0,0 +1,17 @@ +#include "prx/libc/include/general/ThreadName.hpp" +#include + +int main() { + constexpr const char* name = "AnyPS5 queue worker 0x2a"; + const HRESULT set = ThreadName::SetCurrent(name); + if (set == S_FALSE) return 0; + if (FAILED(set)) return 1; + using GetThreadDescriptionFunction = HRESULT(WINAPI*)(HANDLE, PWSTR*); + const auto getThreadDescription = ThreadName::Resolve("GetThreadDescription"); + if (getThreadDescription == nullptr) return 0; + PWSTR description = nullptr; + if (FAILED(getThreadDescription(GetCurrentThread(), &description))) return 2; + const bool matches = description != nullptr && std::wstring(description) == L"AnyPS5 queue worker 0x2a"; + if (description != nullptr) LocalFree(description); + return matches ? 0 : 3; +}