From 606e149bd343c57263db72c3f740ecf8bcfeb718 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Thu, 9 Jan 2025 18:48:15 -0600 Subject: [PATCH 1/9] UI: Create a ColumnIndices struct and pass it by reference to the row ctor instead of recomputing the column index for every column on every row --- .../Utilities/Compat/CompatibilityCsv.cs | 73 ++++++++++++------- .../Compat/CompatibilityList.axaml.cs | 9 --- .../Compat/CompatibilityViewModel.cs | 9 +-- 3 files changed, 51 insertions(+), 40 deletions(-) diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs b/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs index 1e69b42d5..8fc7aeb04 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs +++ b/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs @@ -1,50 +1,71 @@ using Gommon; using nietras.SeparatedValues; using Ryujinx.Ava.Common.Locale; +using Ryujinx.Common.Logging; using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using System.Text; namespace Ryujinx.Ava.Utilities.Compat { + public struct ColumnIndices(SepReaderHeader header) + { + public const string TitleIdCol = "\"title_id\""; + public const string GameNameCol = "\"game_name\""; + public const string LabelsCol = "\"labels\""; + public const string StatusCol = "\"status\""; + public const string LastUpdatedCol = "\"last_updated\""; + + public readonly int TitleId = header.IndexOf(TitleIdCol); + public readonly int GameName = header.IndexOf(GameNameCol); + public readonly int Labels = header.IndexOf(LabelsCol); + public readonly int Status = header.IndexOf(StatusCol); + public readonly int LastUpdated = header.IndexOf(LastUpdatedCol); + } + public class CompatibilityCsv { - public static CompatibilityCsv Shared { get; set; } - - public CompatibilityCsv(SepReader reader) + static CompatibilityCsv() { - var entries = new List(); + using Stream csvStream = Assembly.GetExecutingAssembly() + .GetManifestResourceStream("RyujinxGameCompatibilityList")!; + csvStream.Position = 0; - foreach (var row in reader) - { - entries.Add(new CompatibilityEntry(reader.Header, row)); - } + LoadFromStream(csvStream); + } + + public static void LoadFromStream(Stream stream) + { + var reader = Sep.Reader().From(stream); + var columnIndices = new ColumnIndices(reader.Header); - Entries = entries.Where(x => x.Status != null) - .OrderBy(it => it.GameName).ToArray(); + Entries = reader + .Enumerate(row => new CompatibilityEntry(ref columnIndices, row)) + .OrderBy(it => it.GameName) + .ToArray(); + + Logger.Debug?.Print(LogClass.UI, "Compatibility CSV loaded."); } - public CompatibilityEntry[] Entries { get; } + public static CompatibilityEntry[] Entries { get; private set; } } public class CompatibilityEntry { - public CompatibilityEntry(SepReaderHeader header, SepReader.Row row) + public CompatibilityEntry(ref ColumnIndices indices, SepReader.Row row) { - if (row.ColCount != header.ColNames.Count) - throw new InvalidDataException($"CSV row {row.RowIndex} ({row.ToString()}) has mismatched column count"); - - var titleIdRow = ColStr(row[header.IndexOf("\"title_id\"")]); + var titleIdRow = ColStr(row[indices.TitleId]); TitleId = !string.IsNullOrEmpty(titleIdRow) ? titleIdRow : default(Optional); - GameName = ColStr(row[header.IndexOf("\"game_name\"")]).Trim().Trim('"'); + GameName = ColStr(row[indices.GameName]).Trim().Trim('"'); - IssueLabels = ColStr(row[header.IndexOf("\"labels\"")]).Split(';'); - Status = ColStr(row[header.IndexOf("\"status\"")]).ToLower() switch + Labels = ColStr(row[indices.Labels]).Split(';'); + Status = ColStr(row[indices.Status]).ToLower() switch { "playable" => LocaleKeys.CompatibilityListPlayable, "ingame" => LocaleKeys.CompatibilityListIngame, @@ -54,8 +75,8 @@ namespace Ryujinx.Ava.Utilities.Compat _ => null }; - if (DateTime.TryParse(ColStr(row[header.IndexOf("\"last_updated\"")]), out var dt)) - LastEvent = dt; + if (DateTime.TryParse(ColStr(row[indices.LastUpdated]), out var dt)) + LastUpdated = dt; return; @@ -64,15 +85,15 @@ namespace Ryujinx.Ava.Utilities.Compat public string GameName { get; } public Optional TitleId { get; } - public string[] IssueLabels { get; } + public string[] Labels { get; } public LocaleKeys? Status { get; } - public DateTime LastEvent { get; } + public DateTime LastUpdated { get; } public string LocalizedStatus => LocaleManager.Instance[Status!.Value]; public string FormattedTitleId => TitleId .OrElse(new string(' ', 16)); - public string FormattedIssueLabels => IssueLabels + public string FormattedIssueLabels => Labels .Where(it => !it.StartsWithIgnoreCase("status")) .Select(FormatLabelName) .JoinToString(", "); @@ -82,9 +103,9 @@ namespace Ryujinx.Ava.Utilities.Compat var sb = new StringBuilder("CompatibilityEntry: {"); sb.Append($"{nameof(GameName)}=\"{GameName}\", "); sb.Append($"{nameof(TitleId)}={TitleId}, "); - sb.Append($"{nameof(IssueLabels)}=\"{IssueLabels}\", "); + sb.Append($"{nameof(Labels)}=\"{Labels}\", "); sb.Append($"{nameof(Status)}=\"{Status}\", "); - sb.Append($"{nameof(LastEvent)}=\"{LastEvent}\""); + sb.Append($"{nameof(LastUpdated)}=\"{LastUpdated}\""); sb.Append('}'); return sb.ToString(); diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs b/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs index 80f124121..2d5f08868 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs +++ b/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs @@ -14,15 +14,6 @@ namespace Ryujinx.Ava.Utilities.Compat { public static async Task Show() { - if (CompatibilityCsv.Shared is null) - { - await using Stream csvStream = Assembly.GetExecutingAssembly() - .GetManifestResourceStream("RyujinxGameCompatibilityList")!; - csvStream.Position = 0; - - CompatibilityCsv.Shared = new CompatibilityCsv(Sep.Reader().From(csvStream)); - } - ContentDialog contentDialog = new() { PrimaryButtonText = string.Empty, diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityViewModel.cs b/src/Ryujinx/Utilities/Compat/CompatibilityViewModel.cs index 8140c041f..4bd97cc35 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityViewModel.cs +++ b/src/Ryujinx/Utilities/Compat/CompatibilityViewModel.cs @@ -11,14 +11,13 @@ namespace Ryujinx.Ava.Utilities.Compat { [ObservableProperty] private bool _onlyShowOwnedGames = true; - private IEnumerable _currentEntries = CompatibilityCsv.Shared.Entries; + private IEnumerable _currentEntries = CompatibilityCsv.Entries; private readonly string[] _ownedGameTitleIds = []; private readonly ApplicationLibrary _appLibrary; public IEnumerable CurrentEntries => OnlyShowOwnedGames ? _currentEntries.Where(x => - x.TitleId.Check(tid => _ownedGameTitleIds.ContainsIgnoreCase(tid)) - || _appLibrary.Applications.Items.Any(a => a.Name.EqualsIgnoreCase(x.GameName))) + x.TitleId.Check(tid => _ownedGameTitleIds.ContainsIgnoreCase(tid))) : _currentEntries; public CompatibilityViewModel() {} @@ -39,11 +38,11 @@ namespace Ryujinx.Ava.Utilities.Compat { if (string.IsNullOrEmpty(searchTerm)) { - SetEntries(CompatibilityCsv.Shared.Entries); + SetEntries(CompatibilityCsv.Entries); return; } - SetEntries(CompatibilityCsv.Shared.Entries.Where(x => + SetEntries(CompatibilityCsv.Entries.Where(x => x.GameName.ContainsIgnoreCase(searchTerm) || x.TitleId.Check(tid => tid.ContainsIgnoreCase(searchTerm)))); } From 292e27f0dac05c5d79fe71cb56eba95d0aa8a69a Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Thu, 9 Jan 2025 19:24:48 -0600 Subject: [PATCH 2/9] UI: dispose CSV reader when done + use explicit types --- src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs b/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs index 8fc7aeb04..8b8f456ef 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs +++ b/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs @@ -39,8 +39,8 @@ namespace Ryujinx.Ava.Utilities.Compat public static void LoadFromStream(Stream stream) { - var reader = Sep.Reader().From(stream); - var columnIndices = new ColumnIndices(reader.Header); + using SepReader reader = Sep.Reader().From(stream); + ColumnIndices columnIndices = new(reader.Header); Entries = reader .Enumerate(row => new CompatibilityEntry(ref columnIndices, row)) @@ -57,7 +57,7 @@ namespace Ryujinx.Ava.Utilities.Compat { public CompatibilityEntry(ref ColumnIndices indices, SepReader.Row row) { - var titleIdRow = ColStr(row[indices.TitleId]); + string titleIdRow = ColStr(row[indices.TitleId]); TitleId = !string.IsNullOrEmpty(titleIdRow) ? titleIdRow : default(Optional); @@ -100,7 +100,7 @@ namespace Ryujinx.Ava.Utilities.Compat public override string ToString() { - var sb = new StringBuilder("CompatibilityEntry: {"); + StringBuilder sb = new("CompatibilityEntry: {"); sb.Append($"{nameof(GameName)}=\"{GameName}\", "); sb.Append($"{nameof(TitleId)}={TitleId}, "); sb.Append($"{nameof(Labels)}=\"{Labels}\", "); @@ -161,8 +161,8 @@ namespace Ryujinx.Ava.Utilities.Compat if (value == string.Empty) return string.Empty; - var firstChar = value[0]; - var rest = value[1..]; + char firstChar = value[0]; + string rest = value[1..]; return $"{char.ToUpper(firstChar)}{rest}"; } From c5574b41a127556cd800d413955de6a75213ca85 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Thu, 9 Jan 2025 19:44:24 -0600 Subject: [PATCH 3/9] UI: collapse LoadFromStream into static ctor pass the index get delegate to the struct instead of the entire header --- .../Utilities/Compat/CompatibilityCsv.cs | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs b/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs index 8b8f456ef..b3812faa2 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs +++ b/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs @@ -11,7 +11,7 @@ using System.Text; namespace Ryujinx.Ava.Utilities.Compat { - public struct ColumnIndices(SepReaderHeader header) + public struct ColumnIndices(Func getIndex) { public const string TitleIdCol = "\"title_id\""; public const string GameNameCol = "\"game_name\""; @@ -19,11 +19,11 @@ namespace Ryujinx.Ava.Utilities.Compat public const string StatusCol = "\"status\""; public const string LastUpdatedCol = "\"last_updated\""; - public readonly int TitleId = header.IndexOf(TitleIdCol); - public readonly int GameName = header.IndexOf(GameNameCol); - public readonly int Labels = header.IndexOf(LabelsCol); - public readonly int Status = header.IndexOf(StatusCol); - public readonly int LastUpdated = header.IndexOf(LastUpdatedCol); + public readonly int TitleId = getIndex(TitleIdCol); + public readonly int GameName = getIndex(GameNameCol); + public readonly int Labels = getIndex(LabelsCol); + public readonly int Status = getIndex(StatusCol); + public readonly int LastUpdated = getIndex(LastUpdatedCol); } public class CompatibilityCsv @@ -34,13 +34,8 @@ namespace Ryujinx.Ava.Utilities.Compat .GetManifestResourceStream("RyujinxGameCompatibilityList")!; csvStream.Position = 0; - LoadFromStream(csvStream); - } - - public static void LoadFromStream(Stream stream) - { - using SepReader reader = Sep.Reader().From(stream); - ColumnIndices columnIndices = new(reader.Header); + using SepReader reader = Sep.Reader().From(csvStream); + ColumnIndices columnIndices = new(reader.Header.IndexOf); Entries = reader .Enumerate(row => new CompatibilityEntry(ref columnIndices, row)) From bdd890cf6f8b583d47946abe3988f158dac3140f Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Thu, 9 Jan 2025 19:48:11 -0600 Subject: [PATCH 4/9] UI: logger function name --- src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs b/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs index b3812faa2..742442714 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs +++ b/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs @@ -42,7 +42,7 @@ namespace Ryujinx.Ava.Utilities.Compat .OrderBy(it => it.GameName) .ToArray(); - Logger.Debug?.Print(LogClass.UI, "Compatibility CSV loaded."); + Logger.Debug?.Print(LogClass.UI, "Compatibility CSV loaded.", "LoadCompatCsv"); } public static CompatibilityEntry[] Entries { get; private set; } From 27993b789fbc6943410a06cbb849a0e0d7d648b8 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Thu, 9 Jan 2025 20:23:26 -0600 Subject: [PATCH 5/9] misc: chore: fix some compile warnings --- src/Ryujinx/UI/Helpers/PlayabilityStatusConverter.cs | 6 +++--- src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs | 5 ++++- src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs | 3 +-- src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Ryujinx/UI/Helpers/PlayabilityStatusConverter.cs b/src/Ryujinx/UI/Helpers/PlayabilityStatusConverter.cs index a894f0246..99c6a0fce 100644 --- a/src/Ryujinx/UI/Helpers/PlayabilityStatusConverter.cs +++ b/src/Ryujinx/UI/Helpers/PlayabilityStatusConverter.cs @@ -12,8 +12,8 @@ namespace Ryujinx.Ava.UI.Helpers private static readonly Lazy _shared = new(() => new()); public static PlayabilityStatusConverter Shared => _shared.Value; - public object Convert(object? value, Type _, object? __, CultureInfo ___) => - value.Cast() switch + public object Convert(object value, Type _, object __, CultureInfo ___) + => value.Cast() switch { LocaleKeys.CompatibilityListNothing or LocaleKeys.CompatibilityListBoots or @@ -22,7 +22,7 @@ namespace Ryujinx.Ava.UI.Helpers _ => Brushes.ForestGreen }; - public object ConvertBack(object? value, Type _, object? __, CultureInfo ___) + public object ConvertBack(object value, Type _, object __, CultureInfo ___) => throw new NotSupportedException(); } } diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index e11d855a6..6ca82d38b 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -741,7 +741,10 @@ namespace Ryujinx.Ava.UI.ViewModels Applications.ToObservableChangeSet() .Filter(Filter) .Sort(GetComparer()) - .Bind(out _appsObservableList).AsObservableList(); +#pragma warning disable MVVMTK0034 + .Bind(out _appsObservableList) +#pragma warning enable MVVMTK0034 + .AsObservableList(); OnPropertyChanged(nameof(AppsObservableList)); } diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs b/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs index 742442714..e3d630b60 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs +++ b/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs @@ -3,7 +3,6 @@ using nietras.SeparatedValues; using Ryujinx.Ava.Common.Locale; using Ryujinx.Common.Logging; using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -11,7 +10,7 @@ using System.Text; namespace Ryujinx.Ava.Utilities.Compat { - public struct ColumnIndices(Func getIndex) + public struct ColumnIndices(Func, int> getIndex) { public const string TitleIdCol = "\"title_id\""; public const string GameNameCol = "\"game_name\""; diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs b/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs index 2d5f08868..841db23a8 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs +++ b/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml.cs @@ -42,7 +42,7 @@ namespace Ryujinx.Ava.Utilities.Compat InitializeComponent(); } - private void TextBox_OnTextChanged(object? sender, TextChangedEventArgs e) + private void TextBox_OnTextChanged(object sender, TextChangedEventArgs e) { if (DataContext is not CompatibilityViewModel cvm) return; From 845c86f545ddd27e0bade490ff161886c7a587b6 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Thu, 9 Jan 2025 21:14:35 -0600 Subject: [PATCH 6/9] misc: chore: cleanup AppletMetadata.CanStart --- src/Ryujinx/Utilities/AppletMetadata.cs | 26 ++++++++++++------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/Ryujinx/Utilities/AppletMetadata.cs b/src/Ryujinx/Utilities/AppletMetadata.cs index 82baed7d3..42c23ee12 100644 --- a/src/Ryujinx/Utilities/AppletMetadata.cs +++ b/src/Ryujinx/Utilities/AppletMetadata.cs @@ -32,29 +32,27 @@ namespace Ryujinx.Ava.Utilities public string GetContentPath(ContentManager contentManager) => (contentManager ?? _contentManager) - .GetInstalledContentPath(ProgramId, StorageId.BuiltInSystem, NcaContentType.Program); + ?.GetInstalledContentPath(ProgramId, StorageId.BuiltInSystem, NcaContentType.Program); public bool CanStart(ContentManager contentManager, out ApplicationData appData, out BlitStruct appControl) { contentManager ??= _contentManager; - if (contentManager == null) - { - appData = null; - appControl = new BlitStruct(0); - return false; - } + if (contentManager == null) + goto BadData; + + string contentPath = GetContentPath(contentManager); + if (string.IsNullOrEmpty(contentPath)) + goto BadData; appData = new() { Name = Name, Id = ProgramId, Path = GetContentPath(contentManager) }; - - if (string.IsNullOrEmpty(appData.Path)) - { - appControl = new BlitStruct(0); - return false; - } - appControl = StructHelpers.CreateCustomNacpData(Name, Version); return true; + + BadData: + appData = null; + appControl = new BlitStruct(0); + return false; } } } From cca429d46a15e8b5fdcff0d548c10e60c0d6055f Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Thu, 9 Jan 2025 21:42:54 -0600 Subject: [PATCH 7/9] misc: chore: restore not enable --- src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 6ca82d38b..17b9ea98c 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -743,7 +743,7 @@ namespace Ryujinx.Ava.UI.ViewModels .Sort(GetComparer()) #pragma warning disable MVVMTK0034 .Bind(out _appsObservableList) -#pragma warning enable MVVMTK0034 +#pragma warning restore MVVMTK0034 .AsObservableList(); OnPropertyChanged(nameof(AppsObservableList)); From 918ec1bde3b84650e0ad18e0fe712381dc1276f9 Mon Sep 17 00:00:00 2001 From: LotP1 <68976644+LotP1@users.noreply.github.com> Date: Fri, 10 Jan 2025 04:43:18 +0100 Subject: [PATCH 8/9] cores rework (#505) This PR changes the core count to be defined in the device instead of being a const value. This is mostly a change for future features I want to implement and should not impact any functionality. The console will now log the range of cores requested from the application, and for now, if the requested range is not 0 to 2 (the 3 cores used for application emulation), it will give an error message which tells the user to contact me on discord. I'm doing this because I'm interested in finding applications/games that don't use 3 cores and the error will be removed in the future once I've gotten enough data. --- src/Ryujinx.HLE/HOS/Horizon.cs | 2 +- src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs | 1 + src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs | 2 +- src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs | 2 +- .../HOS/Kernel/Process/KProcessCapabilities.cs | 17 +++++++++++------ .../HOS/Kernel/SupervisorCall/Syscall.cs | 2 +- .../HOS/Kernel/Threading/KScheduler.cs | 18 +++++++++++++----- src/Ryujinx.HLE/HOS/Services/ServerBase.cs | 6 +++--- src/Ryujinx.HLE/Switch.cs | 2 ++ src/Ryujinx/Headless/HeadlessRyujinx.Init.cs | 2 +- 10 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index f9c5ddecf..627b649df 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -284,7 +284,7 @@ namespace Ryujinx.HLE.HOS ProcessCreationInfo creationInfo = new("Service", 1, 0, 0x8000000, 1, Flags, 0, 0); uint[] defaultCapabilities = { - 0x030363F7, + (((uint)KScheduler.CpuCoresCount - 1) << 24) + (((uint)KScheduler.CpuCoresCount - 1) << 16) + 0x63F7u, 0x1FFFFFCF, 0x207FFFEF, 0x47E0060F, diff --git a/src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs b/src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs index 89d788c54..7e2e9cacc 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/KernelContext.cs @@ -63,6 +63,7 @@ namespace Ryujinx.HLE.HOS.Kernel TickSource = tickSource; Device = device; Memory = memory; + KScheduler.CpuCoresCount = device.CpuCoresCount; Running = true; diff --git a/src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs b/src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs index f5ecba752..e05fc8397 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/KernelStatic.cs @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Kernel return result; } - process.DefaultCpuCore = 3; + process.DefaultCpuCore = KScheduler.CpuCoresCount - 1; context.Processes.TryAdd(process.Pid, process); diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index b4aa5ca5c..82c3d2e70 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -277,7 +277,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return result; } - result = Capabilities.InitializeForUser(capabilities, MemoryManager); + result = Capabilities.InitializeForUser(capabilities, MemoryManager, IsApplication); if (result != Result.Success) { diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs index ebab67bb8..5c9f4f100 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcessCapabilities.cs @@ -35,15 +35,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Process DebuggingFlags &= ~3u; KernelReleaseVersion = KProcess.KernelVersionPacked; - return Parse(capabilities, memoryManager); + return Parse(capabilities, memoryManager, false); } - public Result InitializeForUser(ReadOnlySpan capabilities, KPageTableBase memoryManager) + public Result InitializeForUser(ReadOnlySpan capabilities, KPageTableBase memoryManager, bool isApplication) { - return Parse(capabilities, memoryManager); + return Parse(capabilities, memoryManager, isApplication); } - private Result Parse(ReadOnlySpan capabilities, KPageTableBase memoryManager) + private Result Parse(ReadOnlySpan capabilities, KPageTableBase memoryManager, bool isApplication) { int mask0 = 0; int mask1 = 0; @@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process if (cap.GetCapabilityType() != CapabilityType.MapRange) { - Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager); + Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager, isApplication); if (result != Result.Success) { @@ -120,7 +120,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process return Result.Success; } - private Result ParseCapability(uint cap, ref int mask0, ref int mask1, KPageTableBase memoryManager) + private Result ParseCapability(uint cap, ref int mask0, ref int mask1, KPageTableBase memoryManager, bool isApplication) { CapabilityType code = cap.GetCapabilityType(); @@ -176,6 +176,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process AllowedCpuCoresMask = GetMaskFromMinMax(lowestCpuCore, highestCpuCore); AllowedThreadPriosMask = GetMaskFromMinMax(lowestThreadPrio, highestThreadPrio); + if (isApplication && lowestCpuCore == 0 && highestCpuCore != 2) + Ryujinx.Common.Logging.Logger.Error?.Print(Ryujinx.Common.Logging.LogClass.Application, $"Application requested cores with index range {lowestCpuCore} to {highestCpuCore}! Report this to @LotP on the Ryujinx/Ryubing discord server (discord.gg/ryujinx)!"); + else if (isApplication) + Ryujinx.Common.Logging.Logger.Info?.Print(Ryujinx.Common.Logging.LogClass.Application, $"Application requested cores with index range {lowestCpuCore} to {highestCpuCore}"); + break; } diff --git a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs index 2f487243d..1b6433af6 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs @@ -2683,7 +2683,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall return KernelResult.InvalidCombination; } - if ((uint)preferredCore > 3) + if ((uint)preferredCore > KScheduler.CpuCoresCount - 1) { if ((preferredCore | 2) != -1) { diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs index 8ef77902c..19f1b8be0 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs @@ -9,13 +9,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading partial class KScheduler : IDisposable { public const int PrioritiesCount = 64; - public const int CpuCoresCount = 4; + public static int CpuCoresCount; private const int RoundRobinTimeQuantumMs = 10; - private static readonly int[] _preemptionPriorities = { 59, 59, 59, 63 }; - - private static readonly int[] _srcCoresHighestPrioThreads = new int[CpuCoresCount]; + private static int[] _srcCoresHighestPrioThreads; private readonly KernelContext _context; private readonly int _coreId; @@ -47,6 +45,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading _coreId = coreId; _currentThread = null; + + if (_srcCoresHighestPrioThreads == null) + { + _srcCoresHighestPrioThreads = new int[CpuCoresCount]; + } + } + + private static int PreemptionPriorities(int index) + { + return index == CpuCoresCount - 1 ? 63 : 59; } public static ulong SelectThreads(KernelContext context) @@ -437,7 +445,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading for (int core = 0; core < CpuCoresCount; core++) { - RotateScheduledQueue(context, core, _preemptionPriorities[core]); + RotateScheduledQueue(context, core, PreemptionPriorities(core)); } context.CriticalSection.Leave(); diff --git a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs index f67699b90..40329aa36 100644 --- a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -24,14 +24,14 @@ namespace Ryujinx.HLE.HOS.Services // not large enough. private const int PointerBufferSize = 0x8000; - private readonly static uint[] _defaultCapabilities = { - 0x030363F7, + private static uint[] _defaultCapabilities => [ + (((uint)KScheduler.CpuCoresCount - 1) << 24) + (((uint)KScheduler.CpuCoresCount - 1) << 16) + 0x63F7u, 0x1FFFFFCF, 0x207FFFEF, 0x47E0060F, 0x0048BFFF, 0x01007FFF, - }; + ]; // The amount of time Dispose() will wait to Join() the thread executing the ServerLoop() private static readonly TimeSpan _threadJoinTimeout = TimeSpan.FromSeconds(3); diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index 25e65354f..e15fab03a 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -32,6 +32,8 @@ namespace Ryujinx.HLE public TamperMachine TamperMachine { get; } public IHostUIHandler UIHandler { get; } + public int CpuCoresCount = 4; //Switch 1 has 4 cores + public VSyncMode VSyncMode { get; set; } = VSyncMode.Switch; public bool CustomVSyncIntervalEnabled { get; set; } = false; public int CustomVSyncInterval { get; set; } diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs index 19d2fb94e..7d75ac7c1 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -1,4 +1,4 @@ -using DiscordRPC; +using DiscordRPC; using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.SDL2; using Ryujinx.Ava; From 4a4ea557de4b36c27abd8e401494f3b7ed50d41e Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Fri, 10 Jan 2025 01:43:34 -0600 Subject: [PATCH 9/9] UI: compat: show last updated date on entry hover --- Directory.Packages.props | 2 +- src/Ryujinx/Assets/locales.json | 25 +++++++++++++++++++ .../Utilities/Compat/CompatibilityCsv.cs | 8 +++++- .../Utilities/Compat/CompatibilityList.axaml | 7 ++++-- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 203f40588..a480d3d29 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -42,7 +42,7 @@ - + diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 0951ad632..b90e16855 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -22597,6 +22597,31 @@ "zh_TW": "" } }, + { + "ID": "CompatibilityListLastUpdated", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Last updated: {0}", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "CompatibilityListWarning", "Translations": { diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs b/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs index e3d630b60..676cf1a52 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs +++ b/src/Ryujinx/Utilities/Compat/CompatibilityCsv.cs @@ -1,4 +1,5 @@ using Gommon; +using Humanizer; using nietras.SeparatedValues; using Ryujinx.Ava.Common.Locale; using Ryujinx.Common.Logging; @@ -83,6 +84,9 @@ namespace Ryujinx.Ava.Utilities.Compat public LocaleKeys? Status { get; } public DateTime LastUpdated { get; } + public string LocalizedLastUpdated => + LocaleManager.FormatDynamicValue(LocaleKeys.CompatibilityListLastUpdated, LastUpdated.Humanize()); + public string LocalizedStatus => LocaleManager.Instance[Status!.Value]; public string FormattedTitleId => TitleId .OrElse(new string(' ', 16)); @@ -97,7 +101,9 @@ namespace Ryujinx.Ava.Utilities.Compat StringBuilder sb = new("CompatibilityEntry: {"); sb.Append($"{nameof(GameName)}=\"{GameName}\", "); sb.Append($"{nameof(TitleId)}={TitleId}, "); - sb.Append($"{nameof(Labels)}=\"{Labels}\", "); + sb.Append($"{nameof(Labels)}={ + Labels.FormatCollection(it => $"\"{it}\"", separator: ", ", prefix: "[", suffix: "]") + }, "); sb.Append($"{nameof(Status)}=\"{Status}\", "); sb.Append($"{nameof(LastUpdated)}=\"{LastUpdated}\""); sb.Append('}'); diff --git a/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml b/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml index 8e14c3904..73ec84c53 100644 --- a/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml +++ b/src/Ryujinx/Utilities/Compat/CompatibilityList.axaml @@ -44,8 +44,11 @@ ItemsSource="{Binding CurrentEntries}"> - +