From a3596ba858890790ed0f18e092916f11f656a3d9 Mon Sep 17 00:00:00 2001 From: LotP1 <68976644+LotP1@users.noreply.github.com> Date: Tue, 25 Feb 2025 22:34:21 +0100 Subject: [PATCH 1/4] Reset in-memory JIT cache on game quit + fix Purge PPTC (#709) Jit cache now fully resets when booting a game multiple times. This should fix random jit cache crashes. Also removed some redundant code related to region allocation and fixed PPTC Purge not fully purging all PPTC files in the backup folder. --- src/ARMeilleure/Translation/Cache/JitCache.cs | 61 ++++++++++--------- .../Translation/Cache/JitUnwindWindows.cs | 22 +++++++ .../Controls/ApplicationContextMenu.axaml.cs | 2 +- 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index 7b5f2ca81..e480985b1 100644 --- a/src/ARMeilleure/Translation/Cache/JitCache.cs +++ b/src/ARMeilleure/Translation/Cache/JitCache.cs @@ -24,7 +24,7 @@ namespace ARMeilleure.Translation.Cache private static JitCacheInvalidation _jitCacheInvalidator; - private static CacheMemoryAllocator _cacheAllocator; + private static List _cacheAllocators = []; private static readonly List _cacheEntries = []; @@ -40,37 +40,48 @@ namespace ARMeilleure.Translation.Cache public static void Initialize(IJitMemoryAllocator allocator) { - if (_initialized) - { - return; - } - lock (_lock) { if (_initialized) { - return; + if (OperatingSystem.IsWindows()) + { + JitUnwindWindows.RemoveFunctionTableHandler( + _jitRegions[0].Pointer); + } + + for (int i = 0; i < _jitRegions.Count; i++) + { + _jitRegions[i].Dispose(); + } + + _jitRegions.Clear(); + _cacheAllocators.Clear(); } + else + { + _initialized = true; + } + + _activeRegionIndex = 0; ReservedRegion firstRegion = new(allocator, CacheSize); _jitRegions.Add(firstRegion); - _activeRegionIndex = 0; + + CacheMemoryAllocator firstCacheAllocator = new(CacheSize); + _cacheAllocators.Add(firstCacheAllocator); if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS()) { _jitCacheInvalidator = new JitCacheInvalidation(allocator); } - _cacheAllocator = new CacheMemoryAllocator(CacheSize); - if (OperatingSystem.IsWindows()) { JitUnwindWindows.InstallFunctionTableHandler( firstRegion.Pointer, CacheSize, firstRegion.Pointer + Allocate(_pageSize) ); } - - _initialized = true; } } @@ -136,7 +147,7 @@ namespace ARMeilleure.Translation.Cache if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset) { - _cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size)); + _cacheAllocators[_activeRegionIndex].Free(funcOffset, AlignCodeSize(entry.Size)); _cacheEntries.RemoveAt(entryIndex); } @@ -167,30 +178,24 @@ namespace ARMeilleure.Translation.Cache { codeSize = AlignCodeSize(codeSize); - for (int i = _activeRegionIndex; i < _jitRegions.Count; i++) + int allocOffset = _cacheAllocators[_activeRegionIndex].Allocate(codeSize); + + if (allocOffset >= 0) { - int allocOffset = _cacheAllocator.Allocate(codeSize); - - if (allocOffset >= 0) - { - _jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize); - _activeRegionIndex = i; - return allocOffset; - } + _jitRegions[_activeRegionIndex].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize); + return allocOffset; } int exhaustedRegion = _activeRegionIndex; ReservedRegion newRegion = new(_jitRegions[0].Allocator, CacheSize); _jitRegions.Add(newRegion); _activeRegionIndex = _jitRegions.Count - 1; - - int newRegionNumber = _activeRegionIndex; - Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((long)(newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation)."); - - _cacheAllocator = new CacheMemoryAllocator(CacheSize); + Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {_activeRegionIndex} ({((long)(_activeRegionIndex + 1) * CacheSize).Bytes()} Total Allocation)."); - int allocOffsetNew = _cacheAllocator.Allocate(codeSize); + _cacheAllocators.Add(new CacheMemoryAllocator(CacheSize)); + + int allocOffsetNew = _cacheAllocators[_activeRegionIndex].Allocate(codeSize); if (allocOffsetNew < 0) { throw new OutOfMemoryException("Failed to allocate in new Cache Region!"); diff --git a/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs b/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs index 01b2aa8ed..15a1051fa 100644 --- a/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs +++ b/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs @@ -52,6 +52,11 @@ namespace ARMeilleure.Translation.Cache nint context, [MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll); + [LibraryImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static unsafe partial bool RtlDeleteFunctionTable( + ulong tableIdentifier); + private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback; private static int _sizeOfRuntimeFunction; @@ -91,6 +96,23 @@ namespace ARMeilleure.Translation.Cache } } + public static void RemoveFunctionTableHandler(nint codeCachePointer) + { + ulong codeCachePtr = (ulong)codeCachePointer.ToInt64(); + + bool result; + + unsafe + { + result = RtlDeleteFunctionTable(codeCachePtr | 3); + } + + if (!result) + { + throw new InvalidOperationException("Failure removing function table callback."); + } + } + private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context) { int offset = (int)((long)controlPc - context.ToInt64()); diff --git a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs index 32ed6de39..881f6d556 100644 --- a/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs +++ b/src/Ryujinx/UI/Controls/ApplicationContextMenu.axaml.cs @@ -200,7 +200,7 @@ namespace Ryujinx.Ava.UI.Controls if (backupDir.Exists) { cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache")); - cacheFiles.AddRange(mainDir.EnumerateFiles("*.info")); + cacheFiles.AddRange(backupDir.EnumerateFiles("*.info")); } if (cacheFiles.Count > 0) From 1c8276197f3baeee5bf51bafa4ef38ffbc4cf1a6 Mon Sep 17 00:00:00 2001 From: FluffyOMC <45863583+FluffyOMC@users.noreply.github.com> Date: Tue, 25 Feb 2025 16:48:35 -0500 Subject: [PATCH 2/4] SSBU DRPC - Stage Editing (#707) Adds it so the Rich Presence now notices when the player edits a custom stage! --- src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs | 5 +++++ src/Ryujinx/Utilities/PlayReport/PlayReports.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs b/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs index 43e830819..a64379ab1 100644 --- a/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs +++ b/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs @@ -115,6 +115,11 @@ namespace Ryujinx.Ava.Utilities.PlayReport return $"Achievement Unlocked - ID: {anniversary}"; } + if (values.Matched.ContainsKey("is_created")) + { + return "Edited a Custom Stage!"; + } + if (values.Matched.ContainsKey("adv_slot")) { return diff --git a/src/Ryujinx/Utilities/PlayReport/PlayReports.cs b/src/Ryujinx/Utilities/PlayReport/PlayReports.cs index 9feb888b3..0de36a3ce 100644 --- a/src/Ryujinx/Utilities/PlayReport/PlayReports.cs +++ b/src/Ryujinx/Utilities/PlayReport/PlayReports.cs @@ -71,7 +71,7 @@ namespace Ryujinx.Ava.Utilities.PlayReport [ // Metadata to figure out what PlayReport we have. "match_mode", "match_submode", "anniversary", "fighter", "reason", "challenge_count", - "adv_slot", + "adv_slot", "is_created", // List of Fighters "player_1_fighter", "player_2_fighter", "player_3_fighter", "player_4_fighter", "player_5_fighter", "player_6_fighter", "player_7_fighter", "player_8_fighter", From 332bcdfaf1efd84707581aedc7fcd53b78f1b09a Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Tue, 25 Feb 2025 17:34:48 -0600 Subject: [PATCH 3/4] UI: Updater: Add support for eventual Windows on ARM updates --- src/Ryujinx.Common/Helpers/RunningPlatform.cs | 23 ++++++++++- src/Ryujinx/Updater.cs | 41 ++++++++++++++----- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/Ryujinx.Common/Helpers/RunningPlatform.cs b/src/Ryujinx.Common/Helpers/RunningPlatform.cs index 8d85c4a3c..7ec2f18df 100644 --- a/src/Ryujinx.Common/Helpers/RunningPlatform.cs +++ b/src/Ryujinx.Common/Helpers/RunningPlatform.cs @@ -5,15 +5,34 @@ using System.Runtime.InteropServices; namespace Ryujinx.Common.Helper { + public enum OperatingSystemType + { + MacOS, + Linux, + Windows + } + public static class RunningPlatform { + public static readonly OperatingSystemType CurrentOS + = IsMacOS + ? OperatingSystemType.MacOS + : IsWindows + ? OperatingSystemType.Windows + : IsLinux + ? OperatingSystemType.Linux + : throw new PlatformNotSupportedException(); + + public static Architecture Architecture => RuntimeInformation.OSArchitecture; + public static Architecture CurrentProcessArchitecture => RuntimeInformation.ProcessArchitecture; + public static bool IsMacOS => OperatingSystem.IsMacOS(); public static bool IsWindows => OperatingSystem.IsWindows(); public static bool IsLinux => OperatingSystem.IsLinux(); - public static bool IsArm => RuntimeInformation.OSArchitecture is Architecture.Arm64; + public static bool IsArm => Architecture is Architecture.Arm64; - public static bool IsX64 => RuntimeInformation.OSArchitecture is Architecture.X64; + public static bool IsX64 => Architecture is Architecture.X64; public static bool IsIntelMac => IsMacOS && IsX64; public static bool IsArmMac => IsMacOS && IsArm; diff --git a/src/Ryujinx/Updater.cs b/src/Ryujinx/Updater.cs index 338e9de43..7ca5e885a 100644 --- a/src/Ryujinx/Updater.cs +++ b/src/Ryujinx/Updater.cs @@ -43,17 +43,9 @@ namespace Ryujinx.Ava private const int ConnectionCount = 4; private static string _buildVer; + - private static readonly string _platformExt = - RunningPlatform.IsMacOS - ? "macos_universal.app.tar.gz" - : RunningPlatform.IsWindows - ? "win_x64.zip" - : RunningPlatform.IsX64Linux - ? "linux_x64.tar.gz" - : RunningPlatform.IsArmLinux - ? "linux_arm64.tar.gz" - : throw new PlatformNotSupportedException(); + private static readonly string _platformExt = BuildPlatformExtension(); private static string _buildUrl; private static long _buildSize; @@ -780,5 +772,34 @@ namespace Ryujinx.Ava public static void CleanupUpdate() => Directory.GetFiles(_homeDir, "*.ryuold", SearchOption.AllDirectories) .ForEach(File.Delete); + + private static string BuildPlatformExtension() + { + if (RunningPlatform.IsMacOS) + return "macos_universal.app.tar.gz"; + +#pragma warning disable CS8509 // It is exhaustive for any values this can contain. + string osPrefix = RunningPlatform.CurrentOS switch + { + OperatingSystemType.Linux => "linux", + OperatingSystemType.Windows => "win" + }; + + string archSuffix = RunningPlatform.Architecture switch + { + Architecture.Arm64 => "arm64", + Architecture.X64 => "x64", + _ => throw new PlatformNotSupportedException($"Unknown architecture {Enum.GetName(RunningPlatform.Architecture)}."), + }; + + string fileExtension = RunningPlatform.CurrentOS switch +#pragma warning restore CS8509 + { + OperatingSystemType.Linux => "tar.gz", + OperatingSystemType.Windows => "zip" + }; + + return $"{osPrefix}_{archSuffix}.{fileExtension}"; + } } } From 9227cbe5a7768e9bd02b937df8fa52f3b78e06b9 Mon Sep 17 00:00:00 2001 From: Piplup <100526773+piplup55@users.noreply.github.com> Date: Tue, 25 Feb 2025 23:48:47 +0000 Subject: [PATCH 4/4] Dynamic RPC: Improve Pokemon Scarlet/Violet (#723) Updated Pokemon Scarlet and Violet to use multi parser it now displays if your in a team circle and area of the game ![image](https://github.com/user-attachments/assets/6d8d52c5-65a9-4ac3-af91-cb0c03576ad6) --- .../PlayReport/PlayReports.Formatters.cs | 18 +++++++++++------- .../Utilities/PlayReport/PlayReports.cs | 5 ++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs b/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs index a64379ab1..b5215c693 100644 --- a/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs +++ b/src/Ryujinx/Utilities/PlayReport/PlayReports.Formatters.cs @@ -59,12 +59,13 @@ namespace Ryujinx.Ava.Utilities.PlayReport "Race" => "Racing", _ => FormattedValue.ForceReset }; - - private static FormattedValue PokemonSVUnionCircle(SingleValue value) - => value.Matched.BoxedValue is 0 ? "Playing Alone" : "Playing in a group"; - - private static FormattedValue PokemonSVArea(SingleValue value) - => value.Matched.StringValue switch + + private static FormattedValue PokemonSV(MultiValue values) + { + + string playStatus = values.Matched[0].BoxedValue is 0 ? "Playing Alone" : "Playing in a group"; + + FormattedValue locations = values.Matched[1].ToString() switch { // Base Game Locations "a_w01" => "South Area One", @@ -92,10 +93,13 @@ namespace Ryujinx.Ava.Utilities.PlayReport "a_w24" => "South Paldean Sea", "a_w25" => "West Paldean Sea", "a_w26" => "East Paldean Sea", - "a_w27" => "Nouth Paldean Sea", + "a_w27" => "North Paldean Sea", //TODO DLC Locations _ => FormattedValue.ForceReset }; + + return$"{playStatus} in {locations}"; + } private static FormattedValue SuperSmashBrosUltimate_Mode(SparseMultiValue values) { diff --git a/src/Ryujinx/Utilities/PlayReport/PlayReports.cs b/src/Ryujinx/Utilities/PlayReport/PlayReports.cs index 0de36a3ce..c46ae2be5 100644 --- a/src/Ryujinx/Utilities/PlayReport/PlayReports.cs +++ b/src/Ryujinx/Utilities/PlayReport/PlayReports.cs @@ -59,9 +59,8 @@ namespace Ryujinx.Ava.Utilities.PlayReport .AddSpec( ["0100a3d008c5c000", "01008f6008c5e000"], spec => spec - .WithDescription("based on what area of Paldea you're exploring.") - .AddValueFormatter("area_no", PokemonSVArea) - .AddValueFormatter("team_circle", PokemonSVUnionCircle) + .WithDescription("based on if you're playing alone or in a group and what area of Paldea you're exploring.") + .AddMultiValueFormatter(["team_circle", "area_no"], PokemonSV) ) .AddSpec( "01006a800016e000",