diff --git a/src/ARMeilleure/Optimizations.cs b/src/ARMeilleure/Optimizations.cs index ac6510143..231274e41 100644 --- a/src/ARMeilleure/Optimizations.cs +++ b/src/ARMeilleure/Optimizations.cs @@ -8,6 +8,9 @@ namespace ARMeilleure // low-core count PPTC public static bool EcoFriendly { get; set; } = false; + // Jit cache eviction + public static bool CacheEviction { get; set; } = false; + public static bool FastFP { get; set; } = true; public static bool AllowLcqInFunctionTable { get; set; } = true; diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index 48849d2d7..b63b694ae 100644 --- a/src/ARMeilleure/Translation/Cache/JitCache.cs +++ b/src/ARMeilleure/Translation/Cache/JitCache.cs @@ -2,10 +2,12 @@ using ARMeilleure.CodeGen; using ARMeilleure.CodeGen.Unwinding; using ARMeilleure.Memory; using ARMeilleure.Native; +using Ryujinx.Common.Logging; using Ryujinx.Memory; using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Threading; @@ -17,8 +19,15 @@ namespace ARMeilleure.Translation.Cache private static readonly int _pageSize = (int)MemoryBlock.GetPageSize(); private static readonly int _pageMask = _pageSize - 1; - private const int CodeAlignment = 4; // Bytes. - private const int CacheSize = 2047 * 1024 * 1024; + private const int CodeAlignment = 4; + private const int FullCacheSize = 2047 * 1024 * 1024; + private const int ReducedCacheSize = FullCacheSize / 8; + + private const float EvictionTargetPercentage = 0.20f; + private const int MaxEntriesToEvictAtOnce = 100; + + // Simple logging configuration + private const int LogInterval = 5000; // Log every 5000 allocations private static ReservedRegion _jitRegion; private static JitCacheInvalidation _jitCacheInvalidator; @@ -26,9 +35,33 @@ namespace ARMeilleure.Translation.Cache private static CacheMemoryAllocator _cacheAllocator; private static readonly List _cacheEntries = []; + private static readonly Dictionary _entryUsageStats = []; private static readonly Lock _lock = new(); private static bool _initialized; + private static int _cacheSize; + + // Basic statistics + private static int _totalAllocations = 0; + private static int _totalEvictions = 0; + + private class EntryUsageStats + { + public long LastAccessTime { get; private set; } + public int UsageCount { get; private set; } + + public EntryUsageStats() + { + LastAccessTime = DateTime.UtcNow.Ticks; + UsageCount = 1; + } + + public void UpdateUsage() + { + LastAccessTime = DateTime.UtcNow.Ticks; + UsageCount++; + } + } [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll", SetLastError = true)] @@ -48,20 +81,22 @@ namespace ARMeilleure.Translation.Cache return; } - _jitRegion = new ReservedRegion(allocator, CacheSize); + _cacheSize = Optimizations.CacheEviction ? ReducedCacheSize : FullCacheSize; + _jitRegion = new ReservedRegion(allocator, (ulong)_cacheSize); if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS()) { _jitCacheInvalidator = new JitCacheInvalidation(allocator); } - _cacheAllocator = new CacheMemoryAllocator(CacheSize); + _cacheAllocator = new CacheMemoryAllocator(_cacheSize); if (OperatingSystem.IsWindows()) { - JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, CacheSize, _jitRegion.Pointer + Allocate(_pageSize)); + JitUnwindWindows.InstallFunctionTableHandler(_jitRegion.Pointer, (uint)_cacheSize, _jitRegion.Pointer + Allocate(_pageSize)); } + Logger.Info?.Print(LogClass.Cpu, $"JIT Cache initialized: Size={_cacheSize / (1024 * 1024)} MB, Eviction={Optimizations.CacheEviction}"); _initialized = true; } } @@ -73,8 +108,32 @@ namespace ARMeilleure.Translation.Cache lock (_lock) { Debug.Assert(_initialized); + _totalAllocations++; - int funcOffset = Allocate(code.Length); + int funcOffset; + + if (Optimizations.CacheEviction) + { + int codeSize = AlignCodeSize(code.Length); + funcOffset = _cacheAllocator.Allocate(codeSize); + + if (funcOffset < 0) + { + EvictEntries(codeSize); + funcOffset = _cacheAllocator.Allocate(codeSize); + + if (funcOffset < 0) + { + throw new OutOfMemoryException("JIT Cache exhausted even after eviction."); + } + } + + _jitRegion.ExpandIfNeeded((ulong)funcOffset + (ulong)codeSize); + } + else + { + funcOffset = Allocate(code.Length); + } IntPtr funcPtr = _jitRegion.Pointer + funcOffset; @@ -106,6 +165,12 @@ namespace ARMeilleure.Translation.Cache Add(funcOffset, code.Length, func.UnwindInfo); + // Simple periodic logging + if (_totalAllocations % LogInterval == 0) + { + LogCacheStatus(); + } + return funcPtr; } } @@ -122,6 +187,11 @@ namespace ARMeilleure.Translation.Cache { _cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size)); _cacheEntries.RemoveAt(entryIndex); + + if (Optimizations.CacheEviction) + { + _entryUsageStats.Remove(funcOffset); + } } } } @@ -179,6 +249,11 @@ namespace ARMeilleure.Translation.Cache } _cacheEntries.Insert(index, entry); + + if (Optimizations.CacheEviction) + { + _entryUsageStats[offset] = new EntryUsageStats(); + } } public static bool TryFind(int offset, out CacheEntry entry, out int entryIndex) @@ -195,6 +270,12 @@ namespace ARMeilleure.Translation.Cache if (index >= 0) { entry = _cacheEntries[index]; + + if (Optimizations.CacheEviction && _entryUsageStats.TryGetValue(offset, out var stats)) + { + stats.UpdateUsage(); + } + entryIndex = index; return true; } @@ -204,5 +285,83 @@ namespace ARMeilleure.Translation.Cache entryIndex = 0; return false; } + + private static void EvictEntries(int requiredSize) + { + if (!Optimizations.CacheEviction) + { + return; + } + + lock (_lock) + { + int targetSpace = Math.Max(requiredSize, (int)(_cacheSize * EvictionTargetPercentage)); + int freedSpace = 0; + int evictedCount = 0; + + var entriesWithStats = _cacheEntries + .Where(e => _entryUsageStats.ContainsKey(e.Offset)) + .Select(e => new { + Entry = e, + Stats = _entryUsageStats[e.Offset], + Score = CalculateEvictionScore(_entryUsageStats[e.Offset]) + }) + .OrderBy(x => x.Score) + .Take(MaxEntriesToEvictAtOnce) + .ToList(); + + foreach (var item in entriesWithStats) + { + int entrySize = AlignCodeSize(item.Entry.Size); + + int entryIndex = _cacheEntries.BinarySearch(item.Entry); + if (entryIndex >= 0) + { + _cacheAllocator.Free(item.Entry.Offset, entrySize); + _cacheEntries.RemoveAt(entryIndex); + _entryUsageStats.Remove(item.Entry.Offset); + + freedSpace += entrySize; + evictedCount++; + + if (freedSpace >= targetSpace) + { + break; + } + } + } + + _totalEvictions += evictedCount; + + Logger.Info?.Print(LogClass.Cpu, $"JIT Cache: Evicted {evictedCount} entries, freed {freedSpace / (1024 * 1024.0):F2} MB"); + } + } + + private static double CalculateEvictionScore(EntryUsageStats stats) + { + long currentTime = DateTime.UtcNow.Ticks; + long ageInTicks = currentTime - stats.LastAccessTime; + + double ageInSeconds = ageInTicks / 10_000_000.0; + + const double usageWeight = 1.0; + const double ageWeight = 2.0; + + double usageScore = Math.Log10(stats.UsageCount + 1) * usageWeight; + double ageScore = (10.0 / (ageInSeconds + 1.0)) * ageWeight; + + return usageScore + ageScore; + } + + private static void LogCacheStatus() + { + int estimatedUsedSize = _cacheEntries.Sum(e => AlignCodeSize(e.Size)); + double usagePercentage = 100.0 * estimatedUsedSize / _cacheSize; + + Logger.Info?.Print(LogClass.Cpu, + $"JIT Cache status: entries={_cacheEntries.Count}, " + + $"est. used={estimatedUsedSize / (1024 * 1024.0):F2} MB ({usagePercentage:F1}%), " + + $"evictions={_totalEvictions}, allocations={_totalAllocations}"); + } } } diff --git a/src/ARMeilleure/Translation/Delegates.cs b/src/ARMeilleure/Translation/Delegates.cs index 0f4dfd7eb..d763afa68 100644 --- a/src/ARMeilleure/Translation/Delegates.cs +++ b/src/ARMeilleure/Translation/Delegates.cs @@ -1,5 +1,4 @@ using ARMeilleure.Instructions; -using ARMeilleure.State; using System; using System.Collections.Generic; using System.Reflection; diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs index ae7eee58a..755a69518 100644 --- a/src/ARMeilleure/Translation/PTC/Ptc.cs +++ b/src/ARMeilleure/Translation/PTC/Ptc.cs @@ -29,8 +29,8 @@ namespace ARMeilleure.Translation.PTC { private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - - private const uint InternalVersion = 6998; //! To be incremented manually for each change to the ARMeilleure project. + + private const uint InternalVersion = 7008; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; @@ -563,6 +563,7 @@ namespace ARMeilleure.Translation.PTC { if (AreCarriersEmpty() || ContainsBlacklistedFunctions()) { + ResetCarriersIfNeeded(); return; } @@ -869,7 +870,7 @@ namespace ARMeilleure.Translation.PTC Debug.Assert(Profiler.IsAddressInStaticCodeRange(address)); - TranslatedFunction func = translator.Translate(address, executionMode, highCq); + TranslatedFunction func = translator.Translate(address, executionMode, highCq, pptcTranslation: true); if (func == null) { diff --git a/src/ARMeilleure/Translation/Translator.cs b/src/ARMeilleure/Translation/Translator.cs index 646b2ee40..4eb4dd69a 100644 --- a/src/ARMeilleure/Translation/Translator.cs +++ b/src/ARMeilleure/Translation/Translator.cs @@ -219,7 +219,7 @@ namespace ARMeilleure.Translation } } - internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq, bool singleStep = false) + internal TranslatedFunction Translate(ulong address, ExecutionMode mode, bool highCq, bool singleStep = false, bool pptcTranslation = false) { var context = new ArmEmitterContext( Memory, @@ -246,7 +246,12 @@ namespace ARMeilleure.Translation context.Branch(context.GetLabel(address)); } - ControlFlowGraph cfg = EmitAndGetCFG(context, blocks, out Range funcRange, out Counter counter); + ControlFlowGraph cfg = EmitAndGetCFG(context, blocks, out Range funcRange, out Counter counter, pptcTranslation); + + if (cfg == null) + { + return null; + } ulong funcSize = funcRange.End - funcRange.Start; @@ -321,7 +326,8 @@ namespace ARMeilleure.Translation ArmEmitterContext context, Block[] blocks, out Range range, - out Counter counter) + out Counter counter, + bool pptcTranslation) { counter = null; @@ -406,6 +412,14 @@ namespace ARMeilleure.Translation if (opCode.Instruction.Emitter != null) { opCode.Instruction.Emitter(context); + // if we're pre-compiling PPTC functions, and we hit an Undefined instruction as the first + // instruction in the block, mark the function as blacklisted + // this way, we don't pre-compile Exlaunch hooks, which allows ExeFS mods to run with PPTC + if (pptcTranslation && opCode.Instruction.Name == InstName.Und && blkIndex == 0) + { + range = new Range(rangeStart, rangeEnd); + return null; + } } else { diff --git a/src/Kenjinx.Headless.SDL2/Kenjinx.Headless.SDL2.csproj b/src/Kenjinx.Headless.SDL2/Kenjinx.Headless.SDL2.csproj index 3abe57dfd..b7dd89890 100644 --- a/src/Kenjinx.Headless.SDL2/Kenjinx.Headless.SDL2.csproj +++ b/src/Kenjinx.Headless.SDL2/Kenjinx.Headless.SDL2.csproj @@ -1,20 +1,46 @@  - win-x64;osx-x64;linux-x64;win-arm64;osx-arm64;linux-arm64 + Kenjinx.Headless Exe true 2.0.3 $(DefineConstants);$(ExtraDefineConstants) - + Kenjinx.ico false + + + win-x64 + osx-x64 + linux-x64 + + + + true + true + false + true + partial + + + + false + + + + + + + + @@ -37,8 +63,12 @@ - + + + Always + alsoft.ini + Always THIRDPARTY.md @@ -47,6 +77,7 @@ Always LICENSE.txt + diff --git a/src/Kenjinx/AppHost.cs b/src/Kenjinx/AppHost.cs index bb4dcc934..6582d2e43 100644 --- a/src/Kenjinx/AppHost.cs +++ b/src/Kenjinx/AppHost.cs @@ -470,6 +470,7 @@ namespace Ryujinx.Ava public void Start() { ARMeilleure.Optimizations.EcoFriendly = ConfigurationState.Instance.System.EnableLowPowerPtc; + ARMeilleure.Optimizations.CacheEviction = ConfigurationState.Instance.System.EnableJitCacheEviction; if (OperatingSystem.IsWindows()) { diff --git a/src/Kenjinx/Assets/Locales/ar_SA.json b/src/Kenjinx/Assets/Locales/ar_SA.json index 03e4abd20..ed0876501 100644 --- a/src/Kenjinx/Assets/Locales/ar_SA.json +++ b/src/Kenjinx/Assets/Locales/ar_SA.json @@ -147,6 +147,7 @@ "SettingsTabSystemEnableVsync": "VSync", "SettingsTabSystemEnablePptc": "PPTC (ذاكرة التخزين المؤقت للترجمة المستمرة)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "التحقق من سلامة نظام الملفات", "SettingsTabSystemAudioBackend": "خلفية الصوت:", "SettingsTabSystemAudioBackendDummy": "زائف", @@ -589,7 +590,8 @@ "TimeTooltip": "تغيير وقت النظام", "VSyncToggleTooltip": "محاكاة المزامنة العمودية للجهاز. في الأساس محدد الإطار لغالبية الألعاب؛ قد يؤدي تعطيله إلى تشغيل الألعاب بسرعة أعلى أو جعل شاشات التحميل تستغرق وقتا أطول أو تتعطل.\n\nيمكن تبديله داخل اللعبة باستخدام مفتاح التشغيل السريع الذي تفضله (F1 افتراضيا). نوصي بالقيام بذلك إذا كنت تخطط لتعطيله.\n\nاتركه ممكنا إذا لم تكن متأكدا.", "PptcToggleTooltip": "يحفظ وظائف JIT المترجمة بحيث لا تحتاج إلى ترجمتها في كل مرة يتم فيها تحميل اللعبة.\n\nيقلل من التقطيع ويسرع بشكل ملحوظ أوقات التشغيل بعد التشغيل الأول للعبة.\n\nاتركه ممكنا إذا لم تكن متأكدا.", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "قم بتحميل PPTC باستخدام ثلث كمية النوى", + "JitCacheEvictionToggleTooltip": "م بتمكين إخلاء JIT Cache لإدارة الذاكرة بكفاءة", "FsIntegrityToggleTooltip": "يتحقق من وجود ملفات تالفة عند تشغيل لعبة ما، وإذا تم اكتشاف ملفات تالفة، فسيتم عرض خطأ تجزئة في السجل.\n\nليس له أي تأثير على الأداء ويهدف إلى المساعدة في استكشاف الأخطاء وإصلاحها.\n\nاتركه مفعلا إذا كنت غير متأكد.", "AudioBackendTooltip": "يغير الواجهة الخلفية المستخدمة لتقديم الصوت.\n\nSDL2 هو الخيار المفضل، بينما يتم استخدام OpenAL وSoundIO كبديلين. زائف لن يكون لها صوت.\n\nاضبط على SDL2 إذا لم تكن متأكدا.", "MemoryManagerTooltip": "تغيير كيفية تعيين ذاكرة الضيف والوصول إليها. يؤثر بشكل كبير على أداء وحدة المعالجة المركزية التي تمت محاكاتها.\n\nاضبط على المضيف غير محدد إذا لم تكن متأكدا.", diff --git a/src/Kenjinx/Assets/Locales/de_DE.json b/src/Kenjinx/Assets/Locales/de_DE.json index 296840b82..1c5b12b4c 100644 --- a/src/Kenjinx/Assets/Locales/de_DE.json +++ b/src/Kenjinx/Assets/Locales/de_DE.json @@ -148,6 +148,7 @@ "SettingsTabSystemEnableVsync": "VSync", "SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)", "SettingsTabSystemEnableLowPowerPptc": "Kleinleistungs-PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit-Cache-Räumung", "SettingsTabSystemEnableFsIntegrityChecks": "FS Integritätsprüfung", "SettingsTabSystemAudioBackend": "Audio-Backend:", "SettingsTabSystemAudioBackendDummy": "Ohne Funktion", @@ -591,6 +592,7 @@ "VSyncToggleTooltip": "Vertikale Synchronisierung der emulierten Konsole. Diese Option ist quasi ein Frame-Limiter für die meisten Spiele; die Deaktivierung kann dazu führen, dass Spiele mit höherer Geschwindigkeit laufen oder Ladebildschirme länger benötigen/hängen bleiben.\n\nKann beim Spielen mit einem frei wählbaren Hotkey ein- und ausgeschaltet werden (standardmäßig F1). \n\nIm Zweifelsfall AN lassen.", "PptcToggleTooltip": "Speichert übersetzte JIT-Funktionen, sodass jene nicht jedes Mal übersetzt werden müssen, wenn das Spiel geladen wird.\n\nVerringert Stottern und die Zeit beim zweiten und den darauffolgenden Startvorgängen eines Spiels erheblich.\n\nIm Zweifelsfall AN lassen.", "LowPowerPptcToggleTooltip": "Lädt den PPTC mit einem Drittel der verfügbaren Prozessorkernen", + "JitCacheEvictionToggleTooltip": "Aktivieren Sie die JIT-Cache-Eviction, um den Speicher effizient zu verwalten", "FsIntegrityToggleTooltip": "Prüft beim Startvorgang auf beschädigte Dateien und zeigt bei beschädigten Dateien einen Hash-Fehler (Hash Error) im Log an.\n\nDiese Einstellung hat keinen Einfluss auf die Leistung und hilft bei der Fehlersuche.\n\nIm Zweifelsfall AN lassen.", "AudioBackendTooltip": "Ändert das Backend, das zum Rendern von Audio verwendet wird.\n\nSDL2 ist das bevorzugte Audio-Backend, OpenAL und SoundIO sind als Alternativen vorhanden. Dummy wird keinen Audio-Output haben.\n\nIm Zweifelsfall SDL2 auswählen.", "MemoryManagerTooltip": "Ändert wie der Gastspeicher abgebildet wird und wie auf ihn zugegriffen wird. Beinflusst die Leistung der emulierten CPU erheblich.\n\nIm Zweifelsfall Host ungeprüft auswählen.", diff --git a/src/Kenjinx/Assets/Locales/el_GR.json b/src/Kenjinx/Assets/Locales/el_GR.json index 450938b99..df4431cb2 100644 --- a/src/Kenjinx/Assets/Locales/el_GR.json +++ b/src/Kenjinx/Assets/Locales/el_GR.json @@ -147,6 +147,7 @@ "SettingsTabSystemEnableVsync": "Ενεργοποίηση Κατακόρυφου Συγχρονισμού", "SettingsTabSystemEnablePptc": "Ενεργοποίηση PPTC (Profiled Persistent Translation Cache)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "Ενεργοποίηση Ελέγχων Ακεραιότητας FS", "SettingsTabSystemAudioBackend": "Backend Ήχου:", "SettingsTabSystemAudioBackendDummy": "Απενεργοποιημένο", @@ -589,7 +590,8 @@ "TimeTooltip": "Αλλαγή Ώρας Συστήματος", "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", "PptcToggleTooltip": "Ενεργοποιεί ή απενεργοποιεί το PPTC", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "Φορτώστε το PPTC χρησιμοποιώντας το ένα τρίτο της ποσότητας πυρήνων", + "JitCacheEvictionToggleTooltip": "Ενεργοποιήστε την εξαγωγή JIT Cache για αποτελεσματική διαχείριση της μνήμης", "FsIntegrityToggleTooltip": "Ενεργοποιεί τους ελέγχους ακεραιότητας σε αρχεία περιεχομένου παιχνιδιού", "AudioBackendTooltip": "Αλλαγή ήχου υποστήριξης", "MemoryManagerTooltip": "Αλλάξτε τον τρόπο αντιστοίχισης και πρόσβασης στη μνήμη επισκέπτη. Επηρεάζει σε μεγάλο βαθμό την απόδοση της προσομοίωσης της CPU.", diff --git a/src/Kenjinx/Assets/Locales/en_US.json b/src/Kenjinx/Assets/Locales/en_US.json index d6b2adaa8..6c08c67a6 100644 --- a/src/Kenjinx/Assets/Locales/en_US.json +++ b/src/Kenjinx/Assets/Locales/en_US.json @@ -168,6 +168,7 @@ "SettingsTabSystemCustomVSyncIntervalValue": "Custom Refresh Rate Value:", "SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC cache", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "FS Integrity Checks", "SettingsTabSystemAudioBackend": "Audio Backend:", "SettingsTabSystemAudioBackendDummy": "Dummy", @@ -621,7 +622,8 @@ "MatchTimeTooltip": "Sync System Time to match your PC's current date & time.", "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", "PptcToggleTooltip": "Saves translated JIT functions so that they do not need to be translated every time the game loads.\n\nReduces stuttering and significantly speeds up boot times after the first boot of a game.\n\nLeave ON if unsure.", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores", + "JitCacheEvictionToggleTooltip": "Enable JIT Cache eviction to manage memory efficiently", "FsIntegrityToggleTooltip": "Checks for corrupt files when booting a game, and if corrupt files are detected, displays a hash error in the log.\n\nHas no impact on performance and is meant to help troubleshooting.\n\nLeave ON if unsure.", "AudioBackendTooltip": "Changes the backend used to render audio.\n\nSDL2 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound.\n\nSet to SDL2 if unsure.", "MemoryManagerTooltip": "Change how guest memory is mapped and accessed. Greatly affects emulated CPU performance.\n\nSet to HOST UNCHECKED if unsure.", diff --git a/src/Kenjinx/Assets/Locales/es_ES.json b/src/Kenjinx/Assets/Locales/es_ES.json index ca9ef911d..0b2fadd7c 100644 --- a/src/Kenjinx/Assets/Locales/es_ES.json +++ b/src/Kenjinx/Assets/Locales/es_ES.json @@ -147,6 +147,7 @@ "SettingsTabSystemEnableVsync": "Sincronización vertical", "SettingsTabSystemEnablePptc": "PPTC (Cache de Traducción de Perfil Persistente)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "Comprobar integridad de los archivos", "SettingsTabSystemAudioBackend": "Motor de audio:", "SettingsTabSystemAudioBackendDummy": "Vacio", @@ -589,7 +590,8 @@ "TimeTooltip": "Cambia la hora del sistema", "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", "PptcToggleTooltip": "Guarda funciones de JIT traducidas para que no sea necesario traducirlas cada vez que el juego carga.\n\nReduce los tirones y acelera significativamente el tiempo de inicio de los juegos después de haberlos ejecutado al menos una vez.\n\nActívalo si no sabes qué hacer.", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "Cargue el PPTC usando un tercio de la cantidad de núcleos", + "JitCacheEvictionToggleTooltip": "Habilite el desalojo de JIT Cache para administrar la memoria de manera eficiente", "FsIntegrityToggleTooltip": "Comprueba si hay archivos corruptos en los juegos que ejecutes al abrirlos, y si detecta archivos corruptos, muestra un error de Hash en los registros.\n\nEsto no tiene impacto alguno en el rendimiento y está pensado para ayudar a resolver problemas.\n\nActívalo si no sabes qué hacer.", "AudioBackendTooltip": "Cambia el motor usado para renderizar audio.\n\nSDL2 es el preferido, mientras que OpenAL y SoundIO se usan si hay problemas con este. Dummy no produce audio.\n\nSelecciona SDL2 si no sabes qué hacer.", "MemoryManagerTooltip": "Cambia la forma de mapear y acceder a la memoria del guest. Afecta en gran medida al rendimiento de la CPU emulada.\n\nSelecciona \"Host sin verificación\" si no sabes qué hacer.", diff --git a/src/Kenjinx/Assets/Locales/fr_FR.json b/src/Kenjinx/Assets/Locales/fr_FR.json index 7a02346e8..e5ff96d17 100644 --- a/src/Kenjinx/Assets/Locales/fr_FR.json +++ b/src/Kenjinx/Assets/Locales/fr_FR.json @@ -149,6 +149,7 @@ "SettingsTabSystemEnableVsync": "Synchronisation verticale (VSync)", "SettingsTabSystemEnablePptc": "Activer le PPTC (Profiled Persistent Translation Cache)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "Activer la vérification de l'intégrité du système de fichiers", "SettingsTabSystemAudioBackend": "Bibliothèque Audio :", "SettingsTabSystemAudioBackendDummy": "Factice", @@ -592,7 +593,8 @@ "MatchTimeTooltip": "Resynchronise la Date du Système pour qu'elle soit la même que celle du PC.", "VSyncToggleTooltip": "La synchronisation verticale de la console émulée. Essentiellement un limiteur de trame pour la majorité des jeux ; le désactiver peut entraîner un fonctionnement plus rapide des jeux ou prolonger ou bloquer les écrans de chargement.\n\nPeut être activé ou désactivé en jeu avec un raccourci clavier de votre choix (F1 par défaut). Nous recommandons de le faire si vous envisagez de le désactiver.\n\nLaissez activé si vous n'êtes pas sûr.", "PptcToggleTooltip": "Sauvegarde les fonctions JIT afin qu'elles n'aient pas besoin d'être à chaque fois recompiler lorsque le jeu se charge.\n\nRéduit les lags et accélère considérablement le temps de chargement après le premier lancement d'un jeu.\n\nLaissez par défaut si vous n'êtes pas sûr.", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "Chargez le PPTC en utilisant un tiers de la quantité de cœurs", + "JitCacheEvictionToggleTooltip": "Activer l'expulsion du cache JIT pour gérer efficacement la mémoire", "FsIntegrityToggleTooltip": "Vérifie si des fichiers sont corrompus lors du lancement d'un jeu, et si des fichiers corrompus sont détectés, affiche une erreur de hachage dans la console.\n\nN'a aucun impact sur les performances et est destiné à aider le dépannage.\n\nLaissez activer en cas d'incertitude.", "AudioBackendTooltip": "Modifie le backend utilisé pour donnée un rendu audio.\n\nSDL2 est préféré, tandis que OpenAL et SoundIO sont utilisés comme backend secondaire. Le backend Dummy (Factice) ne rends aucun son.\n\nLaissez sur SDL2 si vous n'êtes pas sûr.", "MemoryManagerTooltip": "Change la façon dont la mémoire émulée est mappée et utiliser. Cela affecte grandement les performances du processeur.\n\nRéglez sur Host Uncheked en cas d'incertitude.", diff --git a/src/Kenjinx/Assets/Locales/he_IL.json b/src/Kenjinx/Assets/Locales/he_IL.json index e9a966fcc..31c5a3943 100644 --- a/src/Kenjinx/Assets/Locales/he_IL.json +++ b/src/Kenjinx/Assets/Locales/he_IL.json @@ -147,6 +147,7 @@ "SettingsTabSystemEnableVsync": "VSync", "SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "FS בדיקות תקינות", "SettingsTabSystemAudioBackend": "אחראי שמע:", "SettingsTabSystemAudioBackendDummy": "גולם", @@ -589,7 +590,8 @@ "TimeTooltip": "שנה זמן מערכת", "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", "PptcToggleTooltip": "שומר את פונקציות ה-JIT המתורגמות כך שלא יצטרכו לעבור תרגום שוב כאשר משחק עולה.\n\nמפחית תקיעות ומשפר מהירות עלייה של המערכת אחרי הפתיחה הראשונה של המשחק.\n\nמוטב להשאיר דלוק אם לא בטוחים.", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "טען את ה-PPTC באמצעות שליש מכמות הליבות", + "JitCacheEvictionToggleTooltip": "אפשר פינוי JIT Cache לניהול זיכרון ביעילות", "FsIntegrityToggleTooltip": "בודק לקבצים שגויים כאשר משחק עולה, ואם מתגלים כאלו, מציג את מזהה השגיאה שלהם לקובץ הלוג.\n\nאין לכך השפעה על הביצועים ונועד לעזור לבדיקה וניפוי שגיאות של האמולטור.\n\nמוטב להשאיר דלוק אם לא בטוחים.", "AudioBackendTooltip": "משנה את אחראי השמע.\n\nSDL2 הוא הנבחר, למראת שOpenAL וגם SoundIO משומשים כאפשרויות חלופיות. אפשרות הDummy לא תשמיע קול כלל.\n\nמוטב להשאיר על SDL2 אם לא בטוחים.", "MemoryManagerTooltip": "שנה איך שזיכרון מארח מיוחד ומונגד. משפיע מאוד על ביצועי המעבד המדומה.\n\nמוטב להשאיר על מארח לא מבוקר אם לא בטוחים.", diff --git a/src/Kenjinx/Assets/Locales/it_IT.json b/src/Kenjinx/Assets/Locales/it_IT.json index 686a9bd70..4f3ec71f8 100644 --- a/src/Kenjinx/Assets/Locales/it_IT.json +++ b/src/Kenjinx/Assets/Locales/it_IT.json @@ -147,6 +147,7 @@ "SettingsTabSystemEnableVsync": "Attiva VSync", "SettingsTabSystemEnablePptc": "Attiva PPTC (Profiled Persistent Translation Cache)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "Attiva controlli d'integrità FS", "SettingsTabSystemAudioBackend": "Backend audio:", "SettingsTabSystemAudioBackendDummy": "Dummy", @@ -590,7 +591,8 @@ "MatchTimeTooltip": "Sincronizza data e ora del sistema con quelle del PC.", "VSyncToggleTooltip": "Sincronizzazione verticale della console Emulata. Essenzialmente un limitatore di frame per la maggior parte dei giochi; disabilitarlo può far girare giochi a velocità più alta, allungare le schermate di caricamento o farle bloccare.\n\nPuò essere attivata in gioco con un tasto di scelta rapida (F1 per impostazione predefinita). Ti consigliamo di farlo se hai intenzione di disabilitarlo.\n\nLascia ON se non sei sicuro.", "PptcToggleTooltip": "Salva le funzioni JIT tradotte in modo che non debbano essere tradotte tutte le volte che si avvia un determinato gioco.\n\nRiduce i fenomeni di stuttering e velocizza sensibilmente gli avvii successivi del gioco.\n\nNel dubbio, lascia l'opzione attiva.", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "Carica il PPTC utilizzando un terzo della quantità di core", + "JitCacheEvictionToggleTooltip": "Abilita l'eliminazione della cache JIT per gestire la memoria in modo efficiente", "FsIntegrityToggleTooltip": "Controlla la presenza di file corrotti quando si avvia un gioco. Se vengono rilevati dei file corrotti, verrà mostrato un errore di hash nel log.\n\nQuesta opzione non influisce sulle prestazioni ed è pensata per facilitare la risoluzione dei problemi.\n\nNel dubbio, lascia l'opzione attiva.", "AudioBackendTooltip": "Cambia il backend usato per riprodurre l'audio.\n\nSDL2 è quello preferito, mentre OpenAL e SoundIO sono usati come ripiego. Dummy non riprodurrà alcun suono.\n\nNel dubbio, imposta l'opzione su SDL2.", "MemoryManagerTooltip": "Cambia il modo in cui la memoria guest è mappata e vi si accede. Influisce notevolmente sulle prestazioni della CPU emulata.\n\nNel dubbio, imposta l'opzione su Host Unchecked.", diff --git a/src/Kenjinx/Assets/Locales/ja_JP.json b/src/Kenjinx/Assets/Locales/ja_JP.json index 32b1e0c6e..fefe2f0c8 100644 --- a/src/Kenjinx/Assets/Locales/ja_JP.json +++ b/src/Kenjinx/Assets/Locales/ja_JP.json @@ -147,6 +147,7 @@ "SettingsTabSystemEnableVsync": "VSync", "SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "ファイルシステム整合性チェック", "SettingsTabSystemAudioBackend": "音声バックエンド:", "SettingsTabSystemAudioBackendDummy": "ダミー", @@ -589,7 +590,8 @@ "TimeTooltip": "システムの時刻を変更します", "VSyncToggleTooltip": "エミュレートされたゲーム機の垂直同期です. 多くのゲームにおいて, フレームリミッタとして機能します. 無効にすると, ゲームが高速で実行されたり, ロード中に時間がかかったり, 止まったりすることがあります.\n\n設定したホットキー(デフォルトではF1)で, ゲーム内で切り替え可能です. 無効にする場合は, この操作を行うことをおすすめします.\n\nよくわからない場合はオンのままにしてください.", "PptcToggleTooltip": "翻訳されたJIT関数をセーブすることで, ゲームをロードするたびに毎回翻訳する処理を不要とします.\n\n一度ゲームを起動すれば,二度目以降の起動時遅延を大きく軽減できます.\n\nよくわからない場合はオンのままにしてください.", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "コア数の 3 分の 1 を使用して PPTC をロードします。", + "JitCacheEvictionToggleTooltip": "JIT キャッシュのエビクションを有効にしてメモリを効率的に管理する", "FsIntegrityToggleTooltip": "ゲーム起動時にファイル破損をチェックし,破損が検出されたらログにハッシュエラーを表示します..\n\nパフォーマンスには影響なく, トラブルシューティングに役立ちます.\n\nよくわからない場合はオンのままにしてください.", "AudioBackendTooltip": "音声レンダリングに使用するバックエンドを変更します.\n\nSDL2 が優先され, OpenAL と SoundIO はフォールバックとして使用されます. ダミーは音声出力しません.\n\nよくわからない場合は SDL2 を設定してください.", "MemoryManagerTooltip": "ゲストメモリのマップ/アクセス方式を変更します. エミュレートされるCPUのパフォーマンスに大きな影響を与えます.\n\nよくわからない場合は「ホスト,チェックなし」を設定してください.", diff --git a/src/Kenjinx/Assets/Locales/ko_KR.json b/src/Kenjinx/Assets/Locales/ko_KR.json index 19b5a6fa2..4c8e64b67 100644 --- a/src/Kenjinx/Assets/Locales/ko_KR.json +++ b/src/Kenjinx/Assets/Locales/ko_KR.json @@ -150,6 +150,7 @@ "SettingsTabSystemEnableVsync": "수직 동기화", "SettingsTabSystemEnablePptc": "PPTC(프로파일된 영구 번역 캐시)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "파일 시스템 무결성 검사", "SettingsTabSystemAudioBackend": "음향 후단부 :", "SettingsTabSystemAudioBackendDummy": "더미", @@ -593,7 +594,8 @@ "MatchTimeTooltip": "시스템 시간을 PC의 현재 날짜 및 시간과 일치하도록 다시 동기화합니다.", "VSyncToggleTooltip": "에뮬레이트된 콘솔의 수직 동기화. 기본적으로 대부분의 게임에 대한 프레임 제한 장치로, 비활성화시 게임이 더 빠른 속도로 실행되거나 로딩 화면이 더 오래 걸리거나 멈출 수 있습니다.\n\n게임 내에서 선호하는 핫키로 전환할 수 있습니다(기본값 F1). 핫키를 비활성화할 계획이라면 이 작업을 수행하는 것이 좋습니다.\n\n이 옵션에 대해 잘 모른다면 켜기를 권장드립니다.", "PptcToggleTooltip": "게임이 불러올 때마다 번역할 필요가 없도록 번역된 JIT 기능을 저장합니다.\n\n게임을 처음 부팅한 후 끊김 현상을 줄이고 부팅 시간을 크게 단축합니다.\n\n확실하지 않으면 켜 두세요.", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "코어 양의 1/3을 사용하여 PPTC를 로드합니다.", + "JitCacheEvictionToggleTooltip": "메모리를 효율적으로 관리하기 위해 JIT 캐시 제거를 활성화합니다.", "FsIntegrityToggleTooltip": "게임을 부팅할 때 손상된 파일을 확인하고 손상된 파일이 감지되면 로그에 해시 오류를 표시합니다.\n\n성능에 영향을 미치지 않으며 문제 해결에 도움이 됩니다.\n\n확실하지 않으면 켜 두세요.", "AudioBackendTooltip": "오디오를 렌더링하는 데 사용되는 백엔드를 변경합니다.\n\nSDL2가 선호되는 반면 OpenAL 및 사운드IO는 폴백으로 사용됩니다. 더미는 소리가 나지 않습니다.\n\n확실하지 않으면 SDL2로 설정하세요.", "MemoryManagerTooltip": "게스트 메모리가 매핑되고 접속되는 방식을 변경합니다. 에뮬레이트된 CPU 성능에 크게 영향을 미칩니다.\n\n확실하지 않은 경우 호스트 확인 안함으로 설정하세요.", diff --git a/src/Kenjinx/Assets/Locales/pl_PL.json b/src/Kenjinx/Assets/Locales/pl_PL.json index d2d13490e..eb64cea23 100644 --- a/src/Kenjinx/Assets/Locales/pl_PL.json +++ b/src/Kenjinx/Assets/Locales/pl_PL.json @@ -147,6 +147,7 @@ "SettingsTabSystemEnableVsync": "Synchronizacja pionowa", "SettingsTabSystemEnablePptc": "PPTC (Profilowana pamięć podręczna trwałych łłumaczeń)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "Sprawdzanie integralności systemu plików", "SettingsTabSystemAudioBackend": "Backend Dżwięku:", "SettingsTabSystemAudioBackendDummy": "Atrapa", @@ -589,7 +590,8 @@ "TimeTooltip": "Zmień czas systemowy", "VSyncToggleTooltip": "Synchronizacja pionowa emulowanej konsoli. Zasadniczo ogranicznik klatek dla większości gier; wyłączenie jej może spowodować, że gry będą działać z większą szybkością, ekrany wczytywania wydłużą się lub nawet utkną.\n\nMoże być przełączana w grze za pomocą preferowanego skrótu klawiszowego. Zalecamy to zrobić, jeśli planujesz ją wyłączyć.\n\nW razie wątpliwości pozostaw WŁĄCZONĄ.", "PptcToggleTooltip": "Zapisuje przetłumaczone funkcje JIT, dzięki czemu nie muszą być tłumaczone za każdym razem, gdy gra się ładuje.\n\nZmniejsza zacinanie się i znacznie przyspiesza uruchamianie po pierwszym uruchomieniu gry.\n\nJeśli nie masz pewności, pozostaw WŁĄCZONE", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "Załaduj PPTC, używając jednej trzeciej liczby rdzeni", + "JitCacheEvictionToggleTooltip": "Włącz eksmisję pamięci podręcznej JIT, aby efektywnie zarządzać pamięcią", "FsIntegrityToggleTooltip": "Sprawdza pliki podczas uruchamiania gry i jeśli zostaną wykryte uszkodzone pliki, wyświetla w dzienniku błąd hash.\n\nNie ma wpływu na wydajność i ma pomóc w rozwiązywaniu problemów.\n\nPozostaw WŁĄCZONE, jeśli nie masz pewności.", "AudioBackendTooltip": "Zmienia backend używany do renderowania dźwięku.\n\nSDL2 jest preferowany, podczas gdy OpenAL i SoundIO są używane jako rezerwy. Dummy nie będzie odtwarzać dźwięku.\n\nW razie wątpliwości ustaw SDL2.", "MemoryManagerTooltip": "Zmień sposób mapowania i uzyskiwania dostępu do pamięci gości. Znacznie wpływa na wydajność emulowanego procesora.\n\nUstaw na HOST UNCHECKED, jeśli nie masz pewności.", diff --git a/src/Kenjinx/Assets/Locales/pt_BR.json b/src/Kenjinx/Assets/Locales/pt_BR.json index 2d4ddbcff..c2113899f 100644 --- a/src/Kenjinx/Assets/Locales/pt_BR.json +++ b/src/Kenjinx/Assets/Locales/pt_BR.json @@ -150,6 +150,7 @@ "SettingsTabSystemEnableVsync": "Habilitar sincronia vertical", "SettingsTabSystemEnablePptc": "Habilitar PPTC (Profiled Persistent Translation Cache)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "Habilitar verificação de integridade do sistema de arquivos", "SettingsTabSystemAudioBackend": "Biblioteca de saída de áudio:", "SettingsTabSystemAudioBackendDummy": "Nenhuma", @@ -592,7 +593,8 @@ "TimeTooltip": "Mudar a hora do sistema", "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", "PptcToggleTooltip": "Habilita ou desabilita PPTC", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "Carregue o PPTC usando um terço da quantidade de núcleos", + "JitCacheEvictionToggleTooltip": "Habilite a remoção do JIT Cache para gerenciar a memória com eficiência", "FsIntegrityToggleTooltip": "Habilita ou desabilita verificação de integridade dos arquivos do jogo", "AudioBackendTooltip": "Mudar biblioteca de áudio", "MemoryManagerTooltip": "Muda como a memória do sistema convidado é acessada. Tem um grande impacto na performance da CPU emulada.", diff --git a/src/Kenjinx/Assets/Locales/ru_RU.json b/src/Kenjinx/Assets/Locales/ru_RU.json index d79f77232..ebb90d241 100644 --- a/src/Kenjinx/Assets/Locales/ru_RU.json +++ b/src/Kenjinx/Assets/Locales/ru_RU.json @@ -168,6 +168,7 @@ "SettingsTabSystemCustomVSyncIntervalValue": "Значение пользовательской частоты обновления:", "SettingsTabSystemEnablePptc": "Использовать PPTC (Profiled Persistent Translation Cache)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "Проверка целостности файловой системы", "SettingsTabSystemAudioBackend": "Аудио бэкенд:", "SettingsTabSystemAudioBackendDummy": "Без звука", @@ -621,6 +622,7 @@ "VSyncToggleTooltip": "Эмуляция вертикальной синхронизации консоли, которая ограничивает количество кадров в секунду в большинстве игр; отключение может привести к тому, что игры будут запущены с более высокой частотой кадров, но загрузка игры может занять больше времени, либо игра не запустится вообще.\n\nМожно включать и выключать эту настройку непосредственно в игре с помощью горячих клавиш (F1 по умолчанию). Если планируете отключить вертикальную синхронизацию, рекомендуем настроить горячие клавиши.\n\nРекомендуется оставить включенным.", "PptcToggleTooltip": "Сохраняет скомпилированные JIT-функции для того, чтобы не преобразовывать их по новой каждый раз при запуске игры.\n\nУменьшает статтеры и значительно ускоряет последующую загрузку игр.\n\nРекомендуется оставить включенным.", "LowPowerPptcToggleTooltip": "Загружает PPTC, используя треть от количества ядер.", + "JitCacheEvictionToggleTooltip": "Включите вытеснение JIT-кэша для эффективного управления памятью.", "FsIntegrityToggleTooltip": "Проверяет файлы при загрузке игры и если обнаружены поврежденные файлы, выводит сообщение о поврежденном хэше в журнале.\n\nНе влияет на производительность и необходим для помощи в устранении неполадок.\n\nРекомендуется оставить включенным.", "AudioBackendTooltip": "Изменяет используемый аудио бэкенд для рендера звука.\n\nSDL2 является предпочтительным вариантом, в то время как OpenAL и SoundIO используются в качестве резервных.\n\nРекомендуется использование SDL2.", "MemoryManagerTooltip": "Меняет разметку и доступ к гостевой памяти. Значительно влияет на производительность процессора.\n\nРекомендуется оставить \"Хост не установлен\"", diff --git a/src/Kenjinx/Assets/Locales/th_TH.json b/src/Kenjinx/Assets/Locales/th_TH.json index 17de0c557..610049b7b 100644 --- a/src/Kenjinx/Assets/Locales/th_TH.json +++ b/src/Kenjinx/Assets/Locales/th_TH.json @@ -147,6 +147,7 @@ "SettingsTabSystemEnableVsync": "VSync", "SettingsTabSystemEnablePptc": "PPTC (แคชโปรไฟล์การแปลแบบถาวร)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "ตรวจสอบความถูกต้องของ FS", "SettingsTabSystemAudioBackend": "ระบบเสียงเบื้องหลัง:", "SettingsTabSystemAudioBackendDummy": "Dummy", @@ -589,7 +590,8 @@ "TimeTooltip": "เปลี่ยนเวลาของระบบ", "VSyncToggleTooltip": "Vertical Sync ของคอนโซลจำลอง โดยพื้นฐานแล้วเป็นตัวจำกัดเฟรมสำหรับเกมส่วนใหญ่ การปิดใช้งานอาจทำให้เกมทำงานด้วยความเร็วสูงขึ้น หรือทำให้หน้าจอการโหลดใช้เวลานานขึ้นหรือค้าง\n\nสามารถสลับได้ในเกมด้วยปุ่มลัดตามที่คุณต้องการ (F1 เป็นค่าเริ่มต้น) เราขอแนะนำให้ทำเช่นนี้หากคุณวางแผนที่จะปิดการใช้งาน\n\nหากคุณไม่แน่ใจให้ปล่อยไว้อย่างนั้น", "PptcToggleTooltip": "บันทึกฟังก์ชั่น JIT ที่แปลแล้ว ดังนั้นจึงไม่จำเป็นต้องแปลทุกครั้งที่โหลดเกม\n\nลดอาการกระตุกและเร่งความเร็วการบูตได้อย่างมากหลังจากการบูตครั้งแรกของเกม\n\nปล่อยไว้หากคุณไม่แน่ใจ", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "โหลด PPTC โดยใช้หนึ่งในสามของจำนวนคอร์", + "JitCacheEvictionToggleTooltip": "เปิดใช้งานการขับไล่ JIT Cache เพื่อจัดการหน่วยความจำอย่างมีประสิทธิภาพ", "FsIntegrityToggleTooltip": "ตรวจสอบไฟล์ที่เสียหายเมื่อบูตเกม และหากตรวจพบไฟล์ที่เสียหาย จะแสดงข้อผิดพลาดของแฮชในบันทึก\n\nไม่มีผลกระทบต่อประสิทธิภาพการทำงานและมีไว้เพื่อช่วยในการแก้ไขปัญหา\n\nปล่อยไว้หากคุณไม่แน่ใจ", "AudioBackendTooltip": "เปลี่ยนแบ็กเอนด์ที่ใช้ในการเรนเดอร์เสียง\n\nSDL2 เป็นที่ต้องการ ในขณะที่ OpenAL และ SoundIO ถูกใช้เป็นทางเลือกสำรอง ดัมมี่จะไม่มีเสียง\n\nปล่อยไว้หากคุณไม่แน่ใจ", "MemoryManagerTooltip": "เปลี่ยนวิธีการแมปและเข้าถึงหน่วยความจำของผู้เยี่ยมชม ส่งผลอย่างมากต่อประสิทธิภาพการทำงานของ CPU ที่จำลอง\n\nตั้งค่าเป็น ไม่ทำการตรวจสอบ โฮสต์ หากคุณไม่แน่ใจ", diff --git a/src/Kenjinx/Assets/Locales/tr_TR.json b/src/Kenjinx/Assets/Locales/tr_TR.json index df8b6ed95..784b1c0af 100644 --- a/src/Kenjinx/Assets/Locales/tr_TR.json +++ b/src/Kenjinx/Assets/Locales/tr_TR.json @@ -147,6 +147,7 @@ "SettingsTabSystemEnableVsync": "Dikey Eşitleme", "SettingsTabSystemEnablePptc": "PPTC (Profilli Sürekli Çeviri Önbelleği)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "FS Bütünlük Kontrolleri", "SettingsTabSystemAudioBackend": "Ses Motoru:", "SettingsTabSystemAudioBackendDummy": "Yapay", @@ -589,7 +590,8 @@ "TimeTooltip": "Sistem Saatini Değiştir", "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", "PptcToggleTooltip": "Çevrilen JIT fonksiyonlarını oyun her açıldığında çevrilmek zorunda kalmaması için kaydeder.\n\nTeklemeyi azaltır ve ilk açılıştan sonra oyunların ilk açılış süresini ciddi biçimde hızlandırır.\n\nEmin değilseniz aktif halde bırakın.", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "Çekirdek miktarının üçte birini kullanarak PPTC'yi yükleyin", + "JitCacheEvictionToggleTooltip": "Belleği verimli bir şekilde yönetmek için JIT Önbellek tahliyesini etkinleştirin", "FsIntegrityToggleTooltip": "Oyun açarken hatalı dosyaların olup olmadığını kontrol eder, ve hatalı dosya bulursa log dosyasında hash hatası görüntüler.\n\nPerformansa herhangi bir etkisi yoktur ve sorun gidermeye yardımcı olur.\n\nEmin değilseniz aktif halde bırakın.", "AudioBackendTooltip": "Ses çıkış motorunu değiştirir.\n\nSDL2 tercih edilen seçenektir, OpenAL ve SoundIO ise alternatif olarak kullanılabilir. Dummy seçeneğinde ses çıkışı olmayacaktır.\n\nEmin değilseniz SDL2 seçeneğine ayarlayın.", "MemoryManagerTooltip": "Guest hafızasının nasıl tahsis edilip erişildiğini değiştirir. Emüle edilen CPU performansını ciddi biçimde etkiler.\n\nEmin değilseniz HOST UNCHECKED seçeneğine ayarlayın.", diff --git a/src/Kenjinx/Assets/Locales/uk_UA.json b/src/Kenjinx/Assets/Locales/uk_UA.json index 87e5d1009..ef3be2ad4 100644 --- a/src/Kenjinx/Assets/Locales/uk_UA.json +++ b/src/Kenjinx/Assets/Locales/uk_UA.json @@ -150,6 +150,7 @@ "SettingsTabSystemEnableVsync": "Вертикальна синхронізація", "SettingsTabSystemEnablePptc": "PPTC (профільований постійний кеш перекладу)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "Перевірка цілісності FS", "SettingsTabSystemAudioBackend": "Аудіосистема:", "SettingsTabSystemAudioBackendDummy": "Dummy", @@ -593,7 +594,8 @@ "MatchTimeTooltip": "Синхронізувати системний час, щоб він відповідав поточній даті та часу вашого ПК.", "VSyncToggleTooltip": "Емульована вертикальна синхронізація консолі. По суті, обмежувач кадрів для більшості ігор; його вимкнення може призвести до того, що ігри працюватимуть на вищій швидкості, екрани завантаження триватимуть довше чи зупинятимуться.\n\nМожна перемикати в грі гарячою клавішею (За умовчанням F1). Якщо ви плануєте вимкнути функцію, рекомендуємо зробити це через гарячу клавішу.\n\nЗалиште увімкненим, якщо не впевнені.", "PptcToggleTooltip": "Зберігає перекладені функції JIT, щоб їх не потрібно було перекладати кожного разу, коли гра завантажується.\n\nЗменшує заїкання та значно прискорює час завантаження після першого завантаження гри.\n\nЗалиште увімкненим, якщо не впевнені.", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "Завантажте PPTC, використовуючи третину кількості ядер", + "JitCacheEvictionToggleTooltip": "Увімкніть видалення кешу JIT для ефективного керування пам’яттю", "FsIntegrityToggleTooltip": "Перевіряє наявність пошкоджених файлів під час завантаження гри, і якщо виявлено пошкоджені файли, показує помилку хешу в журналі.\n\nНе впливає на продуктивність і призначений для усунення несправностей.\n\nЗалиште увімкненим, якщо не впевнені.", "AudioBackendTooltip": "Змінює серверну частину, яка використовується для відтворення аудіо.\n\nSDL2 є кращим, тоді як OpenAL і SoundIO використовуються як резервні варіанти. Dummy не матиме звуку.\n\nВстановіть SDL2, якщо не впевнені.", "MemoryManagerTooltip": "Змінює спосіб відображення та доступу до гостьової пам’яті. Значно впливає на продуктивність емульованого ЦП.\n\nВстановіть «Неперевірений хост», якщо не впевнені.", diff --git a/src/Kenjinx/Assets/Locales/zh_CN.json b/src/Kenjinx/Assets/Locales/zh_CN.json index 89150368a..5ac60d154 100644 --- a/src/Kenjinx/Assets/Locales/zh_CN.json +++ b/src/Kenjinx/Assets/Locales/zh_CN.json @@ -150,6 +150,7 @@ "SettingsTabSystemEnableVsync": "启用垂直同步", "SettingsTabSystemEnablePptc": "开启 PPTC 缓存", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "启用文件系统完整性检查", "SettingsTabSystemAudioBackend": "音频处理引擎:", "SettingsTabSystemAudioBackendDummy": "无", @@ -593,7 +594,8 @@ "MatchTimeTooltip": "重新同步系统时间以匹配您电脑的当前日期和时间。", "VSyncToggleTooltip": "模拟控制台的垂直同步,开启后会降低大部分游戏的帧率。关闭后,可以获得更高的帧率,但也可能导致游戏画面加载耗时更长或卡住。\n\n在游戏中可以使用热键进行切换(默认为 F1 键)。\n\n如果不确定,请保持开启状态。", "PptcToggleTooltip": "缓存已编译的游戏指令,这样每次游戏加载时就无需重新编译。\n\n可以减少卡顿和启动时间,提高游戏响应速度。\n\n如果不确定,请保持开启状态。", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "使用三分之一的核心数量加载 PPTC", + "JitCacheEvictionToggleTooltip": "启用 JIT 缓存驱逐以有效管理内存", "FsIntegrityToggleTooltip": "启动游戏时检查游戏文件的完整性,并在日志中记录损坏的文件。\n\n对性能没有影响,用于排查故障。\n\n如果不确定,请保持开启状态。", "AudioBackendTooltip": "更改音频处理引擎。\n\n推荐选择“SDL2”,另外“OpenAL”和“SoundIO”可以作为备选,选择“无”将没有声音。\n\n如果不确定,请设置为“SDL2”。", "MemoryManagerTooltip": "更改模拟器内存映射和访问的方式,对模拟器 CPU 的性能影响很大。\n\n如果不确定,请设置为“跳过检查的本机映射”。", diff --git a/src/Kenjinx/Assets/Locales/zh_TW.json b/src/Kenjinx/Assets/Locales/zh_TW.json index c33c26a35..c5c3563f3 100644 --- a/src/Kenjinx/Assets/Locales/zh_TW.json +++ b/src/Kenjinx/Assets/Locales/zh_TW.json @@ -150,6 +150,7 @@ "SettingsTabSystemEnableVsync": "垂直同步", "SettingsTabSystemEnablePptc": "PPTC (剖析式持久轉譯快取, Profiled Persistent Translation Cache)", "SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC", + "SettingsTabSystemEnableJitCacheEviction": "Jit Cache Eviction", "SettingsTabSystemEnableFsIntegrityChecks": "檔案系統完整性檢查", "SettingsTabSystemAudioBackend": "音效後端:", "SettingsTabSystemAudioBackendDummy": "虛設 (Dummy)", @@ -593,7 +594,8 @@ "MatchTimeTooltip": "重新同步系統韌體時間至 PC 目前的日期和時間。", "VSyncToggleTooltip": "模擬遊戲機的垂直同步。對大多數遊戲來說,它本質上是一個幀率限制器;停用它可能會導致遊戲以更高的速度執行,或使載入畫面耗時更長或卡住。\n\n可以在遊戲中使用快速鍵進行切換 (預設為 F1)。如果您打算停用,我們建議您這樣做。\n\n如果不確定,請保持開啟狀態。", "PptcToggleTooltip": "儲存已轉譯的 JIT 函數,這樣每次載入遊戲時就無需再轉譯這些函數。\n\n減少遊戲首次啟動後的卡頓現象,並大大加快啟動時間。\n\n如果不確定,請保持開啟狀態。", - "LowPowerPptcToggleTooltip": "Load the PPTC using a third of the amount of cores.", + "LowPowerPptcToggleTooltip": "使用三分之一的核心數量加載 PPTC", + "JitCacheEvictionToggleTooltip": "啟用 JIT 快取驅逐以有效管理內存", "FsIntegrityToggleTooltip": "在啟動遊戲時檢查損壞的檔案,如果檢測到損壞的檔案,則在日誌中顯示雜湊值錯誤。\n\n對效能沒有影響,旨在幫助排除故障。\n\n如果不確定,請保持開啟狀態。", "AudioBackendTooltip": "變更用於繪製音訊的後端。\n\nSDL2 是首選,而 OpenAL 和 SoundIO 則作為備用。虛設 (Dummy) 將沒有聲音。\n\n如果不確定,請設定為 SDL2。", "MemoryManagerTooltip": "變更客體記憶體的映射和存取方式。這會極大地影響模擬 CPU 效能。\n\n如果不確定,請設定為主體略過檢查模式。", diff --git a/src/Kenjinx/Kenjinx.csproj b/src/Kenjinx/Kenjinx.csproj index 6cedb7166..a11fbab99 100644 --- a/src/Kenjinx/Kenjinx.csproj +++ b/src/Kenjinx/Kenjinx.csproj @@ -14,12 +14,19 @@ false + + win-x64 + osx-x64 + linux-x64 + + true + true false true partial diff --git a/src/Kenjinx/UI/ViewModels/MainWindowViewModel.cs b/src/Kenjinx/UI/ViewModels/MainWindowViewModel.cs index 96ff2156d..9f22bbd6d 100644 --- a/src/Kenjinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Kenjinx/UI/ViewModels/MainWindowViewModel.cs @@ -1223,15 +1223,15 @@ namespace Ryujinx.Ava.UI.ViewModels { try { - // Creazione di una copia completa di Applications List appsCopy; + lock (_applicationsLock) { appsCopy = new List(Applications); } - // Filtraggio e ordinamento in una lista temporanea var tempApplications = new List(); + foreach (var app in appsCopy) { if (Filter(app)) @@ -1240,10 +1240,8 @@ namespace Ryujinx.Ava.UI.ViewModels } } - // Ordinamento alfabetico tempApplications.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); - // Creazione delle directory di salvataggio foreach (var application in tempApplications) { try @@ -1252,19 +1250,16 @@ namespace Ryujinx.Ava.UI.ViewModels } catch (Exception ex) { - // Log dell'errore se necessario Logger.Error?.Print(LogClass.Application, $"Failed to create save directory for {application.Name}: {ex.Message}"); } } - // Ricreazione dell'observable collection in modo thread-safe Dispatcher.UIThread.InvokeAsync(() => { try { lock (_applicationsLock) { - // Ricrea la collezione osservabile in modo più sicuro var source = Applications.ToObservableChangeSet(); var filtered = source.Filter(Filter); var sorted = filtered.Sort(GetComparer()); diff --git a/src/Kenjinx/UI/ViewModels/SettingsViewModel.cs b/src/Kenjinx/UI/ViewModels/SettingsViewModel.cs index 2b6e0072f..ccf2b56c7 100644 --- a/src/Kenjinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Kenjinx/UI/ViewModels/SettingsViewModel.cs @@ -227,6 +227,8 @@ namespace Ryujinx.Ava.UI.ViewModels } public bool EnablePptc { get; set; } public bool EnableLowPowerPptc { get; set; } + + public bool EnableJitCacheEviction { get; set; } public bool EnableInternetAccess { get; set; } public bool EnableFsIntegrityChecks { get; set; } public bool IgnoreMissingServices { get; set; } @@ -571,6 +573,7 @@ namespace Ryujinx.Ava.UI.ViewModels // CPU EnablePptc = config.System.EnablePtc; EnableLowPowerPptc = config.System.EnableLowPowerPtc; + EnableJitCacheEviction = config.System.EnableJitCacheEviction; MemoryMode = (int)config.System.MemoryManagerMode.Value; UseHypervisor = config.System.UseHypervisor; @@ -679,6 +682,7 @@ namespace Ryujinx.Ava.UI.ViewModels // CPU config.System.EnablePtc.Value = EnablePptc; config.System.EnableLowPowerPtc.Value = EnableLowPowerPptc; + config.System.EnableJitCacheEviction.Value = EnableJitCacheEviction; config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode; config.System.UseHypervisor.Value = UseHypervisor; diff --git a/src/Kenjinx/UI/Views/Settings/SettingsCPUView.axaml b/src/Kenjinx/UI/Views/Settings/SettingsCPUView.axaml index 48a50c9cf..0632da896 100644 --- a/src/Kenjinx/UI/Views/Settings/SettingsCPUView.axaml +++ b/src/Kenjinx/UI/Views/Settings/SettingsCPUView.axaml @@ -36,6 +36,10 @@ + + + diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/KenjinxNative.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/KenjinxNative.kt index 526489f83..05736bda9 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/KenjinxNative.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/KenjinxNative.kt @@ -16,6 +16,8 @@ interface KenjinxNativeJna : Library { vSyncMode: Int, enableDockedMode: Boolean, enablePptc: Boolean, + enableLowPowerPptc: Boolean, + enableJitCacheEviction: Boolean, enableInternetAccess: Boolean, enableFsIntegrityChecks: Boolean, fsGlobalAccessLogMode: Int, diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt index 9545b7a98..9450960ca 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/MainActivity.kt @@ -248,4 +248,24 @@ class MainActivity : BaseActivity() { } } } + + fun shutdownAndRestart() { + // Create an intent to restart the app + val packageManager = packageManager + val intent = packageManager.getLaunchIntentForPackage(packageName) + val componentName = intent?.component + val restartIntent = Intent.makeRestartActivityTask(componentName) + + // Clean up resources if needed + mainViewModel?.let { + // Perform any critical cleanup + it.performanceManager?.setTurboMode(false) + } + + // Start the new activity directly + startActivity(restartIntent) + + // Force immediate process termination + Runtime.getRuntime().exit(0) + } } diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/GameModel.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/GameModel.kt index f59b69490..9683c801e 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/GameModel.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/GameModel.kt @@ -50,6 +50,10 @@ class GameModel(var file: DocumentFile, val context: Context) { } } + fun isUnknown() : Boolean { + return (titleName == "Unknown") + } + fun open(): Int { descriptor = context.contentResolver.openFileDescriptor(file.uri, "rw") diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/HomeViewModel.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/HomeViewModel.kt index 92e9073a7..25efe8412 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/HomeViewModel.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/HomeViewModel.kt @@ -107,6 +107,11 @@ class HomeViewModel( for(game in loadedCache) { game.getGameInfo() + + if(game.isUnknown()) + { + loadedCache.remove(game); + } } } finally { isLoading.value = false diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt index 3640ae1c3..5647c516d 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/MainViewModel.kt @@ -179,6 +179,8 @@ class MainViewModel(val activity: MainActivity) { settings.vSyncMode.ordinal, settings.enableDocked, settings.enablePptc, + settings.enableLowPowerPptc, + settings.enableJitCacheEviction, false, settings.enableFsIntegrityChecks, settings.fsGlobalAccessLogMode, @@ -285,6 +287,8 @@ class MainViewModel(val activity: MainActivity) { settings.vSyncMode.ordinal, settings.enableDocked, settings.enablePptc, + settings.enableLowPowerPptc, + settings.enableJitCacheEviction, false, settings.enableFsIntegrityChecks, settings.fsGlobalAccessLogMode, diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/QuickSettings.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/QuickSettings.kt index 9433134d0..e052d6449 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/QuickSettings.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/QuickSettings.kt @@ -7,6 +7,8 @@ import androidx.preference.PreferenceManager class QuickSettings(val activity: Activity) { var ignoreMissingServices: Boolean var enablePptc: Boolean + var enableLowPowerPptc: Boolean + var enableJitCacheEviction: Boolean var enableFsIntegrityChecks: Boolean var fsGlobalAccessLogMode: Int var enableDocked: Boolean @@ -39,11 +41,13 @@ class QuickSettings(val activity: Activity) { init { memoryManagerMode = MemoryManagerMode.values()[sharedPref.getInt("memoryManagerMode", MemoryManagerMode.HostMappedUnsafe.ordinal)] - useNce = sharedPref.getBoolean("useNce", true) + useNce = sharedPref.getBoolean("useNce", false) memoryConfiguration = MemoryConfiguration.values()[sharedPref.getInt("memoryConfiguration", MemoryConfiguration.MemoryConfiguration4GiB.ordinal)] vSyncMode = VSyncMode.values()[sharedPref.getInt("vSyncMode", VSyncMode.Switch.ordinal)] enableDocked = sharedPref.getBoolean("enableDocked", true) enablePptc = sharedPref.getBoolean("enablePptc", true) + enableLowPowerPptc = sharedPref.getBoolean("enableLowPowerPptc", false) + enableJitCacheEviction = sharedPref.getBoolean("enableJitCacheEviction", true) enableFsIntegrityChecks = sharedPref.getBoolean("enableFsIntegrityChecks", false) fsGlobalAccessLogMode = sharedPref.getInt("fsGlobalAccessLogMode", 0) ignoreMissingServices = sharedPref.getBoolean("ignoreMissingServices", false) @@ -78,6 +82,8 @@ class QuickSettings(val activity: Activity) { editor.putInt("vSyncMode", vSyncMode.ordinal) editor.putBoolean("enableDocked", enableDocked) editor.putBoolean("enablePptc", enablePptc) + editor.putBoolean("enableLowPowerPptc", enableLowPowerPptc) + editor.putBoolean("enableJitCacheEviction", enableJitCacheEviction) editor.putBoolean("enableFsIntegrityChecks", enableFsIntegrityChecks) editor.putInt("fsGlobalAccessLogMode", fsGlobalAccessLogMode) editor.putBoolean("ignoreMissingServices", ignoreMissingServices) diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/SettingsViewModel.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/SettingsViewModel.kt index c6f0effc4..8deafe73b 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/SettingsViewModel.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/viewmodels/SettingsViewModel.kt @@ -31,14 +31,6 @@ class SettingsViewModel(val activity: MainActivity) { sharedPref = getPreferences() previousFolderCallback = activity.storageHelper!!.onFolderSelected previousFileCallback = activity.storageHelper!!.onFileSelected - activity.storageHelper!!.onFolderSelected = { _, folder -> - run { - val p = folder.getAbsolutePath(activity) - val editor = sharedPref.edit() - editor?.putString("gameFolder", p) - editor?.apply() - } - } } private fun getPreferences(): SharedPreferences { @@ -52,6 +44,8 @@ class SettingsViewModel(val activity: MainActivity) { vSyncMode: MutableState, enableDocked: MutableState, enablePptc: MutableState, + enableLowPowerPptc: MutableState, + enableJitCacheEviction: MutableState, enableFsIntegrityChecks: MutableState, fsGlobalAccessLogMode: MutableState, ignoreMissingServices: MutableState, @@ -77,11 +71,13 @@ class SettingsViewModel(val activity: MainActivity) { enableGraphicsLogs: MutableState ) { memoryManagerMode.value = MemoryManagerMode.values()[sharedPref.getInt("memoryManagerMode", MemoryManagerMode.HostMappedUnsafe.ordinal)] - useNce.value = sharedPref.getBoolean("useNce", true) + useNce.value = sharedPref.getBoolean("useNce", false) memoryConfiguration.value = MemoryConfiguration.values()[sharedPref.getInt("memoryConfiguration", MemoryConfiguration.MemoryConfiguration4GiB.ordinal)] vSyncMode.value = VSyncMode.values()[sharedPref.getInt("vSyncMode", VSyncMode.Switch.ordinal)] enableDocked.value = sharedPref.getBoolean("enableDocked", true) enablePptc.value = sharedPref.getBoolean("enablePptc", true) + enableLowPowerPptc.value = sharedPref.getBoolean("enableLowPowerPptc", false) + enableJitCacheEviction.value = sharedPref.getBoolean("enableJitCacheEviction", false) enableFsIntegrityChecks.value = sharedPref.getBoolean("enableFsIntegrityChecks", false) fsGlobalAccessLogMode.value = sharedPref.getInt("fsGlobalAccessLogMode", 0) ignoreMissingServices.value = sharedPref.getBoolean("ignoreMissingServices", false) @@ -102,7 +98,7 @@ class SettingsViewModel(val activity: MainActivity) { enableErrorLogs.value = sharedPref.getBoolean("enableErrorLogs", true) enableGuestLogs.value = sharedPref.getBoolean("enableGuestLogs", true) enableFsAccessLogs.value = sharedPref.getBoolean("enableFsAccessLogs", false) - enableTraceLogs.value = sharedPref.getBoolean("enableStubLogs", false) + enableTraceLogs.value = sharedPref.getBoolean("enableTraceLogs", false) enableDebugLogs.value = sharedPref.getBoolean("enableDebugLogs", false) enableGraphicsLogs.value = sharedPref.getBoolean("enableGraphicsLogs", false) } @@ -114,6 +110,8 @@ class SettingsViewModel(val activity: MainActivity) { vSyncMode: MutableState, enableDocked: MutableState, enablePptc: MutableState, + enableLowPowerPptc: MutableState, + enableJitCacheEviction: MutableState, enableFsIntegrityChecks: MutableState, fsGlobalAccessLogMode: MutableState, ignoreMissingServices: MutableState, @@ -146,6 +144,8 @@ class SettingsViewModel(val activity: MainActivity) { editor.putInt("vSyncMode", vSyncMode.value.ordinal) editor.putBoolean("enableDocked", enableDocked.value) editor.putBoolean("enablePptc", enablePptc.value) + editor.putBoolean("enableLowPowerPptc", enableLowPowerPptc.value) + editor.putBoolean("enableJitCacheEviction", enableJitCacheEviction.value) editor.putBoolean("enableFsIntegrityChecks", enableFsIntegrityChecks.value) editor.putInt("fsGlobalAccessLogMode", fsGlobalAccessLogMode.value) editor.putBoolean("ignoreMissingServices", ignoreMissingServices.value) @@ -171,7 +171,6 @@ class SettingsViewModel(val activity: MainActivity) { editor.putBoolean("enableGraphicsLogs", enableGraphicsLogs.value) editor.apply() - activity.storageHelper!!.onFolderSelected = previousFolderCallback KenjinxNative.loggingSetEnabled(LogLevel.Info, enableInfoLogs.value) KenjinxNative.loggingSetEnabled(LogLevel.Stub, enableStubLogs.value) @@ -187,6 +186,15 @@ class SettingsViewModel(val activity: MainActivity) { fun openGameFolder() { val path = sharedPref.getString("gameFolder", "") ?: "" + activity.storageHelper!!.onFolderSelected = { _, folder -> + val p = folder.getAbsolutePath(activity) + val editor = sharedPref.edit() + editor.putString("gameFolder", p) + editor.apply() + activity.storageHelper!!.onFolderSelected = previousFolderCallback + activity.shutdownAndRestart() + } + if (path.isEmpty()) activity.storageHelper?.storage?.openFolderPicker() else @@ -197,22 +205,20 @@ class SettingsViewModel(val activity: MainActivity) { } fun selectKey(installState: MutableState) { - if (installState.value != KeyInstallState.None) + if (installState.value != KeyInstallState.File) return + activity.storageHelper!!.onFileSelected = { _, files -> - run { - activity.storageHelper!!.onFileSelected = previousFileCallback - val file = files.firstOrNull() - file?.apply { - if (name == "prod.keys") { - selectedKeyFile = file - installState.value = KeyInstallState.Query - } - else { - installState.value = KeyInstallState.Cancelled - } + val file = files.firstOrNull() + file?.apply { + if (name == "prod.keys") { + selectedKeyFile = file + installState.value = KeyInstallState.Query + } else { + installState.value = KeyInstallState.Cancelled } } + activity.storageHelper!!.onFileSelected = previousFileCallback } activity.storageHelper?.storage?.openFilePicker() } @@ -221,7 +227,7 @@ class SettingsViewModel(val activity: MainActivity) { if (installState.value != KeyInstallState.Query) return if (selectedKeyFile == null) { - installState.value = KeyInstallState.None + installState.value = KeyInstallState.File return } selectedKeyFile?.apply { @@ -247,37 +253,35 @@ class SettingsViewModel(val activity: MainActivity) { fun clearKeySelection(installState: MutableState) { selectedKeyFile = null - installState.value = KeyInstallState.None + installState.value = KeyInstallState.File } fun selectFirmware(installState: MutableState) { - if (installState.value != FirmwareInstallState.None) + if (installState.value != FirmwareInstallState.File) return + activity.storageHelper!!.onFileSelected = { _, files -> - run { - activity.storageHelper!!.onFileSelected = previousFileCallback - val file = files.firstOrNull() - file?.apply { - if (extension == "xci" || extension == "zip") { - installState.value = FirmwareInstallState.Verifying - thread { - Thread.sleep(1000) - val descriptor = activity.contentResolver.openFileDescriptor(file.uri, "rw") - descriptor?.use { d -> - selectedFirmwareVersion = KenjinxNative.deviceVerifyFirmware(d.fd, extension == "xci") - selectedFirmwareFile = file - if (!selectedFirmwareVersion.isEmpty()) { - installState.value = FirmwareInstallState.Query - } else { - installState.value = FirmwareInstallState.Cancelled - } + val file = files.firstOrNull() + file?.apply { + if (extension == "xci" || extension == "zip") { + installState.value = FirmwareInstallState.Verifying + thread { + val descriptor = activity.contentResolver.openFileDescriptor(file.uri, "rw") + descriptor?.use { d -> + selectedFirmwareVersion = KenjinxNative.deviceVerifyFirmware(d.fd, extension == "xci") + selectedFirmwareFile = file + if (!selectedFirmwareVersion.isEmpty()) { + installState.value = FirmwareInstallState.Query + } else { + installState.value = FirmwareInstallState.Cancelled } } - } else { - installState.value = FirmwareInstallState.Cancelled } + } else { + installState.value = FirmwareInstallState.Cancelled } } + activity.storageHelper!!.onFileSelected = previousFileCallback } activity.storageHelper?.storage?.openFilePicker() } @@ -286,7 +290,7 @@ class SettingsViewModel(val activity: MainActivity) { if (installState.value != FirmwareInstallState.Query) return if (selectedFirmwareFile == null) { - installState.value = FirmwareInstallState.None + installState.value = FirmwareInstallState.File return } selectedFirmwareFile?.apply { @@ -312,18 +316,18 @@ class SettingsViewModel(val activity: MainActivity) { fun clearFirmwareSelection(installState: MutableState) { selectedFirmwareFile = null selectedFirmwareVersion = "" - installState.value = FirmwareInstallState.None + installState.value = FirmwareInstallState.File } - fun importAppData( - file: DocumentFile, - dataImportState: MutableState + fun resetAppData( + dataResetState: MutableState ) { - dataImportState.value = DataImportState.Import - try { - MainActivity.StorageHelper?.apply { - val stream = file.openInputStream(storage.context) - stream?.apply { + dataResetState.value = DataResetState.Reset + thread { + Thread.sleep(1000) + + try { + MainActivity.StorageHelper?.apply { val folders = listOf("bis", "games", "profiles", "system") for (f in folders) { val dir = File(MainActivity.AppPath + "${File.separator}${f}") @@ -333,42 +337,73 @@ class SettingsViewModel(val activity: MainActivity) { dir.mkdirs() } - ZipInputStream(stream).use { zip -> - while (true) { - val header = zip.nextEntry ?: break - if (!folders.any { header.fileName.startsWith(it) }) { - continue - } - val filePath = - MainActivity.AppPath + File.separator + header.fileName + } + } finally { + dataResetState.value = DataResetState.Done + KenjinxNative.deviceReloadFilesystem() + MainActivity.mainViewModel?.refreshFirmwareVersion() + } + } + } - if (!header.isDirectory) { - val bos = BufferedOutputStream(FileOutputStream(filePath)) - val bytesIn = ByteArray(4096) - var read: Int = 0 - while (zip.read(bytesIn).also { read = it } > 0) { - bos.write(bytesIn, 0, read) + fun importAppData( + file: DocumentFile, + dataImportState: MutableState + ) { + dataImportState.value = DataImportState.Import + thread { + Thread.sleep(1000) + + try { + MainActivity.StorageHelper?.apply { + val stream = file.openInputStream(storage.context) + stream?.apply { + val folders = listOf("bis", "games", "profiles", "system") + for (f in folders) { + val dir = File(MainActivity.AppPath + "${File.separator}${f}") + if (dir.exists()) { + dir.deleteRecursively() + } + + dir.mkdirs() + } + ZipInputStream(stream).use { zip -> + while (true) { + val header = zip.nextEntry ?: break + if (!folders.any { header.fileName.startsWith(it) }) { + continue + } + val filePath = + MainActivity.AppPath + File.separator + header.fileName + + if (!header.isDirectory) { + val bos = BufferedOutputStream(FileOutputStream(filePath)) + val bytesIn = ByteArray(4096) + var read: Int = 0 + while (zip.read(bytesIn).also { read = it } > 0) { + bos.write(bytesIn, 0, read) + } + bos.close() + } else { + val dir = File(filePath) + dir.mkdir() } - bos.close() - } else { - val dir = File(filePath) - dir.mkdir() } } + stream.close() } - stream.close() } + } finally { + dataImportState.value = DataImportState.Done + KenjinxNative.deviceReloadFilesystem() + MainActivity.mainViewModel?.refreshFirmwareVersion() } - } finally { - dataImportState.value = DataImportState.Done - KenjinxNative.deviceReloadFilesystem() - MainActivity.mainViewModel?.refreshFirmwareVersion() } } } enum class KeyInstallState { - None, + File, Cancelled, Query, Install, @@ -376,7 +411,7 @@ enum class KeyInstallState { } enum class FirmwareInstallState { - None, + File, Cancelled, Verifying, Query, @@ -384,8 +419,14 @@ enum class FirmwareInstallState { Done } +enum class DataResetState { + Query, + Reset, + Done +} + enum class DataImportState { - None, + File, Query, Import, Done diff --git a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/SettingViews.kt b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/SettingViews.kt index 468f6329a..35c8e2c49 100644 --- a/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/SettingViews.kt +++ b/src/KenjinxAndroid/app/src/main/java/org/kenjinx/android/views/SettingViews.kt @@ -22,6 +22,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Build +import androidx.compose.material.icons.filled.Create import androidx.compose.material.icons.filled.FileDownload import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.MailOutline @@ -62,6 +63,7 @@ import kotlin.concurrent.thread import org.kenjinx.android.MainActivity import org.kenjinx.android.providers.DocumentProvider import org.kenjinx.android.viewmodels.DataImportState +import org.kenjinx.android.viewmodels.DataResetState import org.kenjinx.android.viewmodels.FirmwareInstallState import org.kenjinx.android.viewmodels.KeyInstallState import org.kenjinx.android.viewmodels.MainViewModel @@ -90,6 +92,8 @@ class SettingViews { val vSyncMode = remember { mutableStateOf(VSyncMode.Switch) } val enableDocked = remember { mutableStateOf(false) } val enablePptc = remember { mutableStateOf(false) } + val enableLowPowerPptc = remember { mutableStateOf(false) } + val enableJitCacheEviction = remember { mutableStateOf(false) } var enableFsIntegrityChecks = remember { mutableStateOf(false) } var fsGlobalAccessLogMode = remember { mutableStateOf(0) } val ignoreMissingServices = remember { mutableStateOf(false) } @@ -100,12 +104,14 @@ class SettingViews { val maxAnisotropy = remember { mutableStateOf(0f) } val useVirtualController = remember { mutableStateOf(true) } val showKeyDialog = remember { mutableStateOf(false) } - val keyInstallState = remember { mutableStateOf(KeyInstallState.None) } + val keyInstallState = remember { mutableStateOf(KeyInstallState.File) } val showFirwmareDialog = remember { mutableStateOf(false) } - val firmwareInstallState = remember { mutableStateOf(FirmwareInstallState.None) } + val firmwareInstallState = remember { mutableStateOf(FirmwareInstallState.File) } val firmwareVersion = remember { mutableStateOf(mainViewModel.firmwareVersion) } + val showDataResetDialog = remember { mutableStateOf(false) } val showDataImportDialog = remember { mutableStateOf(false) } - val dataImportState = remember { mutableStateOf(DataImportState.None) } + val dataResetState = remember { mutableStateOf(DataResetState.Query) } + val dataImportState = remember { mutableStateOf(DataImportState.File) } var dataFile = remember { mutableStateOf(null) } val isGrid = remember { mutableStateOf(true) } val useSwitchLayout = remember { mutableStateOf(true) } @@ -131,6 +137,8 @@ class SettingViews { vSyncMode, enableDocked, enablePptc, + enableLowPowerPptc, + enableJitCacheEviction, enableFsIntegrityChecks, fsGlobalAccessLogMode, ignoreMissingServices, @@ -172,6 +180,8 @@ class SettingViews { vSyncMode, enableDocked, enablePptc, + enableLowPowerPptc, + enableJitCacheEviction, enableFsIntegrityChecks, fsGlobalAccessLogMode, ignoreMissingServices, @@ -236,23 +246,6 @@ class SettingViews { modifier = Modifier.align(Alignment.CenterVertically) ) } - Row( - modifier = Modifier - .fillMaxWidth() - .height(56.dp) - .padding(horizontal = 8.dp, vertical = 8.dp), - horizontalArrangement = Arrangement.SpaceBetween, - ){ - ActionButton( - onClick = { - settingsViewModel.openGameFolder() - }, - text = "Add Game Folder", - icon = Icons.Default.Add, - modifier = Modifier.weight(1f), - isFullWidth = false, - ) - } Row( modifier = Modifier .fillMaxWidth() @@ -287,7 +280,40 @@ class SettingViews { isFullWidth = false, ) } - + Row( + modifier = Modifier + .fillMaxWidth() + .height(56.dp) + .padding(horizontal = 8.dp, vertical = 8.dp), + horizontalArrangement = Arrangement.SpaceBetween, + ){ + ActionButton( + onClick = { + settingsViewModel.openGameFolder() + }, + text = "Add Game Folder", + icon = Icons.Default.Add, + modifier = Modifier.weight(1f), + isFullWidth = false, + ) + } + Row( + modifier = Modifier + .fillMaxWidth() + .height(56.dp) + .padding(horizontal = 8.dp, vertical = 8.dp), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + ActionButton( + onClick = { + showDataResetDialog.value = true + }, + text = "Reinit App Data", + icon = Icons.Default.Create, + modifier = Modifier.weight(1f), + isFullWidth = false, + ) + } Row( modifier = Modifier .fillMaxWidth() @@ -359,6 +385,7 @@ class SettingViews { if (keyInstallState.value != KeyInstallState.Install) { showKeyDialog.value = false settingsViewModel.clearKeySelection(keyInstallState) + keyInstallState.value = KeyInstallState.File } } ) { @@ -378,7 +405,7 @@ class SettingViews { ) when (keyInstallState.value) { - KeyInstallState.None -> { + KeyInstallState.File -> { Text( text = "Select a key file to install key from.", modifier = Modifier @@ -437,7 +464,7 @@ class SettingViews { keyInstallState ) - if (keyInstallState.value == KeyInstallState.None) { + if (keyInstallState.value == KeyInstallState.File) { showKeyDialog.value = false settingsViewModel.clearKeySelection(keyInstallState) } @@ -561,6 +588,7 @@ class SettingViews { if (firmwareInstallState.value != FirmwareInstallState.Install) { showFirwmareDialog.value = false settingsViewModel.clearFirmwareSelection(firmwareInstallState) + firmwareInstallState.value = FirmwareInstallState.File } } ) { @@ -580,7 +608,7 @@ class SettingViews { ) when (firmwareInstallState.value) { - FirmwareInstallState.None -> { + FirmwareInstallState.File -> { Text( text = "Select a zip or xci file to install firmware from.", modifier = Modifier @@ -635,7 +663,7 @@ class SettingViews { onClick = { settingsViewModel.installFirmware(firmwareInstallState) - if (firmwareInstallState.value == FirmwareInstallState.None) { + if (firmwareInstallState.value == FirmwareInstallState.File) { showFirwmareDialog.value = false settingsViewModel.clearFirmwareSelection(firmwareInstallState) } @@ -779,13 +807,125 @@ class SettingViews { } } } + SimpleAlertDialog.Custom( + showDialog = showDataResetDialog, + onDismissRequest = { + if (dataResetState.value != DataResetState.Reset) { + showDataResetDialog.value = false + dataResetState.value = DataResetState.Query + } + } + ) { + Column( + modifier = Modifier + .padding(16.dp) + .fillMaxWidth(), + verticalArrangement = Arrangement.SpaceBetween, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "App Data Reinit", + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp), + textAlign = TextAlign.Center + ) + + when (dataResetState.value) { + DataResetState.Query -> { + Text( + text = "Current bis, games, profiles and system folders will be reset. Do you want to continue?", + modifier = Modifier + .fillMaxWidth() + .padding(start = 8.dp, bottom = 8.dp), + textAlign = TextAlign.Start + ) + Row( + horizontalArrangement = Arrangement.SpaceEvenly, + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp) + ) { + Button( + onClick = { + thread { + settingsViewModel.resetAppData(dataResetState) + } + }, + modifier = Modifier + .weight(1f) + .padding(horizontal = 8.dp) + ) { + Text(text = "Yes") + } + Button( + onClick = { + showDataResetDialog.value = false + }, + modifier = Modifier + .weight(1f) + .padding(horizontal = 8.dp) + ) { + Text(text = "No") + } + } + } + DataResetState.Reset -> { + Text( + text = "Resetting app data...", + modifier = Modifier + .fillMaxWidth() + .padding(start = 8.dp, bottom = 8.dp), + textAlign = TextAlign.Start + ) + LinearProgressIndicator( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp) + ) + } + DataResetState.Done -> { + Text( + text = "Data reset completed successfully.", + modifier = Modifier + .fillMaxWidth() + .padding(start = 8.dp, bottom = 8.dp), + textAlign = TextAlign.Start + ) + + Row( + horizontalArrangement = Arrangement.SpaceEvenly, + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp) + ) { + Button( + onClick = { + showDataResetDialog.value = false + firmwareVersion.value = mainViewModel.firmwareVersion + dataResetState.value = DataResetState.Query + mainViewModel.userViewModel.refreshUsers() + mainViewModel.homeViewModel.requestReload() + mainViewModel.activity.shutdownAndRestart() + }, + modifier = Modifier + .weight(1f) + .padding(horizontal = 8.dp) + ) { + Text(text = "Close") + } + } + } + } + } + } SimpleAlertDialog.Custom( showDialog = showDataImportDialog, onDismissRequest = { if (dataImportState.value != DataImportState.Import) { showDataImportDialog.value = false dataFile.value = null - dataImportState.value = DataImportState.None + dataImportState.value = DataImportState.File } } ) { @@ -805,9 +945,9 @@ class SettingViews { ) when (dataImportState.value) { - DataImportState.None -> { + DataImportState.File -> { Text( - text = "Select a zip file to import app data from.", + text = "Select a zip file to import bis, games, profiles and system folders from another Android installation.", modifier = Modifier .fillMaxWidth() .padding(start = 8.dp, bottom = 8.dp), @@ -854,7 +994,7 @@ class SettingViews { onClick = { showDataImportDialog.value = false dataFile.value = null - dataImportState.value = DataImportState.None + dataImportState.value = DataImportState.File }, modifier = Modifier .weight(1f) @@ -942,9 +1082,10 @@ class SettingViews { showDataImportDialog.value = false dataFile.value = null firmwareVersion.value = mainViewModel.firmwareVersion - dataImportState.value = DataImportState.None + dataImportState.value = DataImportState.File mainViewModel.userViewModel.refreshUsers() mainViewModel.homeViewModel.requestReload() + mainViewModel.activity.shutdownAndRestart() }, modifier = Modifier .weight(1f) @@ -1032,6 +1173,8 @@ class SettingViews { Column(modifier = Modifier.fillMaxWidth()) { useNce.SwitchSelector(label = "Enable NCE (Native Code Execution)") enablePptc.SwitchSelector(label = "Enable PPTC (Profiled Persistent Translation Cache)") + enableLowPowerPptc.SwitchSelector(label = "Enable Low-Power PPTC") + enableJitCacheEviction.SwitchSelector(label = "Enable Jit Cache Eviction") MemoryModeDropdown( selectedMemoryManagerMode = memoryManagerMode.value, onModeSelected = { mode -> diff --git a/src/LibKenjinx/Android/JniExportedMethods.cs b/src/LibKenjinx/Android/JniExportedMethods.cs index eb001ccc9..c7c7cbb27 100644 --- a/src/LibKenjinx/Android/JniExportedMethods.cs +++ b/src/LibKenjinx/Android/JniExportedMethods.cs @@ -79,6 +79,8 @@ namespace LibKenjinx int vSyncMode, bool enableDockedMode, bool enablePtc, + bool enableLowPowerPtc, + bool enableJitCacheEviction, bool enableInternetAccess, bool enableFsIntegrityChecks, int fsGlobalAccessLogMode, @@ -98,6 +100,8 @@ namespace LibKenjinx (VSyncMode)vSyncMode, enableDockedMode, enablePtc, + enableLowPowerPtc, + enableJitCacheEviction, enableInternetAccess, enableFsIntegrityChecks, fsGlobalAccessLogMode, diff --git a/src/LibKenjinx/LibKenjinx.Device.cs b/src/LibKenjinx/LibKenjinx.Device.cs index 1ccca4584..960c4fddd 100644 --- a/src/LibKenjinx/LibKenjinx.Device.cs +++ b/src/LibKenjinx/LibKenjinx.Device.cs @@ -26,6 +26,8 @@ namespace LibKenjinx VSyncMode vSyncMode, bool enableDockedMode, bool enablePtc, + bool enableLowPowerPtc, + bool enableJitCacheEviction, bool enableInternetAccess, bool enableFsIntegrityChecks, int fsGlobalAccessLogMode, @@ -45,6 +47,8 @@ namespace LibKenjinx vSyncMode, enableDockedMode, enablePtc, + enableLowPowerPtc, + enableJitCacheEviction, enableInternetAccess, enableFsIntegrityChecks, fsGlobalAccessLogMode, diff --git a/src/LibKenjinx/LibKenjinx.Graphics.cs b/src/LibKenjinx/LibKenjinx.Graphics.cs index 5bb20614c..932a2d08e 100644 --- a/src/LibKenjinx/LibKenjinx.Graphics.cs +++ b/src/LibKenjinx/LibKenjinx.Graphics.cs @@ -10,6 +10,7 @@ using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; +using Ryujinx.UI.Common.Configuration; using Silk.NET.Vulkan; using System; using System.Collections.Generic; @@ -94,6 +95,9 @@ namespace LibKenjinx { return; } + ARMeilleure.Optimizations.EcoFriendly = SwitchDevice!.EnableLowPowerPtc; + ARMeilleure.Optimizations.CacheEviction = SwitchDevice!.EnableJitCacheEviction; + var device = SwitchDevice!.EmulationContext!; _gpuDoneEvent = new ManualResetEvent(true); diff --git a/src/LibKenjinx/LibKenjinx.Native.cs b/src/LibKenjinx/LibKenjinx.Native.cs index 5971a505f..f016361ec 100644 --- a/src/LibKenjinx/LibKenjinx.Native.cs +++ b/src/LibKenjinx/LibKenjinx.Native.cs @@ -41,6 +41,8 @@ namespace LibKenjinx VSyncMode vSyncMode, bool enableDockedMode, bool enablePtc, + bool enableLowPowerPtc, + bool enableJitCacheEviction, bool enableInternetAccess, bool enableFsIntegrityChecks, int fsGlobalAccessLogMode, @@ -55,6 +57,8 @@ namespace LibKenjinx vSyncMode, enableDockedMode, enablePtc, + enableLowPowerPtc, + enableJitCacheEviction, enableInternetAccess, enableFsIntegrityChecks, fsGlobalAccessLogMode, diff --git a/src/LibKenjinx/LibKenjinx.cs b/src/LibKenjinx/LibKenjinx.cs index 556518853..6a4c643be 100644 --- a/src/LibKenjinx/LibKenjinx.cs +++ b/src/LibKenjinx/LibKenjinx.cs @@ -419,7 +419,7 @@ namespace LibKenjinx } // Return the ControlFS - controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None); + controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, SwitchDevice.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None); titleId = controlNca?.Header.TitleId.ToString("x16"); } @@ -486,7 +486,7 @@ namespace LibKenjinx if (patchNca != null && controlNca != null) { - updatedControlFs = controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None); + updatedControlFs = controlNca.OpenFileSystem(NcaSectionType.Data, SwitchDevice.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None); return true; } @@ -708,6 +708,10 @@ namespace LibKenjinx public InputManager? InputManager { get; set; } public Switch? EmulationContext { get; set; } public IHostUIHandler? HostUiHandler { get; set; } + public bool EnableLowPowerPtc { get; set; } + public bool EnableJitCacheEviction { get; set; } + + public bool EnableFsIntegrityChecks { get; set; } internal void DisposeContext() { @@ -803,6 +807,8 @@ namespace LibKenjinx VSyncMode vSyncMode, bool enableDockedMode, bool enablePtc, + bool enableLowPowerPtc, + bool enableJitCacheEviction, bool enableInternetAccess, bool enableFsIntegrityChecks, int fsGlobalAccessLogMode, @@ -824,6 +830,10 @@ namespace LibKenjinx renderer = new ThreadedRenderer(renderer); } + EnableLowPowerPtc = enableLowPowerPtc; + EnableJitCacheEviction = enableJitCacheEviction; + EnableFsIntegrityChecks = enableFsIntegrityChecks; + HLEConfiguration configuration = new HLEConfiguration(VirtualFileSystem, LibHacHorizonManager, ContentManager, @@ -839,7 +849,7 @@ namespace LibKenjinx enableDockedMode, enablePtc, enableInternetAccess, - enableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, + EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, fsGlobalAccessLogMode, 0, timeZone, diff --git a/src/Ryujinx.Common/ReleaseInformation.cs b/src/Ryujinx.Common/ReleaseInformation.cs index 12e77d85f..01a5bfafb 100644 --- a/src/Ryujinx.Common/ReleaseInformation.cs +++ b/src/Ryujinx.Common/ReleaseInformation.cs @@ -27,6 +27,6 @@ namespace Ryujinx.Common public static bool IsFlatHubBuild => IsValid && ReleaseChannelOwner.Equals(FlatHubChannelOwner); - public static string Version => (IsValid || !RuntimeFeature.IsDynamicCodeCompiled) ? (PlatformInfo.IsBionic ? "Android_2.0.3" : BuildVersion) : Assembly.GetEntryAssembly()!.GetCustomAttribute()?.InformationalVersion; + public static string Version => (IsValid || !RuntimeFeature.IsDynamicCodeCompiled) ? (PlatformInfo.IsBionic ? "Bionic_2.0.3" : BuildVersion) : Assembly.GetEntryAssembly()!.GetCustomAttribute()?.InformationalVersion; } } diff --git a/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs b/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs index 6515113f4..bfdb95b59 100644 --- a/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs +++ b/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs @@ -1,8 +1,11 @@ +using ARMeilleure; using ARMeilleure.Memory; +using Ryujinx.Common.Logging; using Ryujinx.Memory; using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Threading; @@ -14,8 +17,15 @@ namespace Ryujinx.Cpu.LightningJit.Cache private static readonly int _pageSize = (int)MemoryBlock.GetPageSize(); private static readonly int _pageMask = _pageSize - 1; - private const int CodeAlignment = 4; // Bytes. - private const int CacheSize = 2047 * 1024 * 1024; + private const int CodeAlignment = 4; + private const int FullCacheSize = 2047 * 1024 * 1024; + private const int ReducedCacheSize = FullCacheSize / 8; + + private const float EvictionTargetPercentage = 0.20f; + private const int MaxEntriesToEvictAtOnce = 100; + + // Simple logging configuration + private const int LogInterval = 5000; // Log every 5000 allocations private static ReservedRegion _jitRegion; private static JitCacheInvalidation _jitCacheInvalidator; @@ -23,9 +33,33 @@ namespace Ryujinx.Cpu.LightningJit.Cache private static CacheMemoryAllocator _cacheAllocator; private static readonly List _cacheEntries = []; + private static readonly Dictionary _entryUsageStats = []; private static readonly Lock _lock = new(); private static bool _initialized; + private static int _cacheSize; + + // Basic statistics + private static int _totalAllocations = 0; + private static int _totalEvictions = 0; + + private class EntryUsageStats + { + public long LastAccessTime { get; private set; } + public int UsageCount { get; private set; } + + public EntryUsageStats() + { + LastAccessTime = DateTime.UtcNow.Ticks; + UsageCount = 1; + } + + public void UpdateUsage() + { + LastAccessTime = DateTime.UtcNow.Ticks; + UsageCount++; + } + } [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll", SetLastError = true)] @@ -45,15 +79,17 @@ namespace Ryujinx.Cpu.LightningJit.Cache return; } - _jitRegion = new ReservedRegion(allocator, CacheSize); + _cacheSize = Optimizations.CacheEviction ? ReducedCacheSize : FullCacheSize; + _jitRegion = new ReservedRegion(allocator, (ulong)_cacheSize); if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS()) { _jitCacheInvalidator = new JitCacheInvalidation(allocator); } - _cacheAllocator = new CacheMemoryAllocator(CacheSize); + _cacheAllocator = new CacheMemoryAllocator(_cacheSize); + Logger.Info?.Print(LogClass.Cpu, $"Lightning JIT Cache initialized: Size={_cacheSize / (1024 * 1024)} MB, Eviction={Optimizations.CacheEviction}"); _initialized = true; } } @@ -63,8 +99,32 @@ namespace Ryujinx.Cpu.LightningJit.Cache lock (_lock) { Debug.Assert(_initialized); + _totalAllocations++; - int funcOffset = Allocate(code.Length); + int funcOffset; + + if (Optimizations.CacheEviction) + { + int codeSize = AlignCodeSize(code.Length); + funcOffset = _cacheAllocator.Allocate(codeSize); + + if (funcOffset < 0) + { + EvictEntries(codeSize); + funcOffset = _cacheAllocator.Allocate(codeSize); + + if (funcOffset < 0) + { + throw new OutOfMemoryException("JIT Cache exhausted even after eviction."); + } + } + + _jitRegion.ExpandIfNeeded((ulong)funcOffset + (ulong)codeSize); + } + else + { + funcOffset = Allocate(code.Length); + } IntPtr funcPtr = _jitRegion.Pointer + funcOffset; @@ -96,6 +156,12 @@ namespace Ryujinx.Cpu.LightningJit.Cache Add(funcOffset, code.Length); + // Simple periodic logging + if (_totalAllocations % LogInterval == 0) + { + LogCacheStatus(); + } + return funcPtr; } } @@ -112,6 +178,11 @@ namespace Ryujinx.Cpu.LightningJit.Cache { _cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size)); _cacheEntries.RemoveAt(entryIndex); + + if (Optimizations.CacheEviction) + { + _entryUsageStats.Remove(funcOffset); + } } } } @@ -169,6 +240,11 @@ namespace Ryujinx.Cpu.LightningJit.Cache } _cacheEntries.Insert(index, entry); + + if (Optimizations.CacheEviction) + { + _entryUsageStats[offset] = new EntryUsageStats(); + } } public static bool TryFind(int offset, out CacheEntry entry, out int entryIndex) @@ -185,6 +261,12 @@ namespace Ryujinx.Cpu.LightningJit.Cache if (index >= 0) { entry = _cacheEntries[index]; + + if (Optimizations.CacheEviction && _entryUsageStats.TryGetValue(offset, out var stats)) + { + stats.UpdateUsage(); + } + entryIndex = index; return true; } @@ -194,5 +276,83 @@ namespace Ryujinx.Cpu.LightningJit.Cache entryIndex = 0; return false; } + + private static void EvictEntries(int requiredSize) + { + if (!Optimizations.CacheEviction) + { + return; + } + + lock (_lock) + { + int targetSpace = Math.Max(requiredSize, (int)(_cacheSize * EvictionTargetPercentage)); + int freedSpace = 0; + int evictedCount = 0; + + var entriesWithStats = _cacheEntries + .Where(e => _entryUsageStats.ContainsKey(e.Offset)) + .Select(e => new { + Entry = e, + Stats = _entryUsageStats[e.Offset], + Score = CalculateEvictionScore(_entryUsageStats[e.Offset]) + }) + .OrderBy(x => x.Score) + .Take(MaxEntriesToEvictAtOnce) + .ToList(); + + foreach (var item in entriesWithStats) + { + int entrySize = AlignCodeSize(item.Entry.Size); + + int entryIndex = _cacheEntries.BinarySearch(item.Entry); + if (entryIndex >= 0) + { + _cacheAllocator.Free(item.Entry.Offset, entrySize); + _cacheEntries.RemoveAt(entryIndex); + _entryUsageStats.Remove(item.Entry.Offset); + + freedSpace += entrySize; + evictedCount++; + + if (freedSpace >= targetSpace) + { + break; + } + } + } + + _totalEvictions += evictedCount; + + Logger.Info?.Print(LogClass.Cpu, $"Lightning JIT Cache: Evicted {evictedCount} entries, freed {freedSpace / (1024 * 1024.0):F2} MB"); + } + } + + private static double CalculateEvictionScore(EntryUsageStats stats) + { + long currentTime = DateTime.UtcNow.Ticks; + long ageInTicks = currentTime - stats.LastAccessTime; + + double ageInSeconds = ageInTicks / 10_000_000.0; + + const double usageWeight = 1.0; + const double ageWeight = 2.0; + + double usageScore = Math.Log10(stats.UsageCount + 1) * usageWeight; + double ageScore = (10.0 / (ageInSeconds + 1.0)) * ageWeight; + + return usageScore + ageScore; + } + + private static void LogCacheStatus() + { + int estimatedUsedSize = _cacheEntries.Sum(e => AlignCodeSize(e.Size)); + double usagePercentage = 100.0 * estimatedUsedSize / _cacheSize; + + Logger.Info?.Print(LogClass.Cpu, + $"Lightning JIT Cache status: entries={_cacheEntries.Count}, " + + $"est. used={estimatedUsedSize / (1024 * 1024.0):F2} MB ({usagePercentage:F1}%), " + + $"evictions={_totalEvictions}, allocations={_totalAllocations}"); + } } } diff --git a/src/Ryujinx.Cpu/LightningJit/Cache/NoWxCache.cs b/src/Ryujinx.Cpu/LightningJit/Cache/NoWxCache.cs index 2fc644a04..d2541ac19 100644 --- a/src/Ryujinx.Cpu/LightningJit/Cache/NoWxCache.cs +++ b/src/Ryujinx.Cpu/LightningJit/Cache/NoWxCache.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache { private const int CodeAlignment = 4; // Bytes. private const int SharedCacheSize = 2047 * 1024 * 1024; - private const int LocalCacheSize = 128 * 1024 * 1024; + private const int LocalCacheSize = 256 * 1024 * 1024; // How many calls to the same function we allow until we pad the shared cache to force the function to become available there // and allow the guest to take the fast path. diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index eb6c689b8..8d1ae1fdf 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -512,13 +512,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Definitions.Stage == ShaderStage.Fragment && context.Definitions.DualSourceBlend) { IoDefinition firstOutput = outputs.ElementAtOrDefault(0); - IoDefinition secondOutput = outputs.ElementAtOrDefault(1); - if (firstOutput.Location + 1 == secondOutput.Location) - { - DeclareOutputDualSourceBlendAttribute(context, firstOutput.Location); - outputs = outputs.Skip(2); - } + DeclareOutputDualSourceBlendAttribute(context, firstOutput.Location); + outputs = outputs.Skip(2); } foreach (var ioDefinition in outputs) diff --git a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index 18c4b6d54..022532af8 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -9,6 +9,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { public static void RunPass(TransformContext context) { + for (int blkIndex = 0; blkIndex < context.Blocks.Length; blkIndex++) + { + XmadOptimizer.RunPass(context.Blocks[blkIndex]); + } + RunOptimizationPasses(context.Blocks, context.ResourceManager); // TODO: Some of those are not optimizations and shouldn't be here. diff --git a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs index d1fbca0eb..54cad966a 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -181,13 +181,29 @@ namespace Ryujinx.Graphics.Shader.Translation private static void EmitOutputsInitialization(EmitterContext context, AttributeUsage attributeUsage, IGpuAccessor gpuAccessor, ShaderStage stage) { - // Compute has no output attributes, and fragment is the last stage, so we - // don't need to initialize outputs on those stages. - if (stage == ShaderStage.Compute || stage == ShaderStage.Fragment) + // Compute has no output attributes, so we + // don't need to initialize outputs on that stage. + if (stage == ShaderStage.Compute) { return; } + if (stage == ShaderStage.Fragment) + { + // Fragment is the last stage, so we don't need to + // initialize outputs unless we're using DSB, in which + // we need to make sure the ouput has a valid value. + if (gpuAccessor.QueryGraphicsState().DualSourceBlendEnable) + { + for (int i = 0; i < 4; i++) + { + context.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(1), Const(i), ConstF(0)); + } + } + + return; + } + if (stage == ShaderStage.Vertex) { InitializeVertexOutputs(context); diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index b529e931d..0f637cf48 100644 --- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -169,7 +169,7 @@ namespace Ryujinx.Graphics.Vulkan // If binding 3 is immediately used, use an alternate set of reserved bindings. ReadOnlyCollection uniformUsage = layout.SetUsages[0].Usages; bool hasBinding3 = uniformUsage.Any(x => x.Binding == 3); - int[] reserved = isCompute ? Array.Empty() : gd.GetPushDescriptorReservedBindings(hasBinding3); + int[] reserved = isCompute ? [] : gd.GetPushDescriptorReservedBindings(hasBinding3); // Can't use any of the reserved usages. for (int i = 0; i < uniformUsage.Count; i++) @@ -240,7 +240,7 @@ namespace Ryujinx.Graphics.Vulkan for (int setIndex = 0; setIndex < sets.Count; setIndex++) { - List currentSegments = new(); + List currentSegments = []; ResourceDescriptor currentDescriptor = default; int currentCount = 0; @@ -298,7 +298,7 @@ namespace Ryujinx.Graphics.Vulkan for (int setIndex = 0; setIndex < setUsages.Count; setIndex++) { - List currentSegments = new(); + List currentSegments = []; ResourceUsage currentUsage = default; int currentCount = 0; diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 47e3a481d..aa3f284b5 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Vulkan var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples); - var usage = GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, true); + var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities); var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit; @@ -307,7 +307,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public static ImageUsageFlags GetImageUsage(Format format, in HardwareCapabilities capabilities, bool isMsImageStorageSupported, bool extendedUsage) + public static ImageUsageFlags GetImageUsage(Format format, Target target, in HardwareCapabilities capabilities) { var usage = DefaultUsageFlags; @@ -320,11 +320,19 @@ namespace Ryujinx.Graphics.Vulkan usage |= ImageUsageFlags.ColorAttachmentBit; } - if ((format.IsImageCompatible() && isMsImageStorageSupported) || extendedUsage) + bool isMsImageStorageSupported = capabilities.SupportsShaderStorageImageMultisample; + + if (format.IsImageCompatible() && (isMsImageStorageSupported || !target.IsMultisample())) { usage |= ImageUsageFlags.StorageBit; } + if (capabilities.SupportsAttachmentFeedbackLoop && + (usage & (ImageUsageFlags.DepthStencilAttachmentBit | ImageUsageFlags.ColorAttachmentBit)) != 0) + { + usage |= ImageUsageFlags.AttachmentFeedbackLoopBitExt; + } + return usage; } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index 64d976a45..ffbb98f34 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -64,8 +64,7 @@ namespace Ryujinx.Graphics.Vulkan bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample(); var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported); - var usage = TextureStorage.GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, false); - + var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities); var levels = (uint)info.Levels; var layers = (uint)info.GetLayers(); diff --git a/src/Ryujinx.Graphics.Vulkan/Window.cs b/src/Ryujinx.Graphics.Vulkan/Window.cs index 3fe805b4f..2b2e509c6 100644 --- a/src/Ryujinx.Graphics.Vulkan/Window.cs +++ b/src/Ryujinx.Graphics.Vulkan/Window.cs @@ -468,9 +468,9 @@ namespace Ryujinx.Graphics.Vulkan _gd.CommandBufferPool.Return( cbs, - stackalloc[] { _imageAvailableSemaphores[semaphoreIndex] }, - stackalloc[] { PipelineStageFlags.ColorAttachmentOutputBit }, - stackalloc[] { _renderFinishedSemaphores[semaphoreIndex] }); + [_imageAvailableSemaphores[semaphoreIndex]], + [PipelineStageFlags.ColorAttachmentOutputBit], + [_renderFinishedSemaphores[semaphoreIndex]]); // TODO: Present queue. var semaphore = _renderFinishedSemaphores[semaphoreIndex]; diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index fdc292076..8e93cb4c8 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process // TODO: Remove once we no longer need to initialize it externally. HandleTable = new KHandleTable(); - _threads = new LinkedList(); + _threads = []; Debugger = new HleProcessDebugger(this); } diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs index e4281249f..a6d42628c 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs @@ -121,7 +121,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions device.System.KernelContext, metaLoader, nacpData, - device.System.EnablePtc, + enablePtc, modLoadResult.Hash, true, programName, diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs index c4d288971..f7427c690 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs @@ -305,6 +305,13 @@ namespace Ryujinx.HLE.Loaders.Processes if (string.IsNullOrWhiteSpace(programName)) { + foreach (ApplicationControlProperty.ApplicationTitle nacpTitles in nacpData.Value.Title) + { + if (nacpTitles.Name[0] != 0) + continue; + + programName = nacpTitles.NameString.ToString(); + } programName = Array.Find(nacpData.Value.Title.AsReadOnlySpan().ToArray(), x => x.Name[0] != 0).NameString.ToString(); } diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs index 16ecbe5d1..37c0a92d1 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs @@ -235,7 +235,7 @@ namespace Ryujinx.HLE.Loaders.Processes ulong programId, byte programIndex, byte[] arguments = null, - params IExecutable[] executables) + params ReadOnlySpan executables) { context.Device.System.ServiceTable.WaitServicesReady(); @@ -255,12 +255,17 @@ namespace Ryujinx.HLE.Loaders.Processes ulong codeStart = ((meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL) + CodeStartOffset; ulong codeSize = 0; - IEnumerable buildIds = executables.Select(e => (e switch + string[] buildIds = new string[executables.Length]; + + for (int i = 0; i < executables.Length; i++) { - NsoExecutable nso => Convert.ToHexString(nso.BuildId), - NroExecutable nro => Convert.ToHexString(nro.Header.BuildId), - _ => string.Empty, - }).ToUpper()); + buildIds[i] = (executables[i] switch + { + NsoExecutable nso => Convert.ToHexString(nso.BuildId), + NroExecutable nro => Convert.ToHexString(nro.Header.BuildId), + _ => string.Empty + }).ToUpper(); + } NceCpuCodePatch[] nsoPatch = new NceCpuCodePatch[executables.Length]; ulong[] nsoBase = new ulong[executables.Length]; diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs index 725197469..408e0e8f1 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs @@ -16,7 +16,7 @@ namespace Ryujinx.UI.Common.Configuration /// /// The current version of the file format /// - public const int CurrentVersion = 58; + public const int CurrentVersion = 59; /// /// Version of the configuration file format @@ -240,6 +240,11 @@ namespace Ryujinx.UI.Common.Configuration /// public bool EnableLowPowerPtc { get; set; } + /// + /// Enables or disables JIT cache eviction to avoid out of memory errors in some games + /// + public bool EnableJitCacheEviction { get; set; } + /// /// Enables or disables guest Internet access /// diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs index b95606687..95593513a 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs @@ -345,6 +345,11 @@ namespace Ryujinx.UI.Common.Configuration /// public ReactiveObject EnableLowPowerPtc { get; private set; } + /// + /// Enables or disables JIT cache eviction to avoid out of memory errors in some games + /// + public ReactiveObject EnableJitCacheEviction { get; private set; } + /// /// Enables or disables guest Internet access /// @@ -405,6 +410,8 @@ namespace Ryujinx.UI.Common.Configuration EnablePtc.Event += static (sender, e) => LogValueChange(e, nameof(EnablePtc)); EnableLowPowerPtc = new ReactiveObject(); EnableLowPowerPtc.Event += static (sender, e) => LogValueChange(e, nameof(EnableLowPowerPtc)); + EnableJitCacheEviction = new ReactiveObject(); + EnableJitCacheEviction.Event += static (sender, e) => LogValueChange(e, nameof(EnableJitCacheEviction)); EnableInternetAccess = new ReactiveObject(); EnableInternetAccess.Event += static (sender, e) => LogValueChange(e, nameof(EnableInternetAccess)); EnableFsIntegrityChecks = new ReactiveObject(); @@ -775,6 +782,7 @@ namespace Ryujinx.UI.Common.Configuration EnableColorSpacePassthrough = Graphics.EnableColorSpacePassthrough, EnablePtc = System.EnablePtc, EnableLowPowerPtc = System.EnableLowPowerPtc, + EnableJitCacheEviction = System.EnableJitCacheEviction, EnableInternetAccess = System.EnableInternetAccess, EnableFsIntegrityChecks = System.EnableFsIntegrityChecks, FsGlobalAccessLogMode = System.FsGlobalAccessLogMode, @@ -1647,7 +1655,16 @@ namespace Ryujinx.UI.Common.Configuration configurationFileUpdated = true; } - + + if (configurationFileFormat.Version < 59) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 59."); + + configurationFileFormat.EnableJitCacheEviction = false; + + configurationFileUpdated = true; + } + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; @@ -1692,6 +1709,7 @@ namespace Ryujinx.UI.Common.Configuration Graphics.EnableColorSpacePassthrough.Value = configurationFileFormat.EnableColorSpacePassthrough; System.EnablePtc.Value = configurationFileFormat.EnablePtc; System.EnableLowPowerPtc.Value = configurationFileFormat.EnableLowPowerPtc; + System.EnableJitCacheEviction.Value = configurationFileFormat.EnableJitCacheEviction; System.EnableInternetAccess.Value = configurationFileFormat.EnableInternetAccess; System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks; System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode;