From 46fc45b952f65a11467f2423aace053886c00edc Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 16 Oct 2025 13:58:31 -0700 Subject: [PATCH 1/2] SHMStats: Add a 16-byte alignment guarantee We want to take advantage of 16-byte single-copy atomicity. Which I am relying on, but didn't codify it the first time. Additionally add comments to explain that new members should be added to the end to allow tools time to gain support gradually. This will allow me to add new members without fully breaking mangohud, they'll just not display the new information until support is added. We're not guaranteeing backwards compatibility, just an attempt not to constantly churn the format unless necessary. This way if we do break compatibility, the tool will have an upper bound on supported versions before needing to rewrite code. --- FEXCore/include/FEXCore/Utils/SHMStats.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/FEXCore/include/FEXCore/Utils/SHMStats.h b/FEXCore/include/FEXCore/Utils/SHMStats.h index 20767620c..8da368050 100644 --- a/FEXCore/include/FEXCore/Utils/SHMStats.h +++ b/FEXCore/include/FEXCore/Utils/SHMStats.h @@ -39,6 +39,8 @@ enum class AppType : uint8_t { WIN_WOW64, }; +// Only append new members to the end of {ThreadStatsHeader, ThreadStats} to allow old tools time to support new information. +// FEX isn't guaranteeing /not/ breaking compatibility with versions, but trying to not cause too much churn. struct ThreadStatsHeader { uint8_t Version; AppType app_type; @@ -63,6 +65,9 @@ struct ThreadStats { uint64_t AccumulatedFloatFallbackCount; }; +// Ensure 16-byte alignment to take advantage of ARM single-copy atomicity. +static_assert(sizeof(ThreadStats) % 16 == 0, "Needs to be 16-byte aligned!"); + template class AccumulationBlock final { public: From 6ee77eb1a131e1a4dfcb686884ca5d22024fd283 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 16 Oct 2025 14:07:29 -0700 Subject: [PATCH 2/2] SHMStats: Adds ThreadStats size to header Reused the padding area so the header format doesn't change. If it is non-zero then it should be used by the tool. --- FEXCore/include/FEXCore/Utils/SHMStats.h | 2 +- Source/Common/SHMStats.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/FEXCore/include/FEXCore/Utils/SHMStats.h b/FEXCore/include/FEXCore/Utils/SHMStats.h index 8da368050..fba5cec22 100644 --- a/FEXCore/include/FEXCore/Utils/SHMStats.h +++ b/FEXCore/include/FEXCore/Utils/SHMStats.h @@ -44,7 +44,7 @@ enum class AppType : uint8_t { struct ThreadStatsHeader { uint8_t Version; AppType app_type; - uint8_t _pad[2]; + uint16_t ThreadStatsSize; char fex_version[48]; std::atomic Head; std::atomic Size; diff --git a/Source/Common/SHMStats.cpp b/Source/Common/SHMStats.cpp index eb124ad83..c522f42f8 100644 --- a/Source/Common/SHMStats.cpp +++ b/Source/Common/SHMStats.cpp @@ -13,10 +13,11 @@ void StatAllocBase::SaveHeader(FEXCore::SHMStats::AppType AppType) { Head = reinterpret_cast(Base); Head->Size.store(CurrentSize, std::memory_order_relaxed); Head->Version = FEXCore::SHMStats::STATS_VERSION; + Head->app_type = AppType; + Head->ThreadStatsSize = sizeof(FEXCore::SHMStats::ThreadStats); std::string_view GitString = GIT_DESCRIBE_STRING; strncpy(Head->fex_version, GitString.data(), std::min(GitString.size(), sizeof(Head->fex_version))); - Head->app_type = AppType; Stats = reinterpret_cast(reinterpret_cast(Base) + sizeof(FEXCore::SHMStats::ThreadStatsHeader));